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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC