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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC