/[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 987 - (hide annotations) (download) (as text)
Tue Dec 19 11:19:55 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 72566 byte(s)
* Revised error verbosity in general and on session load/save;
  hour-glass wait cursor is now displayed on session load/save;
  keyboard shortcuts changed on MIDI instruments view context;
  improved channel strip arrangement on session open/load;
  instrument map entry removal confirmation (as optional);
  corrected some tooltip text strings.

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

  ViewVC Help
Powered by ViewVC