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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1738 - (hide annotations) (download)
Wed May 14 15:24:22 2008 UTC (15 years, 11 months ago) by capela
File size: 80179 byte(s)
* Messages file logging makes its first long overdue appearance,
  with user configurable settings in View/Options.../Server/Logging.

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

  ViewVC Help
Powered by ViewVC