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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC