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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC