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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC