/[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 400 - (hide annotations) (download) (as text)
Mon Feb 21 15:02:58 2005 UTC (19 years, 2 months ago) by capela
File MIME type: text/x-c++hdr
File size: 59104 byte(s)
Major internal switch regarding channel strip change management.

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

  ViewVC Help
Powered by ViewVC