/[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 409 - (hide annotations) (download) (as text)
Thu Feb 24 12:10:54 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59666 byte(s)
* Channels are now properly renumbered when saving to a
  session LSCP script, assuming that it should be always
  loaded from scratch (i.e. zero channels).

* Disabled MIDI port setting on channel dialog, and on
  session file save, as its use is still troublesome.

* Descrimination as for drag-and-drop of instrument files
  has been refined and fixed again.

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

  ViewVC Help
Powered by ViewVC