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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1474 - (hide annotations) (download)
Mon Nov 5 20:47:38 2007 UTC (16 years, 4 months ago) by schoenebeck
File size: 79074 byte(s)
* Qt4 migration: fixed another bunch of ghost connections, fixed engine
  combo box in channel form, fixed stdout ouptut in message window (a lot
  of white lines were shown), show channel strip on the work space (and not
  in the nirvana of the desktop universe)

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

  ViewVC Help
Powered by ViewVC