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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC