/[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 664 - (hide annotations) (download) (as text)
Sat Jun 18 22:44:56 2005 UTC (18 years, 10 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65946 byte(s)
* Minor stream/voice usage auto-refresh cycle optimization.

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

  ViewVC Help
Powered by ViewVC