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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC