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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1507 - (show annotations) (download)
Wed Nov 21 23:22:18 2007 UTC (14 years ago) by capela
File size: 78898 byte(s)
- Qt4 migration: more tiny-fixes, specially on the sampler channel
strip appearence and channels menu content.

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

  ViewVC Help
Powered by ViewVC