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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1626 - (show annotations) (download)
Sat Jan 5 13:29:11 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 74776 byte(s)
* Added dialog when the application exits which lets the user decide
  whether to keep the LinuxSampler running or not.

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

  ViewVC Help
Powered by ViewVC