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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC