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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2038 - (show annotations) (download)
Thu Jan 7 18:42:26 2010 UTC (8 years, 4 months ago) by capela
File size: 81469 byte(s)
* MIDI Device Status menu is disabled when no MIDI device exists;
  a menu separator has been added.

* Window manager's close button was found missing from the Devices
  and Instruments widgets when on Qt >= 4.5, now fixed.

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

  ViewVC Help
Powered by ViewVC