/[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 395 - (hide annotations) (download) (as text)
Sun Feb 20 19:13:33 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 58877 byte(s)
* Drag-and-drop to an existing channel strip is now also
  featured, allowing the in-place change of the channel
  sampler instrument file.

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

  ViewVC Help
Powered by ViewVC