/[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 455 - (hide annotations) (download) (as text)
Mon Mar 14 12:59:27 2005 UTC (19 years ago) by capela
File MIME type: text/x-c++hdr
File size: 64096 byte(s)
* Device configuration is now included and saved on session files.

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 capela 388 break;
602 capela 109 }
603     }
604     // Try to make it snappy :)
605     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
606     }
607    
608     // Ok. we've read it.
609     file.close();
610    
611     // Have we any errors?
612     if (iErrors > 0)
613 capela 388 appendMessagesError(tr("Session could not be loaded\nfrom \"%1\".\n\nSorry.").arg(sFilename));
614 capela 109
615 capela 395 // Now we'll try to create (update) the whole GUI session.
616     updateSession();
617 capela 109
618     // Save as default session directory.
619     if (m_pOptions)
620     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
621     // We're not dirty anymore.
622     m_iDirtyCount = 0;
623     // Stabilize form...
624     m_sFilename = sFilename;
625     updateRecentFiles(sFilename);
626     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
627 capela 301
628     // Make that an overall update.
629 capela 109 stabilizeForm();
630     return true;
631     }
632    
633    
634     // Save current session to specific file path.
635     bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
636     {
637     // Open and write into real file.
638     QFile file(sFilename);
639     if (!file.open(IO_WriteOnly | IO_Truncate)) {
640     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
641     return false;
642     }
643    
644     // Write the file.
645 capela 455 int iErrors = 0;
646 capela 109 QTextStream ts(&file);
647     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
648     ts << "# " << tr("Version")
649     << ": " QSAMPLER_VERSION << endl;
650     ts << "# " << tr("Build")
651     << ": " __DATE__ " " __TIME__ << endl;
652     ts << "#" << endl;
653     ts << "# " << tr("File")
654     << ": " << QFileInfo(sFilename).fileName() << endl;
655     ts << "# " << tr("Date")
656 capela 455 << ": " << QDate::currentDate().toString("MMM dd yyyy")
657 capela 109 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
658     ts << "#" << endl;
659     ts << endl;
660 capela 455 // It is assumed that this new kind of device+session file
661     // will be loaded from a complete
662     int *piDeviceIDs;
663     int iDevice;
664     ts << "RESET" << endl;
665     // Audio device mapping.
666     QMap<int, int> audioDeviceMap;
667     piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Audio);
668     for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
669     qsamplerDevice device(m_pClient, qsamplerDevice::Audio, piDeviceIDs[iDevice]);
670     ts << endl;
671     ts << "# " << device.deviceTypeName() << " " << device.driverName()
672     << " " << tr("Device") << " " << iDevice << endl;
673     ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
674     qsamplerDeviceParamMap& params = device.params();
675     qsamplerDeviceParamMap::ConstIterator iter;
676     for (iter = params.begin(); iter != params.end(); ++iter)
677     ts << " " << iter.key() << "='" << iter.data().value << "'";
678     ts << endl;
679     audioDeviceMap[device.deviceID()] = iDevice;
680     // Try to keep it snappy :)
681     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
682     }
683     // MIDI device mapping.
684     QMap<int, int> midiDeviceMap;
685     piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Midi);
686     for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
687     qsamplerDevice device(m_pClient, qsamplerDevice::Midi, piDeviceIDs[iDevice]);
688     ts << "# " << device.deviceTypeName() << " " << device.driverName()
689     << " " << tr("Device") << " " << iDevice << endl;
690     ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
691     qsamplerDeviceParamMap& params = device.params();
692     qsamplerDeviceParamMap::ConstIterator iter;
693     for (iter = params.begin(); iter != params.end(); ++iter)
694     ts << " " << iter.key() << "='" << iter.data().value << "'";
695     ts << endl;
696     midiDeviceMap[device.deviceID()] = iDevice;
697     // Try to keep it snappy :)
698     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
699     }
700     ts << endl;
701     // Sampler channel mapping.
702 capela 109 QWidgetList wlist = m_pWorkspace->windowList();
703     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
704 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
705     if (pChannelStrip) {
706     qsamplerChannel *pChannel = pChannelStrip->channel();
707     if (pChannel) {
708 capela 455 ts << "# " << tr("Channel") << " " << iChannel << endl;
709 capela 264 ts << "ADD CHANNEL" << endl;
710 capela 455 if (audioDeviceMap.isEmpty()) {
711     ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
712     << " " << pChannel->audioDriver() << endl;
713     } else {
714     ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
715     << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
716     }
717     if (midiDeviceMap.isEmpty()) {
718     ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
719     << " " << pChannel->midiDriver() << endl;
720     } else {
721     ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
722     << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
723     }
724     ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
725     << " " << pChannel->midiPort() << endl;
726 capela 409 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
727 capela 304 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
728     ts << "ALL";
729     else
730 capela 264 ts << pChannel->midiChannel();
731     ts << endl;
732 capela 409 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
733     ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl;
734     ts << "SET CHANNEL VOLUME " << iChannel << " " << pChannel->volume() << endl;
735 capela 264 ts << endl;
736     }
737     }
738 capela 109 // Try to keep it snappy :)
739     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
740     }
741    
742     // Ok. we've wrote it.
743     file.close();
744    
745     // Have we any errors?
746     if (iErrors > 0)
747     appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
748    
749     // Save as default session directory.
750     if (m_pOptions)
751     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
752     // We're not dirty anymore.
753     m_iDirtyCount = 0;
754     // Stabilize form...
755     m_sFilename = sFilename;
756     updateRecentFiles(sFilename);
757     appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
758     stabilizeForm();
759     return true;
760     }
761    
762    
763 capela 433 // Session change receiver slot.
764     void qsamplerMainForm::sessionDirty (void)
765     {
766     // Just mark the dirty form.
767     m_iDirtyCount++;
768     // and update the form status...
769     stabilizeForm();
770     }
771    
772    
773 capela 109 //-------------------------------------------------------------------------
774     // qsamplerMainForm -- File Action slots.
775    
776     // Create a new sampler session.
777     void qsamplerMainForm::fileNew (void)
778     {
779     // Of course we'll start clean new.
780     newSession();
781     }
782    
783    
784     // Open an existing sampler session.
785     void qsamplerMainForm::fileOpen (void)
786     {
787     // Open it right away.
788     openSession();
789     }
790    
791    
792     // Open a recent file session.
793     void qsamplerMainForm::fileOpenRecent ( int iIndex )
794     {
795     // Check if we can safely close the current session...
796     if (m_pOptions && closeSession(true)) {
797     QString sFilename = m_pOptions->recentFiles[iIndex];
798     loadSessionFile(sFilename);
799     }
800     }
801    
802    
803     // Save current sampler session.
804     void qsamplerMainForm::fileSave (void)
805     {
806     // Save it right away.
807     saveSession(false);
808     }
809    
810    
811     // Save current sampler session with another name.
812     void qsamplerMainForm::fileSaveAs (void)
813     {
814     // Save it right away, maybe with another name.
815     saveSession(true);
816     }
817    
818    
819 capela 255 // Reset the sampler instance.
820     void qsamplerMainForm::fileReset (void)
821     {
822     if (m_pClient == NULL)
823     return;
824    
825     // Ask user whether he/she want's an internal sampler reset...
826     if (QMessageBox::warning(this, tr("Warning"),
827     tr("Resetting the sampler instance will close\n"
828     "all device and channel configurations.\n\n"
829     "Please note that this operation may cause\n"
830     "temporary MIDI and Audio disruption\n\n"
831     "Do you want to reset the sampler engine now?"),
832     tr("Reset"), tr("Cancel")) > 0)
833     return;
834    
835     // Just do the reset, after closing down current session...
836 capela 261 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
837 capela 255 appendMessagesClient("lscp_reset_sampler");
838 capela 261 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
839     return;
840     }
841    
842     // Log this.
843     appendMessages(tr("Sampler reset."));
844 capela 395
845     // Make it a new session...
846     newSession();
847 capela 255 }
848    
849    
850 capela 109 // Restart the client/server instance.
851     void qsamplerMainForm::fileRestart (void)
852     {
853     if (m_pOptions == NULL)
854     return;
855    
856     bool bRestart = true;
857    
858     // Ask user whether he/she want's a complete restart...
859     // (if we're currently up and running)
860     if (bRestart && m_pClient) {
861     bRestart = (QMessageBox::warning(this, tr("Warning"),
862     tr("New settings will be effective after\n"
863     "restarting the client/server connection.\n\n"
864     "Please note that this operation may cause\n"
865     "temporary MIDI and Audio disruption\n\n"
866     "Do you want to restart the connection now?"),
867     tr("Restart"), tr("Cancel")) == 0);
868     }
869    
870     // Are we still for it?
871     if (bRestart && closeSession(true)) {
872     // Stop server, it will force the client too.
873     stopServer();
874     // Reschedule a restart...
875     startSchedule(m_pOptions->iStartDelay);
876     }
877     }
878    
879    
880     // Exit application program.
881     void qsamplerMainForm::fileExit (void)
882     {
883     // Go for close the whole thing.
884     close();
885     }
886    
887    
888     //-------------------------------------------------------------------------
889     // qsamplerMainForm -- Edit Action slots.
890    
891     // Add a new sampler channel.
892     void qsamplerMainForm::editAddChannel (void)
893     {
894     if (m_pClient == NULL)
895     return;
896    
897 capela 303 // Just create the channel instance...
898     qsamplerChannel *pChannel = new qsamplerChannel(this);
899     if (pChannel == NULL)
900     return;
901    
902     // Before we show it up, may be we'll
903     // better ask for some initial values?
904     if (!pChannel->channelSetup(this)) {
905     delete pChannel;
906     return;
907     }
908    
909     // And give it to the strip (will own the channel instance, if successful).
910     if (!createChannelStrip(pChannel)) {
911     delete pChannel;
912     return;
913     }
914    
915     // Make that an overall update.
916     m_iDirtyCount++;
917     stabilizeForm();
918 capela 109 }
919    
920    
921     // Remove current sampler channel.
922     void qsamplerMainForm::editRemoveChannel (void)
923     {
924     if (m_pClient == NULL)
925     return;
926    
927 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
928     if (pChannelStrip == NULL)
929     return;
930    
931     qsamplerChannel *pChannel = pChannelStrip->channel();
932 capela 109 if (pChannel == NULL)
933     return;
934    
935     // Prompt user if he/she's sure about this...
936     if (m_pOptions && m_pOptions->bConfirmRemove) {
937     if (QMessageBox::warning(this, tr("Warning"),
938     tr("About to remove channel:\n\n"
939     "%1\n\n"
940     "Are you sure?")
941 capela 264 .arg(pChannelStrip->caption()),
942 capela 109 tr("OK"), tr("Cancel")) > 0)
943     return;
944     }
945    
946     // Remove the existing sampler channel.
947 capela 295 if (!pChannel->removeChannel())
948 capela 109 return;
949    
950     // Just delete the channel strip.
951 capela 265 delete pChannelStrip;
952    
953 capela 109 // Do we auto-arrange?
954     if (m_pOptions && m_pOptions->bAutoArrange)
955     channelsArrange();
956    
957     // We'll be dirty, for sure...
958     m_iDirtyCount++;
959     stabilizeForm();
960     }
961    
962    
963     // Setup current sampler channel.
964     void qsamplerMainForm::editSetupChannel (void)
965     {
966     if (m_pClient == NULL)
967     return;
968    
969 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
970     if (pChannelStrip == NULL)
971 capela 109 return;
972    
973     // Just invoque the channel strip procedure.
974 capela 295 pChannelStrip->channelSetup();
975 capela 109 }
976    
977    
978     // Reset current sampler channel.
979     void qsamplerMainForm::editResetChannel (void)
980     {
981     if (m_pClient == NULL)
982     return;
983    
984 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
985     if (pChannelStrip == NULL)
986     return;
987    
988 capela 400 // Just invoque the channel strip procedure.
989     pChannelStrip->channelReset();
990 capela 109 }
991    
992    
993 capela 404 // Reset all sampler channels.
994     void qsamplerMainForm::editResetAllChannels (void)
995     {
996     if (m_pClient == NULL)
997     return;
998    
999     // Invoque the channel strip procedure,
1000     // for all channels out there...
1001     m_pWorkspace->setUpdatesEnabled(false);
1002     QWidgetList wlist = m_pWorkspace->windowList();
1003     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1004     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1005     if (pChannelStrip)
1006     pChannelStrip->channelReset();
1007     }
1008     m_pWorkspace->setUpdatesEnabled(true);
1009     }
1010    
1011    
1012 capela 109 //-------------------------------------------------------------------------
1013     // qsamplerMainForm -- View Action slots.
1014    
1015     // Show/hide the main program window menubar.
1016     void qsamplerMainForm::viewMenubar ( bool bOn )
1017     {
1018     if (bOn)
1019     MenuBar->show();
1020     else
1021     MenuBar->hide();
1022     }
1023    
1024    
1025     // Show/hide the main program window toolbar.
1026     void qsamplerMainForm::viewToolbar ( bool bOn )
1027     {
1028     if (bOn) {
1029     fileToolbar->show();
1030     editToolbar->show();
1031     channelsToolbar->show();
1032     } else {
1033     fileToolbar->hide();
1034     editToolbar->hide();
1035     channelsToolbar->hide();
1036     }
1037     }
1038    
1039    
1040     // Show/hide the main program window statusbar.
1041     void qsamplerMainForm::viewStatusbar ( bool bOn )
1042     {
1043     if (bOn)
1044     statusBar()->show();
1045     else
1046     statusBar()->hide();
1047     }
1048    
1049    
1050     // Show/hide the messages window logger.
1051     void qsamplerMainForm::viewMessages ( bool bOn )
1052     {
1053     if (bOn)
1054     m_pMessages->show();
1055     else
1056     m_pMessages->hide();
1057     }
1058    
1059    
1060 capela 428 // Show/hide the device configurator form.
1061     void qsamplerMainForm::viewDevices (void)
1062     {
1063     if (m_pOptions == NULL)
1064     return;
1065    
1066     if (m_pDeviceForm) {
1067     m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1068     m_pDeviceForm->setClient(m_pClient);
1069     if (m_pDeviceForm->isVisible()) {
1070     m_pDeviceForm->hide();
1071     } else {
1072     m_pDeviceForm->show();
1073     m_pDeviceForm->raise();
1074     m_pDeviceForm->setActiveWindow();
1075     }
1076     }
1077     }
1078    
1079    
1080 capela 109 // Show options dialog.
1081     void qsamplerMainForm::viewOptions (void)
1082     {
1083     if (m_pOptions == NULL)
1084     return;
1085    
1086     qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1087     if (pOptionsForm) {
1088     // Check out some initial nullities(tm)...
1089 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1090     if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1091     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1092 capela 109 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1093     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1094     // To track down deferred or immediate changes.
1095     QString sOldServerHost = m_pOptions->sServerHost;
1096     int iOldServerPort = m_pOptions->iServerPort;
1097     int iOldServerTimeout = m_pOptions->iServerTimeout;
1098     bool bOldServerStart = m_pOptions->bServerStart;
1099     QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1100     QString sOldDisplayFont = m_pOptions->sDisplayFont;
1101 capela 267 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1102 capela 119 int iOldMaxVolume = m_pOptions->iMaxVolume;
1103 capela 109 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1104 capela 454 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1105 capela 109 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1106     int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1107     int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1108     bool bOldCompletePath = m_pOptions->bCompletePath;
1109 capela 371 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1110 capela 109 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1111     // Load the current setup settings.
1112     pOptionsForm->setup(m_pOptions);
1113     // Show the setup dialog...
1114     if (pOptionsForm->exec()) {
1115     // Warn if something will be only effective on next run.
1116     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1117 capela 454 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1118     ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1119     (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1120 capela 109 QMessageBox::information(this, tr("Information"),
1121     tr("Some settings may be only effective\n"
1122     "next time you start this program."), tr("OK"));
1123     updateMessagesCapture();
1124     }
1125     // Check wheather something immediate has changed.
1126     if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1127     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1128     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1129     updateRecentFilesMenu();
1130 capela 371 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1131     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1132     updateInstrumentNames();
1133 capela 267 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1134     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1135     updateDisplayEffect();
1136 capela 109 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1137     updateDisplayFont();
1138 capela 119 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1139     updateMaxVolume();
1140 capela 109 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1141     updateMessagesFont();
1142     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1143     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1144     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1145     updateMessagesLimit();
1146     // And now the main thing, whether we'll do client/server recycling?
1147     if ((sOldServerHost != m_pOptions->sServerHost) ||
1148     (iOldServerPort != m_pOptions->iServerPort) ||
1149     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1150     ( bOldServerStart && !m_pOptions->bServerStart) ||
1151     (!bOldServerStart && m_pOptions->bServerStart) ||
1152     (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1153     fileRestart();
1154     }
1155     // Done.
1156     delete pOptionsForm;
1157     }
1158    
1159     // This makes it.
1160     stabilizeForm();
1161     }
1162    
1163    
1164     //-------------------------------------------------------------------------
1165     // qsamplerMainForm -- Channels action slots.
1166    
1167     // Arrange channel strips.
1168     void qsamplerMainForm::channelsArrange (void)
1169     {
1170     // Full width vertical tiling
1171     QWidgetList wlist = m_pWorkspace->windowList();
1172     if (wlist.isEmpty())
1173     return;
1174    
1175     m_pWorkspace->setUpdatesEnabled(false);
1176     int y = 0;
1177     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1178 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1179     /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1180 capela 109 // Prevent flicker...
1181 capela 264 pChannelStrip->hide();
1182     pChannelStrip->showNormal();
1183 capela 109 } */
1184 capela 264 pChannelStrip->adjustSize();
1185 capela 109 int iWidth = m_pWorkspace->width();
1186 capela 264 if (iWidth < pChannelStrip->width())
1187     iWidth = pChannelStrip->width();
1188     // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1189     int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1190     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1191 capela 109 y += iHeight;
1192     }
1193     m_pWorkspace->setUpdatesEnabled(true);
1194    
1195     stabilizeForm();
1196     }
1197    
1198    
1199     // Auto-arrange channel strips.
1200     void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1201     {
1202     if (m_pOptions == NULL)
1203     return;
1204    
1205     // Toggle the auto-arrange flag.
1206     m_pOptions->bAutoArrange = bOn;
1207    
1208     // If on, update whole workspace...
1209     if (m_pOptions->bAutoArrange)
1210     channelsArrange();
1211     }
1212    
1213    
1214     //-------------------------------------------------------------------------
1215     // qsamplerMainForm -- Help Action slots.
1216    
1217     // Show information about the Qt toolkit.
1218     void qsamplerMainForm::helpAboutQt (void)
1219     {
1220     QMessageBox::aboutQt(this);
1221     }
1222    
1223    
1224     // Show information about application program.
1225     void qsamplerMainForm::helpAbout (void)
1226     {
1227     // Stuff the about box text...
1228     QString sText = "<p>\n";
1229     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1230     sText += "<br />\n";
1231     sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1232     sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1233     #ifdef CONFIG_DEBUG
1234     sText += "<small><font color=\"red\">";
1235     sText += tr("Debugging option enabled.");
1236     sText += "</font></small><br />";
1237     #endif
1238 capela 176 #ifndef CONFIG_LIBGIG
1239     sText += "<small><font color=\"red\">";
1240     sText += tr("GIG (libgig) file support disabled.");
1241     sText += "</font></small><br />";
1242     #endif
1243 capela 382 #ifndef CONFIG_INSTRUMENT_NAME
1244     sText += "<small><font color=\"red\">";
1245     sText += tr("LSCP (liblscp) instrument_name support disabled.");
1246     sText += "</font></small><br />";
1247     #endif
1248 capela 109 sText += "<br />\n";
1249     sText += tr("Using") + ": ";
1250     sText += ::lscp_client_package();
1251     sText += " ";
1252     sText += ::lscp_client_version();
1253     sText += "<br />\n";
1254     sText += "<br />\n";
1255     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1256     sText += "<br />\n";
1257     sText += "<small>";
1258     sText += QSAMPLER_COPYRIGHT "<br />\n";
1259     sText += "<br />\n";
1260     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1261     sText += tr("under the terms of the GNU General Public License version 2 or later.");
1262     sText += "</small>";
1263     sText += "</p>\n";
1264    
1265     QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1266     }
1267    
1268    
1269     //-------------------------------------------------------------------------
1270     // qsamplerMainForm -- Main window stabilization.
1271    
1272     void qsamplerMainForm::stabilizeForm (void)
1273     {
1274     // Update the main application caption...
1275 capela 418 QString sSessionName = sessionName(m_sFilename);
1276 capela 109 if (m_iDirtyCount > 0)
1277 capela 418 sSessionName += '*';
1278     setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1279 capela 109
1280     // Update the main menu state...
1281 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1282 capela 109 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1283 capela 264 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1284 capela 109 fileNewAction->setEnabled(bHasClient);
1285     fileOpenAction->setEnabled(bHasClient);
1286     fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1287     fileSaveAsAction->setEnabled(bHasClient);
1288 capela 255 fileResetAction->setEnabled(bHasClient);
1289 capela 109 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1290     editAddChannelAction->setEnabled(bHasClient);
1291     editRemoveChannelAction->setEnabled(bHasChannel);
1292     editSetupChannelAction->setEnabled(bHasChannel);
1293     editResetChannelAction->setEnabled(bHasChannel);
1294 capela 404 editResetAllChannelsAction->setEnabled(bHasChannel);
1295 capela 428 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1296     viewDevicesAction->setOn(m_pDeviceForm && m_pDeviceForm->isVisible());
1297     viewDevicesAction->setEnabled(bHasClient);
1298 capela 109 channelsArrangeAction->setEnabled(bHasChannel);
1299    
1300     // Client/Server status...
1301     if (bHasClient) {
1302 capela 428 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1303     m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1304 capela 109 } else {
1305 capela 428 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1306     m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1307 capela 109 }
1308     // Channel status...
1309     if (bHasChannel)
1310 capela 428 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1311 capela 109 else
1312 capela 428 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1313 capela 109 // Session status...
1314     if (m_iDirtyCount > 0)
1315 capela 428 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1316 capela 109 else
1317 capela 428 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1318 capela 109
1319     // Recent files menu.
1320     m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1321    
1322     // Always make the latest message visible.
1323     if (m_pMessages)
1324     m_pMessages->scrollToBottom();
1325     }
1326    
1327    
1328     // Channel change receiver slot.
1329 capela 400 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1330 capela 109 {
1331 capela 400 // Add this strip to the changed list...
1332     if (m_changedStrips.containsRef(pChannelStrip) == 0)
1333     m_changedStrips.append(pChannelStrip);
1334    
1335 capela 109 // Just mark the dirty form.
1336     m_iDirtyCount++;
1337     // and update the form status...
1338     stabilizeForm();
1339     }
1340    
1341    
1342 capela 395 // Grab and restore current sampler channels session.
1343     void qsamplerMainForm::updateSession (void)
1344     {
1345     // Retrieve the current channel list.
1346     int *piChannelIDs = ::lscp_list_channels(m_pClient);
1347     if (piChannelIDs == NULL) {
1348     if (::lscp_client_get_errno(m_pClient)) {
1349     appendMessagesClient("lscp_list_channels");
1350     appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1351     }
1352     return;
1353     }
1354    
1355     // Try to (re)create each channel.
1356     m_pWorkspace->setUpdatesEnabled(false);
1357     for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1358     // Check if theres already a channel strip for this one...
1359     if (!channelStrip(piChannelIDs[iChannel]))
1360     createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1361     // Make it visibly responsive...
1362     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1363     }
1364     m_pWorkspace->setUpdatesEnabled(true);
1365     }
1366    
1367    
1368 capela 109 // Update the recent files list and menu.
1369     void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1370     {
1371     if (m_pOptions == NULL)
1372     return;
1373    
1374     // Remove from list if already there (avoid duplicates)
1375     QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1376     if (iter != m_pOptions->recentFiles.end())
1377     m_pOptions->recentFiles.remove(iter);
1378     // Put it to front...
1379     m_pOptions->recentFiles.push_front(sFilename);
1380    
1381     // May update the menu.
1382     updateRecentFilesMenu();
1383     }
1384    
1385    
1386     // Update the recent files list and menu.
1387     void qsamplerMainForm::updateRecentFilesMenu (void)
1388     {
1389     if (m_pOptions == NULL)
1390     return;
1391    
1392     // Time to keep the list under limits.
1393     int iRecentFiles = m_pOptions->recentFiles.count();
1394     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1395     m_pOptions->recentFiles.pop_back();
1396     iRecentFiles--;
1397     }
1398    
1399     // rebuild the recent files menu...
1400     m_pRecentFilesMenu->clear();
1401     for (int i = 0; i < iRecentFiles; i++) {
1402     const QString& sFilename = m_pOptions->recentFiles[i];
1403     if (QFileInfo(sFilename).exists()) {
1404     m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1405     .arg(i + 1).arg(sessionName(sFilename)),
1406     this, SLOT(fileOpenRecent(int)), 0, i);
1407     }
1408     }
1409     }
1410    
1411    
1412 capela 371 // Force update of the channels instrument names mode.
1413     void qsamplerMainForm::updateInstrumentNames (void)
1414     {
1415     // Full channel list update...
1416     QWidgetList wlist = m_pWorkspace->windowList();
1417     if (wlist.isEmpty())
1418     return;
1419    
1420     m_pWorkspace->setUpdatesEnabled(false);
1421     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1422     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1423     if (pChannelStrip)
1424     pChannelStrip->updateInstrumentName(true);
1425     }
1426     m_pWorkspace->setUpdatesEnabled(true);
1427     }
1428    
1429    
1430 capela 109 // Force update of the channels display font.
1431     void qsamplerMainForm::updateDisplayFont (void)
1432     {
1433     if (m_pOptions == NULL)
1434     return;
1435    
1436     // Check if display font is legal.
1437     if (m_pOptions->sDisplayFont.isEmpty())
1438     return;
1439     // Realize it.
1440     QFont font;
1441     if (!font.fromString(m_pOptions->sDisplayFont))
1442     return;
1443    
1444     // Full channel list update...
1445     QWidgetList wlist = m_pWorkspace->windowList();
1446     if (wlist.isEmpty())
1447     return;
1448    
1449     m_pWorkspace->setUpdatesEnabled(false);
1450     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1451 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1452     if (pChannelStrip)
1453     pChannelStrip->setDisplayFont(font);
1454 capela 109 }
1455     m_pWorkspace->setUpdatesEnabled(true);
1456     }
1457    
1458    
1459 capela 267 // Update channel strips background effect.
1460     void qsamplerMainForm::updateDisplayEffect (void)
1461     {
1462     QPixmap pm;
1463     if (m_pOptions->bDisplayEffect)
1464     pm = QPixmap::fromMimeSource("displaybg1.png");
1465    
1466     // Full channel list update...
1467     QWidgetList wlist = m_pWorkspace->windowList();
1468     if (wlist.isEmpty())
1469     return;
1470    
1471     m_pWorkspace->setUpdatesEnabled(false);
1472     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1473     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1474     if (pChannelStrip)
1475     pChannelStrip->setDisplayBackground(pm);
1476     }
1477     m_pWorkspace->setUpdatesEnabled(true);
1478     }
1479    
1480    
1481 capela 119 // Force update of the channels maximum volume setting.
1482     void qsamplerMainForm::updateMaxVolume (void)
1483     {
1484     if (m_pOptions == NULL)
1485     return;
1486    
1487     // Full channel list update...
1488     QWidgetList wlist = m_pWorkspace->windowList();
1489     if (wlist.isEmpty())
1490     return;
1491    
1492     m_pWorkspace->setUpdatesEnabled(false);
1493     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1494 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1495     if (pChannelStrip)
1496     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1497 capela 119 }
1498     m_pWorkspace->setUpdatesEnabled(true);
1499     }
1500    
1501    
1502 capela 109 //-------------------------------------------------------------------------
1503     // qsamplerMainForm -- Messages window form handlers.
1504    
1505     // Messages output methods.
1506     void qsamplerMainForm::appendMessages( const QString& s )
1507     {
1508     if (m_pMessages)
1509     m_pMessages->appendMessages(s);
1510    
1511     statusBar()->message(s, 3000);
1512     }
1513    
1514     void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1515     {
1516     if (m_pMessages)
1517     m_pMessages->appendMessagesColor(s, c);
1518    
1519     statusBar()->message(s, 3000);
1520     }
1521    
1522     void qsamplerMainForm::appendMessagesText( const QString& s )
1523     {
1524     if (m_pMessages)
1525     m_pMessages->appendMessagesText(s);
1526     }
1527    
1528     void qsamplerMainForm::appendMessagesError( const QString& s )
1529     {
1530     if (m_pMessages)
1531     m_pMessages->show();
1532    
1533     appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1534    
1535     QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1536     }
1537    
1538    
1539     // This is a special message format, just for client results.
1540     void qsamplerMainForm::appendMessagesClient( const QString& s )
1541     {
1542     if (m_pClient == NULL)
1543     return;
1544    
1545     appendMessagesColor(s + QString(": %1 (errno=%2)")
1546     .arg(::lscp_client_get_result(m_pClient))
1547     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1548     }
1549    
1550    
1551     // Force update of the messages font.
1552     void qsamplerMainForm::updateMessagesFont (void)
1553     {
1554     if (m_pOptions == NULL)
1555     return;
1556    
1557     if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1558     QFont font;
1559     if (font.fromString(m_pOptions->sMessagesFont))
1560     m_pMessages->setMessagesFont(font);
1561     }
1562     }
1563    
1564    
1565     // Update messages window line limit.
1566     void qsamplerMainForm::updateMessagesLimit (void)
1567     {
1568     if (m_pOptions == NULL)
1569     return;
1570    
1571     if (m_pMessages) {
1572     if (m_pOptions->bMessagesLimit)
1573     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1574     else
1575     m_pMessages->setMessagesLimit(0);
1576     }
1577     }
1578    
1579    
1580     // Enablement of the messages capture feature.
1581     void qsamplerMainForm::updateMessagesCapture (void)
1582     {
1583     if (m_pOptions == NULL)
1584     return;
1585    
1586     if (m_pMessages)
1587     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1588     }
1589    
1590    
1591     //-------------------------------------------------------------------------
1592     // qsamplerMainForm -- MDI channel strip management.
1593    
1594     // The channel strip creation executive.
1595 capela 303 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1596 capela 109 {
1597 capela 303 if (m_pClient == NULL || pChannel == NULL)
1598 capela 295 return NULL;
1599 capela 109
1600     // Prepare for auto-arrange?
1601 capela 264 qsamplerChannelStrip *pChannelStrip = NULL;
1602 capela 109 int y = 0;
1603     if (m_pOptions && m_pOptions->bAutoArrange) {
1604     QWidgetList wlist = m_pWorkspace->windowList();
1605     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1606 capela 264 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1607 capela 395 if (pChannelStrip) {
1608     // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1609     y += pChannelStrip->parentWidget()->frameGeometry().height();
1610     }
1611 capela 109 }
1612     }
1613    
1614     // Add a new channel itema...
1615     WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1616 capela 264 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1617 capela 303 if (pChannelStrip == NULL)
1618     return NULL;
1619    
1620     // Actual channel strip setup...
1621     pChannelStrip->setup(pChannel);
1622 capela 295 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1623 capela 267 // Set some initial aesthetic options...
1624     if (m_pOptions) {
1625     // Background display effect...
1626     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1627     // We'll need a display font.
1628     QFont font;
1629     if (font.fromString(m_pOptions->sDisplayFont))
1630     pChannelStrip->setDisplayFont(font);
1631     // Maximum allowed volume setting.
1632     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1633     }
1634 capela 295
1635 capela 109 // Now we show up us to the world.
1636 capela 264 pChannelStrip->show();
1637 capela 109 // Only then, we'll auto-arrange...
1638     if (m_pOptions && m_pOptions->bAutoArrange) {
1639     int iWidth = m_pWorkspace->width();
1640     // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1641 capela 264 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1642     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1643 capela 109 }
1644 capela 400
1645     // This is pretty new, so we'll watch for it closely.
1646     channelStripChanged(pChannelStrip);
1647    
1648 capela 295 // Return our successful reference...
1649     return pChannelStrip;
1650 capela 109 }
1651    
1652    
1653     // Retrieve the active channel strip.
1654 capela 264 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1655 capela 109 {
1656     return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1657     }
1658    
1659    
1660     // Retrieve a channel strip by index.
1661 capela 264 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1662 capela 109 {
1663     QWidgetList wlist = m_pWorkspace->windowList();
1664     if (wlist.isEmpty())
1665 capela 395 return NULL;
1666 capela 109
1667     return (qsamplerChannelStrip *) wlist.at(iChannel);
1668     }
1669    
1670    
1671 capela 395 // Retrieve a channel strip by sampler channel id.
1672     qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1673     {
1674     QWidgetList wlist = m_pWorkspace->windowList();
1675     if (wlist.isEmpty())
1676     return NULL;
1677    
1678     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1679     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1680     if (pChannelStrip) {
1681     qsamplerChannel *pChannel = pChannelStrip->channel();
1682     if (pChannel && pChannel->channelID() == iChannelID)
1683     return pChannelStrip;
1684     }
1685     }
1686    
1687     // Not found.
1688     return NULL;
1689     }
1690    
1691    
1692 capela 109 // Construct the windows menu.
1693     void qsamplerMainForm::channelsMenuAboutToShow (void)
1694     {
1695     channelsMenu->clear();
1696     channelsArrangeAction->addTo(channelsMenu);
1697     channelsAutoArrangeAction->addTo(channelsMenu);
1698    
1699     QWidgetList wlist = m_pWorkspace->windowList();
1700     if (!wlist.isEmpty()) {
1701     channelsMenu->insertSeparator();
1702     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1703 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1704     if (pChannelStrip) {
1705     int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1706     channelsMenu->setItemParameter(iItemID, iChannel);
1707     channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1708     }
1709 capela 109 }
1710     }
1711     }
1712    
1713    
1714     // Windows menu activation slot
1715     void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1716     {
1717 capela 264 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1718     if (pChannelStrip)
1719     pChannelStrip->showNormal();
1720     pChannelStrip->setFocus();
1721 capela 109 }
1722    
1723    
1724     //-------------------------------------------------------------------------
1725     // qsamplerMainForm -- Timer stuff.
1726    
1727     // Set the pseudo-timer delay schedule.
1728     void qsamplerMainForm::startSchedule ( int iStartDelay )
1729     {
1730     m_iStartDelay = 1 + (iStartDelay * 1000);
1731     m_iTimerDelay = 0;
1732     }
1733    
1734     // Suspend the pseudo-timer delay schedule.
1735     void qsamplerMainForm::stopSchedule (void)
1736     {
1737     m_iStartDelay = 0;
1738     m_iTimerDelay = 0;
1739     }
1740    
1741     // Timer slot funtion.
1742     void qsamplerMainForm::timerSlot (void)
1743     {
1744     if (m_pOptions == NULL)
1745     return;
1746    
1747     // Is it the first shot on server start after a few delay?
1748     if (m_iTimerDelay < m_iStartDelay) {
1749     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1750     if (m_iTimerDelay >= m_iStartDelay) {
1751     // If we cannot start it now, maybe a lil'mo'later ;)
1752     if (!startClient()) {
1753     m_iStartDelay += m_iTimerDelay;
1754     m_iTimerDelay = 0;
1755     }
1756     }
1757     }
1758    
1759     // Refresh each channel usage, on each period...
1760 capela 400 if (m_pClient && (m_changedStrips.count() > 0 || m_pOptions->bAutoRefresh)) {
1761 capela 109 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1762 capela 296 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1763 capela 109 m_iTimerSlot = 0;
1764 capela 400 // Update the channel information for each pending strip...
1765     for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1766     pChannelStrip;
1767     pChannelStrip = m_changedStrips.next()) {
1768     // If successfull, remove from pending list...
1769     if (pChannelStrip->updateChannelInfo())
1770     m_changedStrips.remove(pChannelStrip);
1771     }
1772     // Update the channel stream usage for each strip...
1773 capela 109 QWidgetList wlist = m_pWorkspace->windowList();
1774     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1775 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1776 capela 400 if (pChannelStrip && pChannelStrip->isVisible())
1777     pChannelStrip->updateChannelUsage();
1778 capela 109 }
1779     }
1780     }
1781    
1782     // Register the next timer slot.
1783     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1784     }
1785    
1786    
1787     //-------------------------------------------------------------------------
1788     // qsamplerMainForm -- Server stuff.
1789    
1790     // Start linuxsampler server...
1791     void qsamplerMainForm::startServer (void)
1792     {
1793     if (m_pOptions == NULL)
1794     return;
1795    
1796     // Aren't already a client, are we?
1797     if (!m_pOptions->bServerStart || m_pClient)
1798     return;
1799    
1800     // Is the server process instance still here?
1801     if (m_pServer) {
1802     switch (QMessageBox::warning(this, tr("Warning"),
1803     tr("Could not start the LinuxSampler server.\n\n"
1804     "Maybe it ss already started."),
1805     tr("Stop"), tr("Kill"), tr("Cancel"))) {
1806     case 0:
1807     m_pServer->tryTerminate();
1808     break;
1809     case 1:
1810     m_pServer->kill();
1811     break;
1812     }
1813     return;
1814     }
1815    
1816     // Reset our timer counters...
1817     stopSchedule();
1818    
1819     // OK. Let's build the startup process...
1820     m_pServer = new QProcess(this);
1821    
1822     // Setup stdout/stderr capture...
1823     //if (m_pOptions->bStdoutCapture) {
1824     m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1825     QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1826     QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1827     //}
1828     // The unforgiveable signal communication...
1829     QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1830    
1831     // Build process arguments...
1832     m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1833    
1834     appendMessages(tr("Server is starting..."));
1835     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1836    
1837     // Go jack, go...
1838     if (!m_pServer->start()) {
1839     appendMessagesError(tr("Could not start server.\n\nSorry."));
1840     processServerExit();
1841     return;
1842     }
1843    
1844     // Show startup results...
1845     appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1846    
1847     // Reset (yet again) the timer counters,
1848     // but this time is deferred as the user opted.
1849     startSchedule(m_pOptions->iStartDelay);
1850     stabilizeForm();
1851     }
1852    
1853    
1854     // Stop linuxsampler server...
1855     void qsamplerMainForm::stopServer (void)
1856     {
1857     // Stop client code.
1858     stopClient();
1859    
1860     // And try to stop server.
1861     if (m_pServer) {
1862     appendMessages(tr("Server is stopping..."));
1863 capela 122 if (m_pServer->isRunning())
1864 capela 109 m_pServer->tryTerminate();
1865     }
1866    
1867 capela 122 // Give it some time to terminate gracefully and stabilize...
1868     QTime t;
1869     t.start();
1870     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1871     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1872    
1873 capela 109 // Do final processing anyway.
1874     processServerExit();
1875     }
1876    
1877    
1878     // Stdout handler...
1879     void qsamplerMainForm::readServerStdout (void)
1880     {
1881     if (m_pMessages)
1882     m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1883     }
1884    
1885    
1886     // Linuxsampler server cleanup.
1887     void qsamplerMainForm::processServerExit (void)
1888     {
1889     // Force client code cleanup.
1890     stopClient();
1891    
1892     // Flush anything that maybe pending...
1893     if (m_pMessages)
1894     m_pMessages->flushStdoutBuffer();
1895    
1896     if (m_pServer) {
1897     // Force final server shutdown...
1898     appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1899     if (!m_pServer->normalExit())
1900     m_pServer->kill();
1901     // Destroy it.
1902     delete m_pServer;
1903     m_pServer = NULL;
1904     }
1905    
1906     // Again, make status visible stable.
1907     stabilizeForm();
1908     }
1909    
1910    
1911     //-------------------------------------------------------------------------
1912     // qsamplerMainForm -- Client stuff.
1913    
1914     // The LSCP client callback procedure.
1915 capela 149 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1916 capela 109 {
1917 capela 149 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1918     if (pMainForm == NULL)
1919     return LSCP_FAILED;
1920    
1921     // ATTN: DO NOT EVER call any GUI code here,
1922 capela 145 // as this is run under some other thread context.
1923     // A custom event must be posted here...
1924 capela 149 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1925 capela 109
1926     return LSCP_OK;
1927     }
1928    
1929    
1930     // Start our almighty client...
1931     bool qsamplerMainForm::startClient (void)
1932     {
1933     // Have it a setup?
1934     if (m_pOptions == NULL)
1935     return false;
1936    
1937     // Aren't we already started, are we?
1938     if (m_pClient)
1939     return true;
1940    
1941     // Log prepare here.
1942     appendMessages(tr("Client connecting..."));
1943    
1944     // Create the client handle...
1945     m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1946     if (m_pClient == NULL) {
1947     // Is this the first try?
1948     // maybe we need to start a local server...
1949     if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1950     appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1951     else
1952     startServer();
1953     // This is always a failure.
1954     stabilizeForm();
1955     return false;
1956     }
1957     // Just set receive timeout value, blindly.
1958     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1959     appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1960    
1961     // We may stop scheduling around.
1962     stopSchedule();
1963    
1964     // We'll accept drops from now on...
1965     setAcceptDrops(true);
1966    
1967     // Log success here.
1968     appendMessages(tr("Client connected."));
1969    
1970 capela 428 // Hard-notify device configuration form,
1971     // if visible, that we're ready...
1972     if (m_pDeviceForm && m_pDeviceForm->isVisible())
1973     m_pDeviceForm->setClient(m_pClient);
1974    
1975 capela 109 // Is any session pending to be loaded?
1976     if (!m_pOptions->sSessionFile.isEmpty()) {
1977     // Just load the prabably startup session...
1978     if (loadSessionFile(m_pOptions->sSessionFile)) {
1979     m_pOptions->sSessionFile = QString::null;
1980     return true;
1981     }
1982     }
1983    
1984     // Make a new session
1985     return newSession();
1986     }
1987    
1988    
1989     // Stop client...
1990     void qsamplerMainForm::stopClient (void)
1991     {
1992     if (m_pClient == NULL)
1993     return;
1994    
1995 capela 428 // Hard-notify device configuration form,
1996     // if visible, that we're running out...
1997     if (m_pDeviceForm && m_pDeviceForm->isVisible())
1998     m_pDeviceForm->setClient(NULL);
1999    
2000 capela 109 // Log prepare here.
2001     appendMessages(tr("Client disconnecting..."));
2002    
2003     // Clear timer counters...
2004     stopSchedule();
2005    
2006     // We'll reject drops from now on...
2007     setAcceptDrops(false);
2008    
2009     // Force any channel strips around, but
2010     // but avoid removing the corresponding
2011     // channels from the back-end server.
2012     m_iDirtyCount = 0;
2013     closeSession(false);
2014    
2015     // Close us as a client...
2016     lscp_client_destroy(m_pClient);
2017     m_pClient = NULL;
2018    
2019     // Log final here.
2020     appendMessages(tr("Client disconnected."));
2021    
2022     // Make visible status.
2023     stabilizeForm();
2024     }
2025    
2026    
2027     // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC