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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1514 - (show annotations) (download)
Fri Nov 23 10:51:37 2007 UTC (16 years, 4 months ago) by capela
File size: 74718 byte(s)
* The current selected/activated channel-strip is now visually
  evident while in the application workspace (highlighting).

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

  ViewVC Help
Powered by ViewVC