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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC