/[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 391 - (hide annotations) (download) (as text)
Fri Feb 18 10:28:45 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 57852 byte(s)
* Drag-and-drop of instrument files now fixed.

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 296 m_iUntitled = 0;
112     m_iDirtyCount = 0;
113     m_iChangeCount = 0;
114 capela 109
115     m_pServer = NULL;
116     m_pClient = NULL;
117    
118     m_iStartDelay = 0;
119     m_iTimerDelay = 0;
120    
121     m_iTimerSlot = 0;
122    
123 capela 340 #ifdef HAVE_SIGNAL_H
124     // Set to ignore any fatal "Broken pipe" signals.
125     ::signal(SIGPIPE, SIG_IGN);
126     #endif
127    
128 capela 109 // Make it an MDI workspace.
129     m_pWorkspace = new QWorkspace(this);
130     m_pWorkspace->setScrollBarsEnabled(true);
131     // Set the activation connection.
132     QObject::connect(m_pWorkspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(stabilizeForm()));
133     // Make it shine :-)
134     setCentralWidget(m_pWorkspace);
135    
136     // Create some statusbar labels...
137     QLabel *pLabel;
138     // Client status.
139     pLabel = new QLabel(tr("Connected"), this);
140     pLabel->setAlignment(Qt::AlignLeft);
141     pLabel->setMinimumSize(pLabel->sizeHint());
142     m_status[QSAMPLER_STATUS_CLIENT] = pLabel;
143     statusBar()->addWidget(pLabel);
144     // Server address.
145     pLabel = new QLabel(this);
146     pLabel->setAlignment(Qt::AlignLeft);
147     m_status[QSAMPLER_STATUS_SERVER] = pLabel;
148     statusBar()->addWidget(pLabel, 1);
149     // Channel title.
150     pLabel = new QLabel(this);
151     pLabel->setAlignment(Qt::AlignLeft);
152     m_status[QSAMPLER_STATUS_CHANNEL] = pLabel;
153     statusBar()->addWidget(pLabel, 2);
154     // Session modification status.
155     pLabel = new QLabel(tr("MOD"), this);
156     pLabel->setAlignment(Qt::AlignHCenter);
157     pLabel->setMinimumSize(pLabel->sizeHint());
158     m_status[QSAMPLER_STATUS_SESSION] = pLabel;
159     statusBar()->addWidget(pLabel);
160    
161     // Create the recent files sub-menu.
162     m_pRecentFilesMenu = new QPopupMenu(this);
163     fileMenu->insertSeparator(4);
164     fileMenu->insertItem(tr("Recent &Files"), m_pRecentFilesMenu, 0, 5);
165    
166     #if defined(WIN32)
167     WSAStartup(MAKEWORD(1, 1), &_wsaData);
168     #endif
169     }
170    
171    
172     // Kind of destructor.
173     void qsamplerMainForm::destroy (void)
174     {
175     // Do final processing anyway.
176     processServerExit();
177    
178     // Delete recentfiles menu.
179     if (m_pRecentFilesMenu)
180     delete m_pRecentFilesMenu;
181     // Delete status item labels one by one.
182     if (m_status[QSAMPLER_STATUS_CLIENT])
183     delete m_status[QSAMPLER_STATUS_CLIENT];
184     if (m_status[QSAMPLER_STATUS_SERVER])
185     delete m_status[QSAMPLER_STATUS_SERVER];
186     if (m_status[QSAMPLER_STATUS_CHANNEL])
187     delete m_status[QSAMPLER_STATUS_CHANNEL];
188     if (m_status[QSAMPLER_STATUS_SESSION])
189     delete m_status[QSAMPLER_STATUS_SESSION];
190    
191     // Finally drop any widgets around...
192     if (m_pMessages)
193     delete m_pMessages;
194     if (m_pWorkspace)
195     delete m_pWorkspace;
196    
197     #if defined(WIN32)
198     WSACleanup();
199     #endif
200     }
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     *iter = (*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null);
310     }
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     QStringList files;
321    
322     pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
323     }
324    
325    
326 capela 109 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
327     {
328 capela 388 QStringList files;
329 capela 391
330     if (!decodeDragFiles(pDropEvent, files))
331     return;
332    
333     for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
334     const QString& sPath = QUrl(*iter).path();
335     if (qsamplerChannel::isInstrumentFile(sPath)) {
336     // Try to create a new channel from instrument file...
337     qsamplerChannel *pChannel = new qsamplerChannel(this);
338     if (pChannel == NULL)
339     return;
340     // Start setting the instrument filename...
341     pChannel->setInstrument(sPath, 0);
342     // Before we show it up, may be we'll
343     // better ask for some initial values?
344     if (!pChannel->channelSetup(this)) {
345     delete pChannel;
346     return;
347     }
348     // Finally, give it to a new channel strip...
349     if (!createChannelStrip(pChannel)) {
350     delete pChannel;
351     return;
352     }
353     // Make that an overall update.
354     m_iDirtyCount++;
355     m_iChangeCount++;
356     stabilizeForm();
357     } // Otherwise, load an usual session file (LSCP script)...
358     else if (closeSession(true))
359     loadSessionFile(sPath);
360     // Make it look responsive...:)
361     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
362     }
363 capela 109 }
364    
365    
366 capela 149 // Custome event handler.
367     void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
368     {
369     // For the time being, just pump it to messages.
370     if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
371     qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
372     appendMessagesColor(tr("Notify event: %1 data: %2")
373     .arg(::lscp_event_to_text(pEvent->event()))
374     .arg(pEvent->data()), "#996699");
375     }
376     }
377    
378    
379 capela 127 // Context menu event handler.
380     void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
381     {
382     stabilizeForm();
383    
384     editMenu->exec(pEvent->globalPos());
385     }
386    
387    
388 capela 109 //-------------------------------------------------------------------------
389     // qsamplerMainForm -- Brainless public property accessors.
390    
391     // The global options settings property.
392     qsamplerOptions *qsamplerMainForm::options (void)
393     {
394     return m_pOptions;
395     }
396    
397     // The LSCP client descriptor property.
398     lscp_client_t *qsamplerMainForm::client (void)
399     {
400     return m_pClient;
401     }
402    
403    
404     //-------------------------------------------------------------------------
405     // qsamplerMainForm -- Session file stuff.
406    
407     // Format the displayable session filename.
408     QString qsamplerMainForm::sessionName ( const QString& sFilename )
409     {
410     bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
411     QString sSessionName = sFilename;
412     if (sSessionName.isEmpty())
413     sSessionName = tr("Untitled") + QString::number(m_iUntitled);
414     else if (!bCompletePath)
415     sSessionName = QFileInfo(sSessionName).fileName();
416     return sSessionName;
417     }
418    
419    
420     // Create a new session file from scratch.
421     bool qsamplerMainForm::newSession (void)
422     {
423     // Check if we can do it.
424     if (!closeSession(true))
425     return false;
426    
427     // 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     // Now we'll try to create the whole GUI session.
600 capela 303 int *piChannelIDs = ::lscp_list_channels(m_pClient);
601     if (piChannelIDs == NULL) {
602     appendMessagesClient("lscp_list_channels");
603     appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
604 capela 369 } else {
605     // Try to (re)create each channel.
606     m_pWorkspace->setUpdatesEnabled(false);
607     for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
608     createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
609     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
610     }
611     m_pWorkspace->setUpdatesEnabled(true);
612     }
613 capela 109
614     // Save as default session directory.
615     if (m_pOptions)
616     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
617     // We're not dirty anymore.
618     m_iDirtyCount = 0;
619     // Stabilize form...
620     m_sFilename = sFilename;
621     updateRecentFiles(sFilename);
622     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
623 capela 301
624     // Make that an overall update.
625     m_iChangeCount++;
626 capela 109 stabilizeForm();
627     return true;
628     }
629    
630    
631     // Save current session to specific file path.
632     bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
633     {
634     // Open and write into real file.
635     QFile file(sFilename);
636     if (!file.open(IO_WriteOnly | IO_Truncate)) {
637     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
638     return false;
639     }
640    
641     // Write the file.
642     int iErrors = 0;
643     QTextStream ts(&file);
644     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
645     ts << "# " << tr("Version")
646     << ": " QSAMPLER_VERSION << endl;
647     ts << "# " << tr("Build")
648     << ": " __DATE__ " " __TIME__ << endl;
649     ts << "#" << endl;
650     ts << "# " << tr("File")
651     << ": " << QFileInfo(sFilename).fileName() << endl;
652     ts << "# " << tr("Date")
653     << ": " << QDate::currentDate().toString("MMMM dd yyyy")
654     << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
655     ts << "#" << endl;
656     ts << endl;
657     QWidgetList wlist = m_pWorkspace->windowList();
658     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
659 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
660     if (pChannelStrip) {
661     qsamplerChannel *pChannel = pChannelStrip->channel();
662     if (pChannel) {
663     int iChannelID = pChannel->channelID();
664     ts << "# " << pChannelStrip->caption() << endl;
665     ts << "ADD CHANNEL" << endl;
666     ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID << " " << pChannel->audioDriver() << endl;
667     ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl;
668     ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl;
669     ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " ";
670 capela 304 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
671     ts << "ALL";
672     else
673 capela 264 ts << pChannel->midiChannel();
674     ts << endl;
675     ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannelID << endl;
676     ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannelID << endl;
677     ts << "SET CHANNEL VOLUME " << iChannelID << " " << pChannel->volume() << endl;
678     ts << endl;
679     }
680     }
681 capela 109 // Try to keep it snappy :)
682     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
683     }
684    
685     // Ok. we've wrote it.
686     file.close();
687    
688     // Have we any errors?
689     if (iErrors > 0)
690     appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
691    
692     // Save as default session directory.
693     if (m_pOptions)
694     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
695     // We're not dirty anymore.
696     m_iDirtyCount = 0;
697     // Stabilize form...
698     m_sFilename = sFilename;
699     updateRecentFiles(sFilename);
700     appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
701     stabilizeForm();
702     return true;
703     }
704    
705    
706     //-------------------------------------------------------------------------
707     // qsamplerMainForm -- File Action slots.
708    
709     // Create a new sampler session.
710     void qsamplerMainForm::fileNew (void)
711     {
712     // Of course we'll start clean new.
713     newSession();
714     }
715    
716    
717     // Open an existing sampler session.
718     void qsamplerMainForm::fileOpen (void)
719     {
720     // Open it right away.
721     openSession();
722     }
723    
724    
725     // Open a recent file session.
726     void qsamplerMainForm::fileOpenRecent ( int iIndex )
727     {
728     // Check if we can safely close the current session...
729     if (m_pOptions && closeSession(true)) {
730     QString sFilename = m_pOptions->recentFiles[iIndex];
731     loadSessionFile(sFilename);
732     }
733     }
734    
735    
736     // Save current sampler session.
737     void qsamplerMainForm::fileSave (void)
738     {
739     // Save it right away.
740     saveSession(false);
741     }
742    
743    
744     // Save current sampler session with another name.
745     void qsamplerMainForm::fileSaveAs (void)
746     {
747     // Save it right away, maybe with another name.
748     saveSession(true);
749     }
750    
751    
752 capela 255 // Reset the sampler instance.
753     void qsamplerMainForm::fileReset (void)
754     {
755     if (m_pClient == NULL)
756     return;
757    
758     // Ask user whether he/she want's an internal sampler reset...
759     if (QMessageBox::warning(this, tr("Warning"),
760     tr("Resetting the sampler instance will close\n"
761     "all device and channel configurations.\n\n"
762     "Please note that this operation may cause\n"
763     "temporary MIDI and Audio disruption\n\n"
764     "Do you want to reset the sampler engine now?"),
765     tr("Reset"), tr("Cancel")) > 0)
766     return;
767    
768     // Just do the reset, after closing down current session...
769 capela 261 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
770 capela 255 appendMessagesClient("lscp_reset_sampler");
771 capela 261 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
772     return;
773     }
774    
775     // Log this.
776     appendMessages(tr("Sampler reset."));
777 capela 255 }
778    
779    
780 capela 109 // Restart the client/server instance.
781     void qsamplerMainForm::fileRestart (void)
782     {
783     if (m_pOptions == NULL)
784     return;
785    
786     bool bRestart = true;
787    
788     // Ask user whether he/she want's a complete restart...
789     // (if we're currently up and running)
790     if (bRestart && m_pClient) {
791     bRestart = (QMessageBox::warning(this, tr("Warning"),
792     tr("New settings will be effective after\n"
793     "restarting the client/server connection.\n\n"
794     "Please note that this operation may cause\n"
795     "temporary MIDI and Audio disruption\n\n"
796     "Do you want to restart the connection now?"),
797     tr("Restart"), tr("Cancel")) == 0);
798     }
799    
800     // Are we still for it?
801     if (bRestart && closeSession(true)) {
802     // Stop server, it will force the client too.
803     stopServer();
804     // Reschedule a restart...
805     startSchedule(m_pOptions->iStartDelay);
806     }
807     }
808    
809    
810     // Exit application program.
811     void qsamplerMainForm::fileExit (void)
812     {
813     // Go for close the whole thing.
814     close();
815     }
816    
817    
818     //-------------------------------------------------------------------------
819     // qsamplerMainForm -- Edit Action slots.
820    
821     // Add a new sampler channel.
822     void qsamplerMainForm::editAddChannel (void)
823     {
824     if (m_pClient == NULL)
825     return;
826    
827 capela 303 // Just create the channel instance...
828     qsamplerChannel *pChannel = new qsamplerChannel(this);
829     if (pChannel == NULL)
830     return;
831    
832     // Before we show it up, may be we'll
833     // better ask for some initial values?
834     if (!pChannel->channelSetup(this)) {
835     delete pChannel;
836     return;
837     }
838    
839     // And give it to the strip (will own the channel instance, if successful).
840     if (!createChannelStrip(pChannel)) {
841     delete pChannel;
842     return;
843     }
844    
845     // Make that an overall update.
846     m_iDirtyCount++;
847     m_iChangeCount++;
848     stabilizeForm();
849 capela 109 }
850    
851    
852     // Remove current sampler channel.
853     void qsamplerMainForm::editRemoveChannel (void)
854     {
855     if (m_pClient == NULL)
856     return;
857    
858 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
859     if (pChannelStrip == NULL)
860     return;
861    
862     qsamplerChannel *pChannel = pChannelStrip->channel();
863 capela 109 if (pChannel == NULL)
864     return;
865    
866     // Prompt user if he/she's sure about this...
867     if (m_pOptions && m_pOptions->bConfirmRemove) {
868     if (QMessageBox::warning(this, tr("Warning"),
869     tr("About to remove channel:\n\n"
870     "%1\n\n"
871     "Are you sure?")
872 capela 264 .arg(pChannelStrip->caption()),
873 capela 109 tr("OK"), tr("Cancel")) > 0)
874     return;
875     }
876    
877     // Remove the existing sampler channel.
878 capela 295 if (!pChannel->removeChannel())
879 capela 109 return;
880    
881     // Just delete the channel strip.
882 capela 265 delete pChannelStrip;
883    
884 capela 109 // Do we auto-arrange?
885     if (m_pOptions && m_pOptions->bAutoArrange)
886     channelsArrange();
887    
888     // We'll be dirty, for sure...
889     m_iDirtyCount++;
890     stabilizeForm();
891     }
892    
893    
894     // Setup current sampler channel.
895     void qsamplerMainForm::editSetupChannel (void)
896     {
897     if (m_pClient == NULL)
898     return;
899    
900 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
901     if (pChannelStrip == NULL)
902 capela 109 return;
903    
904     // Just invoque the channel strip procedure.
905 capela 295 pChannelStrip->channelSetup();
906 capela 109 }
907    
908    
909     // Reset current sampler channel.
910     void qsamplerMainForm::editResetChannel (void)
911     {
912     if (m_pClient == NULL)
913     return;
914    
915 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
916     if (pChannelStrip == NULL)
917     return;
918    
919     qsamplerChannel *pChannel = pChannelStrip->channel();
920 capela 109 if (pChannel == NULL)
921     return;
922    
923 capela 295 // Reset the existing sampler channel.
924     pChannel->resetChannel();
925 capela 109
926 capela 298 // And force a deferred update.
927     m_iChangeCount++;
928 capela 109 }
929    
930    
931     //-------------------------------------------------------------------------
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     channelsArrangeAction->setEnabled(bHasChannel);
1191     viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1192    
1193     // Client/Server status...
1194     if (bHasClient) {
1195     m_status[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1196     m_status[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1197     } else {
1198     m_status[QSAMPLER_STATUS_CLIENT]->clear();
1199     m_status[QSAMPLER_STATUS_SERVER]->clear();
1200     }
1201     // Channel status...
1202     if (bHasChannel)
1203 capela 264 m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1204 capela 109 else
1205     m_status[QSAMPLER_STATUS_CHANNEL]->clear();
1206     // Session status...
1207     if (m_iDirtyCount > 0)
1208     m_status[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1209     else
1210     m_status[QSAMPLER_STATUS_SESSION]->clear();
1211    
1212     // Recent files menu.
1213     m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1214    
1215     // Always make the latest message visible.
1216     if (m_pMessages)
1217     m_pMessages->scrollToBottom();
1218     }
1219    
1220    
1221     // Channel change receiver slot.
1222 capela 264 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip * )
1223 capela 109 {
1224 capela 296 // Flag that we're update those channel strips.
1225     m_iChangeCount++;
1226 capela 109 // Just mark the dirty form.
1227     m_iDirtyCount++;
1228     // and update the form status...
1229     stabilizeForm();
1230     }
1231    
1232    
1233     // Update the recent files list and menu.
1234     void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1235     {
1236     if (m_pOptions == NULL)
1237     return;
1238    
1239     // Remove from list if already there (avoid duplicates)
1240     QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1241     if (iter != m_pOptions->recentFiles.end())
1242     m_pOptions->recentFiles.remove(iter);
1243     // Put it to front...
1244     m_pOptions->recentFiles.push_front(sFilename);
1245    
1246     // May update the menu.
1247     updateRecentFilesMenu();
1248     }
1249    
1250    
1251     // Update the recent files list and menu.
1252     void qsamplerMainForm::updateRecentFilesMenu (void)
1253     {
1254     if (m_pOptions == NULL)
1255     return;
1256    
1257     // Time to keep the list under limits.
1258     int iRecentFiles = m_pOptions->recentFiles.count();
1259     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1260     m_pOptions->recentFiles.pop_back();
1261     iRecentFiles--;
1262     }
1263    
1264     // rebuild the recent files menu...
1265     m_pRecentFilesMenu->clear();
1266     for (int i = 0; i < iRecentFiles; i++) {
1267     const QString& sFilename = m_pOptions->recentFiles[i];
1268     if (QFileInfo(sFilename).exists()) {
1269     m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1270     .arg(i + 1).arg(sessionName(sFilename)),
1271     this, SLOT(fileOpenRecent(int)), 0, i);
1272     }
1273     }
1274     }
1275    
1276    
1277 capela 371 // Force update of the channels instrument names mode.
1278     void qsamplerMainForm::updateInstrumentNames (void)
1279     {
1280     // Full channel list update...
1281     QWidgetList wlist = m_pWorkspace->windowList();
1282     if (wlist.isEmpty())
1283     return;
1284    
1285     m_pWorkspace->setUpdatesEnabled(false);
1286     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1287     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1288     if (pChannelStrip)
1289     pChannelStrip->updateInstrumentName(true);
1290     }
1291     m_pWorkspace->setUpdatesEnabled(true);
1292     }
1293    
1294    
1295 capela 109 // Force update of the channels display font.
1296     void qsamplerMainForm::updateDisplayFont (void)
1297     {
1298     if (m_pOptions == NULL)
1299     return;
1300    
1301     // Check if display font is legal.
1302     if (m_pOptions->sDisplayFont.isEmpty())
1303     return;
1304     // Realize it.
1305     QFont font;
1306     if (!font.fromString(m_pOptions->sDisplayFont))
1307     return;
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 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1317     if (pChannelStrip)
1318     pChannelStrip->setDisplayFont(font);
1319 capela 109 }
1320     m_pWorkspace->setUpdatesEnabled(true);
1321     }
1322    
1323    
1324 capela 267 // Update channel strips background effect.
1325     void qsamplerMainForm::updateDisplayEffect (void)
1326     {
1327     QPixmap pm;
1328     if (m_pOptions->bDisplayEffect)
1329     pm = QPixmap::fromMimeSource("displaybg1.png");
1330    
1331     // Full channel list update...
1332     QWidgetList wlist = m_pWorkspace->windowList();
1333     if (wlist.isEmpty())
1334     return;
1335    
1336     m_pWorkspace->setUpdatesEnabled(false);
1337     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1338     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1339     if (pChannelStrip)
1340     pChannelStrip->setDisplayBackground(pm);
1341     }
1342     m_pWorkspace->setUpdatesEnabled(true);
1343     }
1344    
1345    
1346 capela 119 // Force update of the channels maximum volume setting.
1347     void qsamplerMainForm::updateMaxVolume (void)
1348     {
1349     if (m_pOptions == NULL)
1350     return;
1351    
1352     // Full channel list update...
1353     QWidgetList wlist = m_pWorkspace->windowList();
1354     if (wlist.isEmpty())
1355     return;
1356    
1357     m_pWorkspace->setUpdatesEnabled(false);
1358     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1359 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1360     if (pChannelStrip)
1361     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1362 capela 119 }
1363     m_pWorkspace->setUpdatesEnabled(true);
1364     }
1365    
1366    
1367 capela 109 //-------------------------------------------------------------------------
1368     // qsamplerMainForm -- Messages window form handlers.
1369    
1370     // Messages output methods.
1371     void qsamplerMainForm::appendMessages( const QString& s )
1372     {
1373     if (m_pMessages)
1374     m_pMessages->appendMessages(s);
1375    
1376     statusBar()->message(s, 3000);
1377     }
1378    
1379     void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1380     {
1381     if (m_pMessages)
1382     m_pMessages->appendMessagesColor(s, c);
1383    
1384     statusBar()->message(s, 3000);
1385     }
1386    
1387     void qsamplerMainForm::appendMessagesText( const QString& s )
1388     {
1389     if (m_pMessages)
1390     m_pMessages->appendMessagesText(s);
1391     }
1392    
1393     void qsamplerMainForm::appendMessagesError( const QString& s )
1394     {
1395     if (m_pMessages)
1396     m_pMessages->show();
1397    
1398     appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1399    
1400     QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1401     }
1402    
1403    
1404     // This is a special message format, just for client results.
1405     void qsamplerMainForm::appendMessagesClient( const QString& s )
1406     {
1407     if (m_pClient == NULL)
1408     return;
1409    
1410     appendMessagesColor(s + QString(": %1 (errno=%2)")
1411     .arg(::lscp_client_get_result(m_pClient))
1412     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1413     }
1414    
1415    
1416     // Force update of the messages font.
1417     void qsamplerMainForm::updateMessagesFont (void)
1418     {
1419     if (m_pOptions == NULL)
1420     return;
1421    
1422     if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1423     QFont font;
1424     if (font.fromString(m_pOptions->sMessagesFont))
1425     m_pMessages->setMessagesFont(font);
1426     }
1427     }
1428    
1429    
1430     // Update messages window line limit.
1431     void qsamplerMainForm::updateMessagesLimit (void)
1432     {
1433     if (m_pOptions == NULL)
1434     return;
1435    
1436     if (m_pMessages) {
1437     if (m_pOptions->bMessagesLimit)
1438     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1439     else
1440     m_pMessages->setMessagesLimit(0);
1441     }
1442     }
1443    
1444    
1445     // Enablement of the messages capture feature.
1446     void qsamplerMainForm::updateMessagesCapture (void)
1447     {
1448     if (m_pOptions == NULL)
1449     return;
1450    
1451     if (m_pMessages)
1452     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1453     }
1454    
1455    
1456     //-------------------------------------------------------------------------
1457     // qsamplerMainForm -- MDI channel strip management.
1458    
1459     // The channel strip creation executive.
1460 capela 303 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1461 capela 109 {
1462 capela 303 if (m_pClient == NULL || pChannel == NULL)
1463 capela 295 return NULL;
1464 capela 109
1465     // Prepare for auto-arrange?
1466 capela 264 qsamplerChannelStrip *pChannelStrip = NULL;
1467 capela 109 int y = 0;
1468     if (m_pOptions && m_pOptions->bAutoArrange) {
1469     QWidgetList wlist = m_pWorkspace->windowList();
1470     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1471 capela 264 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1472     // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1473     y += pChannelStrip->parentWidget()->frameGeometry().height();
1474 capela 109 }
1475     }
1476    
1477     // Add a new channel itema...
1478     WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1479 capela 264 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1480 capela 303 if (pChannelStrip == NULL)
1481     return NULL;
1482    
1483     // Actual channel strip setup...
1484     pChannelStrip->setup(pChannel);
1485 capela 295 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1486 capela 267 // Set some initial aesthetic options...
1487     if (m_pOptions) {
1488     // Background display effect...
1489     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1490     // We'll need a display font.
1491     QFont font;
1492     if (font.fromString(m_pOptions->sDisplayFont))
1493     pChannelStrip->setDisplayFont(font);
1494     // Maximum allowed volume setting.
1495     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1496     }
1497 capela 295
1498 capela 109 // Now we show up us to the world.
1499 capela 264 pChannelStrip->show();
1500 capela 109 // Only then, we'll auto-arrange...
1501     if (m_pOptions && m_pOptions->bAutoArrange) {
1502     int iWidth = m_pWorkspace->width();
1503     // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1504 capela 264 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1505     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1506 capela 109 }
1507 capela 295
1508     // Return our successful reference...
1509     return pChannelStrip;
1510 capela 109 }
1511    
1512    
1513     // Retrieve the active channel strip.
1514 capela 264 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1515 capela 109 {
1516     return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1517     }
1518    
1519    
1520     // Retrieve a channel strip by index.
1521 capela 264 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1522 capela 109 {
1523     QWidgetList wlist = m_pWorkspace->windowList();
1524     if (wlist.isEmpty())
1525     return 0;
1526    
1527     return (qsamplerChannelStrip *) wlist.at(iChannel);
1528     }
1529    
1530    
1531     // Construct the windows menu.
1532     void qsamplerMainForm::channelsMenuAboutToShow (void)
1533     {
1534     channelsMenu->clear();
1535     channelsArrangeAction->addTo(channelsMenu);
1536     channelsAutoArrangeAction->addTo(channelsMenu);
1537    
1538     QWidgetList wlist = m_pWorkspace->windowList();
1539     if (!wlist.isEmpty()) {
1540     channelsMenu->insertSeparator();
1541     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1542 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1543     if (pChannelStrip) {
1544     int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1545     channelsMenu->setItemParameter(iItemID, iChannel);
1546     channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1547     }
1548 capela 109 }
1549     }
1550     }
1551    
1552    
1553     // Windows menu activation slot
1554     void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1555     {
1556 capela 264 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1557     if (pChannelStrip)
1558     pChannelStrip->showNormal();
1559     pChannelStrip->setFocus();
1560 capela 109 }
1561    
1562    
1563     //-------------------------------------------------------------------------
1564     // qsamplerMainForm -- Timer stuff.
1565    
1566     // Set the pseudo-timer delay schedule.
1567     void qsamplerMainForm::startSchedule ( int iStartDelay )
1568     {
1569     m_iStartDelay = 1 + (iStartDelay * 1000);
1570     m_iTimerDelay = 0;
1571     }
1572    
1573     // Suspend the pseudo-timer delay schedule.
1574     void qsamplerMainForm::stopSchedule (void)
1575     {
1576     m_iStartDelay = 0;
1577     m_iTimerDelay = 0;
1578     }
1579    
1580     // Timer slot funtion.
1581     void qsamplerMainForm::timerSlot (void)
1582     {
1583     if (m_pOptions == NULL)
1584     return;
1585    
1586     // Is it the first shot on server start after a few delay?
1587     if (m_iTimerDelay < m_iStartDelay) {
1588     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1589     if (m_iTimerDelay >= m_iStartDelay) {
1590     // If we cannot start it now, maybe a lil'mo'later ;)
1591     if (!startClient()) {
1592     m_iStartDelay += m_iTimerDelay;
1593     m_iTimerDelay = 0;
1594     }
1595     }
1596     }
1597    
1598     // Refresh each channel usage, on each period...
1599 capela 296 if (m_pClient && (m_iChangeCount > 0 || m_pOptions->bAutoRefresh)) {
1600 capela 109 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1601 capela 296 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1602 capela 109 m_iTimerSlot = 0;
1603 capela 296 m_iChangeCount = 0;
1604 capela 109 QWidgetList wlist = m_pWorkspace->windowList();
1605     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1606 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1607 capela 297 if (pChannelStrip && pChannelStrip->isVisible()) {
1608     // If we can't make it clean, try next time.
1609     if (!pChannelStrip->updateChannelUsage())
1610     m_iChangeCount++;
1611     }
1612 capela 109 }
1613     }
1614     }
1615    
1616     // Register the next timer slot.
1617     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1618     }
1619    
1620    
1621     //-------------------------------------------------------------------------
1622     // qsamplerMainForm -- Server stuff.
1623    
1624     // Start linuxsampler server...
1625     void qsamplerMainForm::startServer (void)
1626     {
1627     if (m_pOptions == NULL)
1628     return;
1629    
1630     // Aren't already a client, are we?
1631     if (!m_pOptions->bServerStart || m_pClient)
1632     return;
1633    
1634     // Is the server process instance still here?
1635     if (m_pServer) {
1636     switch (QMessageBox::warning(this, tr("Warning"),
1637     tr("Could not start the LinuxSampler server.\n\n"
1638     "Maybe it ss already started."),
1639     tr("Stop"), tr("Kill"), tr("Cancel"))) {
1640     case 0:
1641     m_pServer->tryTerminate();
1642     break;
1643     case 1:
1644     m_pServer->kill();
1645     break;
1646     }
1647     return;
1648     }
1649    
1650     // Reset our timer counters...
1651     stopSchedule();
1652    
1653     // OK. Let's build the startup process...
1654     m_pServer = new QProcess(this);
1655    
1656     // Setup stdout/stderr capture...
1657     //if (m_pOptions->bStdoutCapture) {
1658     m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1659     QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1660     QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1661     //}
1662     // The unforgiveable signal communication...
1663     QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1664    
1665     // Build process arguments...
1666     m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1667    
1668     appendMessages(tr("Server is starting..."));
1669     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1670    
1671     // Go jack, go...
1672     if (!m_pServer->start()) {
1673     appendMessagesError(tr("Could not start server.\n\nSorry."));
1674     processServerExit();
1675     return;
1676     }
1677    
1678     // Show startup results...
1679     appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1680    
1681     // Reset (yet again) the timer counters,
1682     // but this time is deferred as the user opted.
1683     startSchedule(m_pOptions->iStartDelay);
1684     stabilizeForm();
1685     }
1686    
1687    
1688     // Stop linuxsampler server...
1689     void qsamplerMainForm::stopServer (void)
1690     {
1691     // Stop client code.
1692     stopClient();
1693    
1694     // And try to stop server.
1695     if (m_pServer) {
1696     appendMessages(tr("Server is stopping..."));
1697 capela 122 if (m_pServer->isRunning())
1698 capela 109 m_pServer->tryTerminate();
1699     }
1700    
1701 capela 122 // Give it some time to terminate gracefully and stabilize...
1702     QTime t;
1703     t.start();
1704     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1705     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1706    
1707 capela 109 // Do final processing anyway.
1708     processServerExit();
1709     }
1710    
1711    
1712     // Stdout handler...
1713     void qsamplerMainForm::readServerStdout (void)
1714     {
1715     if (m_pMessages)
1716     m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1717     }
1718    
1719    
1720     // Linuxsampler server cleanup.
1721     void qsamplerMainForm::processServerExit (void)
1722     {
1723     // Force client code cleanup.
1724     stopClient();
1725    
1726     // Flush anything that maybe pending...
1727     if (m_pMessages)
1728     m_pMessages->flushStdoutBuffer();
1729    
1730     if (m_pServer) {
1731     // Force final server shutdown...
1732     appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1733     if (!m_pServer->normalExit())
1734     m_pServer->kill();
1735     // Destroy it.
1736     delete m_pServer;
1737     m_pServer = NULL;
1738     }
1739    
1740     // Again, make status visible stable.
1741     stabilizeForm();
1742     }
1743    
1744    
1745     //-------------------------------------------------------------------------
1746     // qsamplerMainForm -- Client stuff.
1747    
1748     // The LSCP client callback procedure.
1749 capela 149 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1750 capela 109 {
1751 capela 149 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1752     if (pMainForm == NULL)
1753     return LSCP_FAILED;
1754    
1755     // ATTN: DO NOT EVER call any GUI code here,
1756 capela 145 // as this is run under some other thread context.
1757     // A custom event must be posted here...
1758 capela 149 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1759 capela 109
1760     return LSCP_OK;
1761     }
1762    
1763    
1764     // Start our almighty client...
1765     bool qsamplerMainForm::startClient (void)
1766     {
1767     // Have it a setup?
1768     if (m_pOptions == NULL)
1769     return false;
1770    
1771     // Aren't we already started, are we?
1772     if (m_pClient)
1773     return true;
1774    
1775     // Log prepare here.
1776     appendMessages(tr("Client connecting..."));
1777    
1778     // Create the client handle...
1779     m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1780     if (m_pClient == NULL) {
1781     // Is this the first try?
1782     // maybe we need to start a local server...
1783     if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1784     appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1785     else
1786     startServer();
1787     // This is always a failure.
1788     stabilizeForm();
1789     return false;
1790     }
1791     // Just set receive timeout value, blindly.
1792     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1793     appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1794    
1795     // We may stop scheduling around.
1796     stopSchedule();
1797    
1798     // We'll accept drops from now on...
1799     setAcceptDrops(true);
1800    
1801     // Log success here.
1802     appendMessages(tr("Client connected."));
1803    
1804     // Is any session pending to be loaded?
1805     if (!m_pOptions->sSessionFile.isEmpty()) {
1806     // Just load the prabably startup session...
1807     if (loadSessionFile(m_pOptions->sSessionFile)) {
1808     m_pOptions->sSessionFile = QString::null;
1809     return true;
1810     }
1811     }
1812    
1813     // Make a new session
1814     return newSession();
1815     }
1816    
1817    
1818     // Stop client...
1819     void qsamplerMainForm::stopClient (void)
1820     {
1821     if (m_pClient == NULL)
1822     return;
1823    
1824     // Log prepare here.
1825     appendMessages(tr("Client disconnecting..."));
1826    
1827     // Clear timer counters...
1828     stopSchedule();
1829    
1830     // We'll reject drops from now on...
1831     setAcceptDrops(false);
1832    
1833     // Force any channel strips around, but
1834     // but avoid removing the corresponding
1835     // channels from the back-end server.
1836     m_iDirtyCount = 0;
1837     closeSession(false);
1838    
1839     // Close us as a client...
1840     lscp_client_destroy(m_pClient);
1841     m_pClient = NULL;
1842    
1843     // Log final here.
1844     appendMessages(tr("Client disconnected."));
1845    
1846     // Make visible status.
1847     stabilizeForm();
1848     }
1849    
1850    
1851     // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC