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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1475 - (show annotations) (download)
Tue Nov 6 22:12:32 2007 UTC (16 years, 4 months ago) by schoenebeck
File size: 79099 byte(s)
* Qt4 migration: trivial but important - show channel strips in their own
  independent MDI windows

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 pChannelStrip = new ChannelStrip();
2170 if (pChannelStrip == NULL)
2171 return NULL;
2172
2173 m_pWorkspace->addWindow(pChannelStrip, Qt::Tool);
2174
2175 // Actual channel strip setup...
2176 pChannelStrip->setup(pChannel);
2177 QObject::connect(pChannelStrip,
2178 SIGNAL(channelChanged(ChannelStrip*)),
2179 SLOT(channelStripChanged(ChannelStrip*)));
2180 // Set some initial aesthetic options...
2181 if (m_pOptions) {
2182 // Background display effect...
2183 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2184 // We'll need a display font.
2185 QFont font;
2186 if (font.fromString(m_pOptions->sDisplayFont))
2187 pChannelStrip->setDisplayFont(font);
2188 // Maximum allowed volume setting.
2189 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2190 }
2191
2192 // Now we show up us to the world.
2193 pChannelStrip->show();
2194 // Only then, we'll auto-arrange...
2195 if (m_pOptions && m_pOptions->bAutoArrange) {
2196 int iWidth = m_pWorkspace->width();
2197 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
2198 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height(); pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
2199 }
2200
2201 // This is pretty new, so we'll watch for it closely.
2202 channelStripChanged(pChannelStrip);
2203
2204 // Return our successful reference...
2205 return pChannelStrip;
2206 }
2207
2208
2209 // Retrieve the active channel strip.
2210 ChannelStrip* MainForm::activeChannelStrip (void)
2211 {
2212 return (ChannelStrip*) m_pWorkspace->activeWindow();
2213 }
2214
2215
2216 // Retrieve a channel strip by index.
2217 ChannelStrip* MainForm::channelStripAt ( int iChannel )
2218 {
2219 QWidgetList wlist = m_pWorkspace->windowList();
2220 if (wlist.isEmpty())
2221 return NULL;
2222
2223 return (ChannelStrip*) wlist.at(iChannel);
2224 }
2225
2226
2227 // Retrieve a channel strip by sampler channel id.
2228 ChannelStrip* MainForm::channelStrip ( int iChannelID )
2229 {
2230 QWidgetList wlist = m_pWorkspace->windowList();
2231 if (wlist.isEmpty())
2232 return NULL;
2233
2234 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2235 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2236 if (pChannelStrip) {
2237 qsamplerChannel *pChannel = pChannelStrip->channel();
2238 if (pChannel && pChannel->channelID() == iChannelID)
2239 return pChannelStrip;
2240 }
2241 }
2242
2243 // Not found.
2244 return NULL;
2245 }
2246
2247
2248 // Construct the windows menu.
2249 void MainForm::channelsMenuAboutToShow (void)
2250 {
2251 ui.channelsMenu->clear();
2252 ui.channelsArrangeAction->addTo(ui.channelsMenu);
2253 ui.channelsAutoArrangeAction->addTo(ui.channelsMenu);
2254
2255 QWidgetList wlist = m_pWorkspace->windowList();
2256 if (!wlist.isEmpty()) {
2257 ui.channelsMenu->insertSeparator();
2258 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2259 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2260 if (pChannelStrip) {
2261 int iItemID = ui.channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
2262 ui.channelsMenu->setItemParameter(iItemID, iChannel);
2263 ui.channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
2264 }
2265 }
2266 }
2267 }
2268
2269
2270 // Windows menu activation slot
2271 void MainForm::channelsMenuActivated ( int iChannel )
2272 {
2273 ChannelStrip* pChannelStrip = channelStripAt(iChannel);
2274 if (pChannelStrip)
2275 pChannelStrip->showNormal();
2276 pChannelStrip->setFocus();
2277 }
2278
2279
2280 //-------------------------------------------------------------------------
2281 // qsamplerMainForm -- Timer stuff.
2282
2283 // Set the pseudo-timer delay schedule.
2284 void MainForm::startSchedule ( int iStartDelay )
2285 {
2286 m_iStartDelay = 1 + (iStartDelay * 1000);
2287 m_iTimerDelay = 0;
2288 }
2289
2290 // Suspend the pseudo-timer delay schedule.
2291 void MainForm::stopSchedule (void)
2292 {
2293 m_iStartDelay = 0;
2294 m_iTimerDelay = 0;
2295 }
2296
2297 // Timer slot funtion.
2298 void MainForm::timerSlot (void)
2299 {
2300 if (m_pOptions == NULL)
2301 return;
2302
2303 // Is it the first shot on server start after a few delay?
2304 if (m_iTimerDelay < m_iStartDelay) {
2305 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2306 if (m_iTimerDelay >= m_iStartDelay) {
2307 // If we cannot start it now, maybe a lil'mo'later ;)
2308 if (!startClient()) {
2309 m_iStartDelay += m_iTimerDelay;
2310 m_iTimerDelay = 0;
2311 }
2312 }
2313 }
2314
2315 if (m_pClient) {
2316 // Update the channel information for each pending strip...
2317 if (m_changedStrips.count() > 0) {
2318 for (ChannelStrip* pChannelStrip = m_changedStrips.first();
2319 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
2320 // If successfull, remove from pending list...
2321 if (pChannelStrip->updateChannelInfo())
2322 m_changedStrips.remove(pChannelStrip);
2323 }
2324 }
2325 // Refresh each channel usage, on each period...
2326 if (m_pOptions->bAutoRefresh) {
2327 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2328 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2329 m_iTimerSlot = 0;
2330 // Update the channel stream usage for each strip...
2331 QWidgetList wlist = m_pWorkspace->windowList();
2332 for (int iChannel = 0;
2333 iChannel < (int) wlist.count(); iChannel++) {
2334 ChannelStrip* pChannelStrip
2335 = (ChannelStrip*) wlist.at(iChannel);
2336 if (pChannelStrip && pChannelStrip->isVisible())
2337 pChannelStrip->updateChannelUsage();
2338 }
2339 }
2340 }
2341 }
2342
2343 // Register the next timer slot.
2344 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2345 }
2346
2347
2348 //-------------------------------------------------------------------------
2349 // qsamplerMainForm -- Server stuff.
2350
2351 // Start linuxsampler server...
2352 void MainForm::startServer (void)
2353 {
2354 if (m_pOptions == NULL)
2355 return;
2356
2357 // Aren't already a client, are we?
2358 if (!m_pOptions->bServerStart || m_pClient)
2359 return;
2360
2361 // Is the server process instance still here?
2362 if (m_pServer) {
2363 switch (QMessageBox::warning(this,
2364 QSAMPLER_TITLE ": " + tr("Warning"),
2365 tr("Could not start the LinuxSampler server.\n\n"
2366 "Maybe it ss already started."),
2367 tr("Stop"), tr("Kill"), tr("Cancel"))) {
2368 case 0:
2369 m_pServer->terminate();
2370 break;
2371 case 1:
2372 m_pServer->kill();
2373 break;
2374 }
2375 return;
2376 }
2377
2378 // Reset our timer counters...
2379 stopSchedule();
2380
2381 // OK. Let's build the startup process...
2382 m_pServer = new QProcess(this);
2383
2384 // Setup stdout/stderr capture...
2385 // if (m_pOptions->bStdoutCapture) {
2386 //m_pServer->setProcessChannelMode(
2387 // QProcess::StandardOutput);
2388 QObject::connect(m_pServer,
2389 SIGNAL(readyReadStandardOutput()),
2390 SLOT(readServerStdout()));
2391 QObject::connect(m_pServer,
2392 SIGNAL(readyReadStandardError()),
2393 SLOT(readServerStdout()));
2394 // }
2395 // The unforgiveable signal communication...
2396 QObject::connect(m_pServer,
2397 SIGNAL(finished(int,QProcess::ExitStatus)),
2398 SLOT(processServerExit()));
2399
2400 // Build process arguments...
2401 QStringList serverCmdLine = QStringList::split(' ', m_pOptions->sServerCmdLine);
2402
2403 appendMessages(tr("Server is starting..."));
2404 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2405
2406
2407
2408 const QString prog = (serverCmdLine.size() > 0) ? serverCmdLine[0] : QString();
2409 const QStringList args = serverCmdLine.mid(1);
2410
2411 // Go jack, go...
2412 m_pServer->start(prog, args);
2413 if (!m_pServer->waitForStarted()) {
2414 appendMessagesError(tr("Could not start server.\n\nSorry."));
2415 processServerExit();
2416 return;
2417 }
2418
2419 // Show startup results...
2420 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2421
2422 // Reset (yet again) the timer counters,
2423 // but this time is deferred as the user opted.
2424 startSchedule(m_pOptions->iStartDelay);
2425 stabilizeForm();
2426 }
2427
2428
2429 // Stop linuxsampler server...
2430 void MainForm::stopServer (void)
2431 {
2432 // Stop client code.
2433 stopClient();
2434
2435 // And try to stop server.
2436 if (m_pServer) {
2437 appendMessages(tr("Server is stopping..."));
2438 if (m_pServer->state() == QProcess::Running)
2439 m_pServer->terminate();
2440 }
2441
2442 // Give it some time to terminate gracefully and stabilize...
2443 QTime t;
2444 t.start();
2445 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2446 QApplication::processEvents(QEventLoop::ExcludeUserInput);
2447
2448 // Do final processing anyway.
2449 processServerExit();
2450 }
2451
2452
2453 // Stdout handler...
2454 void MainForm::readServerStdout (void)
2455 {
2456 if (m_pMessages)
2457 m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2458 }
2459
2460
2461 // Linuxsampler server cleanup.
2462 void MainForm::processServerExit (void)
2463 {
2464 // Force client code cleanup.
2465 stopClient();
2466
2467 // Flush anything that maybe pending...
2468 if (m_pMessages)
2469 m_pMessages->flushStdoutBuffer();
2470
2471 if (m_pServer) {
2472 // Force final server shutdown...
2473 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
2474 m_pServer->terminate();
2475 if (!m_pServer->waitForFinished(2000))
2476 m_pServer->kill();
2477 // Destroy it.
2478 delete m_pServer;
2479 m_pServer = NULL;
2480 }
2481
2482 // Again, make status visible stable.
2483 stabilizeForm();
2484 }
2485
2486
2487 //-------------------------------------------------------------------------
2488 // qsamplerMainForm -- Client stuff.
2489
2490 // The LSCP client callback procedure.
2491 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2492 {
2493 MainForm* pMainForm = (MainForm *) pvData;
2494 if (pMainForm == NULL)
2495 return LSCP_FAILED;
2496
2497 // ATTN: DO NOT EVER call any GUI code here,
2498 // as this is run under some other thread context.
2499 // A custom event must be posted here...
2500 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2501
2502 return LSCP_OK;
2503 }
2504
2505
2506 // Start our almighty client...
2507 bool MainForm::startClient (void)
2508 {
2509 // Have it a setup?
2510 if (m_pOptions == NULL)
2511 return false;
2512
2513 // Aren't we already started, are we?
2514 if (m_pClient)
2515 return true;
2516
2517 // Log prepare here.
2518 appendMessages(tr("Client connecting..."));
2519
2520 // Create the client handle...
2521 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2522 if (m_pClient == NULL) {
2523 // Is this the first try?
2524 // maybe we need to start a local server...
2525 if ((m_pServer && m_pServer->state() == QProcess::Running) || !m_pOptions->bServerStart)
2526 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2527 else
2528 startServer();
2529 // This is always a failure.
2530 stabilizeForm();
2531 return false;
2532 }
2533 // Just set receive timeout value, blindly.
2534 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2535 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2536
2537 // Subscribe to channel info change notifications...
2538 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2539 appendMessagesClient("lscp_client_subscribe");
2540
2541 // We may stop scheduling around.
2542 stopSchedule();
2543
2544 // We'll accept drops from now on...
2545 setAcceptDrops(true);
2546
2547 // Log success here.
2548 appendMessages(tr("Client connected."));
2549
2550 // Hard-notify instrumnet and device configuration forms,
2551 // if visible, that we're ready...
2552 if (m_pInstrumentListForm)
2553 m_pInstrumentListForm->refreshInstruments();
2554 if (m_pDeviceForm)
2555 m_pDeviceForm->refreshDevices();
2556
2557 // Is any session pending to be loaded?
2558 if (!m_pOptions->sSessionFile.isEmpty()) {
2559 // Just load the prabably startup session...
2560 if (loadSessionFile(m_pOptions->sSessionFile)) {
2561 m_pOptions->sSessionFile = QString::null;
2562 return true;
2563 }
2564 }
2565
2566 // Make a new session
2567 return newSession();
2568 }
2569
2570
2571 // Stop client...
2572 void MainForm::stopClient (void)
2573 {
2574 if (m_pClient == NULL)
2575 return;
2576
2577 // Log prepare here.
2578 appendMessages(tr("Client disconnecting..."));
2579
2580 // Clear timer counters...
2581 stopSchedule();
2582
2583 // We'll reject drops from now on...
2584 setAcceptDrops(false);
2585
2586 // Force any channel strips around, but
2587 // but avoid removing the corresponding
2588 // channels from the back-end server.
2589 m_iDirtyCount = 0;
2590 closeSession(false);
2591
2592 // Close us as a client...
2593 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2594 ::lscp_client_destroy(m_pClient);
2595 m_pClient = NULL;
2596
2597 // Hard-notify instrumnet and device configuration forms,
2598 // if visible, that we're running out...
2599 if (m_pInstrumentListForm)
2600 m_pInstrumentListForm->refreshInstruments();
2601 if (m_pDeviceForm)
2602 m_pDeviceForm->refreshDevices();
2603
2604 // Log final here.
2605 appendMessages(tr("Client disconnected."));
2606
2607 // Make visible status.
2608 stabilizeForm();
2609 }
2610
2611 } // namespace QSampler
2612
2613
2614 // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC