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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1626 - (hide annotations) (download)
Sat Jan 5 13:29:11 2008 UTC (16 years, 2 months ago) by schoenebeck
File size: 74776 byte(s)
* Added dialog when the application exits which lets the user decide
  whether to keep the LinuxSampler running or not.

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

  ViewVC Help
Powered by ViewVC