/[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 751 - (hide annotations) (download) (as text)
Fri Aug 19 17:10:16 2005 UTC (18 years, 8 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66364 byte(s)
* Added MUTE/SOLO buttons to individual channel strips.

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

  ViewVC Help
Powered by ViewVC