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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC