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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1693 - (show annotations) (download)
Fri Feb 15 17:08:33 2008 UTC (11 years, 11 months ago) by schoenebeck
File size: 75424 byte(s)
* bugfix: MIDI activity was only shown on the 1st channel strip

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

  ViewVC Help
Powered by ViewVC