/[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 418 - (hide annotations) (download) (as text)
Wed Mar 2 16:23:10 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59666 byte(s)
Minor corrections; Changed widget destruction order on main form close.

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 capela 418
177     #if defined(WIN32)
178     WSACleanup();
179     #endif
180    
181     // Finally drop any widgets around...
182     if (m_pMessages)
183     delete m_pMessages;
184     if (m_pWorkspace)
185     delete m_pWorkspace;
186    
187 capela 109 // Delete status item labels one by one.
188     if (m_status[QSAMPLER_STATUS_CLIENT])
189     delete m_status[QSAMPLER_STATUS_CLIENT];
190     if (m_status[QSAMPLER_STATUS_SERVER])
191     delete m_status[QSAMPLER_STATUS_SERVER];
192     if (m_status[QSAMPLER_STATUS_CHANNEL])
193     delete m_status[QSAMPLER_STATUS_CHANNEL];
194     if (m_status[QSAMPLER_STATUS_SESSION])
195     delete m_status[QSAMPLER_STATUS_SESSION];
196    
197 capela 418 // Delete recentfiles menu.
198     if (m_pRecentFilesMenu)
199     delete m_pRecentFilesMenu;
200 capela 109 }
201    
202    
203     // Make and set a proper setup options step.
204     void qsamplerMainForm::setup ( qsamplerOptions *pOptions )
205     {
206     // We got options?
207     m_pOptions = pOptions;
208    
209     // Some child forms are to be created right now.
210     m_pMessages = new qsamplerMessages(this);
211     // Set message defaults...
212     updateMessagesFont();
213     updateMessagesLimit();
214     updateMessagesCapture();
215     // Set the visibility signal.
216     QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), this, SLOT(stabilizeForm()));
217    
218     // Initial decorations toggle state.
219     viewMenubarAction->setOn(m_pOptions->bMenubar);
220     viewToolbarAction->setOn(m_pOptions->bToolbar);
221     viewStatusbarAction->setOn(m_pOptions->bStatusbar);
222     channelsAutoArrangeAction->setOn(m_pOptions->bAutoArrange);
223    
224     // Initial decorations visibility state.
225     viewMenubar(m_pOptions->bMenubar);
226     viewToolbar(m_pOptions->bToolbar);
227     viewStatusbar(m_pOptions->bStatusbar);
228    
229     // Restore whole dock windows state.
230     QString sDockables = m_pOptions->settings().readEntry("/Layout/DockWindows" , QString::null);
231     if (sDockables.isEmpty()) {
232     // Message window is forced to dock on the bottom.
233     moveDockWindow(m_pMessages, Qt::DockBottom);
234     } else {
235     // Make it as the last time.
236     QTextIStream istr(&sDockables);
237     istr >> *this;
238     }
239     // Try to restore old window positioning.
240     m_pOptions->loadWidgetGeometry(this);
241    
242     // Final startup stabilization...
243     updateRecentFilesMenu();
244     stabilizeForm();
245    
246     // Make it ready :-)
247     statusBar()->message(tr("Ready"), 3000);
248    
249     // We'll try to start immediately...
250     startSchedule(0);
251    
252     // Register the first timer slot.
253     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
254     }
255    
256    
257     // Window close event handlers.
258     bool qsamplerMainForm::queryClose (void)
259     {
260     bool bQueryClose = closeSession(false);
261    
262     // Try to save current general state...
263     if (m_pOptions) {
264     // Some windows default fonts is here on demand too.
265     if (bQueryClose && m_pMessages)
266     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
267     // Try to save current positioning.
268     if (bQueryClose) {
269     // Save decorations state.
270     m_pOptions->bMenubar = MenuBar->isVisible();
271     m_pOptions->bToolbar = (fileToolbar->isVisible() || editToolbar->isVisible() || channelsToolbar->isVisible());
272     m_pOptions->bStatusbar = statusBar()->isVisible();
273     // Save the dock windows state.
274     QString sDockables;
275     QTextOStream ostr(&sDockables);
276     ostr << *this;
277     m_pOptions->settings().writeEntry("/Layout/DockWindows", sDockables);
278     // And the main windows state.
279     m_pOptions->saveWidgetGeometry(this);
280     // Stop client and/or server, gracefully.
281     stopServer();
282     }
283     }
284    
285     return bQueryClose;
286     }
287    
288    
289     void qsamplerMainForm::closeEvent ( QCloseEvent *pCloseEvent )
290     {
291     if (queryClose())
292     pCloseEvent->accept();
293     else
294     pCloseEvent->ignore();
295     }
296    
297    
298 capela 388 // Drag'n'drop file handler.
299     bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
300 capela 109 {
301 capela 388 bool bDecode = false;
302 capela 109
303 capela 388 if (QTextDrag::canDecode(pEvent)) {
304     QString sText;
305     bDecode = QTextDrag::decode(pEvent, sText);
306     if (bDecode) {
307     files = QStringList::split('\n', sText);
308     for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
309 capela 409 *iter = QUrl((*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null)).path();
310 capela 388 }
311 capela 109 }
312    
313 capela 388 return bDecode;
314 capela 109 }
315    
316    
317 capela 388 // Window drag-n-drop event handlers.
318     void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
319     {
320 capela 395 QStringList files;
321     pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
322 capela 388 }
323    
324    
325 capela 109 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
326     {
327 capela 388 QStringList files;
328 capela 391
329     if (!decodeDragFiles(pDropEvent, files))
330     return;
331    
332     for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
333 capela 409 const QString& sPath = *iter;
334 capela 391 if (qsamplerChannel::isInstrumentFile(sPath)) {
335     // Try to create a new channel from instrument file...
336     qsamplerChannel *pChannel = new qsamplerChannel(this);
337     if (pChannel == NULL)
338     return;
339     // Start setting the instrument filename...
340     pChannel->setInstrument(sPath, 0);
341     // Before we show it up, may be we'll
342     // better ask for some initial values?
343     if (!pChannel->channelSetup(this)) {
344     delete pChannel;
345     return;
346     }
347     // Finally, give it to a new channel strip...
348     if (!createChannelStrip(pChannel)) {
349     delete pChannel;
350     return;
351     }
352     // Make that an overall update.
353     m_iDirtyCount++;
354     stabilizeForm();
355     } // Otherwise, load an usual session file (LSCP script)...
356     else if (closeSession(true))
357     loadSessionFile(sPath);
358     // Make it look responsive...:)
359     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
360     }
361 capela 109 }
362    
363    
364 capela 149 // Custome event handler.
365     void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
366     {
367     // For the time being, just pump it to messages.
368     if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
369     qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
370     appendMessagesColor(tr("Notify event: %1 data: %2")
371     .arg(::lscp_event_to_text(pEvent->event()))
372     .arg(pEvent->data()), "#996699");
373     }
374     }
375    
376    
377 capela 127 // Context menu event handler.
378     void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
379     {
380     stabilizeForm();
381    
382     editMenu->exec(pEvent->globalPos());
383     }
384    
385    
386 capela 109 //-------------------------------------------------------------------------
387     // qsamplerMainForm -- Brainless public property accessors.
388    
389     // The global options settings property.
390     qsamplerOptions *qsamplerMainForm::options (void)
391     {
392     return m_pOptions;
393     }
394    
395     // The LSCP client descriptor property.
396     lscp_client_t *qsamplerMainForm::client (void)
397     {
398     return m_pClient;
399     }
400    
401    
402     //-------------------------------------------------------------------------
403     // qsamplerMainForm -- Session file stuff.
404    
405     // Format the displayable session filename.
406     QString qsamplerMainForm::sessionName ( const QString& sFilename )
407     {
408     bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
409     QString sSessionName = sFilename;
410     if (sSessionName.isEmpty())
411     sSessionName = tr("Untitled") + QString::number(m_iUntitled);
412     else if (!bCompletePath)
413     sSessionName = QFileInfo(sSessionName).fileName();
414     return sSessionName;
415     }
416    
417    
418     // Create a new session file from scratch.
419     bool qsamplerMainForm::newSession (void)
420     {
421     // Check if we can do it.
422     if (!closeSession(true))
423     return false;
424    
425 capela 395 // Give us what the server has, right now...
426     updateSession();
427    
428 capela 109 // Ok increment untitled count.
429     m_iUntitled++;
430    
431     // Stabilize form.
432     m_sFilename = QString::null;
433     m_iDirtyCount = 0;
434     appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
435     stabilizeForm();
436    
437     return true;
438     }
439    
440    
441     // Open an existing sampler session.
442     bool qsamplerMainForm::openSession (void)
443     {
444     if (m_pOptions == NULL)
445     return false;
446    
447     // Ask for the filename to open...
448     QString sFilename = QFileDialog::getOpenFileName(
449     m_pOptions->sSessionDir, // Start here.
450     tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
451     this, 0, // Parent and name (none)
452     tr("Open Session") // Caption.
453     );
454    
455     // Have we cancelled?
456     if (sFilename.isEmpty())
457     return false;
458    
459     // Check if we're going to discard safely the current one...
460     if (!closeSession(true))
461     return false;
462    
463     // Load it right away.
464     return loadSessionFile(sFilename);
465     }
466    
467    
468     // Save current sampler session with another name.
469     bool qsamplerMainForm::saveSession ( bool bPrompt )
470     {
471     if (m_pOptions == NULL)
472     return false;
473    
474     QString sFilename = m_sFilename;
475    
476     // Ask for the file to save, if there's none...
477     if (bPrompt || sFilename.isEmpty()) {
478     // If none is given, assume default directory.
479     if (sFilename.isEmpty())
480     sFilename = m_pOptions->sSessionDir;
481     // Prompt the guy...
482     sFilename = QFileDialog::getSaveFileName(
483     sFilename, // Start here.
484     tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
485     this, 0, // Parent and name (none)
486     tr("Save Session") // Caption.
487     );
488     // Have we cancelled it?
489     if (sFilename.isEmpty())
490     return false;
491     // Enforce .lscp extension...
492     if (QFileInfo(sFilename).extension().isEmpty())
493     sFilename += ".lscp";
494     // Check if already exists...
495     if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
496     if (QMessageBox::warning(this, tr("Warning"),
497     tr("The file already exists:\n\n"
498     "\"%1\"\n\n"
499     "Do you want to replace it?")
500     .arg(sFilename),
501     tr("Replace"), tr("Cancel")) > 0)
502     return false;
503     }
504     }
505    
506     // Save it right away.
507     return saveSessionFile(sFilename);
508     }
509    
510    
511     // Close current session.
512     bool qsamplerMainForm::closeSession ( bool bForce )
513     {
514     bool bClose = true;
515    
516     // Are we dirty enough to prompt it?
517     if (m_iDirtyCount > 0) {
518     switch (QMessageBox::warning(this, tr("Warning"),
519     tr("The current session has been changed:\n\n"
520     "\"%1\"\n\n"
521     "Do you want to save the changes?")
522     .arg(sessionName(m_sFilename)),
523     tr("Save"), tr("Discard"), tr("Cancel"))) {
524     case 0: // Save...
525     bClose = saveSession(false);
526     // Fall thru....
527     case 1: // Discard
528     break;
529     default: // Cancel.
530     bClose = false;
531     break;
532     }
533     }
534    
535     // If we may close it, dot it.
536     if (bClose) {
537     // Remove all channel strips from sight...
538     m_pWorkspace->setUpdatesEnabled(false);
539     QWidgetList wlist = m_pWorkspace->windowList();
540     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
541 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
542     if (pChannelStrip) {
543     qsamplerChannel *pChannel = pChannelStrip->channel();
544 capela 295 if (bForce && pChannel)
545     pChannel->removeChannel();
546 capela 264 delete pChannelStrip;
547     }
548 capela 109 }
549     m_pWorkspace->setUpdatesEnabled(true);
550     // We're now clean, for sure.
551     m_iDirtyCount = 0;
552     }
553    
554     return bClose;
555     }
556    
557    
558     // Load a session from specific file path.
559     bool qsamplerMainForm::loadSessionFile ( const QString& sFilename )
560     {
561     if (m_pClient == NULL)
562     return false;
563    
564     // Open and read from real file.
565     QFile file(sFilename);
566     if (!file.open(IO_ReadOnly)) {
567     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
568     return false;
569     }
570    
571     // Read the file.
572     int iErrors = 0;
573     QTextStream ts(&file);
574     while (!ts.atEnd()) {
575     // Read the line.
576     QString sCommand = ts.readLine().simplifyWhiteSpace();
577     // If not empty, nor a comment, call the server...
578     if (!sCommand.isEmpty() && sCommand[0] != '#') {
579     appendMessagesColor(sCommand, "#996633");
580     // Remember that, no matter what,
581     // all LSCP commands are CR/LF terminated.
582     sCommand += "\r\n";
583     if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {
584     appendMessagesClient("lscp_client_query");
585     iErrors++;
586 capela 388 break;
587 capela 109 }
588     }
589     // Try to make it snappy :)
590     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
591     }
592    
593     // Ok. we've read it.
594     file.close();
595    
596     // Have we any errors?
597     if (iErrors > 0)
598 capela 388 appendMessagesError(tr("Session could not be loaded\nfrom \"%1\".\n\nSorry.").arg(sFilename));
599 capela 109
600 capela 395 // Now we'll try to create (update) the whole GUI session.
601     updateSession();
602 capela 109
603     // Save as default session directory.
604     if (m_pOptions)
605     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
606     // We're not dirty anymore.
607     m_iDirtyCount = 0;
608     // Stabilize form...
609     m_sFilename = sFilename;
610     updateRecentFiles(sFilename);
611     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
612 capela 301
613     // Make that an overall update.
614 capela 109 stabilizeForm();
615     return true;
616     }
617    
618    
619     // Save current session to specific file path.
620     bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
621     {
622     // Open and write into real file.
623     QFile file(sFilename);
624     if (!file.open(IO_WriteOnly | IO_Truncate)) {
625     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
626     return false;
627     }
628    
629     // Write the file.
630     int iErrors = 0;
631     QTextStream ts(&file);
632     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
633     ts << "# " << tr("Version")
634     << ": " QSAMPLER_VERSION << endl;
635     ts << "# " << tr("Build")
636     << ": " __DATE__ " " __TIME__ << endl;
637     ts << "#" << endl;
638     ts << "# " << tr("File")
639     << ": " << QFileInfo(sFilename).fileName() << endl;
640     ts << "# " << tr("Date")
641     << ": " << QDate::currentDate().toString("MMMM dd yyyy")
642     << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
643     ts << "#" << endl;
644     ts << endl;
645     QWidgetList wlist = m_pWorkspace->windowList();
646     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
647 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
648     if (pChannelStrip) {
649     qsamplerChannel *pChannel = pChannelStrip->channel();
650     if (pChannel) {
651 capela 409 ts << "# Channel " << iChannel << endl;
652 capela 264 ts << "ADD CHANNEL" << endl;
653 capela 409 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel << " " << pChannel->audioDriver() << endl;
654     ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel << " " << pChannel->midiDriver() << endl;
655     // ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel << " " << pChannel->midiPort() << endl;
656     ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
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 capela 409 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
663     ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl;
664     ts << "SET CHANNEL VOLUME " << iChannel << " " << pChannel->volume() << endl;
665 capela 264 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 capela 418 QString sSessionName = sessionName(m_sFilename);
1173 capela 109 if (m_iDirtyCount > 0)
1174 capela 418 sSessionName += '*';
1175     setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1176 capela 109
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