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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1514 - (hide annotations) (download)
Fri Nov 23 10:51:37 2007 UTC (16 years, 4 months ago) by capela
File size: 74718 byte(s)
* The current selected/activated channel-strip is now visually
  evident while in the application workspace (highlighting).

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

  ViewVC Help
Powered by ViewVC