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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1693 - (hide annotations) (download)
Fri Feb 15 17:08:33 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 75424 byte(s)
* bugfix: MIDI activity was only shown on the 1st channel strip

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

  ViewVC Help
Powered by ViewVC