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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1515 - (hide annotations) (download)
Fri Nov 23 18:15:33 2007 UTC (16 years, 4 months ago) by capela
File size: 74028 byte(s)
* Fixed sure crash when removing channel strips.
* Force auto-arrange when adding channel strips,
  among other incremental fixes.

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

  ViewVC Help
Powered by ViewVC