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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC