/[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 1025 - (hide annotations) (download) (as text)
Fri Jan 12 21:21:16 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 73948 byte(s)
- FX send numerical ID sequence fixed for session save.

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

  ViewVC Help
Powered by ViewVC