/[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 487 - (hide annotations) (download) (as text)
Thu Mar 31 14:17:19 2005 UTC (19 years ago) by capela
File MIME type: text/x-c++hdr
File size: 65802 byte(s)
* Device setup is now also accessible from the sampler channel dialog.

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

  ViewVC Help
Powered by ViewVC