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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC