/[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 986 - (hide annotations) (download) (as text)
Mon Dec 18 16:51:50 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 71412 byte(s)
* Fixed session file save, correcting MIDI instrument map (re)numbering.

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

  ViewVC Help
Powered by ViewVC