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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1815 - (hide annotations) (download)
Mon Dec 22 10:04:59 2008 UTC (15 years, 4 months ago) by capela
File size: 80946 byte(s)
* Tuning options tab layout stretch; bumped to 0.2.1.21

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

  ViewVC Help
Powered by ViewVC