/[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 980 - (hide annotations) (download) (as text)
Sun Dec 17 22:29:29 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 71291 byte(s)
* Revised and extended MIDI instrument mapping feature; this time
  two (2) MIDI maps are being implicitly created, ones designated
  as 'Chromatic' (0) and another as 'Drum Kits' (1), which can be
  assigned to each sampler channel. (ATTN: this commit elevates the
  requirements for liblscp >= 0.5.0, also on todays CVS and pending
  proper release very soon).

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

  ViewVC Help
Powered by ViewVC