/[svn]/qsampler/trunk/src/qsamplerMainForm.ui.h
ViewVC logotype

Annotation of /qsampler/trunk/src/qsamplerMainForm.ui.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 479 - (hide annotations) (download) (as text)
Sat Mar 19 12:10:41 2005 UTC (15 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65742 byte(s)
* Fixed MIDI device parameter settings on session save.

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

  ViewVC Help
Powered by ViewVC