/[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 404 - (hide annotations) (download) (as text)
Wed Feb 23 13:47:47 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59744 byte(s)
* Added new menu and toolbar option: Reset All Channels.

* Channel setup changes are now properly filtered, as for
  only those settings that are actually changed gets applied;
  change information are now also posted to messages window.

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 capela 404 // Reset all sampler channels.
914     void qsamplerMainForm::editResetAllChannels (void)
915     {
916     if (m_pClient == NULL)
917     return;
918    
919     // Invoque the channel strip procedure,
920     // for all channels out there...
921     m_pWorkspace->setUpdatesEnabled(false);
922     QWidgetList wlist = m_pWorkspace->windowList();
923     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
924     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
925     if (pChannelStrip)
926     pChannelStrip->channelReset();
927     }
928     m_pWorkspace->setUpdatesEnabled(true);
929     }
930    
931    
932 capela 109 //-------------------------------------------------------------------------
933     // qsamplerMainForm -- View Action slots.
934    
935     // Show/hide the main program window menubar.
936     void qsamplerMainForm::viewMenubar ( bool bOn )
937     {
938     if (bOn)
939     MenuBar->show();
940     else
941     MenuBar->hide();
942     }
943    
944    
945     // Show/hide the main program window toolbar.
946     void qsamplerMainForm::viewToolbar ( bool bOn )
947     {
948     if (bOn) {
949     fileToolbar->show();
950     editToolbar->show();
951     channelsToolbar->show();
952     } else {
953     fileToolbar->hide();
954     editToolbar->hide();
955     channelsToolbar->hide();
956     }
957     }
958    
959    
960     // Show/hide the main program window statusbar.
961     void qsamplerMainForm::viewStatusbar ( bool bOn )
962     {
963     if (bOn)
964     statusBar()->show();
965     else
966     statusBar()->hide();
967     }
968    
969    
970     // Show/hide the messages window logger.
971     void qsamplerMainForm::viewMessages ( bool bOn )
972     {
973     if (bOn)
974     m_pMessages->show();
975     else
976     m_pMessages->hide();
977     }
978    
979    
980     // Show options dialog.
981     void qsamplerMainForm::viewOptions (void)
982     {
983     if (m_pOptions == NULL)
984     return;
985    
986     qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
987     if (pOptionsForm) {
988     // Check out some initial nullities(tm)...
989 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
990     if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
991     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
992 capela 109 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
993     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
994     // To track down deferred or immediate changes.
995     QString sOldServerHost = m_pOptions->sServerHost;
996     int iOldServerPort = m_pOptions->iServerPort;
997     int iOldServerTimeout = m_pOptions->iServerTimeout;
998     bool bOldServerStart = m_pOptions->bServerStart;
999     QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1000     QString sOldDisplayFont = m_pOptions->sDisplayFont;
1001 capela 267 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1002 capela 119 int iOldMaxVolume = m_pOptions->iMaxVolume;
1003 capela 109 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1004     bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1005     int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1006     int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1007     bool bOldCompletePath = m_pOptions->bCompletePath;
1008 capela 371 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1009 capela 109 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1010     // Load the current setup settings.
1011     pOptionsForm->setup(m_pOptions);
1012     // Show the setup dialog...
1013     if (pOptionsForm->exec()) {
1014     // Warn if something will be only effective on next run.
1015     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1016     (!bOldStdoutCapture && m_pOptions->bStdoutCapture)) {
1017     QMessageBox::information(this, tr("Information"),
1018     tr("Some settings may be only effective\n"
1019     "next time you start this program."), tr("OK"));
1020     updateMessagesCapture();
1021     }
1022     // Check wheather something immediate has changed.
1023     if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1024     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1025     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1026     updateRecentFilesMenu();
1027 capela 371 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1028     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1029     updateInstrumentNames();
1030 capela 267 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1031     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1032     updateDisplayEffect();
1033 capela 109 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1034     updateDisplayFont();
1035 capela 119 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1036     updateMaxVolume();
1037 capela 109 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1038     updateMessagesFont();
1039     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1040     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1041     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1042     updateMessagesLimit();
1043     // And now the main thing, whether we'll do client/server recycling?
1044     if ((sOldServerHost != m_pOptions->sServerHost) ||
1045     (iOldServerPort != m_pOptions->iServerPort) ||
1046     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1047     ( bOldServerStart && !m_pOptions->bServerStart) ||
1048     (!bOldServerStart && m_pOptions->bServerStart) ||
1049     (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1050     fileRestart();
1051     }
1052     // Done.
1053     delete pOptionsForm;
1054     }
1055    
1056     // This makes it.
1057     stabilizeForm();
1058     }
1059    
1060    
1061     //-------------------------------------------------------------------------
1062     // qsamplerMainForm -- Channels action slots.
1063    
1064     // Arrange channel strips.
1065     void qsamplerMainForm::channelsArrange (void)
1066     {
1067     // Full width vertical tiling
1068     QWidgetList wlist = m_pWorkspace->windowList();
1069     if (wlist.isEmpty())
1070     return;
1071    
1072     m_pWorkspace->setUpdatesEnabled(false);
1073     int y = 0;
1074     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1075 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1076     /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1077 capela 109 // Prevent flicker...
1078 capela 264 pChannelStrip->hide();
1079     pChannelStrip->showNormal();
1080 capela 109 } */
1081 capela 264 pChannelStrip->adjustSize();
1082 capela 109 int iWidth = m_pWorkspace->width();
1083 capela 264 if (iWidth < pChannelStrip->width())
1084     iWidth = pChannelStrip->width();
1085     // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1086     int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1087     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1088 capela 109 y += iHeight;
1089     }
1090     m_pWorkspace->setUpdatesEnabled(true);
1091    
1092     stabilizeForm();
1093     }
1094    
1095    
1096     // Auto-arrange channel strips.
1097     void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1098     {
1099     if (m_pOptions == NULL)
1100     return;
1101    
1102     // Toggle the auto-arrange flag.
1103     m_pOptions->bAutoArrange = bOn;
1104    
1105     // If on, update whole workspace...
1106     if (m_pOptions->bAutoArrange)
1107     channelsArrange();
1108     }
1109    
1110    
1111     //-------------------------------------------------------------------------
1112     // qsamplerMainForm -- Help Action slots.
1113    
1114     // Show information about the Qt toolkit.
1115     void qsamplerMainForm::helpAboutQt (void)
1116     {
1117     QMessageBox::aboutQt(this);
1118     }
1119    
1120    
1121     // Show information about application program.
1122     void qsamplerMainForm::helpAbout (void)
1123     {
1124     // Stuff the about box text...
1125     QString sText = "<p>\n";
1126     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1127     sText += "<br />\n";
1128     sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1129     sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1130     #ifdef CONFIG_DEBUG
1131     sText += "<small><font color=\"red\">";
1132     sText += tr("Debugging option enabled.");
1133     sText += "</font></small><br />";
1134     #endif
1135 capela 176 #ifndef CONFIG_LIBGIG
1136     sText += "<small><font color=\"red\">";
1137     sText += tr("GIG (libgig) file support disabled.");
1138     sText += "</font></small><br />";
1139     #endif
1140 capela 382 #ifndef CONFIG_INSTRUMENT_NAME
1141     sText += "<small><font color=\"red\">";
1142     sText += tr("LSCP (liblscp) instrument_name support disabled.");
1143     sText += "</font></small><br />";
1144     #endif
1145 capela 109 sText += "<br />\n";
1146     sText += tr("Using") + ": ";
1147     sText += ::lscp_client_package();
1148     sText += " ";
1149     sText += ::lscp_client_version();
1150     sText += "<br />\n";
1151     sText += "<br />\n";
1152     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1153     sText += "<br />\n";
1154     sText += "<small>";
1155     sText += QSAMPLER_COPYRIGHT "<br />\n";
1156     sText += "<br />\n";
1157     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1158     sText += tr("under the terms of the GNU General Public License version 2 or later.");
1159     sText += "</small>";
1160     sText += "</p>\n";
1161    
1162     QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1163     }
1164    
1165    
1166     //-------------------------------------------------------------------------
1167     // qsamplerMainForm -- Main window stabilization.
1168    
1169     void qsamplerMainForm::stabilizeForm (void)
1170     {
1171     // Update the main application caption...
1172     QString sSessioName = sessionName(m_sFilename);
1173     if (m_iDirtyCount > 0)
1174     sSessioName += '*';
1175     setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessioName));
1176    
1177     // Update the main menu state...
1178 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1179 capela 109 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1180 capela 264 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1181 capela 109 fileNewAction->setEnabled(bHasClient);
1182     fileOpenAction->setEnabled(bHasClient);
1183     fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1184     fileSaveAsAction->setEnabled(bHasClient);
1185 capela 255 fileResetAction->setEnabled(bHasClient);
1186 capela 109 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1187     editAddChannelAction->setEnabled(bHasClient);
1188     editRemoveChannelAction->setEnabled(bHasChannel);
1189     editSetupChannelAction->setEnabled(bHasChannel);
1190     editResetChannelAction->setEnabled(bHasChannel);
1191 capela 404 editResetAllChannelsAction->setEnabled(bHasChannel);
1192 capela 109 channelsArrangeAction->setEnabled(bHasChannel);
1193     viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1194    
1195     // Client/Server status...
1196     if (bHasClient) {
1197     m_status[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1198     m_status[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1199     } else {
1200     m_status[QSAMPLER_STATUS_CLIENT]->clear();
1201     m_status[QSAMPLER_STATUS_SERVER]->clear();
1202     }
1203     // Channel status...
1204     if (bHasChannel)
1205 capela 264 m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1206 capela 109 else
1207     m_status[QSAMPLER_STATUS_CHANNEL]->clear();
1208     // Session status...
1209     if (m_iDirtyCount > 0)
1210     m_status[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1211     else
1212     m_status[QSAMPLER_STATUS_SESSION]->clear();
1213    
1214     // Recent files menu.
1215     m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1216    
1217     // Always make the latest message visible.
1218     if (m_pMessages)
1219     m_pMessages->scrollToBottom();
1220     }
1221    
1222    
1223     // Channel change receiver slot.
1224 capela 400 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1225 capela 109 {
1226 capela 400 // Add this strip to the changed list...
1227     if (m_changedStrips.containsRef(pChannelStrip) == 0)
1228     m_changedStrips.append(pChannelStrip);
1229    
1230 capela 109 // Just mark the dirty form.
1231     m_iDirtyCount++;
1232     // and update the form status...
1233     stabilizeForm();
1234     }
1235    
1236    
1237 capela 395 // Grab and restore current sampler channels session.
1238     void qsamplerMainForm::updateSession (void)
1239     {
1240     // Retrieve the current channel list.
1241     int *piChannelIDs = ::lscp_list_channels(m_pClient);
1242     if (piChannelIDs == NULL) {
1243     if (::lscp_client_get_errno(m_pClient)) {
1244     appendMessagesClient("lscp_list_channels");
1245     appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1246     }
1247     return;
1248     }
1249    
1250     // Try to (re)create each channel.
1251     m_pWorkspace->setUpdatesEnabled(false);
1252     for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1253     // Check if theres already a channel strip for this one...
1254     if (!channelStrip(piChannelIDs[iChannel]))
1255     createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1256     // Make it visibly responsive...
1257     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1258     }
1259     m_pWorkspace->setUpdatesEnabled(true);
1260     }
1261    
1262    
1263 capela 109 // Update the recent files list and menu.
1264     void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1265     {
1266     if (m_pOptions == NULL)
1267     return;
1268    
1269     // Remove from list if already there (avoid duplicates)
1270     QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1271     if (iter != m_pOptions->recentFiles.end())
1272     m_pOptions->recentFiles.remove(iter);
1273     // Put it to front...
1274     m_pOptions->recentFiles.push_front(sFilename);
1275    
1276     // May update the menu.
1277     updateRecentFilesMenu();
1278     }
1279    
1280    
1281     // Update the recent files list and menu.
1282     void qsamplerMainForm::updateRecentFilesMenu (void)
1283     {
1284     if (m_pOptions == NULL)
1285     return;
1286    
1287     // Time to keep the list under limits.
1288     int iRecentFiles = m_pOptions->recentFiles.count();
1289     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1290     m_pOptions->recentFiles.pop_back();
1291     iRecentFiles--;
1292     }
1293    
1294     // rebuild the recent files menu...
1295     m_pRecentFilesMenu->clear();
1296     for (int i = 0; i < iRecentFiles; i++) {
1297     const QString& sFilename = m_pOptions->recentFiles[i];
1298     if (QFileInfo(sFilename).exists()) {
1299     m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1300     .arg(i + 1).arg(sessionName(sFilename)),
1301     this, SLOT(fileOpenRecent(int)), 0, i);
1302     }
1303     }
1304     }
1305    
1306    
1307 capela 371 // Force update of the channels instrument names mode.
1308     void qsamplerMainForm::updateInstrumentNames (void)
1309     {
1310     // Full channel list update...
1311     QWidgetList wlist = m_pWorkspace->windowList();
1312     if (wlist.isEmpty())
1313     return;
1314    
1315     m_pWorkspace->setUpdatesEnabled(false);
1316     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1317     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1318     if (pChannelStrip)
1319     pChannelStrip->updateInstrumentName(true);
1320     }
1321     m_pWorkspace->setUpdatesEnabled(true);
1322     }
1323    
1324    
1325 capela 109 // Force update of the channels display font.
1326     void qsamplerMainForm::updateDisplayFont (void)
1327     {
1328     if (m_pOptions == NULL)
1329     return;
1330    
1331     // Check if display font is legal.
1332     if (m_pOptions->sDisplayFont.isEmpty())
1333     return;
1334     // Realize it.
1335     QFont font;
1336     if (!font.fromString(m_pOptions->sDisplayFont))
1337     return;
1338    
1339     // Full channel list update...
1340     QWidgetList wlist = m_pWorkspace->windowList();
1341     if (wlist.isEmpty())
1342     return;
1343    
1344     m_pWorkspace->setUpdatesEnabled(false);
1345     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1346 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1347     if (pChannelStrip)
1348     pChannelStrip->setDisplayFont(font);
1349 capela 109 }
1350     m_pWorkspace->setUpdatesEnabled(true);
1351     }
1352    
1353    
1354 capela 267 // Update channel strips background effect.
1355     void qsamplerMainForm::updateDisplayEffect (void)
1356     {
1357     QPixmap pm;
1358     if (m_pOptions->bDisplayEffect)
1359     pm = QPixmap::fromMimeSource("displaybg1.png");
1360    
1361     // Full channel list update...
1362     QWidgetList wlist = m_pWorkspace->windowList();
1363     if (wlist.isEmpty())
1364     return;
1365    
1366     m_pWorkspace->setUpdatesEnabled(false);
1367     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1368     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1369     if (pChannelStrip)
1370     pChannelStrip->setDisplayBackground(pm);
1371     }
1372     m_pWorkspace->setUpdatesEnabled(true);
1373     }
1374    
1375    
1376 capela 119 // Force update of the channels maximum volume setting.
1377     void qsamplerMainForm::updateMaxVolume (void)
1378     {
1379     if (m_pOptions == NULL)
1380     return;
1381    
1382     // Full channel list update...
1383     QWidgetList wlist = m_pWorkspace->windowList();
1384     if (wlist.isEmpty())
1385     return;
1386    
1387     m_pWorkspace->setUpdatesEnabled(false);
1388     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1389 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1390     if (pChannelStrip)
1391     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1392 capela 119 }
1393     m_pWorkspace->setUpdatesEnabled(true);
1394     }
1395    
1396    
1397 capela 109 //-------------------------------------------------------------------------
1398     // qsamplerMainForm -- Messages window form handlers.
1399    
1400     // Messages output methods.
1401     void qsamplerMainForm::appendMessages( const QString& s )
1402     {
1403     if (m_pMessages)
1404     m_pMessages->appendMessages(s);
1405    
1406     statusBar()->message(s, 3000);
1407     }
1408    
1409     void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1410     {
1411     if (m_pMessages)
1412     m_pMessages->appendMessagesColor(s, c);
1413    
1414     statusBar()->message(s, 3000);
1415     }
1416    
1417     void qsamplerMainForm::appendMessagesText( const QString& s )
1418     {
1419     if (m_pMessages)
1420     m_pMessages->appendMessagesText(s);
1421     }
1422    
1423     void qsamplerMainForm::appendMessagesError( const QString& s )
1424     {
1425     if (m_pMessages)
1426     m_pMessages->show();
1427    
1428     appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1429    
1430     QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1431     }
1432    
1433    
1434     // This is a special message format, just for client results.
1435     void qsamplerMainForm::appendMessagesClient( const QString& s )
1436     {
1437     if (m_pClient == NULL)
1438     return;
1439    
1440     appendMessagesColor(s + QString(": %1 (errno=%2)")
1441     .arg(::lscp_client_get_result(m_pClient))
1442     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1443     }
1444    
1445    
1446     // Force update of the messages font.
1447     void qsamplerMainForm::updateMessagesFont (void)
1448     {
1449     if (m_pOptions == NULL)
1450     return;
1451    
1452     if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1453     QFont font;
1454     if (font.fromString(m_pOptions->sMessagesFont))
1455     m_pMessages->setMessagesFont(font);
1456     }
1457     }
1458    
1459    
1460     // Update messages window line limit.
1461     void qsamplerMainForm::updateMessagesLimit (void)
1462     {
1463     if (m_pOptions == NULL)
1464     return;
1465    
1466     if (m_pMessages) {
1467     if (m_pOptions->bMessagesLimit)
1468     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1469     else
1470     m_pMessages->setMessagesLimit(0);
1471     }
1472     }
1473    
1474    
1475     // Enablement of the messages capture feature.
1476     void qsamplerMainForm::updateMessagesCapture (void)
1477     {
1478     if (m_pOptions == NULL)
1479     return;
1480    
1481     if (m_pMessages)
1482     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1483     }
1484    
1485    
1486     //-------------------------------------------------------------------------
1487     // qsamplerMainForm -- MDI channel strip management.
1488    
1489     // The channel strip creation executive.
1490 capela 303 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1491 capela 109 {
1492 capela 303 if (m_pClient == NULL || pChannel == NULL)
1493 capela 295 return NULL;
1494 capela 109
1495     // Prepare for auto-arrange?
1496 capela 264 qsamplerChannelStrip *pChannelStrip = NULL;
1497 capela 109 int y = 0;
1498     if (m_pOptions && m_pOptions->bAutoArrange) {
1499     QWidgetList wlist = m_pWorkspace->windowList();
1500     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1501 capela 264 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1502 capela 395 if (pChannelStrip) {
1503     // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1504     y += pChannelStrip->parentWidget()->frameGeometry().height();
1505     }
1506 capela 109 }
1507     }
1508    
1509     // Add a new channel itema...
1510     WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1511 capela 264 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1512 capela 303 if (pChannelStrip == NULL)
1513     return NULL;
1514    
1515     // Actual channel strip setup...
1516     pChannelStrip->setup(pChannel);
1517 capela 295 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1518 capela 267 // Set some initial aesthetic options...
1519     if (m_pOptions) {
1520     // Background display effect...
1521     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1522     // We'll need a display font.
1523     QFont font;
1524     if (font.fromString(m_pOptions->sDisplayFont))
1525     pChannelStrip->setDisplayFont(font);
1526     // Maximum allowed volume setting.
1527     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1528     }
1529 capela 295
1530 capela 109 // Now we show up us to the world.
1531 capela 264 pChannelStrip->show();
1532 capela 109 // Only then, we'll auto-arrange...
1533     if (m_pOptions && m_pOptions->bAutoArrange) {
1534     int iWidth = m_pWorkspace->width();
1535     // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1536 capela 264 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1537     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1538 capela 109 }
1539 capela 400
1540     // This is pretty new, so we'll watch for it closely.
1541     channelStripChanged(pChannelStrip);
1542    
1543 capela 295 // Return our successful reference...
1544     return pChannelStrip;
1545 capela 109 }
1546    
1547    
1548     // Retrieve the active channel strip.
1549 capela 264 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1550 capela 109 {
1551     return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1552     }
1553    
1554    
1555     // Retrieve a channel strip by index.
1556 capela 264 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1557 capela 109 {
1558     QWidgetList wlist = m_pWorkspace->windowList();
1559     if (wlist.isEmpty())
1560 capela 395 return NULL;
1561 capela 109
1562     return (qsamplerChannelStrip *) wlist.at(iChannel);
1563     }
1564    
1565    
1566 capela 395 // Retrieve a channel strip by sampler channel id.
1567     qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1568     {
1569     QWidgetList wlist = m_pWorkspace->windowList();
1570     if (wlist.isEmpty())
1571     return NULL;
1572    
1573     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1574     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1575     if (pChannelStrip) {
1576     qsamplerChannel *pChannel = pChannelStrip->channel();
1577     if (pChannel && pChannel->channelID() == iChannelID)
1578     return pChannelStrip;
1579     }
1580     }
1581    
1582     // Not found.
1583     return NULL;
1584     }
1585    
1586    
1587 capela 109 // Construct the windows menu.
1588     void qsamplerMainForm::channelsMenuAboutToShow (void)
1589     {
1590     channelsMenu->clear();
1591     channelsArrangeAction->addTo(channelsMenu);
1592     channelsAutoArrangeAction->addTo(channelsMenu);
1593    
1594     QWidgetList wlist = m_pWorkspace->windowList();
1595     if (!wlist.isEmpty()) {
1596     channelsMenu->insertSeparator();
1597     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1598 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1599     if (pChannelStrip) {
1600     int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1601     channelsMenu->setItemParameter(iItemID, iChannel);
1602     channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1603     }
1604 capela 109 }
1605     }
1606     }
1607    
1608    
1609     // Windows menu activation slot
1610     void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1611     {
1612 capela 264 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1613     if (pChannelStrip)
1614     pChannelStrip->showNormal();
1615     pChannelStrip->setFocus();
1616 capela 109 }
1617    
1618    
1619     //-------------------------------------------------------------------------
1620     // qsamplerMainForm -- Timer stuff.
1621    
1622     // Set the pseudo-timer delay schedule.
1623     void qsamplerMainForm::startSchedule ( int iStartDelay )
1624     {
1625     m_iStartDelay = 1 + (iStartDelay * 1000);
1626     m_iTimerDelay = 0;
1627     }
1628    
1629     // Suspend the pseudo-timer delay schedule.
1630     void qsamplerMainForm::stopSchedule (void)
1631     {
1632     m_iStartDelay = 0;
1633     m_iTimerDelay = 0;
1634     }
1635    
1636     // Timer slot funtion.
1637     void qsamplerMainForm::timerSlot (void)
1638     {
1639     if (m_pOptions == NULL)
1640     return;
1641    
1642     // Is it the first shot on server start after a few delay?
1643     if (m_iTimerDelay < m_iStartDelay) {
1644     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1645     if (m_iTimerDelay >= m_iStartDelay) {
1646     // If we cannot start it now, maybe a lil'mo'later ;)
1647     if (!startClient()) {
1648     m_iStartDelay += m_iTimerDelay;
1649     m_iTimerDelay = 0;
1650     }
1651     }
1652     }
1653    
1654     // Refresh each channel usage, on each period...
1655 capela 400 if (m_pClient && (m_changedStrips.count() > 0 || m_pOptions->bAutoRefresh)) {
1656 capela 109 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1657 capela 296 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1658 capela 109 m_iTimerSlot = 0;
1659 capela 400 // Update the channel information for each pending strip...
1660     for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1661     pChannelStrip;
1662     pChannelStrip = m_changedStrips.next()) {
1663     // If successfull, remove from pending list...
1664     if (pChannelStrip->updateChannelInfo())
1665     m_changedStrips.remove(pChannelStrip);
1666     }
1667     // Update the channel stream usage for each strip...
1668 capela 109 QWidgetList wlist = m_pWorkspace->windowList();
1669     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1670 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1671 capela 400 if (pChannelStrip && pChannelStrip->isVisible())
1672     pChannelStrip->updateChannelUsage();
1673 capela 109 }
1674     }
1675     }
1676    
1677     // Register the next timer slot.
1678     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1679     }
1680    
1681    
1682     //-------------------------------------------------------------------------
1683     // qsamplerMainForm -- Server stuff.
1684    
1685     // Start linuxsampler server...
1686     void qsamplerMainForm::startServer (void)
1687     {
1688     if (m_pOptions == NULL)
1689     return;
1690    
1691     // Aren't already a client, are we?
1692     if (!m_pOptions->bServerStart || m_pClient)
1693     return;
1694    
1695     // Is the server process instance still here?
1696     if (m_pServer) {
1697     switch (QMessageBox::warning(this, tr("Warning"),
1698     tr("Could not start the LinuxSampler server.\n\n"
1699     "Maybe it ss already started."),
1700     tr("Stop"), tr("Kill"), tr("Cancel"))) {
1701     case 0:
1702     m_pServer->tryTerminate();
1703     break;
1704     case 1:
1705     m_pServer->kill();
1706     break;
1707     }
1708     return;
1709     }
1710    
1711     // Reset our timer counters...
1712     stopSchedule();
1713    
1714     // OK. Let's build the startup process...
1715     m_pServer = new QProcess(this);
1716    
1717     // Setup stdout/stderr capture...
1718     //if (m_pOptions->bStdoutCapture) {
1719     m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1720     QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1721     QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1722     //}
1723     // The unforgiveable signal communication...
1724     QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1725    
1726     // Build process arguments...
1727     m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1728    
1729     appendMessages(tr("Server is starting..."));
1730     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1731    
1732     // Go jack, go...
1733     if (!m_pServer->start()) {
1734     appendMessagesError(tr("Could not start server.\n\nSorry."));
1735     processServerExit();
1736     return;
1737     }
1738    
1739     // Show startup results...
1740     appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1741    
1742     // Reset (yet again) the timer counters,
1743     // but this time is deferred as the user opted.
1744     startSchedule(m_pOptions->iStartDelay);
1745     stabilizeForm();
1746     }
1747    
1748    
1749     // Stop linuxsampler server...
1750     void qsamplerMainForm::stopServer (void)
1751     {
1752     // Stop client code.
1753     stopClient();
1754    
1755     // And try to stop server.
1756     if (m_pServer) {
1757     appendMessages(tr("Server is stopping..."));
1758 capela 122 if (m_pServer->isRunning())
1759 capela 109 m_pServer->tryTerminate();
1760     }
1761    
1762 capela 122 // Give it some time to terminate gracefully and stabilize...
1763     QTime t;
1764     t.start();
1765     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1766     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1767    
1768 capela 109 // Do final processing anyway.
1769     processServerExit();
1770     }
1771    
1772    
1773     // Stdout handler...
1774     void qsamplerMainForm::readServerStdout (void)
1775     {
1776     if (m_pMessages)
1777     m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1778     }
1779    
1780    
1781     // Linuxsampler server cleanup.
1782     void qsamplerMainForm::processServerExit (void)
1783     {
1784     // Force client code cleanup.
1785     stopClient();
1786    
1787     // Flush anything that maybe pending...
1788     if (m_pMessages)
1789     m_pMessages->flushStdoutBuffer();
1790    
1791     if (m_pServer) {
1792     // Force final server shutdown...
1793     appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1794     if (!m_pServer->normalExit())
1795     m_pServer->kill();
1796     // Destroy it.
1797     delete m_pServer;
1798     m_pServer = NULL;
1799     }
1800    
1801     // Again, make status visible stable.
1802     stabilizeForm();
1803     }
1804    
1805    
1806     //-------------------------------------------------------------------------
1807     // qsamplerMainForm -- Client stuff.
1808    
1809     // The LSCP client callback procedure.
1810 capela 149 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1811 capela 109 {
1812 capela 149 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1813     if (pMainForm == NULL)
1814     return LSCP_FAILED;
1815    
1816     // ATTN: DO NOT EVER call any GUI code here,
1817 capela 145 // as this is run under some other thread context.
1818     // A custom event must be posted here...
1819 capela 149 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1820 capela 109
1821     return LSCP_OK;
1822     }
1823    
1824    
1825     // Start our almighty client...
1826     bool qsamplerMainForm::startClient (void)
1827     {
1828     // Have it a setup?
1829     if (m_pOptions == NULL)
1830     return false;
1831    
1832     // Aren't we already started, are we?
1833     if (m_pClient)
1834     return true;
1835    
1836     // Log prepare here.
1837     appendMessages(tr("Client connecting..."));
1838    
1839     // Create the client handle...
1840     m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1841     if (m_pClient == NULL) {
1842     // Is this the first try?
1843     // maybe we need to start a local server...
1844     if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1845     appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1846     else
1847     startServer();
1848     // This is always a failure.
1849     stabilizeForm();
1850     return false;
1851     }
1852     // Just set receive timeout value, blindly.
1853     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1854     appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1855    
1856     // We may stop scheduling around.
1857     stopSchedule();
1858    
1859     // We'll accept drops from now on...
1860     setAcceptDrops(true);
1861    
1862     // Log success here.
1863     appendMessages(tr("Client connected."));
1864    
1865     // Is any session pending to be loaded?
1866     if (!m_pOptions->sSessionFile.isEmpty()) {
1867     // Just load the prabably startup session...
1868     if (loadSessionFile(m_pOptions->sSessionFile)) {
1869     m_pOptions->sSessionFile = QString::null;
1870     return true;
1871     }
1872     }
1873    
1874     // Make a new session
1875     return newSession();
1876     }
1877    
1878    
1879     // Stop client...
1880     void qsamplerMainForm::stopClient (void)
1881     {
1882     if (m_pClient == NULL)
1883     return;
1884    
1885     // Log prepare here.
1886     appendMessages(tr("Client disconnecting..."));
1887    
1888     // Clear timer counters...
1889     stopSchedule();
1890    
1891     // We'll reject drops from now on...
1892     setAcceptDrops(false);
1893    
1894     // Force any channel strips around, but
1895     // but avoid removing the corresponding
1896     // channels from the back-end server.
1897     m_iDirtyCount = 0;
1898     closeSession(false);
1899    
1900     // Close us as a client...
1901     lscp_client_destroy(m_pClient);
1902     m_pClient = NULL;
1903    
1904     // Log final here.
1905     appendMessages(tr("Client disconnected."));
1906    
1907     // Make visible status.
1908     stabilizeForm();
1909     }
1910    
1911    
1912     // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC