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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC