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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1507 - (hide annotations) (download)
Wed Nov 21 23:22:18 2007 UTC (16 years, 4 months ago) by capela
File size: 78898 byte(s)
- Qt4 migration: more tiny-fixes, specially on the sampler channel
strip appearence and channels menu content.

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

  ViewVC Help
Powered by ViewVC