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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1702 - (hide annotations) (download)
Sun Feb 17 13:53:00 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 79680 byte(s)
* automatically add & remove channel strips as they were removed e.g. by
  other frontends
* refresh device management dialog on external audio device changes as well
* bumped version to 0.2.1.9

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

  ViewVC Help
Powered by ViewVC