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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC