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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC