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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1473 - (show annotations) (download)
Mon Nov 5 19:07:26 2007 UTC (16 years, 4 months ago) by capela
File size: 79187 byte(s)
* Qt4 migration: still far from complete, the .ui files got shaved
  with special regard to some redundant or duplicated signal/slot
  connections and to the options dialog font aesthetics as also
  other minors.

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

  ViewVC Help
Powered by ViewVC