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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1499 - (hide annotations) (download)
Tue Nov 20 16:48:04 2007 UTC (16 years, 4 months ago) by capela
File size: 78757 byte(s)
* Qt4 migration: one first step forward to kiss Qt3Support goodbye.

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

  ViewVC Help
Powered by ViewVC