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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1461 - (hide annotations) (download)
Sun Oct 28 23:30:36 2007 UTC (16 years, 5 months ago) by schoenebeck
File size: 76027 byte(s)
* started to port QSampler to Qt4 (NOTE: this version is yet broken, use
  the latest tarball release 0.1.5 until the Qt4 port is completed)

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

  ViewVC Help
Powered by ViewVC