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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC