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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1515 - (show annotations) (download)
Fri Nov 23 18:15:33 2007 UTC (16 years, 4 months ago) by capela
File size: 74028 byte(s)
* Fixed sure crash when removing channel strips.
* Force auto-arrange when adding channel strips,
  among other incremental fixes.

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

  ViewVC Help
Powered by ViewVC