/[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 736 - (hide annotations) (download) (as text)
Tue Aug 16 10:19:25 2005 UTC (18 years, 7 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 65952 byte(s)
* Fixed compilation error which occured when Qt was
  compiled with -no-stl (fixes #23).

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

  ViewVC Help
Powered by ViewVC