/[svn]/qsampler/trunk/src/qsamplerMainForm.cpp
ViewVC logotype

Annotation of /qsampler/trunk/src/qsamplerMainForm.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2978 - (hide annotations) (download)
Mon Aug 15 19:10:16 2016 UTC (7 years, 8 months ago) by capela
File size: 89139 byte(s)
* Fixed a race condition on creating sampler channels that ended
  in duplicate channel strips; also fixed channel auto-arrange.
1 capela 1464 // qsamplerMainForm.cpp
2     //
3     /****************************************************************************
4 capela 2978 Copyright (C) 2004-2016, rncbc aka Rui Nuno Capela. All rights reserved.
5 capela 2718 Copyright (C) 2007,2008,2015 Christian Schoenebeck
6 capela 1464
7     This program is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; either version 2
10     of the License, or (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write to the Free Software Foundation, Inc.,
19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20    
21     *****************************************************************************/
22    
23 capela 1513 #include "qsamplerAbout.h"
24 schoenebeck 1461 #include "qsamplerMainForm.h"
25    
26     #include "qsamplerOptions.h"
27     #include "qsamplerChannel.h"
28     #include "qsamplerMessages.h"
29    
30     #include "qsamplerChannelStrip.h"
31     #include "qsamplerInstrumentList.h"
32    
33     #include "qsamplerInstrumentListForm.h"
34     #include "qsamplerDeviceForm.h"
35     #include "qsamplerOptionsForm.h"
36 schoenebeck 1698 #include "qsamplerDeviceStatusForm.h"
37 schoenebeck 1461
38 capela 2387 #include <QMdiArea>
39     #include <QMdiSubWindow>
40    
41 capela 1499 #include <QApplication>
42     #include <QProcess>
43     #include <QMessageBox>
44    
45     #include <QRegExp>
46     #include <QTextStream>
47     #include <QFileDialog>
48     #include <QFileInfo>
49     #include <QFile>
50     #include <QUrl>
51    
52     #include <QDragEnterEvent>
53    
54     #include <QStatusBar>
55     #include <QSpinBox>
56     #include <QSlider>
57     #include <QLabel>
58     #include <QTimer>
59     #include <QDateTime>
60    
61 capela 2387 #if QT_VERSION >= 0x050000
62     #include <QMimeData>
63     #endif
64    
65 capela 2038 #if QT_VERSION < 0x040500
66     namespace Qt {
67     const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000);
68     }
69     #endif
70 capela 1499
71 schoenebeck 1461 #ifdef HAVE_SIGNAL_H
72     #include <signal.h>
73     #endif
74    
75     #ifdef CONFIG_LIBGIG
76     #include <gig.h>
77     #endif
78    
79     // Needed for lroundf()
80     #include <math.h>
81    
82     #ifndef CONFIG_ROUND
83     static inline long lroundf ( float x )
84     {
85     if (x >= 0.0f)
86     return long(x + 0.5f);
87     else
88     return long(x - 0.5f);
89     }
90     #endif
91    
92 capela 1558
93     // All winsock apps needs this.
94     #if defined(WIN32)
95     static WSADATA _wsaData;
96     #endif
97    
98    
99 capela 2112 //-------------------------------------------------------------------------
100     // LADISH Level 1 support stuff.
101    
102 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
103 capela 2112
104     #include <QSocketNotifier>
105    
106     #include <sys/types.h>
107     #include <sys/socket.h>
108    
109     #include <signal.h>
110    
111     // File descriptor for SIGUSR1 notifier.
112     static int g_fdUsr1[2];
113    
114     // Unix SIGUSR1 signal handler.
115     static void qsampler_sigusr1_handler ( int /* signo */ )
116     {
117     char c = 1;
118    
119     (::write(g_fdUsr1[0], &c, sizeof(c)) > 0);
120     }
121    
122     #endif // HAVE_SIGNAL_H
123    
124    
125     //-------------------------------------------------------------------------
126     // qsampler -- namespace
127    
128    
129 capela 1558 namespace QSampler {
130    
131 schoenebeck 1461 // Timer constant stuff.
132     #define QSAMPLER_TIMER_MSECS 200
133    
134     // Status bar item indexes
135     #define QSAMPLER_STATUS_CLIENT 0 // Client connection state.
136     #define QSAMPLER_STATUS_SERVER 1 // Currenr server address (host:port)
137     #define QSAMPLER_STATUS_CHANNEL 2 // Active channel caption.
138     #define QSAMPLER_STATUS_SESSION 3 // Current session modification state.
139    
140    
141 capela 2050 // Specialties for thread-callback comunication.
142     #define QSAMPLER_LSCP_EVENT QEvent::Type(QEvent::User + 1)
143    
144    
145 schoenebeck 1461 //-------------------------------------------------------------------------
146 capela 2050 // LscpEvent -- specialty for LSCP callback comunication.
147 schoenebeck 1461
148    
149 capela 2050 class LscpEvent : public QEvent
150 schoenebeck 1461 {
151     public:
152    
153 capela 1509 // Constructor.
154 capela 2050 LscpEvent(lscp_event_t event, const char *pchData, int cchData)
155     : QEvent(QSAMPLER_LSCP_EVENT)
156 capela 1509 {
157     m_event = event;
158     m_data = QString::fromUtf8(pchData, cchData);
159     }
160 schoenebeck 1461
161 capela 1509 // Accessors.
162     lscp_event_t event() { return m_event; }
163     QString& data() { return m_data; }
164 schoenebeck 1461
165     private:
166    
167 capela 1509 // The proper event type.
168     lscp_event_t m_event;
169     // The event data as a string.
170     QString m_data;
171 schoenebeck 1461 };
172    
173    
174     //-------------------------------------------------------------------------
175     // qsamplerMainForm -- Main window form implementation.
176    
177     // Kind of singleton reference.
178     MainForm* MainForm::g_pMainForm = NULL;
179    
180 capela 1509 MainForm::MainForm ( QWidget *pParent )
181     : QMainWindow(pParent)
182     {
183     m_ui.setupUi(this);
184 schoenebeck 1461
185     // Pseudo-singleton reference setup.
186     g_pMainForm = this;
187    
188 capela 1509 // Initialize some pointer references.
189     m_pOptions = NULL;
190 schoenebeck 1461
191 capela 1509 // All child forms are to be created later, not earlier than setup.
192     m_pMessages = NULL;
193     m_pInstrumentListForm = NULL;
194     m_pDeviceForm = NULL;
195 schoenebeck 1461
196 capela 1509 // We'll start clean.
197     m_iUntitled = 0;
198 capela 2978 m_iDirtySetup = 0;
199 capela 1509 m_iDirtyCount = 0;
200 schoenebeck 1461
201 capela 1509 m_pServer = NULL;
202     m_pClient = NULL;
203 schoenebeck 1461
204 capela 1509 m_iStartDelay = 0;
205     m_iTimerDelay = 0;
206 schoenebeck 1461
207 capela 1509 m_iTimerSlot = 0;
208 schoenebeck 1461
209 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
210 capela 2112
211 schoenebeck 1461 // Set to ignore any fatal "Broken pipe" signals.
212     ::signal(SIGPIPE, SIG_IGN);
213 capela 2112
214 capela 2050 // LADISH Level 1 suport.
215 schoenebeck 1461
216 capela 2112 // Initialize file descriptors for SIGUSR1 socket notifier.
217     ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdUsr1);
218     m_pUsr1Notifier
219     = new QSocketNotifier(g_fdUsr1[1], QSocketNotifier::Read, this);
220    
221     QObject::connect(m_pUsr1Notifier,
222     SIGNAL(activated(int)),
223     SLOT(handle_sigusr1()));
224    
225     // Install SIGUSR1 signal handler.
226     struct sigaction usr1;
227     usr1.sa_handler = qsampler_sigusr1_handler;
228 persson 2210 sigemptyset(&usr1.sa_mask);
229 capela 2112 usr1.sa_flags = 0;
230     usr1.sa_flags |= SA_RESTART;
231     ::sigaction(SIGUSR1, &usr1, NULL);
232    
233     #else // HAVE_SIGNAL_H
234    
235 capela 2113 m_pUsr1Notifier = NULL;
236 capela 2112
237     #endif // !HAVE_SIGNAL_H
238    
239 schoenebeck 1461 #ifdef CONFIG_VOLUME
240 capela 1509 // Make some extras into the toolbar...
241 schoenebeck 1461 const QString& sVolumeText = tr("Master volume");
242     m_iVolumeChanging = 0;
243     // Volume slider...
244 capela 1509 m_ui.channelsToolbar->addSeparator();
245     m_pVolumeSlider = new QSlider(Qt::Horizontal, m_ui.channelsToolbar);
246 capela 1515 m_pVolumeSlider->setTickPosition(QSlider::TicksBothSides);
247 schoenebeck 1461 m_pVolumeSlider->setTickInterval(10);
248     m_pVolumeSlider->setPageStep(10);
249 capela 1499 m_pVolumeSlider->setSingleStep(10);
250     m_pVolumeSlider->setMinimum(0);
251     m_pVolumeSlider->setMaximum(100);
252 capela 1466 m_pVolumeSlider->setMaximumHeight(26);
253 schoenebeck 1461 m_pVolumeSlider->setMinimumWidth(160);
254 capela 1499 m_pVolumeSlider->setToolTip(sVolumeText);
255 schoenebeck 1461 QObject::connect(m_pVolumeSlider,
256     SIGNAL(valueChanged(int)),
257     SLOT(volumeChanged(int)));
258 capela 1509 m_ui.channelsToolbar->addWidget(m_pVolumeSlider);
259 schoenebeck 1461 // Volume spin-box
260 capela 1509 m_ui.channelsToolbar->addSeparator();
261     m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar);
262 schoenebeck 1461 m_pVolumeSpinBox->setSuffix(" %");
263 capela 1499 m_pVolumeSpinBox->setMinimum(0);
264     m_pVolumeSpinBox->setMaximum(100);
265     m_pVolumeSpinBox->setToolTip(sVolumeText);
266 schoenebeck 1461 QObject::connect(m_pVolumeSpinBox,
267     SIGNAL(valueChanged(int)),
268     SLOT(volumeChanged(int)));
269 capela 1509 m_ui.channelsToolbar->addWidget(m_pVolumeSpinBox);
270 schoenebeck 1461 #endif
271    
272 capela 1509 // Make it an MDI workspace.
273 capela 2387 m_pWorkspace = new QMdiArea(this);
274 capela 2441 m_pWorkspace->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
275     m_pWorkspace->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
276 schoenebeck 1461 // Set the activation connection.
277     QObject::connect(m_pWorkspace,
278 capela 2387 SIGNAL(subWindowActivated(QMdiSubWindow *)),
279     SLOT(activateStrip(QMdiSubWindow *)));
280 capela 1509 // Make it shine :-)
281     setCentralWidget(m_pWorkspace);
282 schoenebeck 1461
283 capela 1509 // Create some statusbar labels...
284     QLabel *pLabel;
285     // Client status.
286     pLabel = new QLabel(tr("Connected"), this);
287     pLabel->setAlignment(Qt::AlignLeft);
288     pLabel->setMinimumSize(pLabel->sizeHint());
289     m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
290     statusBar()->addWidget(pLabel);
291     // Server address.
292     pLabel = new QLabel(this);
293     pLabel->setAlignment(Qt::AlignLeft);
294     m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
295     statusBar()->addWidget(pLabel, 1);
296     // Channel title.
297     pLabel = new QLabel(this);
298     pLabel->setAlignment(Qt::AlignLeft);
299     m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
300     statusBar()->addWidget(pLabel, 2);
301     // Session modification status.
302     pLabel = new QLabel(tr("MOD"), this);
303     pLabel->setAlignment(Qt::AlignHCenter);
304     pLabel->setMinimumSize(pLabel->sizeHint());
305     m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
306     statusBar()->addWidget(pLabel);
307 schoenebeck 1461
308     #if defined(WIN32)
309 capela 1509 WSAStartup(MAKEWORD(1, 1), &_wsaData);
310 schoenebeck 1461 #endif
311 capela 1466
312 capela 1512 // Some actions surely need those
313     // shortcuts firmly attached...
314     addAction(m_ui.viewMenubarAction);
315     addAction(m_ui.viewToolbarAction);
316    
317 capela 1509 QObject::connect(m_ui.fileNewAction,
318 capela 1499 SIGNAL(triggered()),
319 capela 1466 SLOT(fileNew()));
320 capela 1509 QObject::connect(m_ui.fileOpenAction,
321 capela 1499 SIGNAL(triggered()),
322 capela 1466 SLOT(fileOpen()));
323 capela 1509 QObject::connect(m_ui.fileSaveAction,
324 capela 1499 SIGNAL(triggered()),
325 capela 1466 SLOT(fileSave()));
326 capela 1509 QObject::connect(m_ui.fileSaveAsAction,
327 capela 1499 SIGNAL(triggered()),
328 capela 1466 SLOT(fileSaveAs()));
329 capela 1509 QObject::connect(m_ui.fileResetAction,
330 capela 1499 SIGNAL(triggered()),
331 capela 1466 SLOT(fileReset()));
332 capela 1509 QObject::connect(m_ui.fileRestartAction,
333 capela 1499 SIGNAL(triggered()),
334 capela 1466 SLOT(fileRestart()));
335 capela 1509 QObject::connect(m_ui.fileExitAction,
336 capela 1499 SIGNAL(triggered()),
337 capela 1466 SLOT(fileExit()));
338 capela 1509 QObject::connect(m_ui.editAddChannelAction,
339 capela 1499 SIGNAL(triggered()),
340 capela 1466 SLOT(editAddChannel()));
341 capela 1509 QObject::connect(m_ui.editRemoveChannelAction,
342 capela 1499 SIGNAL(triggered()),
343 capela 1466 SLOT(editRemoveChannel()));
344 capela 1509 QObject::connect(m_ui.editSetupChannelAction,
345 capela 1499 SIGNAL(triggered()),
346 capela 1466 SLOT(editSetupChannel()));
347 capela 1509 QObject::connect(m_ui.editEditChannelAction,
348 capela 1499 SIGNAL(triggered()),
349 capela 1466 SLOT(editEditChannel()));
350 capela 1509 QObject::connect(m_ui.editResetChannelAction,
351 capela 1499 SIGNAL(triggered()),
352 capela 1466 SLOT(editResetChannel()));
353 capela 1509 QObject::connect(m_ui.editResetAllChannelsAction,
354 capela 1499 SIGNAL(triggered()),
355 capela 1466 SLOT(editResetAllChannels()));
356 capela 1509 QObject::connect(m_ui.viewMenubarAction,
357 capela 1466 SIGNAL(toggled(bool)),
358     SLOT(viewMenubar(bool)));
359 capela 1509 QObject::connect(m_ui.viewToolbarAction,
360 capela 1466 SIGNAL(toggled(bool)),
361     SLOT(viewToolbar(bool)));
362 capela 1509 QObject::connect(m_ui.viewStatusbarAction,
363 capela 1466 SIGNAL(toggled(bool)),
364     SLOT(viewStatusbar(bool)));
365 capela 1509 QObject::connect(m_ui.viewMessagesAction,
366 capela 1466 SIGNAL(toggled(bool)),
367     SLOT(viewMessages(bool)));
368 capela 1509 QObject::connect(m_ui.viewInstrumentsAction,
369 capela 1499 SIGNAL(triggered()),
370 capela 1466 SLOT(viewInstruments()));
371 capela 1509 QObject::connect(m_ui.viewDevicesAction,
372 capela 1499 SIGNAL(triggered()),
373 capela 1466 SLOT(viewDevices()));
374 capela 1509 QObject::connect(m_ui.viewOptionsAction,
375 capela 1499 SIGNAL(triggered()),
376 capela 1466 SLOT(viewOptions()));
377 capela 1509 QObject::connect(m_ui.channelsArrangeAction,
378 capela 1499 SIGNAL(triggered()),
379 capela 1466 SLOT(channelsArrange()));
380 capela 1509 QObject::connect(m_ui.channelsAutoArrangeAction,
381 capela 1466 SIGNAL(toggled(bool)),
382     SLOT(channelsAutoArrange(bool)));
383 capela 1509 QObject::connect(m_ui.helpAboutAction,
384 capela 1499 SIGNAL(triggered()),
385 capela 1466 SLOT(helpAbout()));
386 capela 1509 QObject::connect(m_ui.helpAboutQtAction,
387 capela 1499 SIGNAL(triggered()),
388 capela 1466 SLOT(helpAboutQt()));
389 capela 1499
390 capela 1509 QObject::connect(m_ui.fileMenu,
391 capela 1499 SIGNAL(aboutToShow()),
392     SLOT(updateRecentFilesMenu()));
393 capela 1509 QObject::connect(m_ui.channelsMenu,
394 capela 1507 SIGNAL(aboutToShow()),
395     SLOT(channelsMenuAboutToShow()));
396 capela 2681 #ifdef CONFIG_VOLUME
397     QObject::connect(m_ui.channelsToolbar,
398     SIGNAL(orientationChanged(Qt::Orientation)),
399     SLOT(channelsToolbarOrientation(Qt::Orientation)));
400     #endif
401 schoenebeck 1461 }
402    
403     // Destructor.
404     MainForm::~MainForm()
405     {
406 capela 1509 // Do final processing anyway.
407     processServerExit();
408 schoenebeck 1461
409     #if defined(WIN32)
410 capela 1509 WSACleanup();
411 schoenebeck 1461 #endif
412    
413 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
414 capela 2112 if (m_pUsr1Notifier)
415     delete m_pUsr1Notifier;
416     #endif
417    
418 capela 1509 // Finally drop any widgets around...
419     if (m_pDeviceForm)
420     delete m_pDeviceForm;
421     if (m_pInstrumentListForm)
422     delete m_pInstrumentListForm;
423     if (m_pMessages)
424     delete m_pMessages;
425     if (m_pWorkspace)
426     delete m_pWorkspace;
427 schoenebeck 1461
428 capela 1509 // Delete status item labels one by one.
429     if (m_statusItem[QSAMPLER_STATUS_CLIENT])
430     delete m_statusItem[QSAMPLER_STATUS_CLIENT];
431     if (m_statusItem[QSAMPLER_STATUS_SERVER])
432     delete m_statusItem[QSAMPLER_STATUS_SERVER];
433     if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
434     delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
435     if (m_statusItem[QSAMPLER_STATUS_SESSION])
436     delete m_statusItem[QSAMPLER_STATUS_SESSION];
437 schoenebeck 1461
438     #ifdef CONFIG_VOLUME
439     delete m_pVolumeSpinBox;
440     delete m_pVolumeSlider;
441     #endif
442    
443     // Pseudo-singleton reference shut-down.
444     g_pMainForm = NULL;
445     }
446    
447    
448     // Make and set a proper setup options step.
449 capela 1558 void MainForm::setup ( Options *pOptions )
450 schoenebeck 1461 {
451 capela 1509 // We got options?
452     m_pOptions = pOptions;
453 schoenebeck 1461
454 capela 1509 // What style do we create these forms?
455 capela 1499 Qt::WindowFlags wflags = Qt::Window
456     | Qt::CustomizeWindowHint
457     | Qt::WindowTitleHint
458     | Qt::WindowSystemMenuHint
459 capela 2038 | Qt::WindowMinMaxButtonsHint
460     | Qt::WindowCloseButtonHint;
461 capela 1499 if (m_pOptions->bKeepOnTop)
462     wflags |= Qt::Tool;
463 capela 2038
464 capela 1509 // Some child forms are to be created right now.
465 capela 1558 m_pMessages = new Messages(this);
466 capela 1509 m_pDeviceForm = new DeviceForm(this, wflags);
467 schoenebeck 1461 #ifdef CONFIG_MIDI_INSTRUMENT
468 capela 1509 m_pInstrumentListForm = new InstrumentListForm(this, wflags);
469 schoenebeck 1461 #else
470 capela 2038 m_ui.viewInstrumentsAction->setEnabled(false);
471 schoenebeck 1461 #endif
472 capela 2038
473 capela 1739 // Setup messages logging appropriately...
474     m_pMessages->setLogging(
475     m_pOptions->bMessagesLog,
476     m_pOptions->sMessagesLogPath);
477 capela 2038
478 capela 1509 // Set message defaults...
479     updateMessagesFont();
480     updateMessagesLimit();
481     updateMessagesCapture();
482 capela 2978
483 capela 1509 // Set the visibility signal.
484 schoenebeck 1461 QObject::connect(m_pMessages,
485     SIGNAL(visibilityChanged(bool)),
486     SLOT(stabilizeForm()));
487    
488 capela 1509 // Initial decorations toggle state.
489     m_ui.viewMenubarAction->setChecked(m_pOptions->bMenubar);
490     m_ui.viewToolbarAction->setChecked(m_pOptions->bToolbar);
491     m_ui.viewStatusbarAction->setChecked(m_pOptions->bStatusbar);
492     m_ui.channelsAutoArrangeAction->setChecked(m_pOptions->bAutoArrange);
493 schoenebeck 1461
494 capela 1509 // Initial decorations visibility state.
495     viewMenubar(m_pOptions->bMenubar);
496     viewToolbar(m_pOptions->bToolbar);
497     viewStatusbar(m_pOptions->bStatusbar);
498 schoenebeck 1461
499 capela 1509 addDockWidget(Qt::BottomDockWidgetArea, m_pMessages);
500 schoenebeck 1461
501 capela 1499 // Restore whole dock windows state.
502     QByteArray aDockables = m_pOptions->settings().value(
503     "/Layout/DockWindows").toByteArray();
504     if (!aDockables.isEmpty()) {
505     restoreState(aDockables);
506     }
507    
508 capela 1509 // Try to restore old window positioning and initial visibility.
509 capela 2077 m_pOptions->loadWidgetGeometry(this, true);
510 capela 1509 m_pOptions->loadWidgetGeometry(m_pInstrumentListForm);
511     m_pOptions->loadWidgetGeometry(m_pDeviceForm);
512 schoenebeck 1461
513 capela 1509 // Final startup stabilization...
514     updateMaxVolume();
515     updateRecentFilesMenu();
516     stabilizeForm();
517 schoenebeck 1461
518 capela 1509 // Make it ready :-)
519     statusBar()->showMessage(tr("Ready"), 3000);
520 schoenebeck 1461
521 capela 1509 // We'll try to start immediately...
522     startSchedule(0);
523 schoenebeck 1461
524 capela 1509 // Register the first timer slot.
525     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
526 schoenebeck 1461 }
527    
528    
529     // Window close event handlers.
530     bool MainForm::queryClose (void)
531     {
532 capela 1509 bool bQueryClose = closeSession(false);
533 schoenebeck 1461
534 capela 1509 // Try to save current general state...
535     if (m_pOptions) {
536     // Some windows default fonts is here on demand too.
537     if (bQueryClose && m_pMessages)
538     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
539     // Try to save current positioning.
540     if (bQueryClose) {
541     // Save decorations state.
542     m_pOptions->bMenubar = m_ui.MenuBar->isVisible();
543     m_pOptions->bToolbar = (m_ui.fileToolbar->isVisible()
544     || m_ui.editToolbar->isVisible()
545     || m_ui.channelsToolbar->isVisible());
546     m_pOptions->bStatusbar = statusBar()->isVisible();
547     // Save the dock windows state.
548 capela 1499 m_pOptions->settings().setValue("/Layout/DockWindows", saveState());
549 capela 1509 // And the children, and the main windows state,.
550 schoenebeck 1461 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
551     m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
552 capela 2077 m_pOptions->saveWidgetGeometry(this, true);
553 schoenebeck 1461 // Close popup widgets.
554     if (m_pInstrumentListForm)
555     m_pInstrumentListForm->close();
556     if (m_pDeviceForm)
557     m_pDeviceForm->close();
558 capela 1509 // Stop client and/or server, gracefully.
559 schoenebeck 1626 stopServer(true /*interactive*/);
560 capela 1509 }
561     }
562 schoenebeck 1461
563 capela 1509 return bQueryClose;
564 schoenebeck 1461 }
565    
566    
567     void MainForm::closeEvent ( QCloseEvent *pCloseEvent )
568     {
569 schoenebeck 1699 if (queryClose()) {
570     DeviceStatusForm::deleteAllInstances();
571 capela 1509 pCloseEvent->accept();
572 schoenebeck 1699 } else
573 capela 1509 pCloseEvent->ignore();
574 schoenebeck 1461 }
575    
576    
577     // Window drag-n-drop event handlers.
578     void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
579     {
580 capela 1499 // Accept external drags only...
581     if (pDragEnterEvent->source() == NULL
582     && pDragEnterEvent->mimeData()->hasUrls()) {
583     pDragEnterEvent->accept();
584     } else {
585     pDragEnterEvent->ignore();
586     }
587 schoenebeck 1461 }
588    
589    
590 capela 2978 void MainForm::dropEvent ( QDropEvent *pDropEvent )
591 schoenebeck 1461 {
592 capela 1499 // Accept externally originated drops only...
593     if (pDropEvent->source())
594     return;
595 schoenebeck 1461
596 capela 1499 const QMimeData *pMimeData = pDropEvent->mimeData();
597     if (pMimeData->hasUrls()) {
598     QListIterator<QUrl> iter(pMimeData->urls());
599     while (iter.hasNext()) {
600     const QString& sPath = iter.next().toLocalFile();
601 capela 2108 // if (Channel::isDlsInstrumentFile(sPath)) {
602     if (QFileInfo(sPath).exists()) {
603 capela 1499 // Try to create a new channel from instrument file...
604 capela 1558 Channel *pChannel = new Channel();
605 capela 1499 if (pChannel == NULL)
606     return;
607     // Start setting the instrument filename...
608     pChannel->setInstrument(sPath, 0);
609     // Before we show it up, may be we'll
610     // better ask for some initial values?
611     if (!pChannel->channelSetup(this)) {
612     delete pChannel;
613     return;
614     }
615     // Finally, give it to a new channel strip...
616     if (!createChannelStrip(pChannel)) {
617     delete pChannel;
618     return;
619     }
620     // Make that an overall update.
621     m_iDirtyCount++;
622     stabilizeForm();
623     } // Otherwise, load an usual session file (LSCP script)...
624     else if (closeSession(true)) {
625     loadSessionFile(sPath);
626     break;
627 schoenebeck 1461 }
628     }
629     // Make it look responsive...:)
630 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
631 schoenebeck 1461 }
632     }
633    
634 capela 1499
635 schoenebeck 1461 // Custome event handler.
636 capela 2050 void MainForm::customEvent ( QEvent* pEvent )
637 schoenebeck 1461 {
638 capela 1509 // For the time being, just pump it to messages.
639 capela 2050 if (pEvent->type() == QSAMPLER_LSCP_EVENT) {
640     LscpEvent *pLscpEvent = static_cast<LscpEvent *> (pEvent);
641     switch (pLscpEvent->event()) {
642 schoenebeck 1702 case LSCP_EVENT_CHANNEL_COUNT:
643     updateAllChannelStrips(true);
644     break;
645 schoenebeck 1691 case LSCP_EVENT_CHANNEL_INFO: {
646 capela 2978 const int iChannelID = pLscpEvent->data().toInt();
647 schoenebeck 1691 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
648     if (pChannelStrip)
649     channelStripChanged(pChannelStrip);
650     break;
651     }
652 schoenebeck 1698 case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT:
653 schoenebeck 1699 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
654 schoenebeck 1698 DeviceStatusForm::onDevicesChanged();
655     updateViewMidiDeviceStatusMenu();
656 schoenebeck 1699 break;
657     case LSCP_EVENT_MIDI_INPUT_DEVICE_INFO: {
658     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
659 capela 2050 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
660 schoenebeck 1699 DeviceStatusForm::onDeviceChanged(iDeviceID);
661     break;
662     }
663 schoenebeck 1702 case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT:
664     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
665     break;
666     case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO:
667     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
668     break;
669 capela 2036 #if CONFIG_EVENT_CHANNEL_MIDI
670 schoenebeck 1691 case LSCP_EVENT_CHANNEL_MIDI: {
671 capela 2050 const int iChannelID = pLscpEvent->data().section(' ', 0, 0).toInt();
672 schoenebeck 1691 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
673     if (pChannelStrip)
674 capela 2036 pChannelStrip->midiActivityLedOn();
675 schoenebeck 1691 break;
676     }
677 capela 2036 #endif
678     #if CONFIG_EVENT_DEVICE_MIDI
679 schoenebeck 1698 case LSCP_EVENT_DEVICE_MIDI: {
680 capela 2050 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
681     const int iPortID = pLscpEvent->data().section(' ', 1, 1).toInt();
682 capela 2036 DeviceStatusForm *pDeviceStatusForm
683     = DeviceStatusForm::getInstance(iDeviceID);
684 schoenebeck 1698 if (pDeviceStatusForm)
685     pDeviceStatusForm->midiArrived(iPortID);
686     break;
687     }
688 capela 2036 #endif
689 schoenebeck 1691 default:
690 capela 2050 appendMessagesColor(tr("LSCP Event: %1 data: %2")
691     .arg(::lscp_event_to_text(pLscpEvent->event()))
692     .arg(pLscpEvent->data()), "#996699");
693 schoenebeck 1461 }
694 capela 2112 }
695     }
696    
697    
698 capela 2978 // Window resize event handler.
699     void MainForm::resizeEvent ( QResizeEvent * )
700     {
701     if (m_pOptions->bAutoArrange)
702     channelsArrange();
703     }
704    
705    
706 capela 2112 // LADISH Level 1 -- SIGUSR1 signal handler.
707     void MainForm::handle_sigusr1 (void)
708     {
709 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
710 capela 2113
711 capela 2112 char c;
712    
713     if (::read(g_fdUsr1[1], &c, sizeof(c)) > 0)
714 capela 2050 saveSession(false);
715 capela 2113
716     #endif
717 schoenebeck 1461 }
718    
719 capela 2038
720     void MainForm::updateViewMidiDeviceStatusMenu (void)
721     {
722 schoenebeck 1698 m_ui.viewMidiDeviceStatusMenu->clear();
723 capela 2038 const std::map<int, DeviceStatusForm *> statusForms
724     = DeviceStatusForm::getInstances();
725     std::map<int, DeviceStatusForm *>::const_iterator iter
726     = statusForms.begin();
727     for ( ; iter != statusForms.end(); ++iter) {
728     DeviceStatusForm *pStatusForm = iter->second;
729 schoenebeck 1698 m_ui.viewMidiDeviceStatusMenu->addAction(
730 capela 2038 pStatusForm->visibleAction());
731 schoenebeck 1698 }
732     }
733    
734 capela 2038
735 schoenebeck 1461 // Context menu event handler.
736     void MainForm::contextMenuEvent( QContextMenuEvent *pEvent )
737     {
738 capela 1509 stabilizeForm();
739 schoenebeck 1461
740 capela 1509 m_ui.editMenu->exec(pEvent->globalPos());
741 schoenebeck 1461 }
742    
743    
744     //-------------------------------------------------------------------------
745     // qsamplerMainForm -- Brainless public property accessors.
746    
747     // The global options settings property.
748 capela 1558 Options *MainForm::options (void) const
749 schoenebeck 1461 {
750 capela 1509 return m_pOptions;
751 schoenebeck 1461 }
752    
753    
754     // The LSCP client descriptor property.
755 capela 1509 lscp_client_t *MainForm::client (void) const
756 schoenebeck 1461 {
757 capela 1509 return m_pClient;
758 schoenebeck 1461 }
759    
760    
761     // The pseudo-singleton instance accessor.
762     MainForm *MainForm::getInstance (void)
763     {
764     return g_pMainForm;
765     }
766    
767    
768     //-------------------------------------------------------------------------
769     // qsamplerMainForm -- Session file stuff.
770    
771     // Format the displayable session filename.
772     QString MainForm::sessionName ( const QString& sFilename )
773     {
774 capela 2978 const bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
775 capela 1509 QString sSessionName = sFilename;
776     if (sSessionName.isEmpty())
777     sSessionName = tr("Untitled") + QString::number(m_iUntitled);
778     else if (!bCompletePath)
779     sSessionName = QFileInfo(sSessionName).fileName();
780     return sSessionName;
781 schoenebeck 1461 }
782    
783    
784     // Create a new session file from scratch.
785     bool MainForm::newSession (void)
786     {
787 capela 1509 // Check if we can do it.
788     if (!closeSession(true))
789     return false;
790 schoenebeck 1461
791     // Give us what the server has, right now...
792     updateSession();
793    
794 capela 1509 // Ok increment untitled count.
795     m_iUntitled++;
796 schoenebeck 1461
797 capela 1509 // Stabilize form.
798     m_sFilename = QString::null;
799     m_iDirtyCount = 0;
800     appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
801     stabilizeForm();
802 schoenebeck 1461
803 capela 1509 return true;
804 schoenebeck 1461 }
805    
806    
807     // Open an existing sampler session.
808     bool MainForm::openSession (void)
809     {
810 capela 1509 if (m_pOptions == NULL)
811     return false;
812 schoenebeck 1461
813 capela 1509 // Ask for the filename to open...
814 capela 1499 QString sFilename = QFileDialog::getOpenFileName(this,
815     QSAMPLER_TITLE ": " + tr("Open Session"), // Caption.
816     m_pOptions->sSessionDir, // Start here.
817     tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
818     );
819 schoenebeck 1461
820 capela 1509 // Have we cancelled?
821     if (sFilename.isEmpty())
822     return false;
823 schoenebeck 1461
824 capela 1509 // Check if we're going to discard safely the current one...
825     if (!closeSession(true))
826     return false;
827 schoenebeck 1461
828 capela 1509 // Load it right away.
829     return loadSessionFile(sFilename);
830 schoenebeck 1461 }
831    
832    
833     // Save current sampler session with another name.
834     bool MainForm::saveSession ( bool bPrompt )
835     {
836 capela 1509 if (m_pOptions == NULL)
837     return false;
838 schoenebeck 1461
839 capela 1509 QString sFilename = m_sFilename;
840 schoenebeck 1461
841 capela 1509 // Ask for the file to save, if there's none...
842     if (bPrompt || sFilename.isEmpty()) {
843     // If none is given, assume default directory.
844     if (sFilename.isEmpty())
845     sFilename = m_pOptions->sSessionDir;
846     // Prompt the guy...
847 capela 1499 sFilename = QFileDialog::getSaveFileName(this,
848     QSAMPLER_TITLE ": " + tr("Save Session"), // Caption.
849     sFilename, // Start here.
850     tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
851     );
852 capela 1509 // Have we cancelled it?
853     if (sFilename.isEmpty())
854     return false;
855     // Enforce .lscp extension...
856     if (QFileInfo(sFilename).suffix().isEmpty())
857     sFilename += ".lscp";
858     // Check if already exists...
859     if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
860     if (QMessageBox::warning(this,
861 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
862 capela 1509 tr("The file already exists:\n\n"
863     "\"%1\"\n\n"
864     "Do you want to replace it?")
865     .arg(sFilename),
866 capela 1840 QMessageBox::Yes | QMessageBox::No)
867     == QMessageBox::No)
868 capela 1509 return false;
869     }
870     }
871 schoenebeck 1461
872 capela 1509 // Save it right away.
873     return saveSessionFile(sFilename);
874 schoenebeck 1461 }
875    
876    
877     // Close current session.
878     bool MainForm::closeSession ( bool bForce )
879     {
880 capela 1509 bool bClose = true;
881 schoenebeck 1461
882 capela 1509 // Are we dirty enough to prompt it?
883     if (m_iDirtyCount > 0) {
884     switch (QMessageBox::warning(this,
885 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
886 capela 1509 tr("The current session has been changed:\n\n"
887     "\"%1\"\n\n"
888     "Do you want to save the changes?")
889     .arg(sessionName(m_sFilename)),
890 capela 1840 QMessageBox::Save |
891     QMessageBox::Discard |
892     QMessageBox::Cancel)) {
893     case QMessageBox::Save:
894 capela 1509 bClose = saveSession(false);
895     // Fall thru....
896 capela 1840 case QMessageBox::Discard:
897 capela 1509 break;
898     default: // Cancel.
899     bClose = false;
900     break;
901     }
902     }
903 schoenebeck 1461
904 capela 1509 // If we may close it, dot it.
905     if (bClose) {
906     // Remove all channel strips from sight...
907     m_pWorkspace->setUpdatesEnabled(false);
908 capela 2978 const QList<QMdiSubWindow *>& wlist
909     = m_pWorkspace->subWindowList();
910     const int iStripCount = wlist.count();
911     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
912 capela 2387 ChannelStrip *pChannelStrip = NULL;
913 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
914 capela 2387 if (pMdiSubWindow)
915     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
916 capela 1509 if (pChannelStrip) {
917 capela 1558 Channel *pChannel = pChannelStrip->channel();
918 capela 1509 if (bForce && pChannel)
919     pChannel->removeChannel();
920     delete pChannelStrip;
921     }
922 capela 2441 if (pMdiSubWindow)
923     delete pMdiSubWindow;
924 capela 1509 }
925     m_pWorkspace->setUpdatesEnabled(true);
926     // We're now clean, for sure.
927     m_iDirtyCount = 0;
928     }
929 schoenebeck 1461
930 capela 1509 return bClose;
931 schoenebeck 1461 }
932    
933    
934     // Load a session from specific file path.
935     bool MainForm::loadSessionFile ( const QString& sFilename )
936     {
937 capela 1509 if (m_pClient == NULL)
938     return false;
939 schoenebeck 1461
940 capela 1509 // Open and read from real file.
941     QFile file(sFilename);
942     if (!file.open(QIODevice::ReadOnly)) {
943     appendMessagesError(
944     tr("Could not open \"%1\" session file.\n\nSorry.")
945     .arg(sFilename));
946     return false;
947     }
948 schoenebeck 1461
949     // Tell the world we'll take some time...
950     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
951    
952 capela 1509 // Read the file.
953 schoenebeck 1461 int iLine = 0;
954 capela 1509 int iErrors = 0;
955     QTextStream ts(&file);
956     while (!ts.atEnd()) {
957     // Read the line.
958     QString sCommand = ts.readLine().trimmed();
959 schoenebeck 1461 iLine++;
960 capela 1509 // If not empty, nor a comment, call the server...
961     if (!sCommand.isEmpty() && sCommand[0] != '#') {
962 schoenebeck 1461 // Remember that, no matter what,
963     // all LSCP commands are CR/LF terminated.
964     sCommand += "\r\n";
965 capela 1499 if (::lscp_client_query(m_pClient, sCommand.toUtf8().constData())
966     != LSCP_OK) {
967 schoenebeck 1461 appendMessagesColor(QString("%1(%2): %3")
968     .arg(QFileInfo(sFilename).fileName()).arg(iLine)
969 capela 1499 .arg(sCommand.simplified()), "#996633");
970 schoenebeck 1461 appendMessagesClient("lscp_client_query");
971     iErrors++;
972     }
973 capela 1509 }
974     // Try to make it snappy :)
975     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
976     }
977 schoenebeck 1461
978 capela 1509 // Ok. we've read it.
979     file.close();
980 schoenebeck 1461
981     // Now we'll try to create (update) the whole GUI session.
982     updateSession();
983    
984     // We're fornerly done.
985     QApplication::restoreOverrideCursor();
986    
987     // Have we any errors?
988 capela 1509 if (iErrors > 0) {
989     appendMessagesError(
990     tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.")
991     .arg(sFilename));
992     }
993 schoenebeck 1461
994 capela 1509 // Save as default session directory.
995     if (m_pOptions)
996     m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
997 schoenebeck 1461 // We're not dirty anymore, if loaded without errors,
998     m_iDirtyCount = iErrors;
999 capela 1509 // Stabilize form...
1000     m_sFilename = sFilename;
1001     updateRecentFiles(sFilename);
1002     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
1003 schoenebeck 1461
1004 capela 1509 // Make that an overall update.
1005     stabilizeForm();
1006     return true;
1007 schoenebeck 1461 }
1008    
1009    
1010     // Save current session to specific file path.
1011     bool MainForm::saveSessionFile ( const QString& sFilename )
1012     {
1013     if (m_pClient == NULL)
1014     return false;
1015    
1016     // Check whether server is apparently OK...
1017     if (::lscp_get_channels(m_pClient) < 0) {
1018     appendMessagesClient("lscp_get_channels");
1019     return false;
1020     }
1021    
1022 capela 1509 // Open and write into real file.
1023     QFile file(sFilename);
1024     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
1025     appendMessagesError(
1026     tr("Could not open \"%1\" session file.\n\nSorry.")
1027     .arg(sFilename));
1028     return false;
1029     }
1030 schoenebeck 1461
1031     // Tell the world we'll take some time...
1032     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1033    
1034 capela 1509 // Write the file.
1035 capela 2978 int iErrors = 0;
1036 capela 1509 QTextStream ts(&file);
1037     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
1038     ts << "# " << tr("Version")
1039     << ": " QSAMPLER_VERSION << endl;
1040     ts << "# " << tr("Build")
1041     << ": " __DATE__ " " __TIME__ << endl;
1042     ts << "#" << endl;
1043     ts << "# " << tr("File")
1044     << ": " << QFileInfo(sFilename).fileName() << endl;
1045     ts << "# " << tr("Date")
1046     << ": " << QDate::currentDate().toString("MMM dd yyyy")
1047     << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
1048     ts << "#" << endl;
1049     ts << endl;
1050 schoenebeck 1461
1051     // It is assumed that this new kind of device+session file
1052     // will be loaded from a complete initialized server...
1053     int *piDeviceIDs;
1054     int iDevice;
1055     ts << "RESET" << endl;
1056    
1057     // Audio device mapping.
1058     QMap<int, int> audioDeviceMap;
1059 capela 1558 piDeviceIDs = Device::getDevices(m_pClient, Device::Audio);
1060 schoenebeck 1461 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
1061     ts << endl;
1062 capela 1558 Device device(Device::Audio, piDeviceIDs[iDevice]);
1063 schoenebeck 1461 // Audio device specification...
1064     ts << "# " << device.deviceTypeName() << " " << device.driverName()
1065     << " " << tr("Device") << " " << iDevice << endl;
1066     ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
1067 capela 1558 DeviceParamMap::ConstIterator deviceParam;
1068 schoenebeck 1461 for (deviceParam = device.params().begin();
1069     deviceParam != device.params().end();
1070     ++deviceParam) {
1071 capela 1558 const DeviceParam& param = deviceParam.value();
1072 schoenebeck 1461 if (param.value.isEmpty()) ts << "# ";
1073     ts << " " << deviceParam.key() << "='" << param.value << "'";
1074     }
1075     ts << endl;
1076     // Audio channel parameters...
1077     int iPort = 0;
1078 capela 1558 QListIterator<DevicePort *> iter(device.ports());
1079 capela 1499 while (iter.hasNext()) {
1080 capela 1558 DevicePort *pPort = iter.next();
1081     DeviceParamMap::ConstIterator portParam;
1082 schoenebeck 1461 for (portParam = pPort->params().begin();
1083     portParam != pPort->params().end();
1084     ++portParam) {
1085 capela 1558 const DeviceParam& param = portParam.value();
1086 schoenebeck 1461 if (param.fix || param.value.isEmpty()) ts << "# ";
1087     ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
1088     << " " << iPort << " " << portParam.key()
1089     << "='" << param.value << "'" << endl;
1090     }
1091 capela 1499 iPort++;
1092 schoenebeck 1461 }
1093     // Audio device index/id mapping.
1094     audioDeviceMap[device.deviceID()] = iDevice;
1095     // Try to keep it snappy :)
1096 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1097 schoenebeck 1461 }
1098    
1099     // MIDI device mapping.
1100     QMap<int, int> midiDeviceMap;
1101 capela 1558 piDeviceIDs = Device::getDevices(m_pClient, Device::Midi);
1102 schoenebeck 1461 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
1103     ts << endl;
1104 capela 1558 Device device(Device::Midi, piDeviceIDs[iDevice]);
1105 schoenebeck 1461 // MIDI device specification...
1106     ts << "# " << device.deviceTypeName() << " " << device.driverName()
1107     << " " << tr("Device") << " " << iDevice << endl;
1108     ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
1109 capela 1558 DeviceParamMap::ConstIterator deviceParam;
1110 schoenebeck 1461 for (deviceParam = device.params().begin();
1111     deviceParam != device.params().end();
1112     ++deviceParam) {
1113 capela 1558 const DeviceParam& param = deviceParam.value();
1114 schoenebeck 1461 if (param.value.isEmpty()) ts << "# ";
1115     ts << " " << deviceParam.key() << "='" << param.value << "'";
1116     }
1117     ts << endl;
1118     // MIDI port parameters...
1119     int iPort = 0;
1120 capela 1558 QListIterator<DevicePort *> iter(device.ports());
1121 capela 1499 while (iter.hasNext()) {
1122 capela 1558 DevicePort *pPort = iter.next();
1123     DeviceParamMap::ConstIterator portParam;
1124 schoenebeck 1461 for (portParam = pPort->params().begin();
1125     portParam != pPort->params().end();
1126     ++portParam) {
1127 capela 1558 const DeviceParam& param = portParam.value();
1128 schoenebeck 1461 if (param.fix || param.value.isEmpty()) ts << "# ";
1129     ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
1130 capela 1509 << " " << iPort << " " << portParam.key()
1131     << "='" << param.value << "'" << endl;
1132 schoenebeck 1461 }
1133 capela 1499 iPort++;
1134 schoenebeck 1461 }
1135     // MIDI device index/id mapping.
1136     midiDeviceMap[device.deviceID()] = iDevice;
1137     // Try to keep it snappy :)
1138 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1139 schoenebeck 1461 }
1140     ts << endl;
1141    
1142     #ifdef CONFIG_MIDI_INSTRUMENT
1143     // MIDI instrument mapping...
1144     QMap<int, int> midiInstrumentMap;
1145     int *piMaps = ::lscp_list_midi_instrument_maps(m_pClient);
1146     for (int iMap = 0; piMaps && piMaps[iMap] >= 0; iMap++) {
1147 capela 2978 const int iMidiMap = piMaps[iMap];
1148 schoenebeck 1461 const char *pszMapName
1149     = ::lscp_get_midi_instrument_map_name(m_pClient, iMidiMap);
1150     ts << "# " << tr("MIDI instrument map") << " " << iMap;
1151     if (pszMapName)
1152     ts << " - " << pszMapName;
1153     ts << endl;
1154     ts << "ADD MIDI_INSTRUMENT_MAP";
1155     if (pszMapName)
1156     ts << " '" << pszMapName << "'";
1157     ts << endl;
1158     // MIDI instrument mapping...
1159     lscp_midi_instrument_t *pInstrs
1160     = ::lscp_list_midi_instruments(m_pClient, iMidiMap);
1161     for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; iInstr++) {
1162     lscp_midi_instrument_info_t *pInstrInfo
1163     = ::lscp_get_midi_instrument_info(m_pClient, &pInstrs[iInstr]);
1164     if (pInstrInfo) {
1165     ts << "MAP MIDI_INSTRUMENT "
1166     << iMap << " "
1167     << pInstrs[iInstr].bank << " "
1168     << pInstrs[iInstr].prog << " "
1169     << pInstrInfo->engine_name << " '"
1170     << pInstrInfo->instrument_file << "' "
1171     << pInstrInfo->instrument_nr << " "
1172     << pInstrInfo->volume << " ";
1173     switch (pInstrInfo->load_mode) {
1174     case LSCP_LOAD_PERSISTENT:
1175     ts << "PERSISTENT";
1176     break;
1177     case LSCP_LOAD_ON_DEMAND_HOLD:
1178     ts << "ON_DEMAND_HOLD";
1179     break;
1180     case LSCP_LOAD_ON_DEMAND:
1181     case LSCP_LOAD_DEFAULT:
1182     default:
1183     ts << "ON_DEMAND";
1184     break;
1185     }
1186     if (pInstrInfo->name)
1187     ts << " '" << pInstrInfo->name << "'";
1188     ts << endl;
1189     } // Check for errors...
1190     else if (::lscp_client_get_errno(m_pClient)) {
1191     appendMessagesClient("lscp_get_midi_instrument_info");
1192     iErrors++;
1193     }
1194     // Try to keep it snappy :)
1195 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1196 schoenebeck 1461 }
1197     ts << endl;
1198     // Check for errors...
1199     if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) {
1200     appendMessagesClient("lscp_list_midi_instruments");
1201     iErrors++;
1202     }
1203     // MIDI strument index/id mapping.
1204     midiInstrumentMap[iMidiMap] = iMap;
1205     }
1206     // Check for errors...
1207     if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) {
1208     appendMessagesClient("lscp_list_midi_instrument_maps");
1209     iErrors++;
1210     }
1211     #endif // CONFIG_MIDI_INSTRUMENT
1212    
1213     // Sampler channel mapping.
1214 capela 2978 const QList<QMdiSubWindow *>& wlist
1215     = m_pWorkspace->subWindowList();
1216     const int iStripCount = wlist.count();
1217     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1218 capela 2387 ChannelStrip *pChannelStrip = NULL;
1219 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1220 capela 2387 if (pMdiSubWindow)
1221     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1222 capela 1509 if (pChannelStrip) {
1223 capela 1558 Channel *pChannel = pChannelStrip->channel();
1224 capela 1509 if (pChannel) {
1225 capela 2978 const int iChannelID = pChannel->channelID();
1226     ts << "# " << tr("Channel") << " " << iChannelID << endl;
1227 capela 1509 ts << "ADD CHANNEL" << endl;
1228 schoenebeck 1461 if (audioDeviceMap.isEmpty()) {
1229 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID
1230 schoenebeck 1461 << " " << pChannel->audioDriver() << endl;
1231     } else {
1232 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannelID
1233 schoenebeck 1461 << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
1234     }
1235     if (midiDeviceMap.isEmpty()) {
1236 capela 2978 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID
1237 schoenebeck 1461 << " " << pChannel->midiDriver() << endl;
1238     } else {
1239 capela 2978 ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannelID
1240 schoenebeck 1461 << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
1241     }
1242 capela 2978 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID
1243 schoenebeck 1461 << " " << pChannel->midiPort() << endl;
1244 capela 2978 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " ";
1245 capela 1509 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
1246     ts << "ALL";
1247     else
1248     ts << pChannel->midiChannel();
1249     ts << endl;
1250     ts << "LOAD ENGINE " << pChannel->engineName()
1251 capela 2978 << " " << iChannelID << endl;
1252 schoenebeck 1461 if (pChannel->instrumentStatus() < 100) ts << "# ";
1253 capela 1509 ts << "LOAD INSTRUMENT NON_MODAL '"
1254     << pChannel->instrumentFile() << "' "
1255 capela 2978 << pChannel->instrumentNr() << " " << iChannelID << endl;
1256 capela 1558 ChannelRoutingMap::ConstIterator audioRoute;
1257 schoenebeck 1461 for (audioRoute = pChannel->audioRouting().begin();
1258     audioRoute != pChannel->audioRouting().end();
1259     ++audioRoute) {
1260 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannelID
1261 schoenebeck 1461 << " " << audioRoute.key()
1262 capela 1499 << " " << audioRoute.value() << endl;
1263 schoenebeck 1461 }
1264 capela 2978 ts << "SET CHANNEL VOLUME " << iChannelID
1265 schoenebeck 1461 << " " << pChannel->volume() << endl;
1266     if (pChannel->channelMute())
1267 capela 2978 ts << "SET CHANNEL MUTE " << iChannelID << " 1" << endl;
1268 schoenebeck 1461 if (pChannel->channelSolo())
1269 capela 2978 ts << "SET CHANNEL SOLO " << iChannelID << " 1" << endl;
1270 capela 2441 #ifdef CONFIG_MIDI_INSTRUMENT
1271 schoenebeck 1461 if (pChannel->midiMap() >= 0) {
1272 capela 2978 ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannelID
1273 schoenebeck 1461 << " " << midiInstrumentMap[pChannel->midiMap()] << endl;
1274     }
1275 capela 2441 #endif
1276     #ifdef CONFIG_FXSEND
1277 schoenebeck 1461 int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID);
1278     for (int iFxSend = 0;
1279     piFxSends && piFxSends[iFxSend] >= 0;
1280     iFxSend++) {
1281     lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info(
1282     m_pClient, iChannelID, piFxSends[iFxSend]);
1283     if (pFxSendInfo) {
1284 capela 2978 ts << "CREATE FX_SEND " << iChannelID
1285 schoenebeck 1461 << " " << pFxSendInfo->midi_controller;
1286     if (pFxSendInfo->name)
1287     ts << " '" << pFxSendInfo->name << "'";
1288     ts << endl;
1289     int *piRouting = pFxSendInfo->audio_routing;
1290     for (int iAudioSrc = 0;
1291     piRouting && piRouting[iAudioSrc] >= 0;
1292     iAudioSrc++) {
1293     ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL "
1294 capela 2978 << iChannelID
1295 schoenebeck 1461 << " " << iFxSend
1296     << " " << iAudioSrc
1297     << " " << piRouting[iAudioSrc] << endl;
1298     }
1299 capela 2441 #ifdef CONFIG_FXSEND_LEVEL
1300 capela 2978 ts << "SET FX_SEND LEVEL " << iChannelID
1301 schoenebeck 1461 << " " << iFxSend
1302     << " " << pFxSendInfo->level << endl;
1303 capela 2441 #endif
1304 schoenebeck 1461 } // Check for errors...
1305     else if (::lscp_client_get_errno(m_pClient)) {
1306     appendMessagesClient("lscp_get_fxsend_info");
1307     iErrors++;
1308     }
1309     }
1310 capela 2441 #endif
1311 capela 1509 ts << endl;
1312     }
1313     }
1314     // Try to keep it snappy :)
1315     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1316     }
1317 schoenebeck 1461
1318     #ifdef CONFIG_VOLUME
1319     ts << "# " << tr("Global volume level") << endl;
1320     ts << "SET VOLUME " << ::lscp_get_volume(m_pClient) << endl;
1321     ts << endl;
1322     #endif
1323    
1324 capela 1509 // Ok. we've wrote it.
1325     file.close();
1326 schoenebeck 1461
1327     // We're fornerly done.
1328     QApplication::restoreOverrideCursor();
1329    
1330 capela 1509 // Have we any errors?
1331     if (iErrors > 0) {
1332     appendMessagesError(
1333     tr("Some settings could not be saved\n"
1334     "to \"%1\" session file.\n\nSorry.")
1335     .arg(sFilename));
1336     }
1337 schoenebeck 1461
1338 capela 1509 // Save as default session directory.
1339     if (m_pOptions)
1340     m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
1341     // We're not dirty anymore.
1342     m_iDirtyCount = 0;
1343     // Stabilize form...
1344     m_sFilename = sFilename;
1345     updateRecentFiles(sFilename);
1346     appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
1347     stabilizeForm();
1348     return true;
1349 schoenebeck 1461 }
1350    
1351    
1352     // Session change receiver slot.
1353     void MainForm::sessionDirty (void)
1354     {
1355 capela 1509 // Just mark the dirty form.
1356     m_iDirtyCount++;
1357     // and update the form status...
1358     stabilizeForm();
1359 schoenebeck 1461 }
1360    
1361    
1362     //-------------------------------------------------------------------------
1363     // qsamplerMainForm -- File Action slots.
1364    
1365     // Create a new sampler session.
1366     void MainForm::fileNew (void)
1367     {
1368 capela 1509 // Of course we'll start clean new.
1369     newSession();
1370 schoenebeck 1461 }
1371    
1372    
1373     // Open an existing sampler session.
1374     void MainForm::fileOpen (void)
1375     {
1376 capela 1509 // Open it right away.
1377     openSession();
1378 schoenebeck 1461 }
1379    
1380    
1381     // Open a recent file session.
1382 capela 1499 void MainForm::fileOpenRecent (void)
1383 schoenebeck 1461 {
1384 capela 1499 // Retrive filename index from action data...
1385     QAction *pAction = qobject_cast<QAction *> (sender());
1386     if (pAction && m_pOptions) {
1387 capela 2978 const int iIndex = pAction->data().toInt();
1388 capela 1499 if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) {
1389     QString sFilename = m_pOptions->recentFiles[iIndex];
1390     // Check if we can safely close the current session...
1391     if (!sFilename.isEmpty() && closeSession(true))
1392     loadSessionFile(sFilename);
1393     }
1394     }
1395 schoenebeck 1461 }
1396    
1397    
1398     // Save current sampler session.
1399     void MainForm::fileSave (void)
1400     {
1401 capela 1509 // Save it right away.
1402     saveSession(false);
1403 schoenebeck 1461 }
1404    
1405    
1406     // Save current sampler session with another name.
1407     void MainForm::fileSaveAs (void)
1408     {
1409 capela 1509 // Save it right away, maybe with another name.
1410     saveSession(true);
1411 schoenebeck 1461 }
1412    
1413    
1414     // Reset the sampler instance.
1415     void MainForm::fileReset (void)
1416     {
1417 capela 1509 if (m_pClient == NULL)
1418     return;
1419 schoenebeck 1461
1420 capela 1509 // Ask user whether he/she want's an internal sampler reset...
1421 capela 2722 if (m_pOptions && m_pOptions->bConfirmReset) {
1422     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1423     const QString& sText = tr(
1424     "Resetting the sampler instance will close\n"
1425     "all device and channel configurations.\n\n"
1426     "Please note that this operation may cause\n"
1427     "temporary MIDI and Audio disruption.\n\n"
1428     "Do you want to reset the sampler engine now?");
1429     #if 0
1430     if (QMessageBox::warning(this, sTitle, sText,
1431     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1432     return;
1433     #else
1434     QMessageBox mbox(this);
1435     mbox.setIcon(QMessageBox::Warning);
1436     mbox.setWindowTitle(sTitle);
1437     mbox.setText(sText);
1438     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1439     QCheckBox cbox(tr("Don't ask this again"));
1440     cbox.setChecked(false);
1441     cbox.blockSignals(true);
1442     mbox.addButton(&cbox, QMessageBox::ActionRole);
1443     if (mbox.exec() == QMessageBox::Cancel)
1444     return;
1445     if (cbox.isChecked())
1446     m_pOptions->bConfirmReset = false;
1447     #endif
1448     }
1449 schoenebeck 1461
1450     // Trye closing the current session, first...
1451     if (!closeSession(true))
1452     return;
1453    
1454 capela 1509 // Just do the reset, after closing down current session...
1455 schoenebeck 1461 // Do the actual sampler reset...
1456     if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1457     appendMessagesClient("lscp_reset_sampler");
1458     appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1459     return;
1460     }
1461    
1462 capela 1509 // Log this.
1463     appendMessages(tr("Sampler reset."));
1464 schoenebeck 1461
1465     // Make it a new session...
1466     newSession();
1467     }
1468    
1469    
1470     // Restart the client/server instance.
1471     void MainForm::fileRestart (void)
1472     {
1473 capela 1509 if (m_pOptions == NULL)
1474     return;
1475 schoenebeck 1461
1476 capela 1509 bool bRestart = true;
1477 schoenebeck 1461
1478 capela 1509 // Ask user whether he/she want's a complete restart...
1479     // (if we're currently up and running)
1480 capela 2722 if (m_pOptions && m_pOptions->bConfirmRestart) {
1481     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1482     const QString& sText = tr(
1483     "New settings will be effective after\n"
1484 capela 1509 "restarting the client/server connection.\n\n"
1485     "Please note that this operation may cause\n"
1486     "temporary MIDI and Audio disruption.\n\n"
1487 capela 2722 "Do you want to restart the connection now?");
1488     #if 0
1489     if (QMessageBox::warning(this, sTitle, sText,
1490     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1491     bRestart = false;
1492     #else
1493     QMessageBox mbox(this);
1494     mbox.setIcon(QMessageBox::Warning);
1495     mbox.setWindowTitle(sTitle);
1496     mbox.setText(sText);
1497     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1498     QCheckBox cbox(tr("Don't ask this again"));
1499     cbox.setChecked(false);
1500     cbox.blockSignals(true);
1501     mbox.addButton(&cbox, QMessageBox::ActionRole);
1502     if (mbox.exec() == QMessageBox::Cancel)
1503     bRestart = false;
1504     else
1505     if (cbox.isChecked())
1506     m_pOptions->bConfirmRestart = false;
1507     #endif
1508 capela 1509 }
1509 schoenebeck 1461
1510 capela 1509 // Are we still for it?
1511     if (bRestart && closeSession(true)) {
1512     // Stop server, it will force the client too.
1513     stopServer();
1514     // Reschedule a restart...
1515     startSchedule(m_pOptions->iStartDelay);
1516     }
1517 schoenebeck 1461 }
1518    
1519    
1520     // Exit application program.
1521     void MainForm::fileExit (void)
1522     {
1523 capela 1509 // Go for close the whole thing.
1524     close();
1525 schoenebeck 1461 }
1526    
1527    
1528     //-------------------------------------------------------------------------
1529     // qsamplerMainForm -- Edit Action slots.
1530    
1531     // Add a new sampler channel.
1532     void MainForm::editAddChannel (void)
1533     {
1534 capela 2978 ++m_iDirtySetup;
1535     addChannelStrip();
1536     --m_iDirtySetup;
1537     }
1538    
1539     void MainForm::addChannelStrip (void)
1540     {
1541 capela 1509 if (m_pClient == NULL)
1542     return;
1543 schoenebeck 1461
1544 capela 1509 // Just create the channel instance...
1545 capela 1558 Channel *pChannel = new Channel();
1546 capela 1509 if (pChannel == NULL)
1547     return;
1548 schoenebeck 1461
1549 capela 1509 // Before we show it up, may be we'll
1550     // better ask for some initial values?
1551     if (!pChannel->channelSetup(this)) {
1552     delete pChannel;
1553     return;
1554     }
1555 schoenebeck 1461
1556 capela 1509 // And give it to the strip...
1557     // (will own the channel instance, if successful).
1558     if (!createChannelStrip(pChannel)) {
1559     delete pChannel;
1560     return;
1561     }
1562 schoenebeck 1461
1563 capela 1515 // Do we auto-arrange?
1564     if (m_pOptions && m_pOptions->bAutoArrange)
1565     channelsArrange();
1566    
1567 capela 1509 // Make that an overall update.
1568     m_iDirtyCount++;
1569     stabilizeForm();
1570 schoenebeck 1461 }
1571    
1572    
1573     // Remove current sampler channel.
1574     void MainForm::editRemoveChannel (void)
1575     {
1576 capela 2978 ++m_iDirtySetup;
1577     removeChannelStrip();
1578     --m_iDirtySetup;
1579     }
1580    
1581     void MainForm::removeChannelStrip (void)
1582     {
1583 capela 1509 if (m_pClient == NULL)
1584     return;
1585 schoenebeck 1461
1586 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1587 capela 1509 if (pChannelStrip == NULL)
1588     return;
1589 schoenebeck 1461
1590 capela 1558 Channel *pChannel = pChannelStrip->channel();
1591 capela 1509 if (pChannel == NULL)
1592     return;
1593 schoenebeck 1461
1594 capela 1509 // Prompt user if he/she's sure about this...
1595     if (m_pOptions && m_pOptions->bConfirmRemove) {
1596 capela 2722 const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1597     const QString& sText = tr(
1598     "About to remove channel:\n\n"
1599 capela 1509 "%1\n\n"
1600     "Are you sure?")
1601 capela 2722 .arg(pChannelStrip->windowTitle());
1602     #if 0
1603     if (QMessageBox::warning(this, sTitle, sText,
1604     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1605 capela 1509 return;
1606 capela 2722 #else
1607     QMessageBox mbox(this);
1608     mbox.setIcon(QMessageBox::Warning);
1609     mbox.setWindowTitle(sTitle);
1610     mbox.setText(sText);
1611     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1612     QCheckBox cbox(tr("Don't ask this again"));
1613     cbox.setChecked(false);
1614     cbox.blockSignals(true);
1615     mbox.addButton(&cbox, QMessageBox::ActionRole);
1616     if (mbox.exec() == QMessageBox::Cancel)
1617     return;
1618     if (cbox.isChecked())
1619     m_pOptions->bConfirmRemove = false;
1620     #endif
1621 capela 1509 }
1622 schoenebeck 1461
1623 capela 1509 // Remove the existing sampler channel.
1624     if (!pChannel->removeChannel())
1625     return;
1626 schoenebeck 1461
1627 capela 2978 // Just delete the channel strip.
1628     destroyChannelStrip(pChannelStrip);
1629    
1630 capela 1509 // We'll be dirty, for sure...
1631     m_iDirtyCount++;
1632 capela 2978 stabilizeForm();
1633 schoenebeck 1461 }
1634    
1635    
1636     // Setup current sampler channel.
1637     void MainForm::editSetupChannel (void)
1638     {
1639 capela 1509 if (m_pClient == NULL)
1640     return;
1641 schoenebeck 1461
1642 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1643 capela 1509 if (pChannelStrip == NULL)
1644     return;
1645 schoenebeck 1461
1646 capela 1509 // Just invoque the channel strip procedure.
1647     pChannelStrip->channelSetup();
1648 schoenebeck 1461 }
1649    
1650    
1651     // Edit current sampler channel.
1652     void MainForm::editEditChannel (void)
1653     {
1654 capela 1509 if (m_pClient == NULL)
1655     return;
1656 schoenebeck 1461
1657 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1658 capela 1509 if (pChannelStrip == NULL)
1659     return;
1660 schoenebeck 1461
1661 capela 1509 // Just invoque the channel strip procedure.
1662     pChannelStrip->channelEdit();
1663 schoenebeck 1461 }
1664    
1665    
1666     // Reset current sampler channel.
1667     void MainForm::editResetChannel (void)
1668     {
1669 capela 1509 if (m_pClient == NULL)
1670     return;
1671 schoenebeck 1461
1672 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1673 capela 1509 if (pChannelStrip == NULL)
1674     return;
1675 schoenebeck 1461
1676 capela 1509 // Just invoque the channel strip procedure.
1677     pChannelStrip->channelReset();
1678 schoenebeck 1461 }
1679    
1680    
1681     // Reset all sampler channels.
1682     void MainForm::editResetAllChannels (void)
1683     {
1684     if (m_pClient == NULL)
1685     return;
1686    
1687     // Invoque the channel strip procedure,
1688     // for all channels out there...
1689     m_pWorkspace->setUpdatesEnabled(false);
1690 capela 2978 const QList<QMdiSubWindow *>& wlist
1691     = m_pWorkspace->subWindowList();
1692     const int iStripCount = wlist.count();
1693     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1694 capela 2387 ChannelStrip *pChannelStrip = NULL;
1695 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1696 capela 2387 if (pMdiSubWindow)
1697     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1698 schoenebeck 1461 if (pChannelStrip)
1699     pChannelStrip->channelReset();
1700     }
1701     m_pWorkspace->setUpdatesEnabled(true);
1702     }
1703    
1704    
1705     //-------------------------------------------------------------------------
1706     // qsamplerMainForm -- View Action slots.
1707    
1708     // Show/hide the main program window menubar.
1709     void MainForm::viewMenubar ( bool bOn )
1710     {
1711 capela 1509 if (bOn)
1712     m_ui.MenuBar->show();
1713     else
1714     m_ui.MenuBar->hide();
1715 schoenebeck 1461 }
1716    
1717    
1718     // Show/hide the main program window toolbar.
1719     void MainForm::viewToolbar ( bool bOn )
1720     {
1721 capela 1509 if (bOn) {
1722     m_ui.fileToolbar->show();
1723     m_ui.editToolbar->show();
1724     m_ui.channelsToolbar->show();
1725     } else {
1726     m_ui.fileToolbar->hide();
1727     m_ui.editToolbar->hide();
1728     m_ui.channelsToolbar->hide();
1729     }
1730 schoenebeck 1461 }
1731    
1732    
1733     // Show/hide the main program window statusbar.
1734     void MainForm::viewStatusbar ( bool bOn )
1735     {
1736 capela 1509 if (bOn)
1737     statusBar()->show();
1738     else
1739     statusBar()->hide();
1740 schoenebeck 1461 }
1741    
1742    
1743     // Show/hide the messages window logger.
1744     void MainForm::viewMessages ( bool bOn )
1745     {
1746 capela 1509 if (bOn)
1747     m_pMessages->show();
1748     else
1749     m_pMessages->hide();
1750 schoenebeck 1461 }
1751    
1752    
1753     // Show/hide the MIDI instrument list-view form.
1754     void MainForm::viewInstruments (void)
1755     {
1756     if (m_pOptions == NULL)
1757     return;
1758    
1759     if (m_pInstrumentListForm) {
1760     m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1761     if (m_pInstrumentListForm->isVisible()) {
1762     m_pInstrumentListForm->hide();
1763     } else {
1764     m_pInstrumentListForm->show();
1765     m_pInstrumentListForm->raise();
1766 capela 1499 m_pInstrumentListForm->activateWindow();
1767 schoenebeck 1461 }
1768     }
1769     }
1770    
1771    
1772     // Show/hide the device configurator form.
1773     void MainForm::viewDevices (void)
1774     {
1775     if (m_pOptions == NULL)
1776     return;
1777    
1778     if (m_pDeviceForm) {
1779     m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1780     if (m_pDeviceForm->isVisible()) {
1781     m_pDeviceForm->hide();
1782     } else {
1783     m_pDeviceForm->show();
1784     m_pDeviceForm->raise();
1785 capela 1499 m_pDeviceForm->activateWindow();
1786 schoenebeck 1461 }
1787     }
1788     }
1789    
1790    
1791     // Show options dialog.
1792     void MainForm::viewOptions (void)
1793     {
1794 capela 1509 if (m_pOptions == NULL)
1795     return;
1796 schoenebeck 1461
1797 capela 1509 OptionsForm* pOptionsForm = new OptionsForm(this);
1798     if (pOptionsForm) {
1799     // Check out some initial nullities(tm)...
1800 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1801 capela 1509 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1802     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1803     if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1804     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1805     // To track down deferred or immediate changes.
1806 capela 2978 const QString sOldServerHost = m_pOptions->sServerHost;
1807     const int iOldServerPort = m_pOptions->iServerPort;
1808     const int iOldServerTimeout = m_pOptions->iServerTimeout;
1809     const bool bOldServerStart = m_pOptions->bServerStart;
1810     const QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1811     const bool bOldMessagesLog = m_pOptions->bMessagesLog;
1812     const QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath;
1813     const QString sOldDisplayFont = m_pOptions->sDisplayFont;
1814     const bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1815     const int iOldMaxVolume = m_pOptions->iMaxVolume;
1816     const QString sOldMessagesFont = m_pOptions->sMessagesFont;
1817     const bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1818     const bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1819     const int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1820     const int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1821     const bool bOldCompletePath = m_pOptions->bCompletePath;
1822     const bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1823     const int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1824     const int iOldBaseFontSize = m_pOptions->iBaseFontSize;
1825 capela 1509 // Load the current setup settings.
1826     pOptionsForm->setup(m_pOptions);
1827     // Show the setup dialog...
1828     if (pOptionsForm->exec()) {
1829     // Warn if something will be only effective on next run.
1830     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1831     (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1832     ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1833 capela 1749 (!bOldKeepOnTop && m_pOptions->bKeepOnTop) ||
1834     (iOldBaseFontSize != m_pOptions->iBaseFontSize)) {
1835 capela 1509 QMessageBox::information(this,
1836 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Information"),
1837 capela 1509 tr("Some settings may be only effective\n"
1838 capela 1840 "next time you start this program."));
1839 capela 1509 updateMessagesCapture();
1840     }
1841     // Check wheather something immediate has changed.
1842 capela 1738 if (( bOldMessagesLog && !m_pOptions->bMessagesLog) ||
1843     (!bOldMessagesLog && m_pOptions->bMessagesLog) ||
1844     (sOldMessagesLogPath != m_pOptions->sMessagesLogPath))
1845     m_pMessages->setLogging(
1846     m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath);
1847 capela 1509 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1848     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1849     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1850     updateRecentFilesMenu();
1851     if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1852     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1853     updateInstrumentNames();
1854     if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1855     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1856     updateDisplayEffect();
1857     if (sOldDisplayFont != m_pOptions->sDisplayFont)
1858     updateDisplayFont();
1859     if (iOldMaxVolume != m_pOptions->iMaxVolume)
1860     updateMaxVolume();
1861     if (sOldMessagesFont != m_pOptions->sMessagesFont)
1862     updateMessagesFont();
1863     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1864     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1865     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1866     updateMessagesLimit();
1867     // And now the main thing, whether we'll do client/server recycling?
1868     if ((sOldServerHost != m_pOptions->sServerHost) ||
1869     (iOldServerPort != m_pOptions->iServerPort) ||
1870     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1871     ( bOldServerStart && !m_pOptions->bServerStart) ||
1872     (!bOldServerStart && m_pOptions->bServerStart) ||
1873     (sOldServerCmdLine != m_pOptions->sServerCmdLine
1874     && m_pOptions->bServerStart))
1875     fileRestart();
1876     }
1877     // Done.
1878     delete pOptionsForm;
1879     }
1880 schoenebeck 1461
1881 capela 1509 // This makes it.
1882     stabilizeForm();
1883 schoenebeck 1461 }
1884    
1885    
1886     //-------------------------------------------------------------------------
1887     // qsamplerMainForm -- Channels action slots.
1888    
1889     // Arrange channel strips.
1890     void MainForm::channelsArrange (void)
1891     {
1892 capela 1509 // Full width vertical tiling
1893 capela 2978 const QList<QMdiSubWindow *>& wlist
1894     = m_pWorkspace->subWindowList();
1895 capela 1509 if (wlist.isEmpty())
1896     return;
1897 schoenebeck 1461
1898 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
1899     int y = 0;
1900 capela 2978 const int iStripCount = wlist.count();
1901     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1902     QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1903     if (pMdiSubWindow) {
1904     pMdiSubWindow->adjustSize();
1905     int iWidth = m_pWorkspace->width();
1906     if (iWidth < pMdiSubWindow->width())
1907     iWidth = pMdiSubWindow->width();
1908     const int iHeight = pMdiSubWindow->frameGeometry().height();
1909     pMdiSubWindow->setGeometry(0, y, iWidth, iHeight);
1910 capela 2387 y += iHeight;
1911     }
1912 capela 1509 }
1913     m_pWorkspace->setUpdatesEnabled(true);
1914 schoenebeck 1461
1915 capela 1509 stabilizeForm();
1916 schoenebeck 1461 }
1917    
1918    
1919     // Auto-arrange channel strips.
1920     void MainForm::channelsAutoArrange ( bool bOn )
1921     {
1922 capela 1509 if (m_pOptions == NULL)
1923     return;
1924 schoenebeck 1461
1925 capela 1509 // Toggle the auto-arrange flag.
1926     m_pOptions->bAutoArrange = bOn;
1927 schoenebeck 1461
1928 capela 1509 // If on, update whole workspace...
1929     if (m_pOptions->bAutoArrange)
1930     channelsArrange();
1931 schoenebeck 1461 }
1932    
1933    
1934     //-------------------------------------------------------------------------
1935     // qsamplerMainForm -- Help Action slots.
1936    
1937     // Show information about the Qt toolkit.
1938     void MainForm::helpAboutQt (void)
1939     {
1940 capela 1509 QMessageBox::aboutQt(this);
1941 schoenebeck 1461 }
1942    
1943    
1944     // Show information about application program.
1945     void MainForm::helpAbout (void)
1946     {
1947 capela 1509 // Stuff the about box text...
1948     QString sText = "<p>\n";
1949     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1950     sText += "<br />\n";
1951     sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1952     sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1953 schoenebeck 1461 #ifdef CONFIG_DEBUG
1954 capela 1509 sText += "<small><font color=\"red\">";
1955     sText += tr("Debugging option enabled.");
1956     sText += "</font></small><br />";
1957 schoenebeck 1461 #endif
1958     #ifndef CONFIG_LIBGIG
1959 capela 1509 sText += "<small><font color=\"red\">";
1960     sText += tr("GIG (libgig) file support disabled.");
1961     sText += "</font></small><br />";
1962 schoenebeck 1461 #endif
1963     #ifndef CONFIG_INSTRUMENT_NAME
1964 capela 1509 sText += "<small><font color=\"red\">";
1965     sText += tr("LSCP (liblscp) instrument_name support disabled.");
1966     sText += "</font></small><br />";
1967 schoenebeck 1461 #endif
1968     #ifndef CONFIG_MUTE_SOLO
1969 capela 1509 sText += "<small><font color=\"red\">";
1970     sText += tr("Sampler channel Mute/Solo support disabled.");
1971     sText += "</font></small><br />";
1972 schoenebeck 1461 #endif
1973     #ifndef CONFIG_AUDIO_ROUTING
1974 capela 1509 sText += "<small><font color=\"red\">";
1975     sText += tr("LSCP (liblscp) audio_routing support disabled.");
1976     sText += "</font></small><br />";
1977 schoenebeck 1461 #endif
1978     #ifndef CONFIG_FXSEND
1979 capela 1509 sText += "<small><font color=\"red\">";
1980     sText += tr("Sampler channel Effect Sends support disabled.");
1981     sText += "</font></small><br />";
1982 schoenebeck 1461 #endif
1983     #ifndef CONFIG_VOLUME
1984 capela 1509 sText += "<small><font color=\"red\">";
1985     sText += tr("Global volume support disabled.");
1986     sText += "</font></small><br />";
1987 schoenebeck 1461 #endif
1988     #ifndef CONFIG_MIDI_INSTRUMENT
1989 capela 1509 sText += "<small><font color=\"red\">";
1990     sText += tr("MIDI instrument mapping support disabled.");
1991     sText += "</font></small><br />";
1992 schoenebeck 1461 #endif
1993     #ifndef CONFIG_EDIT_INSTRUMENT
1994 capela 1509 sText += "<small><font color=\"red\">";
1995     sText += tr("Instrument editing support disabled.");
1996     sText += "</font></small><br />";
1997 schoenebeck 1461 #endif
1998 capela 1815 #ifndef CONFIG_EVENT_CHANNEL_MIDI
1999     sText += "<small><font color=\"red\">";
2000     sText += tr("Channel MIDI event support disabled.");
2001     sText += "</font></small><br />";
2002     #endif
2003     #ifndef CONFIG_EVENT_DEVICE_MIDI
2004     sText += "<small><font color=\"red\">";
2005     sText += tr("Device MIDI event support disabled.");
2006     sText += "</font></small><br />";
2007     #endif
2008     #ifndef CONFIG_MAX_VOICES
2009     sText += "<small><font color=\"red\">";
2010     sText += tr("Runtime max. voices / disk streams support disabled.");
2011     sText += "</font></small><br />";
2012     #endif
2013 capela 1509 sText += "<br />\n";
2014     sText += tr("Using") + ": ";
2015     sText += ::lscp_client_package();
2016     sText += " ";
2017     sText += ::lscp_client_version();
2018 schoenebeck 1461 #ifdef CONFIG_LIBGIG
2019 capela 1509 sText += ", ";
2020     sText += gig::libraryName().c_str();
2021     sText += " ";
2022     sText += gig::libraryVersion().c_str();
2023 schoenebeck 1461 #endif
2024 capela 1509 sText += "<br />\n";
2025     sText += "<br />\n";
2026     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
2027     sText += "<br />\n";
2028     sText += "<small>";
2029     sText += QSAMPLER_COPYRIGHT "<br />\n";
2030     sText += QSAMPLER_COPYRIGHT2 "<br />\n";
2031     sText += "<br />\n";
2032     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
2033     sText += tr("under the terms of the GNU General Public License version 2 or later.");
2034     sText += "</small>";
2035     sText += "</p>\n";
2036 schoenebeck 1461
2037 capela 1509 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
2038 schoenebeck 1461 }
2039    
2040    
2041     //-------------------------------------------------------------------------
2042     // qsamplerMainForm -- Main window stabilization.
2043    
2044     void MainForm::stabilizeForm (void)
2045     {
2046 capela 1509 // Update the main application caption...
2047     QString sSessionName = sessionName(m_sFilename);
2048     if (m_iDirtyCount > 0)
2049     sSessionName += " *";
2050     setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
2051 schoenebeck 1461
2052 capela 1509 // Update the main menu state...
2053 capela 2038 ChannelStrip *pChannelStrip = activeChannelStrip();
2054 capela 2978 const QList<QMdiSubWindow *>& wlist = m_pWorkspace->subWindowList();
2055     const bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
2056     const bool bHasChannel = (bHasClient && pChannelStrip != NULL);
2057     const bool bHasChannels = (bHasClient && wlist.count() > 0);
2058 capela 1509 m_ui.fileNewAction->setEnabled(bHasClient);
2059     m_ui.fileOpenAction->setEnabled(bHasClient);
2060     m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
2061     m_ui.fileSaveAsAction->setEnabled(bHasClient);
2062     m_ui.fileResetAction->setEnabled(bHasClient);
2063     m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
2064     m_ui.editAddChannelAction->setEnabled(bHasClient);
2065     m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
2066     m_ui.editSetupChannelAction->setEnabled(bHasChannel);
2067 schoenebeck 1461 #ifdef CONFIG_EDIT_INSTRUMENT
2068 capela 1509 m_ui.editEditChannelAction->setEnabled(bHasChannel);
2069 schoenebeck 1461 #else
2070 capela 1509 m_ui.editEditChannelAction->setEnabled(false);
2071 schoenebeck 1461 #endif
2072 capela 1509 m_ui.editResetChannelAction->setEnabled(bHasChannel);
2073 capela 2038 m_ui.editResetAllChannelsAction->setEnabled(bHasChannels);
2074 capela 1509 m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
2075 schoenebeck 1461 #ifdef CONFIG_MIDI_INSTRUMENT
2076 capela 1509 m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
2077 schoenebeck 1461 && m_pInstrumentListForm->isVisible());
2078 capela 1509 m_ui.viewInstrumentsAction->setEnabled(bHasClient);
2079 schoenebeck 1461 #else
2080 capela 1509 m_ui.viewInstrumentsAction->setEnabled(false);
2081 schoenebeck 1461 #endif
2082 capela 1509 m_ui.viewDevicesAction->setChecked(m_pDeviceForm
2083 schoenebeck 1461 && m_pDeviceForm->isVisible());
2084 capela 1509 m_ui.viewDevicesAction->setEnabled(bHasClient);
2085 capela 2038 m_ui.viewMidiDeviceStatusMenu->setEnabled(
2086     DeviceStatusForm::getInstances().size() > 0);
2087     m_ui.channelsArrangeAction->setEnabled(bHasChannels);
2088 schoenebeck 1461
2089     #ifdef CONFIG_VOLUME
2090     // Toolbar widgets are also affected...
2091 capela 1509 m_pVolumeSlider->setEnabled(bHasClient);
2092     m_pVolumeSpinBox->setEnabled(bHasClient);
2093 schoenebeck 1461 #endif
2094    
2095 capela 1509 // Client/Server status...
2096     if (bHasClient) {
2097     m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
2098     m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
2099     + ':' + QString::number(m_pOptions->iServerPort));
2100     } else {
2101     m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
2102     m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
2103     }
2104     // Channel status...
2105     if (bHasChannel)
2106     m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
2107     else
2108     m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
2109     // Session status...
2110     if (m_iDirtyCount > 0)
2111     m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
2112     else
2113     m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
2114 schoenebeck 1461
2115 capela 1509 // Recent files menu.
2116     m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
2117 schoenebeck 1461 }
2118    
2119    
2120     // Global volume change receiver slot.
2121     void MainForm::volumeChanged ( int iVolume )
2122     {
2123     #ifdef CONFIG_VOLUME
2124    
2125     if (m_iVolumeChanging > 0)
2126     return;
2127    
2128     m_iVolumeChanging++;
2129    
2130     // Update the toolbar widgets...
2131     if (m_pVolumeSlider->value() != iVolume)
2132     m_pVolumeSlider->setValue(iVolume);
2133     if (m_pVolumeSpinBox->value() != iVolume)
2134     m_pVolumeSpinBox->setValue(iVolume);
2135    
2136     // Do it as commanded...
2137 capela 2978 const float fVolume = 0.01f * float(iVolume);
2138 schoenebeck 1461 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
2139     appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
2140     else
2141     appendMessagesClient("lscp_set_volume");
2142    
2143     m_iVolumeChanging--;
2144    
2145     m_iDirtyCount++;
2146     stabilizeForm();
2147    
2148     #endif
2149     }
2150    
2151    
2152     // Channel change receiver slot.
2153 capela 2387 void MainForm::channelStripChanged ( ChannelStrip *pChannelStrip )
2154 schoenebeck 1461 {
2155     // Add this strip to the changed list...
2156 capela 1499 if (!m_changedStrips.contains(pChannelStrip)) {
2157 schoenebeck 1461 m_changedStrips.append(pChannelStrip);
2158     pChannelStrip->resetErrorCount();
2159     }
2160    
2161 capela 1509 // Just mark the dirty form.
2162     m_iDirtyCount++;
2163     // and update the form status...
2164     stabilizeForm();
2165 schoenebeck 1461 }
2166    
2167    
2168     // Grab and restore current sampler channels session.
2169     void MainForm::updateSession (void)
2170     {
2171     #ifdef CONFIG_VOLUME
2172 capela 2978 const int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
2173 schoenebeck 1461 m_iVolumeChanging++;
2174     m_pVolumeSlider->setValue(iVolume);
2175     m_pVolumeSpinBox->setValue(iVolume);
2176     m_iVolumeChanging--;
2177     #endif
2178     #ifdef CONFIG_MIDI_INSTRUMENT
2179     // FIXME: Make some room for default instrument maps...
2180 capela 2978 const int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
2181 schoenebeck 1461 if (iMaps < 0)
2182     appendMessagesClient("lscp_get_midi_instrument_maps");
2183     else if (iMaps < 1) {
2184 capela 1499 ::lscp_add_midi_instrument_map(m_pClient,
2185     tr("Chromatic").toUtf8().constData());
2186     ::lscp_add_midi_instrument_map(m_pClient,
2187     tr("Drum Kits").toUtf8().constData());
2188 schoenebeck 1461 }
2189     #endif
2190    
2191 schoenebeck 1702 updateAllChannelStrips(false);
2192    
2193     // Do we auto-arrange?
2194     if (m_pOptions && m_pOptions->bAutoArrange)
2195     channelsArrange();
2196    
2197     // Remember to refresh devices and instruments...
2198     if (m_pInstrumentListForm)
2199     m_pInstrumentListForm->refreshInstruments();
2200     if (m_pDeviceForm)
2201     m_pDeviceForm->refreshDevices();
2202     }
2203    
2204 capela 2387
2205     void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips )
2206     {
2207 capela 2978 // Skip if setting up a new channel strip...
2208     if (m_iDirtySetup > 0)
2209     return;
2210    
2211 schoenebeck 1461 // Retrieve the current channel list.
2212     int *piChannelIDs = ::lscp_list_channels(m_pClient);
2213     if (piChannelIDs == NULL) {
2214     if (::lscp_client_get_errno(m_pClient)) {
2215     appendMessagesClient("lscp_list_channels");
2216 capela 1509 appendMessagesError(
2217     tr("Could not get current list of channels.\n\nSorry."));
2218 schoenebeck 1461 }
2219     } else {
2220     // Try to (re)create each channel.
2221     m_pWorkspace->setUpdatesEnabled(false);
2222 capela 2387 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2223 schoenebeck 1461 // Check if theres already a channel strip for this one...
2224     if (!channelStrip(piChannelIDs[iChannel]))
2225 capela 1558 createChannelStrip(new Channel(piChannelIDs[iChannel]));
2226 schoenebeck 1461 }
2227 schoenebeck 1702 // Do we auto-arrange?
2228     if (m_pOptions && m_pOptions->bAutoArrange)
2229     channelsArrange();
2230     // remove dead channel strips
2231     if (bRemoveDeadStrips) {
2232 capela 2978 const QList<QMdiSubWindow *>& wlist
2233     = m_pWorkspace->subWindowList();
2234     const int iStripCount = wlist.count();
2235     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2236 capela 2387 ChannelStrip *pChannelStrip = NULL;
2237 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2238 capela 2387 if (pMdiSubWindow)
2239     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2240     if (pChannelStrip) {
2241     bool bExists = false;
2242 capela 2978 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2243     Channel *pChannel = pChannelStrip->channel();
2244     if (pChannel == NULL)
2245 capela 2387 break;
2246 capela 2978 if (piChannelIDs[iChannel] == pChannel->channelID()) {
2247 capela 2387 // strip exists, don't touch it
2248     bExists = true;
2249     break;
2250     }
2251 schoenebeck 1702 }
2252 capela 2387 if (!bExists)
2253     destroyChannelStrip(pChannelStrip);
2254 schoenebeck 1702 }
2255     }
2256     }
2257 schoenebeck 1461 m_pWorkspace->setUpdatesEnabled(true);
2258     }
2259 capela 2387
2260     stabilizeForm();
2261 schoenebeck 1461 }
2262    
2263 capela 2387
2264 schoenebeck 1461 // Update the recent files list and menu.
2265     void MainForm::updateRecentFiles ( const QString& sFilename )
2266     {
2267 capela 1509 if (m_pOptions == NULL)
2268     return;
2269 schoenebeck 1461
2270 capela 1509 // Remove from list if already there (avoid duplicates)
2271 capela 2978 const int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
2272 capela 1509 if (iIndex >= 0)
2273     m_pOptions->recentFiles.removeAt(iIndex);
2274     // Put it to front...
2275     m_pOptions->recentFiles.push_front(sFilename);
2276 schoenebeck 1461 }
2277    
2278    
2279     // Update the recent files list and menu.
2280     void MainForm::updateRecentFilesMenu (void)
2281     {
2282 capela 1499 if (m_pOptions == NULL)
2283     return;
2284 schoenebeck 1461
2285 capela 1499 // Time to keep the list under limits.
2286     int iRecentFiles = m_pOptions->recentFiles.count();
2287     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
2288     m_pOptions->recentFiles.pop_back();
2289     iRecentFiles--;
2290     }
2291 schoenebeck 1461
2292 capela 1499 // Rebuild the recent files menu...
2293 capela 1509 m_ui.fileOpenRecentMenu->clear();
2294 capela 1499 for (int i = 0; i < iRecentFiles; i++) {
2295     const QString& sFilename = m_pOptions->recentFiles[i];
2296     if (QFileInfo(sFilename).exists()) {
2297 capela 1509 QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
2298 capela 1499 QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
2299     this, SLOT(fileOpenRecent()));
2300     pAction->setData(i);
2301     }
2302     }
2303 schoenebeck 1461 }
2304    
2305    
2306     // Force update of the channels instrument names mode.
2307     void MainForm::updateInstrumentNames (void)
2308     {
2309 capela 1509 // Full channel list update...
2310 capela 2978 const QList<QMdiSubWindow *>& wlist
2311     = m_pWorkspace->subWindowList();
2312 capela 1509 if (wlist.isEmpty())
2313     return;
2314 schoenebeck 1461
2315 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2316 capela 2978 const int iStripCount = wlist.count();
2317     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2318     ChannelStrip *pChannelStrip = NULL;
2319     QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2320     if (pMdiSubWindow)
2321     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2322 capela 1509 if (pChannelStrip)
2323     pChannelStrip->updateInstrumentName(true);
2324     }
2325     m_pWorkspace->setUpdatesEnabled(true);
2326 schoenebeck 1461 }
2327    
2328    
2329     // Force update of the channels display font.
2330     void MainForm::updateDisplayFont (void)
2331     {
2332 capela 1509 if (m_pOptions == NULL)
2333     return;
2334 schoenebeck 1461
2335 capela 1509 // Check if display font is legal.
2336     if (m_pOptions->sDisplayFont.isEmpty())
2337     return;
2338 capela 2978
2339 capela 1509 // Realize it.
2340     QFont font;
2341     if (!font.fromString(m_pOptions->sDisplayFont))
2342     return;
2343 schoenebeck 1461
2344 capela 1509 // Full channel list update...
2345 capela 2978 const QList<QMdiSubWindow *>& wlist
2346     = m_pWorkspace->subWindowList();
2347 capela 1509 if (wlist.isEmpty())
2348     return;
2349 schoenebeck 1461
2350 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2351 capela 2978 const int iStripCount = wlist.count();
2352     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2353 capela 2387 ChannelStrip *pChannelStrip = NULL;
2354 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2355 capela 2387 if (pMdiSubWindow)
2356     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2357 capela 1509 if (pChannelStrip)
2358     pChannelStrip->setDisplayFont(font);
2359     }
2360     m_pWorkspace->setUpdatesEnabled(true);
2361 schoenebeck 1461 }
2362    
2363    
2364     // Update channel strips background effect.
2365     void MainForm::updateDisplayEffect (void)
2366     {
2367 capela 1509 // Full channel list update...
2368 capela 2978 const QList<QMdiSubWindow *>& wlist
2369     = m_pWorkspace->subWindowList();
2370 capela 1509 if (wlist.isEmpty())
2371     return;
2372 schoenebeck 1461
2373 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2374 capela 2978 const int iStripCount = wlist.count();
2375     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2376 capela 2387 ChannelStrip *pChannelStrip = NULL;
2377 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2378 capela 2387 if (pMdiSubWindow)
2379     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2380 capela 1499 if (pChannelStrip)
2381     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2382 capela 1509 }
2383     m_pWorkspace->setUpdatesEnabled(true);
2384 schoenebeck 1461 }
2385    
2386    
2387     // Force update of the channels maximum volume setting.
2388     void MainForm::updateMaxVolume (void)
2389     {
2390 capela 1509 if (m_pOptions == NULL)
2391     return;
2392 schoenebeck 1461
2393     #ifdef CONFIG_VOLUME
2394     m_iVolumeChanging++;
2395 capela 1499 m_pVolumeSlider->setMaximum(m_pOptions->iMaxVolume);
2396     m_pVolumeSpinBox->setMaximum(m_pOptions->iMaxVolume);
2397 schoenebeck 1461 m_iVolumeChanging--;
2398     #endif
2399    
2400 capela 1509 // Full channel list update...
2401 capela 2978 const QList<QMdiSubWindow *>& wlist
2402     = m_pWorkspace->subWindowList();
2403 capela 1509 if (wlist.isEmpty())
2404     return;
2405 schoenebeck 1461
2406 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2407 capela 2978 const int iStripCount = wlist.count();
2408     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2409 capela 2387 ChannelStrip *pChannelStrip = NULL;
2410 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2411 capela 2387 if (pMdiSubWindow)
2412     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2413 capela 1509 if (pChannelStrip)
2414     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2415     }
2416     m_pWorkspace->setUpdatesEnabled(true);
2417 schoenebeck 1461 }
2418    
2419    
2420     //-------------------------------------------------------------------------
2421     // qsamplerMainForm -- Messages window form handlers.
2422    
2423     // Messages output methods.
2424     void MainForm::appendMessages( const QString& s )
2425     {
2426 capela 1509 if (m_pMessages)
2427     m_pMessages->appendMessages(s);
2428 schoenebeck 1461
2429 capela 1509 statusBar()->showMessage(s, 3000);
2430 schoenebeck 1461 }
2431    
2432     void MainForm::appendMessagesColor( const QString& s, const QString& c )
2433     {
2434 capela 1509 if (m_pMessages)
2435     m_pMessages->appendMessagesColor(s, c);
2436 schoenebeck 1461
2437 capela 1509 statusBar()->showMessage(s, 3000);
2438 schoenebeck 1461 }
2439    
2440     void MainForm::appendMessagesText( const QString& s )
2441     {
2442 capela 1509 if (m_pMessages)
2443     m_pMessages->appendMessagesText(s);
2444 schoenebeck 1461 }
2445    
2446 capela 2722 void MainForm::appendMessagesError( const QString& sText )
2447 schoenebeck 1461 {
2448 capela 1509 if (m_pMessages)
2449     m_pMessages->show();
2450 schoenebeck 1461
2451 capela 2722 appendMessagesColor(sText.simplified(), "#ff0000");
2452 schoenebeck 1461
2453     // Make it look responsive...:)
2454 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2455 schoenebeck 1461
2456 capela 2722 if (m_pOptions && m_pOptions->bConfirmError) {
2457     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Error");
2458     #if 0
2459     QMessageBox::critical(this, sTitle, sText, QMessageBox::Cancel);
2460     #else
2461     QMessageBox mbox(this);
2462     mbox.setIcon(QMessageBox::Critical);
2463     mbox.setWindowTitle(sTitle);
2464     mbox.setText(sText);
2465     mbox.setStandardButtons(QMessageBox::Cancel);
2466     QCheckBox cbox(tr("Don't show this again"));
2467     cbox.setChecked(false);
2468     cbox.blockSignals(true);
2469     mbox.addButton(&cbox, QMessageBox::ActionRole);
2470     if (mbox.exec() && cbox.isChecked())
2471     m_pOptions->bConfirmError = false;
2472     #endif
2473     }
2474 schoenebeck 1461 }
2475    
2476    
2477     // This is a special message format, just for client results.
2478     void MainForm::appendMessagesClient( const QString& s )
2479     {
2480 capela 1509 if (m_pClient == NULL)
2481     return;
2482 schoenebeck 1461
2483 capela 1509 appendMessagesColor(s + QString(": %1 (errno=%2)")
2484     .arg(::lscp_client_get_result(m_pClient))
2485     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2486 schoenebeck 1461
2487     // Make it look responsive...:)
2488 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2489 schoenebeck 1461 }
2490    
2491    
2492     // Force update of the messages font.
2493     void MainForm::updateMessagesFont (void)
2494     {
2495 capela 1509 if (m_pOptions == NULL)
2496     return;
2497 schoenebeck 1461
2498 capela 1509 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2499     QFont font;
2500     if (font.fromString(m_pOptions->sMessagesFont))
2501     m_pMessages->setMessagesFont(font);
2502     }
2503 schoenebeck 1461 }
2504    
2505    
2506     // Update messages window line limit.
2507     void MainForm::updateMessagesLimit (void)
2508     {
2509 capela 1509 if (m_pOptions == NULL)
2510     return;
2511 schoenebeck 1461
2512 capela 1509 if (m_pMessages) {
2513     if (m_pOptions->bMessagesLimit)
2514     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2515     else
2516     m_pMessages->setMessagesLimit(-1);
2517     }
2518 schoenebeck 1461 }
2519    
2520    
2521     // Enablement of the messages capture feature.
2522     void MainForm::updateMessagesCapture (void)
2523     {
2524 capela 1509 if (m_pOptions == NULL)
2525     return;
2526 schoenebeck 1461
2527 capela 1509 if (m_pMessages)
2528     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2529 schoenebeck 1461 }
2530    
2531    
2532     //-------------------------------------------------------------------------
2533     // qsamplerMainForm -- MDI channel strip management.
2534    
2535     // The channel strip creation executive.
2536 capela 2387 ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel )
2537 schoenebeck 1461 {
2538 capela 1509 if (m_pClient == NULL || pChannel == NULL)
2539     return NULL;
2540 schoenebeck 1461
2541 capela 1509 // Add a new channel itema...
2542 capela 1515 ChannelStrip *pChannelStrip = new ChannelStrip();
2543 capela 1509 if (pChannelStrip == NULL)
2544     return NULL;
2545 schoenebeck 1461
2546 capela 1515 // Set some initial channel strip options...
2547 capela 1509 if (m_pOptions) {
2548     // Background display effect...
2549     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2550     // We'll need a display font.
2551     QFont font;
2552 capela 2978 if (!m_pOptions->sDisplayFont.isEmpty() &&
2553     font.fromString(m_pOptions->sDisplayFont))
2554 capela 1509 pChannelStrip->setDisplayFont(font);
2555     // Maximum allowed volume setting.
2556     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2557     }
2558 schoenebeck 1461
2559 capela 1515 // Add it to workspace...
2560 capela 2441 m_pWorkspace->addSubWindow(pChannelStrip,
2561     Qt::SubWindow | Qt::FramelessWindowHint);
2562 capela 1515
2563     // Actual channel strip setup...
2564     pChannelStrip->setup(pChannel);
2565    
2566     QObject::connect(pChannelStrip,
2567 capela 2387 SIGNAL(channelChanged(ChannelStrip *)),
2568     SLOT(channelStripChanged(ChannelStrip *)));
2569 capela 1515
2570 capela 1509 // Now we show up us to the world.
2571     pChannelStrip->show();
2572 schoenebeck 1461
2573     // This is pretty new, so we'll watch for it closely.
2574     channelStripChanged(pChannelStrip);
2575    
2576 capela 1509 // Return our successful reference...
2577     return pChannelStrip;
2578 schoenebeck 1461 }
2579    
2580 capela 2387
2581     void MainForm::destroyChannelStrip ( ChannelStrip *pChannelStrip )
2582     {
2583 capela 2441 QMdiSubWindow *pMdiSubWindow
2584     = static_cast<QMdiSubWindow *> (pChannelStrip->parentWidget());
2585     if (pMdiSubWindow == NULL)
2586     return;
2587    
2588 schoenebeck 1702 // Just delete the channel strip.
2589     delete pChannelStrip;
2590 capela 2441 delete pMdiSubWindow;
2591 schoenebeck 1461
2592 schoenebeck 1702 // Do we auto-arrange?
2593     if (m_pOptions && m_pOptions->bAutoArrange)
2594     channelsArrange();
2595     }
2596    
2597 capela 2387
2598 schoenebeck 1461 // Retrieve the active channel strip.
2599 capela 2387 ChannelStrip *MainForm::activeChannelStrip (void)
2600 schoenebeck 1461 {
2601 capela 2387 QMdiSubWindow *pMdiSubWindow = m_pWorkspace->activeSubWindow();
2602     if (pMdiSubWindow)
2603     return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2604     else
2605     return NULL;
2606 schoenebeck 1461 }
2607    
2608    
2609     // Retrieve a channel strip by index.
2610 capela 2978 ChannelStrip *MainForm::channelStripAt ( int iStrip )
2611 schoenebeck 1461 {
2612 schoenebeck 1702 if (!m_pWorkspace) return NULL;
2613    
2614 capela 2978 const QList<QMdiSubWindow *>& wlist
2615     = m_pWorkspace->subWindowList();
2616 capela 1509 if (wlist.isEmpty())
2617     return NULL;
2618 schoenebeck 1461
2619 capela 2978 if (iStrip < 0 || iStrip >= wlist.count())
2620 schoenebeck 1702 return NULL;
2621    
2622 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2623 capela 2387 if (pMdiSubWindow)
2624     return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2625     else
2626     return NULL;
2627 schoenebeck 1461 }
2628    
2629    
2630     // Retrieve a channel strip by sampler channel id.
2631 capela 2387 ChannelStrip *MainForm::channelStrip ( int iChannelID )
2632 schoenebeck 1461 {
2633 capela 2978 const QList<QMdiSubWindow *>& wlist
2634     = m_pWorkspace->subWindowList();
2635 schoenebeck 1461 if (wlist.isEmpty())
2636     return NULL;
2637    
2638 capela 2978 const int iStripCount = wlist.count();
2639     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2640 capela 2387 ChannelStrip *pChannelStrip = NULL;
2641 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2642 capela 2387 if (pMdiSubWindow)
2643     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2644 schoenebeck 1461 if (pChannelStrip) {
2645 capela 1558 Channel *pChannel = pChannelStrip->channel();
2646 schoenebeck 1461 if (pChannel && pChannel->channelID() == iChannelID)
2647     return pChannelStrip;
2648     }
2649     }
2650    
2651     // Not found.
2652     return NULL;
2653     }
2654    
2655    
2656     // Construct the windows menu.
2657     void MainForm::channelsMenuAboutToShow (void)
2658     {
2659 capela 1509 m_ui.channelsMenu->clear();
2660     m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction);
2661     m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction);
2662 schoenebeck 1461
2663 capela 2978 const QList<QMdiSubWindow *>& wlist
2664     = m_pWorkspace->subWindowList();
2665 capela 1509 if (!wlist.isEmpty()) {
2666     m_ui.channelsMenu->addSeparator();
2667 capela 2978 const int iStripCount = wlist.count();
2668     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2669 capela 2387 ChannelStrip *pChannelStrip = NULL;
2670 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2671 capela 2387 if (pMdiSubWindow)
2672     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2673 capela 1499 if (pChannelStrip) {
2674 capela 1509 QAction *pAction = m_ui.channelsMenu->addAction(
2675     pChannelStrip->windowTitle(),
2676     this, SLOT(channelsMenuActivated()));
2677 capela 1507 pAction->setCheckable(true);
2678     pAction->setChecked(activeChannelStrip() == pChannelStrip);
2679 capela 2978 pAction->setData(iStrip);
2680 capela 1499 }
2681 capela 1509 }
2682     }
2683 schoenebeck 1461 }
2684    
2685    
2686     // Windows menu activation slot
2687 capela 1499 void MainForm::channelsMenuActivated (void)
2688 schoenebeck 1461 {
2689 capela 1499 // Retrive channel index from action data...
2690     QAction *pAction = qobject_cast<QAction *> (sender());
2691     if (pAction == NULL)
2692     return;
2693    
2694 capela 2387 ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt());
2695 capela 1499 if (pChannelStrip) {
2696     pChannelStrip->showNormal();
2697     pChannelStrip->setFocus();
2698     }
2699 schoenebeck 1461 }
2700    
2701    
2702     //-------------------------------------------------------------------------
2703     // qsamplerMainForm -- Timer stuff.
2704    
2705     // Set the pseudo-timer delay schedule.
2706     void MainForm::startSchedule ( int iStartDelay )
2707     {
2708 capela 1509 m_iStartDelay = 1 + (iStartDelay * 1000);
2709     m_iTimerDelay = 0;
2710 schoenebeck 1461 }
2711    
2712     // Suspend the pseudo-timer delay schedule.
2713     void MainForm::stopSchedule (void)
2714     {
2715 capela 1509 m_iStartDelay = 0;
2716     m_iTimerDelay = 0;
2717 schoenebeck 1461 }
2718    
2719     // Timer slot funtion.
2720     void MainForm::timerSlot (void)
2721     {
2722 capela 1509 if (m_pOptions == NULL)
2723     return;
2724 schoenebeck 1461
2725 capela 1509 // Is it the first shot on server start after a few delay?
2726     if (m_iTimerDelay < m_iStartDelay) {
2727     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2728     if (m_iTimerDelay >= m_iStartDelay) {
2729     // If we cannot start it now, maybe a lil'mo'later ;)
2730     if (!startClient()) {
2731     m_iStartDelay += m_iTimerDelay;
2732     m_iTimerDelay = 0;
2733     }
2734     }
2735     }
2736 schoenebeck 1461
2737     if (m_pClient) {
2738     // Update the channel information for each pending strip...
2739 capela 1499 QListIterator<ChannelStrip *> iter(m_changedStrips);
2740     while (iter.hasNext()) {
2741     ChannelStrip *pChannelStrip = iter.next();
2742     // If successfull, remove from pending list...
2743     if (pChannelStrip->updateChannelInfo()) {
2744 capela 2978 const int iChannelStrip = m_changedStrips.indexOf(pChannelStrip);
2745 capela 1499 if (iChannelStrip >= 0)
2746     m_changedStrips.removeAt(iChannelStrip);
2747 schoenebeck 1461 }
2748     }
2749     // Refresh each channel usage, on each period...
2750     if (m_pOptions->bAutoRefresh) {
2751     m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2752     if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2753     m_iTimerSlot = 0;
2754     // Update the channel stream usage for each strip...
2755 capela 2978 const QList<QMdiSubWindow *>& wlist
2756     = m_pWorkspace->subWindowList();
2757     const int iStripCount = wlist.count();
2758     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2759 capela 2387 ChannelStrip *pChannelStrip = NULL;
2760 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2761 capela 2387 if (pMdiSubWindow)
2762     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2763 schoenebeck 1461 if (pChannelStrip && pChannelStrip->isVisible())
2764     pChannelStrip->updateChannelUsage();
2765     }
2766     }
2767     }
2768     }
2769    
2770 capela 1509 // Register the next timer slot.
2771     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2772 schoenebeck 1461 }
2773    
2774    
2775     //-------------------------------------------------------------------------
2776     // qsamplerMainForm -- Server stuff.
2777    
2778     // Start linuxsampler server...
2779     void MainForm::startServer (void)
2780     {
2781 capela 1509 if (m_pOptions == NULL)
2782     return;
2783 schoenebeck 1461
2784 capela 1509 // Aren't already a client, are we?
2785     if (!m_pOptions->bServerStart || m_pClient)
2786     return;
2787 schoenebeck 1461
2788 capela 1509 // Is the server process instance still here?
2789     if (m_pServer) {
2790 capela 1840 if (QMessageBox::warning(this,
2791 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
2792 capela 1509 tr("Could not start the LinuxSampler server.\n\n"
2793 schoenebeck 1626 "Maybe it is already started."),
2794 capela 1840 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
2795 capela 1509 m_pServer->terminate();
2796     m_pServer->kill();
2797     }
2798     return;
2799     }
2800 schoenebeck 1461
2801 capela 1509 // Reset our timer counters...
2802     stopSchedule();
2803 schoenebeck 1461
2804 capela 1509 // Verify we have something to start with...
2805     if (m_pOptions->sServerCmdLine.isEmpty())
2806     return;
2807 schoenebeck 1461
2808 capela 1509 // OK. Let's build the startup process...
2809 schoenebeck 1626 m_pServer = new QProcess();
2810     bForceServerStop = true;
2811 capela 1509
2812     // Setup stdout/stderr capture...
2813 capela 2978 m_pServer->setProcessChannelMode(QProcess::ForwardedChannels);
2814     QObject::connect(m_pServer,
2815     SIGNAL(readyReadStandardOutput()),
2816     SLOT(readServerStdout()));
2817     QObject::connect(m_pServer,
2818     SIGNAL(readyReadStandardError()),
2819     SLOT(readServerStdout()));
2820 capela 1509
2821 schoenebeck 1461 // The unforgiveable signal communication...
2822     QObject::connect(m_pServer,
2823 capela 1559 SIGNAL(finished(int, QProcess::ExitStatus)),
2824 schoenebeck 1461 SLOT(processServerExit()));
2825    
2826 capela 1509 // Build process arguments...
2827     QStringList args = m_pOptions->sServerCmdLine.split(' ');
2828     QString sCommand = args[0];
2829     args.removeAt(0);
2830 schoenebeck 1461
2831 capela 1509 appendMessages(tr("Server is starting..."));
2832     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2833 schoenebeck 1461
2834 capela 1509 // Go linuxsampler, go...
2835     m_pServer->start(sCommand, args);
2836     if (!m_pServer->waitForStarted()) {
2837     appendMessagesError(tr("Could not start server.\n\nSorry."));
2838     processServerExit();
2839     return;
2840     }
2841 schoenebeck 1461
2842 capela 1509 // Show startup results...
2843     appendMessages(
2844     tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2845 schoenebeck 1461
2846 capela 1509 // Reset (yet again) the timer counters,
2847     // but this time is deferred as the user opted.
2848     startSchedule(m_pOptions->iStartDelay);
2849     stabilizeForm();
2850 schoenebeck 1461 }
2851    
2852    
2853     // Stop linuxsampler server...
2854 schoenebeck 1626 void MainForm::stopServer (bool bInteractive)
2855 schoenebeck 1461 {
2856 capela 1509 // Stop client code.
2857     stopClient();
2858 schoenebeck 1461
2859 schoenebeck 1626 if (m_pServer && bInteractive) {
2860     if (QMessageBox::question(this,
2861     QSAMPLER_TITLE ": " + tr("The backend's fate ..."),
2862     tr("You have the option to keep the sampler backend (LinuxSampler)\n"
2863     "running in the background. The sampler would continue to work\n"
2864     "according to your current sampler session and you could alter the\n"
2865     "sampler session at any time by relaunching QSampler.\n\n"
2866 capela 1890 "Do you want LinuxSampler to stop?"),
2867 schoenebeck 2717 QMessageBox::Yes | QMessageBox::No,
2868     QMessageBox::Yes) == QMessageBox::No)
2869 schoenebeck 1626 {
2870     bForceServerStop = false;
2871     }
2872     }
2873    
2874 capela 1509 // And try to stop server.
2875 schoenebeck 1626 if (m_pServer && bForceServerStop) {
2876 capela 1509 appendMessages(tr("Server is stopping..."));
2877 capela 1559 if (m_pServer->state() == QProcess::Running) {
2878 capela 2441 #if defined(WIN32)
2879 capela 1559 // Try harder...
2880     m_pServer->kill();
2881 capela 2441 #else
2882 capela 1559 // Try softly...
2883 capela 1509 m_pServer->terminate();
2884 capela 2441 #endif
2885 capela 1559 }
2886     } // Do final processing anyway.
2887     else processServerExit();
2888 schoenebeck 1461
2889 capela 1509 // Give it some time to terminate gracefully and stabilize...
2890     QTime t;
2891     t.start();
2892     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2893     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2894 schoenebeck 1461 }
2895    
2896    
2897     // Stdout handler...
2898     void MainForm::readServerStdout (void)
2899     {
2900 capela 1509 if (m_pMessages)
2901     m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2902 schoenebeck 1461 }
2903    
2904    
2905     // Linuxsampler server cleanup.
2906     void MainForm::processServerExit (void)
2907     {
2908 capela 1509 // Force client code cleanup.
2909     stopClient();
2910 schoenebeck 1461
2911 capela 1509 // Flush anything that maybe pending...
2912     if (m_pMessages)
2913     m_pMessages->flushStdoutBuffer();
2914 schoenebeck 1461
2915 schoenebeck 1626 if (m_pServer && bForceServerStop) {
2916 capela 1559 if (m_pServer->state() != QProcess::NotRunning) {
2917     appendMessages(tr("Server is being forced..."));
2918     // Force final server shutdown...
2919     m_pServer->kill();
2920     // Give it some time to terminate gracefully and stabilize...
2921     QTime t;
2922     t.start();
2923     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2924     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2925     }
2926 capela 1509 // Force final server shutdown...
2927     appendMessages(
2928     tr("Server was stopped with exit status %1.")
2929     .arg(m_pServer->exitStatus()));
2930     delete m_pServer;
2931     m_pServer = NULL;
2932     }
2933 schoenebeck 1461
2934 capela 1509 // Again, make status visible stable.
2935     stabilizeForm();
2936 schoenebeck 1461 }
2937    
2938    
2939     //-------------------------------------------------------------------------
2940     // qsamplerMainForm -- Client stuff.
2941    
2942     // The LSCP client callback procedure.
2943 capela 1509 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/,
2944     lscp_event_t event, const char *pchData, int cchData, void *pvData )
2945 schoenebeck 1461 {
2946 capela 1509 MainForm* pMainForm = (MainForm *) pvData;
2947     if (pMainForm == NULL)
2948     return LSCP_FAILED;
2949 schoenebeck 1461
2950 capela 1509 // ATTN: DO NOT EVER call any GUI code here,
2951     // as this is run under some other thread context.
2952     // A custom event must be posted here...
2953     QApplication::postEvent(pMainForm,
2954 capela 2050 new LscpEvent(event, pchData, cchData));
2955 schoenebeck 1461
2956 capela 1509 return LSCP_OK;
2957 schoenebeck 1461 }
2958    
2959    
2960     // Start our almighty client...
2961     bool MainForm::startClient (void)
2962     {
2963 capela 1509 // Have it a setup?
2964     if (m_pOptions == NULL)
2965     return false;
2966 schoenebeck 1461
2967 capela 1509 // Aren't we already started, are we?
2968     if (m_pClient)
2969     return true;
2970 schoenebeck 1461
2971 capela 1509 // Log prepare here.
2972     appendMessages(tr("Client connecting..."));
2973 schoenebeck 1461
2974 capela 1509 // Create the client handle...
2975 capela 1499 m_pClient = ::lscp_client_create(
2976     m_pOptions->sServerHost.toUtf8().constData(),
2977     m_pOptions->iServerPort, qsampler_client_callback, this);
2978 capela 1509 if (m_pClient == NULL) {
2979     // Is this the first try?
2980     // maybe we need to start a local server...
2981     if ((m_pServer && m_pServer->state() == QProcess::Running)
2982     || !m_pOptions->bServerStart) {
2983     appendMessagesError(
2984     tr("Could not connect to server as client.\n\nSorry."));
2985     } else {
2986     startServer();
2987     }
2988     // This is always a failure.
2989     stabilizeForm();
2990     return false;
2991     }
2992     // Just set receive timeout value, blindly.
2993     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2994     appendMessages(
2995     tr("Client receive timeout is set to %1 msec.")
2996     .arg(::lscp_client_get_timeout(m_pClient)));
2997 schoenebeck 1461
2998     // Subscribe to channel info change notifications...
2999 schoenebeck 1702 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK)
3000     appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)");
3001 schoenebeck 1461 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
3002 schoenebeck 1691 appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)");
3003 schoenebeck 1461
3004 schoenebeck 1698 DeviceStatusForm::onDevicesChanged(); // initialize
3005     updateViewMidiDeviceStatusMenu();
3006     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK)
3007     appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)");
3008 schoenebeck 1699 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK)
3009     appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)");
3010 schoenebeck 1702 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK)
3011     appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)");
3012     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK)
3013     appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)");
3014 schoenebeck 1698
3015 capela 1704 #if CONFIG_EVENT_CHANNEL_MIDI
3016 schoenebeck 1691 // Subscribe to channel MIDI data notifications...
3017     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK)
3018     appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)");
3019     #endif
3020    
3021 capela 1704 #if CONFIG_EVENT_DEVICE_MIDI
3022 schoenebeck 1698 // Subscribe to channel MIDI data notifications...
3023     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK)
3024     appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)");
3025     #endif
3026    
3027 capela 1509 // We may stop scheduling around.
3028     stopSchedule();
3029 schoenebeck 1461
3030 capela 1509 // We'll accept drops from now on...
3031     setAcceptDrops(true);
3032 schoenebeck 1461
3033 capela 1509 // Log success here.
3034     appendMessages(tr("Client connected."));
3035 schoenebeck 1461
3036     // Hard-notify instrumnet and device configuration forms,
3037     // if visible, that we're ready...
3038     if (m_pInstrumentListForm)
3039 capela 1509 m_pInstrumentListForm->refreshInstruments();
3040 schoenebeck 1461 if (m_pDeviceForm)
3041 capela 1509 m_pDeviceForm->refreshDevices();
3042 schoenebeck 1461
3043 capela 1509 // Is any session pending to be loaded?
3044     if (!m_pOptions->sSessionFile.isEmpty()) {
3045     // Just load the prabably startup session...
3046     if (loadSessionFile(m_pOptions->sSessionFile)) {
3047     m_pOptions->sSessionFile = QString::null;
3048     return true;
3049     }
3050     }
3051 schoenebeck 1461
3052 schoenebeck 1803 // send the current / loaded fine tuning settings to the sampler
3053     m_pOptions->sendFineTuningSettings();
3054    
3055 capela 1509 // Make a new session
3056     return newSession();
3057 schoenebeck 1461 }
3058    
3059    
3060     // Stop client...
3061     void MainForm::stopClient (void)
3062     {
3063 capela 1509 if (m_pClient == NULL)
3064     return;
3065 schoenebeck 1461
3066 capela 1509 // Log prepare here.
3067     appendMessages(tr("Client disconnecting..."));
3068 schoenebeck 1461
3069 capela 1509 // Clear timer counters...
3070     stopSchedule();
3071 schoenebeck 1461
3072 capela 1509 // We'll reject drops from now on...
3073     setAcceptDrops(false);
3074 schoenebeck 1461
3075 capela 1509 // Force any channel strips around, but
3076     // but avoid removing the corresponding
3077     // channels from the back-end server.
3078     m_iDirtyCount = 0;
3079     closeSession(false);
3080 schoenebeck 1461
3081 capela 1509 // Close us as a client...
3082 capela 1704 #if CONFIG_EVENT_DEVICE_MIDI
3083 schoenebeck 1698 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI);
3084     #endif
3085 capela 1704 #if CONFIG_EVENT_CHANNEL_MIDI
3086 schoenebeck 1691 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI);
3087     #endif
3088 schoenebeck 1702 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO);
3089     ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT);
3090 schoenebeck 1699 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO);
3091 schoenebeck 1698 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT);
3092 schoenebeck 1461 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
3093 schoenebeck 1702 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT);
3094 capela 1509 ::lscp_client_destroy(m_pClient);
3095     m_pClient = NULL;
3096 schoenebeck 1461
3097     // Hard-notify instrumnet and device configuration forms,
3098     // if visible, that we're running out...
3099     if (m_pInstrumentListForm)
3100 capela 1509 m_pInstrumentListForm->refreshInstruments();
3101 schoenebeck 1461 if (m_pDeviceForm)
3102 capela 1509 m_pDeviceForm->refreshDevices();
3103 schoenebeck 1461
3104 capela 1509 // Log final here.
3105     appendMessages(tr("Client disconnected."));
3106 schoenebeck 1461
3107 capela 1509 // Make visible status.
3108     stabilizeForm();
3109 schoenebeck 1461 }
3110    
3111 capela 1514
3112     // Channel strip activation/selection.
3113 capela 2387 void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow )
3114 capela 1514 {
3115 capela 2387 ChannelStrip *pChannelStrip = NULL;
3116     if (pMdiSubWindow)
3117     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
3118 capela 1514 if (pChannelStrip)
3119     pChannelStrip->setSelected(true);
3120    
3121     stabilizeForm();
3122     }
3123    
3124    
3125 capela 2681 // Channel toolbar orientation change.
3126     void MainForm::channelsToolbarOrientation ( Qt::Orientation orientation )
3127     {
3128     #ifdef CONFIG_VOLUME
3129     m_pVolumeSlider->setOrientation(orientation);
3130     if (orientation == Qt::Horizontal) {
3131     m_pVolumeSlider->setMinimumHeight(24);
3132     m_pVolumeSlider->setMaximumHeight(32);
3133     m_pVolumeSlider->setMinimumWidth(120);
3134     m_pVolumeSlider->setMaximumWidth(640);
3135     m_pVolumeSpinBox->setMaximumWidth(64);
3136     m_pVolumeSpinBox->setButtonSymbols(QSpinBox::UpDownArrows);
3137     } else {
3138     m_pVolumeSlider->setMinimumHeight(120);
3139     m_pVolumeSlider->setMaximumHeight(480);
3140     m_pVolumeSlider->setMinimumWidth(24);
3141     m_pVolumeSlider->setMaximumWidth(32);
3142     m_pVolumeSpinBox->setMaximumWidth(32);
3143     m_pVolumeSpinBox->setButtonSymbols(QSpinBox::NoButtons);
3144     }
3145     #endif
3146     }
3147    
3148    
3149 schoenebeck 1461 } // namespace QSampler
3150 capela 1464
3151    
3152     // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC