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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC