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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2038 - (hide annotations) (download)
Thu Jan 7 18:42:26 2010 UTC (10 years, 1 month ago) by capela
File size: 81469 byte(s)
* MIDI Device Status menu is disabled when no MIDI device exists;
  a menu separator has been added.

* Window manager's close button was found missing from the Devices
  and Instruments widgets when on Qt >= 4.5, now fixed.

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