/[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 1013 - (hide annotations) (download) (as text)
Mon Jan 8 16:52:48 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 72568 byte(s)
* Instruments window gets its own toolbar (and statusbar too);
  also introducing MIDI instrument map selection to the view;
  MIDI instrument item editing now allows changing map, bank
  or program key values (were previously disabled).

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

  ViewVC Help
Powered by ViewVC