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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1699 - (hide annotations) (download)
Sun Feb 17 10:46:17 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 77646 byte(s)
* refresh device management dialog when device informations changed,
  e.g. on changes caused by other frontends
  (fixes segfault on device changes)
* rebuild device status window on device info changes (including ones
  caused by other frontends)
* bugfix: close all device status windows when main window was closed

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

  ViewVC Help
Powered by ViewVC