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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1698 - (hide annotations) (download)
Sat Feb 16 19:41:05 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 77112 byte(s)
* implemented MIDI device activity windows, selectable from the "View"
  main menu, still quite ugly I admit ;-)
* bumped version to 0.2.1.7

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

  ViewVC Help
Powered by ViewVC