/[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 957 - (hide annotations) (download) (as text)
Wed Nov 29 11:48:26 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 68032 byte(s)
* Added preliminary MIDI instrument mapping support.

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

  ViewVC Help
Powered by ViewVC