/[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 586 - (hide annotations) (download) (as text)
Wed May 25 11:41:35 2005 UTC (18 years, 10 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65470 byte(s)
* Fixed refresh cycle of channel strips in error state,
  which was preventing correct channel info updates.

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

  ViewVC Help
Powered by ViewVC