/[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 757 - (hide annotations) (download) (as text)
Fri Aug 26 23:04:32 2005 UTC (18 years, 7 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66454 byte(s)
* All widget captions changed to include proper application title prefix.

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 capela 428
45     #include "qsamplerDeviceForm.h"
46 capela 109 #include "qsamplerOptionsForm.h"
47    
48 capela 340 #ifdef HAVE_SIGNAL_H
49     #include <signal.h>
50     #endif
51 capela 109
52 capela 605 #ifdef CONFIG_LIBGIG
53     #include <gig.h>
54 schoenebeck 519 #endif
55    
56 capela 109 // Timer constant stuff.
57     #define QSAMPLER_TIMER_MSECS 200
58    
59     // Status bar item indexes
60     #define QSAMPLER_STATUS_CLIENT 0 // Client connection state.
61     #define QSAMPLER_STATUS_SERVER 1 // Currenr server address (host:port)
62     #define QSAMPLER_STATUS_CHANNEL 2 // Active channel caption.
63     #define QSAMPLER_STATUS_SESSION 3 // Current session modification state.
64    
65    
66 capela 149 // All winsock apps needs this.
67 capela 109 #if defined(WIN32)
68     static WSADATA _wsaData;
69     #endif
70    
71 capela 149
72 capela 109 //-------------------------------------------------------------------------
73 capela 149 // qsamplerCustomEvent -- specialty for callback comunication.
74    
75     #define QSAMPLER_CUSTOM_EVENT 1000
76    
77     class qsamplerCustomEvent : public QCustomEvent
78     {
79     public:
80    
81     // Constructor.
82     qsamplerCustomEvent(lscp_event_t event, const char *pchData, int cchData)
83     : QCustomEvent(QSAMPLER_CUSTOM_EVENT)
84     {
85     m_event = event;
86     m_data.setLatin1(pchData, cchData);
87     }
88    
89     // Accessors.
90     lscp_event_t event() { return m_event; }
91     QString& data() { return m_data; }
92    
93     private:
94    
95     // The proper event type.
96     lscp_event_t m_event;
97     // The event data as a string.
98     QString m_data;
99     };
100    
101    
102     //-------------------------------------------------------------------------
103 capela 109 // qsamplerMainForm -- Main window form implementation.
104    
105     // Kind of constructor.
106     void qsamplerMainForm::init (void)
107     {
108     // Initialize some pointer references.
109     m_pOptions = NULL;
110    
111     // All child forms are to be created later, not earlier than setup.
112 capela 428 m_pMessages = NULL;
113     m_pDeviceForm = NULL;
114 capela 109
115     // We'll start clean.
116 capela 400 m_iUntitled = 0;
117     m_iDirtyCount = 0;
118 capela 109
119     m_pServer = NULL;
120     m_pClient = NULL;
121    
122     m_iStartDelay = 0;
123     m_iTimerDelay = 0;
124    
125     m_iTimerSlot = 0;
126    
127 capela 340 #ifdef HAVE_SIGNAL_H
128     // Set to ignore any fatal "Broken pipe" signals.
129     ::signal(SIGPIPE, SIG_IGN);
130     #endif
131    
132 capela 109 // Make it an MDI workspace.
133     m_pWorkspace = new QWorkspace(this);
134     m_pWorkspace->setScrollBarsEnabled(true);
135     // Set the activation connection.
136     QObject::connect(m_pWorkspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(stabilizeForm()));
137     // Make it shine :-)
138     setCentralWidget(m_pWorkspace);
139    
140     // Create some statusbar labels...
141     QLabel *pLabel;
142     // Client status.
143     pLabel = new QLabel(tr("Connected"), this);
144     pLabel->setAlignment(Qt::AlignLeft);
145     pLabel->setMinimumSize(pLabel->sizeHint());
146 capela 428 m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
147 capela 109 statusBar()->addWidget(pLabel);
148     // Server address.
149     pLabel = new QLabel(this);
150     pLabel->setAlignment(Qt::AlignLeft);
151 capela 428 m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
152 capela 109 statusBar()->addWidget(pLabel, 1);
153     // Channel title.
154     pLabel = new QLabel(this);
155     pLabel->setAlignment(Qt::AlignLeft);
156 capela 428 m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
157 capela 109 statusBar()->addWidget(pLabel, 2);
158     // Session modification status.
159     pLabel = new QLabel(tr("MOD"), this);
160     pLabel->setAlignment(Qt::AlignHCenter);
161     pLabel->setMinimumSize(pLabel->sizeHint());
162 capela 428 m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
163 capela 109 statusBar()->addWidget(pLabel);
164    
165     // Create the recent files sub-menu.
166     m_pRecentFilesMenu = new QPopupMenu(this);
167     fileMenu->insertSeparator(4);
168     fileMenu->insertItem(tr("Recent &Files"), m_pRecentFilesMenu, 0, 5);
169    
170     #if defined(WIN32)
171     WSAStartup(MAKEWORD(1, 1), &_wsaData);
172     #endif
173     }
174    
175    
176     // Kind of destructor.
177     void qsamplerMainForm::destroy (void)
178     {
179     // Do final processing anyway.
180     processServerExit();
181 capela 418
182     #if defined(WIN32)
183     WSACleanup();
184     #endif
185    
186     // Finally drop any widgets around...
187 capela 428 if (m_pDeviceForm)
188     delete m_pDeviceForm;
189 capela 418 if (m_pMessages)
190     delete m_pMessages;
191     if (m_pWorkspace)
192     delete m_pWorkspace;
193    
194 capela 109 // Delete status item labels one by one.
195 capela 428 if (m_statusItem[QSAMPLER_STATUS_CLIENT])
196     delete m_statusItem[QSAMPLER_STATUS_CLIENT];
197     if (m_statusItem[QSAMPLER_STATUS_SERVER])
198     delete m_statusItem[QSAMPLER_STATUS_SERVER];
199     if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
200     delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
201     if (m_statusItem[QSAMPLER_STATUS_SESSION])
202     delete m_statusItem[QSAMPLER_STATUS_SESSION];
203 capela 109
204 capela 418 // Delete recentfiles menu.
205     if (m_pRecentFilesMenu)
206     delete m_pRecentFilesMenu;
207 capela 109 }
208    
209    
210     // Make and set a proper setup options step.
211     void qsamplerMainForm::setup ( qsamplerOptions *pOptions )
212     {
213     // We got options?
214     m_pOptions = pOptions;
215    
216 capela 454 // What style do we create these forms?
217     WFlags wflags = Qt::WType_TopLevel;
218     if (m_pOptions->bKeepOnTop)
219     wflags |= Qt::WStyle_Tool;
220 capela 109 // Some child forms are to be created right now.
221     m_pMessages = new qsamplerMessages(this);
222 capela 454 m_pDeviceForm = new qsamplerDeviceForm(this, 0, wflags);
223 capela 487 m_pDeviceForm->setMainForm(this); // An important life immutable!
224 capela 109 // Set message defaults...
225     updateMessagesFont();
226     updateMessagesLimit();
227     updateMessagesCapture();
228     // Set the visibility signal.
229     QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), this, SLOT(stabilizeForm()));
230    
231     // Initial decorations toggle state.
232     viewMenubarAction->setOn(m_pOptions->bMenubar);
233     viewToolbarAction->setOn(m_pOptions->bToolbar);
234     viewStatusbarAction->setOn(m_pOptions->bStatusbar);
235     channelsAutoArrangeAction->setOn(m_pOptions->bAutoArrange);
236    
237     // Initial decorations visibility state.
238     viewMenubar(m_pOptions->bMenubar);
239     viewToolbar(m_pOptions->bToolbar);
240     viewStatusbar(m_pOptions->bStatusbar);
241    
242     // Restore whole dock windows state.
243     QString sDockables = m_pOptions->settings().readEntry("/Layout/DockWindows" , QString::null);
244     if (sDockables.isEmpty()) {
245     // Message window is forced to dock on the bottom.
246     moveDockWindow(m_pMessages, Qt::DockBottom);
247     } else {
248     // Make it as the last time.
249     QTextIStream istr(&sDockables);
250     istr >> *this;
251     }
252 capela 428 // Try to restore old window positioning and initial visibility.
253 capela 109 m_pOptions->loadWidgetGeometry(this);
254 capela 428 m_pOptions->loadWidgetGeometry(m_pDeviceForm);
255 capela 109
256     // Final startup stabilization...
257     updateRecentFilesMenu();
258     stabilizeForm();
259    
260     // Make it ready :-)
261     statusBar()->message(tr("Ready"), 3000);
262    
263     // We'll try to start immediately...
264     startSchedule(0);
265    
266     // Register the first timer slot.
267     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
268     }
269    
270    
271     // Window close event handlers.
272     bool qsamplerMainForm::queryClose (void)
273     {
274     bool bQueryClose = closeSession(false);
275    
276     // Try to save current general state...
277     if (m_pOptions) {
278     // Some windows default fonts is here on demand too.
279     if (bQueryClose && m_pMessages)
280     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
281     // Try to save current positioning.
282     if (bQueryClose) {
283     // Save decorations state.
284     m_pOptions->bMenubar = MenuBar->isVisible();
285     m_pOptions->bToolbar = (fileToolbar->isVisible() || editToolbar->isVisible() || channelsToolbar->isVisible());
286     m_pOptions->bStatusbar = statusBar()->isVisible();
287     // Save the dock windows state.
288     QString sDockables;
289     QTextOStream ostr(&sDockables);
290     ostr << *this;
291     m_pOptions->settings().writeEntry("/Layout/DockWindows", sDockables);
292 capela 428 // And the children, and the main windows state,.
293 capela 586 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
294     m_pOptions->saveWidgetGeometry(this);
295     // Close popup widgets.
296     if (m_pDeviceForm)
297     m_pDeviceForm->close();
298 capela 109 // Stop client and/or server, gracefully.
299     stopServer();
300     }
301     }
302    
303     return bQueryClose;
304     }
305    
306    
307     void qsamplerMainForm::closeEvent ( QCloseEvent *pCloseEvent )
308     {
309     if (queryClose())
310     pCloseEvent->accept();
311     else
312     pCloseEvent->ignore();
313     }
314    
315    
316 capela 388 // Drag'n'drop file handler.
317     bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
318 capela 109 {
319 capela 388 bool bDecode = false;
320 capela 109
321 capela 388 if (QTextDrag::canDecode(pEvent)) {
322     QString sText;
323     bDecode = QTextDrag::decode(pEvent, sText);
324     if (bDecode) {
325     files = QStringList::split('\n', sText);
326     for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
327 capela 409 *iter = QUrl((*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null)).path();
328 capela 388 }
329 capela 109 }
330    
331 capela 388 return bDecode;
332 capela 109 }
333    
334    
335 capela 388 // Window drag-n-drop event handlers.
336     void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
337     {
338 capela 395 QStringList files;
339     pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
340 capela 388 }
341    
342    
343 capela 109 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
344     {
345 capela 388 QStringList files;
346 capela 391
347     if (!decodeDragFiles(pDropEvent, files))
348     return;
349    
350 capela 586 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
351 capela 409 const QString& sPath = *iter;
352 capela 391 if (qsamplerChannel::isInstrumentFile(sPath)) {
353     // Try to create a new channel from instrument file...
354 capela 586 qsamplerChannel *pChannel = new qsamplerChannel(this);
355     if (pChannel == NULL)
356     return;
357 capela 391 // Start setting the instrument filename...
358     pChannel->setInstrument(sPath, 0);
359 capela 586 // Before we show it up, may be we'll
360     // better ask for some initial values?
361     if (!pChannel->channelSetup(this)) {
362     delete pChannel;
363     return;
364     }
365     // Finally, give it to a new channel strip...
366 capela 391 if (!createChannelStrip(pChannel)) {
367 capela 586 delete pChannel;
368     return;
369 capela 391 }
370 capela 586 // Make that an overall update.
371     m_iDirtyCount++;
372     stabilizeForm();
373 capela 391 } // Otherwise, load an usual session file (LSCP script)...
374     else if (closeSession(true))
375     loadSessionFile(sPath);
376     // Make it look responsive...:)
377     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
378     }
379 capela 109 }
380    
381    
382 capela 149 // Custome event handler.
383     void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
384     {
385     // For the time being, just pump it to messages.
386     if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
387     qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
388 capela 662 if (pEvent->event() == LSCP_EVENT_CHANNEL_INFO) {
389     int iChannelID = pEvent->data().toInt();
390     qsamplerChannelStrip *pChannelStrip = channelStrip(iChannelID);
391     if (pChannelStrip)
392     channelStripChanged(pChannelStrip);
393 capela 680 } else {
394     appendMessagesColor(tr("Notify event: %1 data: %2")
395     .arg(::lscp_event_to_text(pEvent->event()))
396     .arg(pEvent->data()), "#996699");
397 capela 662 }
398 capela 149 }
399     }
400    
401    
402 capela 127 // Context menu event handler.
403     void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
404     {
405     stabilizeForm();
406 capela 586
407 capela 127 editMenu->exec(pEvent->globalPos());
408     }
409    
410    
411 capela 109 //-------------------------------------------------------------------------
412     // qsamplerMainForm -- Brainless public property accessors.
413    
414     // The global options settings property.
415     qsamplerOptions *qsamplerMainForm::options (void)
416     {
417     return m_pOptions;
418     }
419    
420     // The LSCP client descriptor property.
421     lscp_client_t *qsamplerMainForm::client (void)
422     {
423     return m_pClient;
424     }
425    
426    
427     //-------------------------------------------------------------------------
428     // qsamplerMainForm -- Session file stuff.
429    
430     // Format the displayable session filename.
431     QString qsamplerMainForm::sessionName ( const QString& sFilename )
432     {
433     bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
434     QString sSessionName = sFilename;
435     if (sSessionName.isEmpty())
436     sSessionName = tr("Untitled") + QString::number(m_iUntitled);
437     else if (!bCompletePath)
438     sSessionName = QFileInfo(sSessionName).fileName();
439     return sSessionName;
440     }
441    
442    
443     // Create a new session file from scratch.
444     bool qsamplerMainForm::newSession (void)
445     {
446     // Check if we can do it.
447     if (!closeSession(true))
448     return false;
449    
450 capela 395 // Give us what the server has, right now...
451     updateSession();
452    
453 capela 109 // Ok increment untitled count.
454     m_iUntitled++;
455    
456     // Stabilize form.
457     m_sFilename = QString::null;
458     m_iDirtyCount = 0;
459     appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
460     stabilizeForm();
461    
462     return true;
463     }
464    
465    
466     // Open an existing sampler session.
467     bool qsamplerMainForm::openSession (void)
468     {
469     if (m_pOptions == NULL)
470     return false;
471    
472     // Ask for the filename to open...
473     QString sFilename = QFileDialog::getOpenFileName(
474 capela 757 m_pOptions->sSessionDir, // Start here.
475     tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
476     this, 0, // Parent and name (none)
477     QSAMPLER_TITLE ": " + tr("Open Session") // Caption.
478 capela 109 );
479    
480     // Have we cancelled?
481     if (sFilename.isEmpty())
482     return false;
483    
484     // Check if we're going to discard safely the current one...
485     if (!closeSession(true))
486     return false;
487    
488     // Load it right away.
489     return loadSessionFile(sFilename);
490     }
491    
492    
493     // Save current sampler session with another name.
494     bool qsamplerMainForm::saveSession ( bool bPrompt )
495     {
496     if (m_pOptions == NULL)
497     return false;
498    
499     QString sFilename = m_sFilename;
500    
501     // Ask for the file to save, if there's none...
502     if (bPrompt || sFilename.isEmpty()) {
503     // If none is given, assume default directory.
504     if (sFilename.isEmpty())
505     sFilename = m_pOptions->sSessionDir;
506     // Prompt the guy...
507     sFilename = QFileDialog::getSaveFileName(
508 capela 757 sFilename, // Start here.
509     tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
510     this, 0, // Parent and name (none)
511     QSAMPLER_TITLE ": " + tr("Save Session") // Caption.
512 capela 109 );
513     // Have we cancelled it?
514     if (sFilename.isEmpty())
515     return false;
516     // Enforce .lscp extension...
517     if (QFileInfo(sFilename).extension().isEmpty())
518     sFilename += ".lscp";
519     // Check if already exists...
520     if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
521 capela 757 if (QMessageBox::warning(this,
522     QSAMPLER_TITLE ": " + tr("Warning"),
523 capela 109 tr("The file already exists:\n\n"
524     "\"%1\"\n\n"
525     "Do you want to replace it?")
526     .arg(sFilename),
527     tr("Replace"), tr("Cancel")) > 0)
528     return false;
529     }
530     }
531    
532     // Save it right away.
533     return saveSessionFile(sFilename);
534     }
535    
536    
537     // Close current session.
538     bool qsamplerMainForm::closeSession ( bool bForce )
539     {
540     bool bClose = true;
541    
542     // Are we dirty enough to prompt it?
543     if (m_iDirtyCount > 0) {
544 capela 757 switch (QMessageBox::warning(this,
545     QSAMPLER_TITLE ": " + tr("Warning"),
546 capela 109 tr("The current session has been changed:\n\n"
547     "\"%1\"\n\n"
548     "Do you want to save the changes?")
549     .arg(sessionName(m_sFilename)),
550     tr("Save"), tr("Discard"), tr("Cancel"))) {
551     case 0: // Save...
552     bClose = saveSession(false);
553     // Fall thru....
554     case 1: // Discard
555     break;
556     default: // Cancel.
557     bClose = false;
558     break;
559     }
560     }
561    
562     // If we may close it, dot it.
563     if (bClose) {
564     // Remove all channel strips from sight...
565     m_pWorkspace->setUpdatesEnabled(false);
566     QWidgetList wlist = m_pWorkspace->windowList();
567     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
568 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
569     if (pChannelStrip) {
570     qsamplerChannel *pChannel = pChannelStrip->channel();
571 capela 295 if (bForce && pChannel)
572     pChannel->removeChannel();
573 capela 264 delete pChannelStrip;
574     }
575 capela 109 }
576     m_pWorkspace->setUpdatesEnabled(true);
577     // We're now clean, for sure.
578     m_iDirtyCount = 0;
579     }
580    
581     return bClose;
582     }
583    
584    
585     // Load a session from specific file path.
586     bool qsamplerMainForm::loadSessionFile ( const QString& sFilename )
587     {
588     if (m_pClient == NULL)
589     return false;
590    
591     // Open and read from real file.
592     QFile file(sFilename);
593     if (!file.open(IO_ReadOnly)) {
594     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
595     return false;
596     }
597    
598     // Read the file.
599     int iErrors = 0;
600     QTextStream ts(&file);
601     while (!ts.atEnd()) {
602     // Read the line.
603     QString sCommand = ts.readLine().simplifyWhiteSpace();
604     // If not empty, nor a comment, call the server...
605     if (!sCommand.isEmpty() && sCommand[0] != '#') {
606     appendMessagesColor(sCommand, "#996633");
607     // Remember that, no matter what,
608     // all LSCP commands are CR/LF terminated.
609     sCommand += "\r\n";
610     if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {
611     appendMessagesClient("lscp_client_query");
612     iErrors++;
613     }
614     }
615     // Try to make it snappy :)
616     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
617     }
618    
619     // Ok. we've read it.
620     file.close();
621    
622 capela 395 // Now we'll try to create (update) the whole GUI session.
623     updateSession();
624 capela 109
625 capela 470 // Have we any errors?
626     if (iErrors > 0)
627     appendMessagesError(tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.").arg(sFilename));
628    
629 capela 109 // Save as default session directory.
630     if (m_pOptions)
631     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
632 capela 470 // We're not dirty anymore, if loaded without errors,
633     m_iDirtyCount = iErrors;
634 capela 109 // Stabilize form...
635     m_sFilename = sFilename;
636     updateRecentFiles(sFilename);
637     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
638 capela 586
639 capela 301 // Make that an overall update.
640 capela 109 stabilizeForm();
641     return true;
642     }
643    
644    
645     // Save current session to specific file path.
646     bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
647     {
648     // Open and write into real file.
649     QFile file(sFilename);
650     if (!file.open(IO_WriteOnly | IO_Truncate)) {
651     appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
652     return false;
653     }
654    
655     // Write the file.
656 capela 455 int iErrors = 0;
657 capela 109 QTextStream ts(&file);
658     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
659     ts << "# " << tr("Version")
660     << ": " QSAMPLER_VERSION << endl;
661     ts << "# " << tr("Build")
662     << ": " __DATE__ " " __TIME__ << endl;
663     ts << "#" << endl;
664     ts << "# " << tr("File")
665     << ": " << QFileInfo(sFilename).fileName() << endl;
666     ts << "# " << tr("Date")
667 capela 455 << ": " << QDate::currentDate().toString("MMM dd yyyy")
668 capela 109 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
669     ts << "#" << endl;
670     ts << endl;
671 capela 455 // It is assumed that this new kind of device+session file
672     // will be loaded from a complete
673     int *piDeviceIDs;
674     int iDevice;
675     ts << "RESET" << endl;
676     // Audio device mapping.
677     QMap<int, int> audioDeviceMap;
678     piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Audio);
679     for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
680 capela 463 ts << endl;
681 capela 484 qsamplerDevice device(this, qsamplerDevice::Audio, piDeviceIDs[iDevice]);
682 capela 463 // Audio device specification...
683 capela 470 ts << "# " << device.deviceTypeName() << " " << device.driverName()
684 capela 586 << " " << tr("Device") << " " << iDevice << endl;
685 capela 455 ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
686 capela 463 qsamplerDeviceParamMap::ConstIterator deviceParam;
687     for (deviceParam = device.params().begin();
688     deviceParam != device.params().end();
689     ++deviceParam) {
690 capela 470 const qsamplerDeviceParam& param = deviceParam.data();
691 capela 479 if (param.value.isEmpty()) ts << "# ";
692     ts << " " << deviceParam.key() << "='" << param.value << "'";
693 capela 463 }
694 capela 455 ts << endl;
695 capela 463 // Audio channel parameters...
696     int iPort = 0;
697     for (qsamplerDevicePort *pPort = device.ports().first();
698     pPort;
699     pPort = device.ports().next(), ++iPort) {
700     qsamplerDeviceParamMap::ConstIterator portParam;
701     for (portParam = pPort->params().begin();
702     portParam != pPort->params().end();
703     ++portParam) {
704 capela 470 const qsamplerDeviceParam& param = portParam.data();
705 capela 479 if (param.fix || param.value.isEmpty()) ts << "# ";
706     ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
707 capela 586 << " " << iPort << " " << portParam.key()
708     << "='" << param.value << "'" << endl;
709 capela 463 }
710     }
711 capela 469 // Audio device index/id mapping.
712 capela 455 audioDeviceMap[device.deviceID()] = iDevice;
713     // Try to keep it snappy :)
714     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
715     }
716     // MIDI device mapping.
717     QMap<int, int> midiDeviceMap;
718     piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Midi);
719     for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
720 capela 463 ts << endl;
721 capela 484 qsamplerDevice device(this, qsamplerDevice::Midi, piDeviceIDs[iDevice]);
722 capela 463 // MIDI device specification...
723 capela 586 ts << "# " << device.deviceTypeName() << " " << device.driverName()
724     << " " << tr("Device") << " " << iDevice << endl;
725 capela 455 ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
726 capela 463 qsamplerDeviceParamMap::ConstIterator deviceParam;
727     for (deviceParam = device.params().begin();
728     deviceParam != device.params().end();
729     ++deviceParam) {
730 capela 470 const qsamplerDeviceParam& param = deviceParam.data();
731 capela 479 if (param.value.isEmpty()) ts << "# ";
732     ts << " " << deviceParam.key() << "='" << param.value << "'";
733 capela 463 }
734 capela 455 ts << endl;
735 capela 463 // MIDI port parameters...
736     int iPort = 0;
737     for (qsamplerDevicePort *pPort = device.ports().first();
738     pPort;
739     pPort = device.ports().next(), ++iPort) {
740     qsamplerDeviceParamMap::ConstIterator portParam;
741     for (portParam = pPort->params().begin();
742     portParam != pPort->params().end();
743     ++portParam) {
744 capela 470 const qsamplerDeviceParam& param = portParam.data();
745 capela 479 if (param.fix || param.value.isEmpty()) ts << "# ";
746     ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
747     << " " << iPort << " " << portParam.key()
748     << "='" << param.value << "'" << endl;
749 capela 463 }
750     }
751 capela 469 // MIDI device index/id mapping.
752     midiDeviceMap[device.deviceID()] = iDevice;
753     // Try to keep it snappy :)
754     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
755 capela 455 }
756     ts << endl;
757     // Sampler channel mapping.
758 capela 109 QWidgetList wlist = m_pWorkspace->windowList();
759     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
760 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
761     if (pChannelStrip) {
762     qsamplerChannel *pChannel = pChannelStrip->channel();
763     if (pChannel) {
764 capela 455 ts << "# " << tr("Channel") << " " << iChannel << endl;
765 capela 264 ts << "ADD CHANNEL" << endl;
766 capela 586 if (audioDeviceMap.isEmpty()) {
767     ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
768     << " " << pChannel->audioDriver() << endl;
769     } else {
770     ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
771     << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
772 capela 455 }
773 capela 586 if (midiDeviceMap.isEmpty()) {
774     ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
775     << " " << pChannel->midiDriver() << endl;
776     } else {
777     ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
778     << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
779     }
780     ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
781     << " " << pChannel->midiPort() << endl;
782 capela 409 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
783 capela 304 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
784     ts << "ALL";
785     else
786 capela 264 ts << pChannel->midiChannel();
787     ts << endl;
788 capela 409 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
789     ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl;
790     ts << "SET CHANNEL VOLUME " << iChannel << " " << pChannel->volume() << endl;
791 capela 751 if (pChannel->channelMute())
792     ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl;
793     if (pChannel->channelSolo())
794     ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl;
795 capela 264 ts << endl;
796     }
797     }
798 capela 109 // Try to keep it snappy :)
799     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
800     }
801    
802     // Ok. we've wrote it.
803     file.close();
804    
805     // Have we any errors?
806     if (iErrors > 0)
807     appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
808    
809     // Save as default session directory.
810     if (m_pOptions)
811     m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
812     // We're not dirty anymore.
813     m_iDirtyCount = 0;
814     // Stabilize form...
815     m_sFilename = sFilename;
816     updateRecentFiles(sFilename);
817     appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
818     stabilizeForm();
819     return true;
820     }
821    
822    
823 capela 433 // Session change receiver slot.
824     void qsamplerMainForm::sessionDirty (void)
825     {
826     // Just mark the dirty form.
827     m_iDirtyCount++;
828     // and update the form status...
829     stabilizeForm();
830     }
831    
832    
833 capela 109 //-------------------------------------------------------------------------
834     // qsamplerMainForm -- File Action slots.
835    
836     // Create a new sampler session.
837     void qsamplerMainForm::fileNew (void)
838     {
839     // Of course we'll start clean new.
840     newSession();
841     }
842    
843    
844     // Open an existing sampler session.
845     void qsamplerMainForm::fileOpen (void)
846     {
847     // Open it right away.
848     openSession();
849     }
850    
851    
852     // Open a recent file session.
853     void qsamplerMainForm::fileOpenRecent ( int iIndex )
854     {
855     // Check if we can safely close the current session...
856     if (m_pOptions && closeSession(true)) {
857     QString sFilename = m_pOptions->recentFiles[iIndex];
858     loadSessionFile(sFilename);
859     }
860     }
861    
862    
863     // Save current sampler session.
864     void qsamplerMainForm::fileSave (void)
865     {
866     // Save it right away.
867     saveSession(false);
868     }
869    
870    
871     // Save current sampler session with another name.
872     void qsamplerMainForm::fileSaveAs (void)
873     {
874     // Save it right away, maybe with another name.
875     saveSession(true);
876     }
877    
878    
879 capela 255 // Reset the sampler instance.
880     void qsamplerMainForm::fileReset (void)
881     {
882     if (m_pClient == NULL)
883     return;
884    
885     // Ask user whether he/she want's an internal sampler reset...
886 capela 757 if (QMessageBox::warning(this,
887     QSAMPLER_TITLE ": " + tr("Warning"),
888 capela 255 tr("Resetting the sampler instance will close\n"
889     "all device and channel configurations.\n\n"
890     "Please note that this operation may cause\n"
891 capela 586 "temporary MIDI and Audio disruption.\n\n"
892 capela 255 "Do you want to reset the sampler engine now?"),
893     tr("Reset"), tr("Cancel")) > 0)
894     return;
895    
896     // Just do the reset, after closing down current session...
897 capela 261 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
898 capela 255 appendMessagesClient("lscp_reset_sampler");
899 capela 261 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
900     return;
901     }
902    
903     // Log this.
904     appendMessages(tr("Sampler reset."));
905 capela 395
906     // Make it a new session...
907     newSession();
908 capela 255 }
909    
910    
911 capela 109 // Restart the client/server instance.
912     void qsamplerMainForm::fileRestart (void)
913     {
914     if (m_pOptions == NULL)
915     return;
916 capela 586
917 capela 109 bool bRestart = true;
918 capela 586
919 capela 109 // Ask user whether he/she want's a complete restart...
920     // (if we're currently up and running)
921     if (bRestart && m_pClient) {
922 capela 757 bRestart = (QMessageBox::warning(this,
923     QSAMPLER_TITLE ": " + tr("Warning"),
924 capela 109 tr("New settings will be effective after\n"
925     "restarting the client/server connection.\n\n"
926     "Please note that this operation may cause\n"
927 capela 586 "temporary MIDI and Audio disruption.\n\n"
928 capela 109 "Do you want to restart the connection now?"),
929     tr("Restart"), tr("Cancel")) == 0);
930     }
931    
932     // Are we still for it?
933     if (bRestart && closeSession(true)) {
934     // Stop server, it will force the client too.
935     stopServer();
936     // Reschedule a restart...
937     startSchedule(m_pOptions->iStartDelay);
938     }
939     }
940    
941    
942     // Exit application program.
943     void qsamplerMainForm::fileExit (void)
944     {
945     // Go for close the whole thing.
946     close();
947     }
948    
949    
950     //-------------------------------------------------------------------------
951     // qsamplerMainForm -- Edit Action slots.
952    
953     // Add a new sampler channel.
954     void qsamplerMainForm::editAddChannel (void)
955     {
956     if (m_pClient == NULL)
957     return;
958    
959 capela 303 // Just create the channel instance...
960     qsamplerChannel *pChannel = new qsamplerChannel(this);
961     if (pChannel == NULL)
962     return;
963    
964     // Before we show it up, may be we'll
965     // better ask for some initial values?
966     if (!pChannel->channelSetup(this)) {
967     delete pChannel;
968     return;
969     }
970 capela 586
971 capela 303 // And give it to the strip (will own the channel instance, if successful).
972     if (!createChannelStrip(pChannel)) {
973     delete pChannel;
974     return;
975     }
976    
977     // Make that an overall update.
978     m_iDirtyCount++;
979     stabilizeForm();
980 capela 109 }
981    
982    
983     // Remove current sampler channel.
984     void qsamplerMainForm::editRemoveChannel (void)
985     {
986     if (m_pClient == NULL)
987     return;
988    
989 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
990     if (pChannelStrip == NULL)
991     return;
992 capela 586
993 capela 264 qsamplerChannel *pChannel = pChannelStrip->channel();
994 capela 109 if (pChannel == NULL)
995     return;
996    
997     // Prompt user if he/she's sure about this...
998     if (m_pOptions && m_pOptions->bConfirmRemove) {
999 capela 757 if (QMessageBox::warning(this,
1000     QSAMPLER_TITLE ": " + tr("Warning"),
1001 capela 109 tr("About to remove channel:\n\n"
1002     "%1\n\n"
1003     "Are you sure?")
1004 capela 264 .arg(pChannelStrip->caption()),
1005 capela 109 tr("OK"), tr("Cancel")) > 0)
1006     return;
1007     }
1008    
1009     // Remove the existing sampler channel.
1010 capela 295 if (!pChannel->removeChannel())
1011 capela 109 return;
1012    
1013     // Just delete the channel strip.
1014 capela 265 delete pChannelStrip;
1015 capela 586
1016 capela 109 // Do we auto-arrange?
1017     if (m_pOptions && m_pOptions->bAutoArrange)
1018     channelsArrange();
1019    
1020     // We'll be dirty, for sure...
1021     m_iDirtyCount++;
1022     stabilizeForm();
1023     }
1024    
1025    
1026     // Setup current sampler channel.
1027     void qsamplerMainForm::editSetupChannel (void)
1028     {
1029     if (m_pClient == NULL)
1030     return;
1031    
1032 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1033     if (pChannelStrip == NULL)
1034 capela 109 return;
1035    
1036     // Just invoque the channel strip procedure.
1037 capela 295 pChannelStrip->channelSetup();
1038 capela 109 }
1039    
1040    
1041     // Reset current sampler channel.
1042     void qsamplerMainForm::editResetChannel (void)
1043     {
1044     if (m_pClient == NULL)
1045     return;
1046    
1047 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1048     if (pChannelStrip == NULL)
1049     return;
1050    
1051 capela 400 // Just invoque the channel strip procedure.
1052     pChannelStrip->channelReset();
1053 capela 109 }
1054    
1055    
1056 capela 404 // Reset all sampler channels.
1057     void qsamplerMainForm::editResetAllChannels (void)
1058     {
1059 capela 586 if (m_pClient == NULL)
1060     return;
1061 capela 404
1062 capela 586 // Invoque the channel strip procedure,
1063 capela 404 // for all channels out there...
1064 capela 586 m_pWorkspace->setUpdatesEnabled(false);
1065     QWidgetList wlist = m_pWorkspace->windowList();
1066     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1067     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1068     if (pChannelStrip)
1069     pChannelStrip->channelReset();
1070     }
1071     m_pWorkspace->setUpdatesEnabled(true);
1072 capela 404 }
1073    
1074    
1075 capela 109 //-------------------------------------------------------------------------
1076     // qsamplerMainForm -- View Action slots.
1077    
1078     // Show/hide the main program window menubar.
1079     void qsamplerMainForm::viewMenubar ( bool bOn )
1080     {
1081     if (bOn)
1082     MenuBar->show();
1083     else
1084     MenuBar->hide();
1085     }
1086    
1087    
1088     // Show/hide the main program window toolbar.
1089     void qsamplerMainForm::viewToolbar ( bool bOn )
1090     {
1091     if (bOn) {
1092     fileToolbar->show();
1093     editToolbar->show();
1094     channelsToolbar->show();
1095     } else {
1096     fileToolbar->hide();
1097     editToolbar->hide();
1098     channelsToolbar->hide();
1099     }
1100     }
1101    
1102    
1103     // Show/hide the main program window statusbar.
1104     void qsamplerMainForm::viewStatusbar ( bool bOn )
1105     {
1106     if (bOn)
1107     statusBar()->show();
1108     else
1109     statusBar()->hide();
1110     }
1111    
1112    
1113     // Show/hide the messages window logger.
1114     void qsamplerMainForm::viewMessages ( bool bOn )
1115     {
1116     if (bOn)
1117     m_pMessages->show();
1118     else
1119     m_pMessages->hide();
1120     }
1121    
1122    
1123 capela 428 // Show/hide the device configurator form.
1124     void qsamplerMainForm::viewDevices (void)
1125     {
1126 capela 586 if (m_pOptions == NULL)
1127     return;
1128 capela 428
1129 capela 586 if (m_pDeviceForm) {
1130     m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1131 capela 428 m_pDeviceForm->setClient(m_pClient);
1132 capela 586 if (m_pDeviceForm->isVisible()) {
1133     m_pDeviceForm->hide();
1134     } else {
1135     m_pDeviceForm->show();
1136     m_pDeviceForm->raise();
1137     m_pDeviceForm->setActiveWindow();
1138     }
1139     }
1140 capela 428 }
1141    
1142    
1143 capela 109 // Show options dialog.
1144     void qsamplerMainForm::viewOptions (void)
1145     {
1146     if (m_pOptions == NULL)
1147     return;
1148    
1149     qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1150     if (pOptionsForm) {
1151     // Check out some initial nullities(tm)...
1152 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1153     if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1154     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1155 capela 109 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1156     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1157     // To track down deferred or immediate changes.
1158     QString sOldServerHost = m_pOptions->sServerHost;
1159     int iOldServerPort = m_pOptions->iServerPort;
1160     int iOldServerTimeout = m_pOptions->iServerTimeout;
1161     bool bOldServerStart = m_pOptions->bServerStart;
1162     QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1163     QString sOldDisplayFont = m_pOptions->sDisplayFont;
1164 capela 267 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1165 capela 119 int iOldMaxVolume = m_pOptions->iMaxVolume;
1166 capela 109 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1167 capela 454 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1168 capela 109 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1169     int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1170     int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1171     bool bOldCompletePath = m_pOptions->bCompletePath;
1172 capela 371 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1173 capela 109 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1174     // Load the current setup settings.
1175     pOptionsForm->setup(m_pOptions);
1176     // Show the setup dialog...
1177     if (pOptionsForm->exec()) {
1178     // Warn if something will be only effective on next run.
1179     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1180 capela 454 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1181     ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1182     (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1183 capela 757 QMessageBox::information(this,
1184     QSAMPLER_TITLE ": " + tr("Information"),
1185 capela 109 tr("Some settings may be only effective\n"
1186     "next time you start this program."), tr("OK"));
1187     updateMessagesCapture();
1188     }
1189     // Check wheather something immediate has changed.
1190     if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1191     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1192     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1193     updateRecentFilesMenu();
1194 capela 371 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1195     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1196     updateInstrumentNames();
1197 capela 267 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1198     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1199     updateDisplayEffect();
1200 capela 109 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1201     updateDisplayFont();
1202 capela 119 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1203     updateMaxVolume();
1204 capela 109 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1205     updateMessagesFont();
1206     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1207     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1208     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1209     updateMessagesLimit();
1210     // And now the main thing, whether we'll do client/server recycling?
1211     if ((sOldServerHost != m_pOptions->sServerHost) ||
1212     (iOldServerPort != m_pOptions->iServerPort) ||
1213     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1214     ( bOldServerStart && !m_pOptions->bServerStart) ||
1215     (!bOldServerStart && m_pOptions->bServerStart) ||
1216     (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1217     fileRestart();
1218     }
1219     // Done.
1220     delete pOptionsForm;
1221     }
1222    
1223     // This makes it.
1224     stabilizeForm();
1225     }
1226    
1227    
1228     //-------------------------------------------------------------------------
1229     // qsamplerMainForm -- Channels action slots.
1230    
1231     // Arrange channel strips.
1232     void qsamplerMainForm::channelsArrange (void)
1233     {
1234     // Full width vertical tiling
1235     QWidgetList wlist = m_pWorkspace->windowList();
1236     if (wlist.isEmpty())
1237     return;
1238    
1239     m_pWorkspace->setUpdatesEnabled(false);
1240     int y = 0;
1241     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1242 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1243     /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1244 capela 109 // Prevent flicker...
1245 capela 264 pChannelStrip->hide();
1246     pChannelStrip->showNormal();
1247 capela 109 } */
1248 capela 264 pChannelStrip->adjustSize();
1249 capela 109 int iWidth = m_pWorkspace->width();
1250 capela 264 if (iWidth < pChannelStrip->width())
1251     iWidth = pChannelStrip->width();
1252     // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1253     int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1254     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1255 capela 109 y += iHeight;
1256     }
1257     m_pWorkspace->setUpdatesEnabled(true);
1258 capela 586
1259 capela 109 stabilizeForm();
1260     }
1261    
1262    
1263     // Auto-arrange channel strips.
1264     void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1265     {
1266     if (m_pOptions == NULL)
1267     return;
1268    
1269     // Toggle the auto-arrange flag.
1270     m_pOptions->bAutoArrange = bOn;
1271    
1272     // If on, update whole workspace...
1273     if (m_pOptions->bAutoArrange)
1274     channelsArrange();
1275     }
1276    
1277    
1278     //-------------------------------------------------------------------------
1279     // qsamplerMainForm -- Help Action slots.
1280    
1281     // Show information about the Qt toolkit.
1282     void qsamplerMainForm::helpAboutQt (void)
1283     {
1284     QMessageBox::aboutQt(this);
1285     }
1286    
1287    
1288     // Show information about application program.
1289     void qsamplerMainForm::helpAbout (void)
1290     {
1291     // Stuff the about box text...
1292     QString sText = "<p>\n";
1293     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1294     sText += "<br />\n";
1295     sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1296     sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1297     #ifdef CONFIG_DEBUG
1298     sText += "<small><font color=\"red\">";
1299     sText += tr("Debugging option enabled.");
1300     sText += "</font></small><br />";
1301     #endif
1302 capela 176 #ifndef CONFIG_LIBGIG
1303     sText += "<small><font color=\"red\">";
1304     sText += tr("GIG (libgig) file support disabled.");
1305     sText += "</font></small><br />";
1306     #endif
1307 capela 382 #ifndef CONFIG_INSTRUMENT_NAME
1308     sText += "<small><font color=\"red\">";
1309     sText += tr("LSCP (liblscp) instrument_name support disabled.");
1310     sText += "</font></small><br />";
1311     #endif
1312 capela 751 #ifndef CONFIG_MUTE_SOLO
1313     sText += "<small><font color=\"red\">";
1314     sText += tr("Sampler channel Mute/Solo support disabled.");
1315     sText += "</font></small><br />";
1316     #endif
1317 capela 109 sText += "<br />\n";
1318     sText += tr("Using") + ": ";
1319     sText += ::lscp_client_package();
1320     sText += " ";
1321     sText += ::lscp_client_version();
1322 capela 605 #ifdef CONFIG_LIBGIG
1323 schoenebeck 519 sText += ", ";
1324 schoenebeck 736 sText += gig::libraryName().c_str();
1325 schoenebeck 519 sText += " ";
1326 schoenebeck 736 sText += gig::libraryVersion().c_str();
1327 schoenebeck 519 #endif
1328 capela 109 sText += "<br />\n";
1329     sText += "<br />\n";
1330     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1331     sText += "<br />\n";
1332     sText += "<small>";
1333     sText += QSAMPLER_COPYRIGHT "<br />\n";
1334     sText += "<br />\n";
1335     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1336     sText += tr("under the terms of the GNU General Public License version 2 or later.");
1337     sText += "</small>";
1338     sText += "</p>\n";
1339    
1340     QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1341     }
1342    
1343    
1344     //-------------------------------------------------------------------------
1345     // qsamplerMainForm -- Main window stabilization.
1346    
1347     void qsamplerMainForm::stabilizeForm (void)
1348     {
1349     // Update the main application caption...
1350 capela 418 QString sSessionName = sessionName(m_sFilename);
1351 capela 109 if (m_iDirtyCount > 0)
1352 capela 418 sSessionName += '*';
1353     setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1354 capela 109
1355     // Update the main menu state...
1356 capela 264 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1357 capela 109 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1358 capela 264 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1359 capela 109 fileNewAction->setEnabled(bHasClient);
1360     fileOpenAction->setEnabled(bHasClient);
1361     fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1362     fileSaveAsAction->setEnabled(bHasClient);
1363 capela 255 fileResetAction->setEnabled(bHasClient);
1364 capela 109 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1365     editAddChannelAction->setEnabled(bHasClient);
1366     editRemoveChannelAction->setEnabled(bHasChannel);
1367     editSetupChannelAction->setEnabled(bHasChannel);
1368     editResetChannelAction->setEnabled(bHasChannel);
1369 capela 404 editResetAllChannelsAction->setEnabled(bHasChannel);
1370 capela 428 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1371     viewDevicesAction->setOn(m_pDeviceForm && m_pDeviceForm->isVisible());
1372     viewDevicesAction->setEnabled(bHasClient);
1373 capela 109 channelsArrangeAction->setEnabled(bHasChannel);
1374    
1375     // Client/Server status...
1376     if (bHasClient) {
1377 capela 428 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1378     m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1379 capela 109 } else {
1380 capela 428 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1381     m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1382 capela 109 }
1383     // Channel status...
1384     if (bHasChannel)
1385 capela 428 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1386 capela 109 else
1387 capela 428 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1388 capela 109 // Session status...
1389     if (m_iDirtyCount > 0)
1390 capela 428 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1391 capela 109 else
1392 capela 428 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1393 capela 109
1394     // Recent files menu.
1395     m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1396    
1397     // Always make the latest message visible.
1398     if (m_pMessages)
1399     m_pMessages->scrollToBottom();
1400     }
1401    
1402    
1403     // Channel change receiver slot.
1404 capela 400 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1405 capela 109 {
1406 capela 400 // Add this strip to the changed list...
1407 capela 586 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1408 capela 400 m_changedStrips.append(pChannelStrip);
1409 capela 586 pChannelStrip->resetErrorCount();
1410     }
1411 capela 400
1412 capela 109 // Just mark the dirty form.
1413     m_iDirtyCount++;
1414     // and update the form status...
1415     stabilizeForm();
1416     }
1417    
1418    
1419 capela 395 // Grab and restore current sampler channels session.
1420     void qsamplerMainForm::updateSession (void)
1421     {
1422     // Retrieve the current channel list.
1423     int *piChannelIDs = ::lscp_list_channels(m_pClient);
1424     if (piChannelIDs == NULL) {
1425     if (::lscp_client_get_errno(m_pClient)) {
1426     appendMessagesClient("lscp_list_channels");
1427     appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1428     }
1429     return;
1430     }
1431    
1432     // Try to (re)create each channel.
1433     m_pWorkspace->setUpdatesEnabled(false);
1434     for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1435     // Check if theres already a channel strip for this one...
1436     if (!channelStrip(piChannelIDs[iChannel]))
1437     createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1438     // Make it visibly responsive...
1439     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1440     }
1441     m_pWorkspace->setUpdatesEnabled(true);
1442 capela 469
1443     // Remember to refresh devices
1444     if (m_pDeviceForm)
1445     m_pDeviceForm->refreshDevices();
1446 capela 395 }
1447    
1448    
1449 capela 109 // Update the recent files list and menu.
1450     void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1451     {
1452     if (m_pOptions == NULL)
1453     return;
1454    
1455     // Remove from list if already there (avoid duplicates)
1456     QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1457     if (iter != m_pOptions->recentFiles.end())
1458     m_pOptions->recentFiles.remove(iter);
1459     // Put it to front...
1460     m_pOptions->recentFiles.push_front(sFilename);
1461    
1462     // May update the menu.
1463     updateRecentFilesMenu();
1464     }
1465    
1466    
1467     // Update the recent files list and menu.
1468     void qsamplerMainForm::updateRecentFilesMenu (void)
1469     {
1470     if (m_pOptions == NULL)
1471     return;
1472    
1473     // Time to keep the list under limits.
1474     int iRecentFiles = m_pOptions->recentFiles.count();
1475     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1476     m_pOptions->recentFiles.pop_back();
1477     iRecentFiles--;
1478     }
1479    
1480     // rebuild the recent files menu...
1481     m_pRecentFilesMenu->clear();
1482     for (int i = 0; i < iRecentFiles; i++) {
1483     const QString& sFilename = m_pOptions->recentFiles[i];
1484     if (QFileInfo(sFilename).exists()) {
1485     m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1486     .arg(i + 1).arg(sessionName(sFilename)),
1487     this, SLOT(fileOpenRecent(int)), 0, i);
1488     }
1489     }
1490     }
1491    
1492    
1493 capela 371 // Force update of the channels instrument names mode.
1494     void qsamplerMainForm::updateInstrumentNames (void)
1495     {
1496     // Full channel list update...
1497     QWidgetList wlist = m_pWorkspace->windowList();
1498     if (wlist.isEmpty())
1499     return;
1500    
1501     m_pWorkspace->setUpdatesEnabled(false);
1502     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1503     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1504     if (pChannelStrip)
1505     pChannelStrip->updateInstrumentName(true);
1506     }
1507     m_pWorkspace->setUpdatesEnabled(true);
1508     }
1509    
1510    
1511 capela 109 // Force update of the channels display font.
1512     void qsamplerMainForm::updateDisplayFont (void)
1513     {
1514     if (m_pOptions == NULL)
1515     return;
1516    
1517     // Check if display font is legal.
1518     if (m_pOptions->sDisplayFont.isEmpty())
1519     return;
1520     // Realize it.
1521     QFont font;
1522     if (!font.fromString(m_pOptions->sDisplayFont))
1523     return;
1524    
1525     // Full channel list update...
1526     QWidgetList wlist = m_pWorkspace->windowList();
1527     if (wlist.isEmpty())
1528     return;
1529    
1530     m_pWorkspace->setUpdatesEnabled(false);
1531     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1532 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1533     if (pChannelStrip)
1534     pChannelStrip->setDisplayFont(font);
1535 capela 109 }
1536     m_pWorkspace->setUpdatesEnabled(true);
1537     }
1538    
1539    
1540 capela 267 // Update channel strips background effect.
1541     void qsamplerMainForm::updateDisplayEffect (void)
1542     {
1543     QPixmap pm;
1544     if (m_pOptions->bDisplayEffect)
1545     pm = QPixmap::fromMimeSource("displaybg1.png");
1546    
1547     // Full channel list update...
1548     QWidgetList wlist = m_pWorkspace->windowList();
1549     if (wlist.isEmpty())
1550     return;
1551    
1552     m_pWorkspace->setUpdatesEnabled(false);
1553     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1554     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1555     if (pChannelStrip)
1556     pChannelStrip->setDisplayBackground(pm);
1557     }
1558     m_pWorkspace->setUpdatesEnabled(true);
1559     }
1560    
1561    
1562 capela 119 // Force update of the channels maximum volume setting.
1563     void qsamplerMainForm::updateMaxVolume (void)
1564     {
1565     if (m_pOptions == NULL)
1566     return;
1567    
1568     // Full channel list update...
1569     QWidgetList wlist = m_pWorkspace->windowList();
1570     if (wlist.isEmpty())
1571     return;
1572    
1573     m_pWorkspace->setUpdatesEnabled(false);
1574     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1575 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1576     if (pChannelStrip)
1577     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1578 capela 119 }
1579     m_pWorkspace->setUpdatesEnabled(true);
1580     }
1581    
1582    
1583 capela 109 //-------------------------------------------------------------------------
1584     // qsamplerMainForm -- Messages window form handlers.
1585    
1586     // Messages output methods.
1587     void qsamplerMainForm::appendMessages( const QString& s )
1588     {
1589     if (m_pMessages)
1590     m_pMessages->appendMessages(s);
1591    
1592     statusBar()->message(s, 3000);
1593     }
1594    
1595     void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1596     {
1597     if (m_pMessages)
1598     m_pMessages->appendMessagesColor(s, c);
1599    
1600     statusBar()->message(s, 3000);
1601     }
1602    
1603     void qsamplerMainForm::appendMessagesText( const QString& s )
1604     {
1605     if (m_pMessages)
1606     m_pMessages->appendMessagesText(s);
1607     }
1608    
1609     void qsamplerMainForm::appendMessagesError( const QString& s )
1610     {
1611     if (m_pMessages)
1612     m_pMessages->show();
1613    
1614     appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1615    
1616 capela 757 QMessageBox::critical(this,
1617     QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
1618 capela 109 }
1619    
1620    
1621     // This is a special message format, just for client results.
1622     void qsamplerMainForm::appendMessagesClient( const QString& s )
1623     {
1624     if (m_pClient == NULL)
1625     return;
1626    
1627     appendMessagesColor(s + QString(": %1 (errno=%2)")
1628     .arg(::lscp_client_get_result(m_pClient))
1629     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1630     }
1631    
1632    
1633     // Force update of the messages font.
1634     void qsamplerMainForm::updateMessagesFont (void)
1635     {
1636     if (m_pOptions == NULL)
1637     return;
1638    
1639     if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1640     QFont font;
1641     if (font.fromString(m_pOptions->sMessagesFont))
1642     m_pMessages->setMessagesFont(font);
1643     }
1644     }
1645    
1646    
1647     // Update messages window line limit.
1648     void qsamplerMainForm::updateMessagesLimit (void)
1649     {
1650     if (m_pOptions == NULL)
1651     return;
1652    
1653     if (m_pMessages) {
1654     if (m_pOptions->bMessagesLimit)
1655     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1656     else
1657 capela 661 m_pMessages->setMessagesLimit(-1);
1658 capela 109 }
1659     }
1660    
1661    
1662     // Enablement of the messages capture feature.
1663     void qsamplerMainForm::updateMessagesCapture (void)
1664     {
1665     if (m_pOptions == NULL)
1666     return;
1667    
1668     if (m_pMessages)
1669     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1670     }
1671    
1672    
1673     //-------------------------------------------------------------------------
1674     // qsamplerMainForm -- MDI channel strip management.
1675    
1676     // The channel strip creation executive.
1677 capela 303 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1678 capela 109 {
1679 capela 303 if (m_pClient == NULL || pChannel == NULL)
1680 capela 295 return NULL;
1681 capela 109
1682     // Prepare for auto-arrange?
1683 capela 264 qsamplerChannelStrip *pChannelStrip = NULL;
1684 capela 109 int y = 0;
1685     if (m_pOptions && m_pOptions->bAutoArrange) {
1686     QWidgetList wlist = m_pWorkspace->windowList();
1687     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1688 capela 264 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1689 capela 395 if (pChannelStrip) {
1690     // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1691     y += pChannelStrip->parentWidget()->frameGeometry().height();
1692     }
1693 capela 109 }
1694     }
1695    
1696     // Add a new channel itema...
1697     WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1698 capela 264 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1699 capela 303 if (pChannelStrip == NULL)
1700     return NULL;
1701 capela 586
1702 capela 303 // Actual channel strip setup...
1703     pChannelStrip->setup(pChannel);
1704 capela 295 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1705 capela 267 // Set some initial aesthetic options...
1706     if (m_pOptions) {
1707     // Background display effect...
1708     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1709     // We'll need a display font.
1710     QFont font;
1711     if (font.fromString(m_pOptions->sDisplayFont))
1712     pChannelStrip->setDisplayFont(font);
1713     // Maximum allowed volume setting.
1714     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1715     }
1716 capela 295
1717 capela 109 // Now we show up us to the world.
1718 capela 264 pChannelStrip->show();
1719 capela 109 // Only then, we'll auto-arrange...
1720     if (m_pOptions && m_pOptions->bAutoArrange) {
1721     int iWidth = m_pWorkspace->width();
1722     // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1723 capela 264 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1724     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1725 capela 109 }
1726 capela 400
1727     // This is pretty new, so we'll watch for it closely.
1728     channelStripChanged(pChannelStrip);
1729    
1730 capela 295 // Return our successful reference...
1731     return pChannelStrip;
1732 capela 109 }
1733    
1734    
1735     // Retrieve the active channel strip.
1736 capela 264 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1737 capela 109 {
1738     return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1739     }
1740    
1741    
1742     // Retrieve a channel strip by index.
1743 capela 264 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1744 capela 109 {
1745     QWidgetList wlist = m_pWorkspace->windowList();
1746     if (wlist.isEmpty())
1747 capela 395 return NULL;
1748 capela 109
1749     return (qsamplerChannelStrip *) wlist.at(iChannel);
1750     }
1751    
1752    
1753 capela 395 // Retrieve a channel strip by sampler channel id.
1754     qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1755     {
1756     QWidgetList wlist = m_pWorkspace->windowList();
1757     if (wlist.isEmpty())
1758     return NULL;
1759 capela 586
1760 capela 395 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1761     qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1762     if (pChannelStrip) {
1763     qsamplerChannel *pChannel = pChannelStrip->channel();
1764     if (pChannel && pChannel->channelID() == iChannelID)
1765     return pChannelStrip;
1766     }
1767     }
1768    
1769     // Not found.
1770     return NULL;
1771     }
1772    
1773    
1774 capela 109 // Construct the windows menu.
1775     void qsamplerMainForm::channelsMenuAboutToShow (void)
1776     {
1777     channelsMenu->clear();
1778     channelsArrangeAction->addTo(channelsMenu);
1779     channelsAutoArrangeAction->addTo(channelsMenu);
1780    
1781     QWidgetList wlist = m_pWorkspace->windowList();
1782     if (!wlist.isEmpty()) {
1783     channelsMenu->insertSeparator();
1784     for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1785 capela 264 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1786     if (pChannelStrip) {
1787     int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1788     channelsMenu->setItemParameter(iItemID, iChannel);
1789     channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1790     }
1791 capela 109 }
1792     }
1793     }
1794    
1795    
1796     // Windows menu activation slot
1797     void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1798     {
1799 capela 264 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1800     if (pChannelStrip)
1801     pChannelStrip->showNormal();
1802     pChannelStrip->setFocus();
1803 capela 109 }
1804    
1805    
1806     //-------------------------------------------------------------------------
1807     // qsamplerMainForm -- Timer stuff.
1808    
1809     // Set the pseudo-timer delay schedule.
1810     void qsamplerMainForm::startSchedule ( int iStartDelay )
1811     {
1812     m_iStartDelay = 1 + (iStartDelay * 1000);
1813     m_iTimerDelay = 0;
1814     }
1815    
1816     // Suspend the pseudo-timer delay schedule.
1817     void qsamplerMainForm::stopSchedule (void)
1818     {
1819     m_iStartDelay = 0;
1820     m_iTimerDelay = 0;
1821     }
1822    
1823     // Timer slot funtion.
1824     void qsamplerMainForm::timerSlot (void)
1825     {
1826     if (m_pOptions == NULL)
1827     return;
1828    
1829     // Is it the first shot on server start after a few delay?
1830     if (m_iTimerDelay < m_iStartDelay) {
1831     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1832     if (m_iTimerDelay >= m_iStartDelay) {
1833     // If we cannot start it now, maybe a lil'mo'later ;)
1834     if (!startClient()) {
1835     m_iStartDelay += m_iTimerDelay;
1836     m_iTimerDelay = 0;
1837     }
1838     }
1839     }
1840 capela 586
1841 capela 664 if (m_pClient) {
1842     // Update the channel information for each pending strip...
1843     if (m_changedStrips.count() > 0) {
1844 capela 586 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1845 capela 664 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
1846 capela 400 // If successfull, remove from pending list...
1847     if (pChannelStrip->updateChannelInfo())
1848 capela 586 m_changedStrips.remove(pChannelStrip);
1849 capela 400 }
1850 capela 664 }
1851     // Refresh each channel usage, on each period...
1852     if (m_pOptions->bAutoRefresh) {
1853     m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1854     if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
1855     m_iTimerSlot = 0;
1856     // Update the channel stream usage for each strip...
1857     QWidgetList wlist = m_pWorkspace->windowList();
1858     for (int iChannel = 0;
1859     iChannel < (int) wlist.count(); iChannel++) {
1860     qsamplerChannelStrip *pChannelStrip
1861     = (qsamplerChannelStrip *) wlist.at(iChannel);
1862     if (pChannelStrip && pChannelStrip->isVisible())
1863     pChannelStrip->updateChannelUsage();
1864     }
1865 capela 586 }
1866     }
1867     }
1868 capela 109
1869     // Register the next timer slot.
1870     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1871     }
1872    
1873    
1874     //-------------------------------------------------------------------------
1875     // qsamplerMainForm -- Server stuff.
1876    
1877     // Start linuxsampler server...
1878     void qsamplerMainForm::startServer (void)
1879     {
1880     if (m_pOptions == NULL)
1881     return;
1882    
1883     // Aren't already a client, are we?
1884     if (!m_pOptions->bServerStart || m_pClient)
1885     return;
1886    
1887     // Is the server process instance still here?
1888     if (m_pServer) {
1889 capela 757 switch (QMessageBox::warning(this,
1890     QSAMPLER_TITLE ": " + tr("Warning"),
1891 capela 109 tr("Could not start the LinuxSampler server.\n\n"
1892     "Maybe it ss already started."),
1893     tr("Stop"), tr("Kill"), tr("Cancel"))) {
1894     case 0:
1895     m_pServer->tryTerminate();
1896     break;
1897     case 1:
1898     m_pServer->kill();
1899     break;
1900     }
1901     return;
1902     }
1903    
1904     // Reset our timer counters...
1905     stopSchedule();
1906    
1907     // OK. Let's build the startup process...
1908     m_pServer = new QProcess(this);
1909    
1910     // Setup stdout/stderr capture...
1911     //if (m_pOptions->bStdoutCapture) {
1912     m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1913     QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1914     QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1915     //}
1916     // The unforgiveable signal communication...
1917     QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1918    
1919     // Build process arguments...
1920     m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1921    
1922     appendMessages(tr("Server is starting..."));
1923     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1924    
1925     // Go jack, go...
1926     if (!m_pServer->start()) {
1927     appendMessagesError(tr("Could not start server.\n\nSorry."));
1928     processServerExit();
1929     return;
1930     }
1931    
1932     // Show startup results...
1933     appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1934    
1935     // Reset (yet again) the timer counters,
1936     // but this time is deferred as the user opted.
1937     startSchedule(m_pOptions->iStartDelay);
1938     stabilizeForm();
1939     }
1940    
1941    
1942     // Stop linuxsampler server...
1943     void qsamplerMainForm::stopServer (void)
1944     {
1945     // Stop client code.
1946     stopClient();
1947    
1948     // And try to stop server.
1949     if (m_pServer) {
1950     appendMessages(tr("Server is stopping..."));
1951 capela 122 if (m_pServer->isRunning())
1952 capela 109 m_pServer->tryTerminate();
1953     }
1954    
1955 capela 122 // Give it some time to terminate gracefully and stabilize...
1956     QTime t;
1957     t.start();
1958     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1959     QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1960    
1961 capela 109 // Do final processing anyway.
1962     processServerExit();
1963     }
1964    
1965    
1966     // Stdout handler...
1967     void qsamplerMainForm::readServerStdout (void)
1968     {
1969     if (m_pMessages)
1970     m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1971     }
1972    
1973    
1974     // Linuxsampler server cleanup.
1975     void qsamplerMainForm::processServerExit (void)
1976     {
1977     // Force client code cleanup.
1978     stopClient();
1979    
1980     // Flush anything that maybe pending...
1981     if (m_pMessages)
1982     m_pMessages->flushStdoutBuffer();
1983    
1984     if (m_pServer) {
1985     // Force final server shutdown...
1986     appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1987     if (!m_pServer->normalExit())
1988     m_pServer->kill();
1989     // Destroy it.
1990     delete m_pServer;
1991     m_pServer = NULL;
1992     }
1993    
1994     // Again, make status visible stable.
1995     stabilizeForm();
1996     }
1997    
1998    
1999     //-------------------------------------------------------------------------
2000     // qsamplerMainForm -- Client stuff.
2001    
2002     // The LSCP client callback procedure.
2003 capela 149 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2004 capela 109 {
2005 capela 149 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
2006     if (pMainForm == NULL)
2007     return LSCP_FAILED;
2008    
2009     // ATTN: DO NOT EVER call any GUI code here,
2010 capela 145 // as this is run under some other thread context.
2011     // A custom event must be posted here...
2012 capela 149 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2013 capela 109
2014     return LSCP_OK;
2015     }
2016    
2017    
2018     // Start our almighty client...
2019     bool qsamplerMainForm::startClient (void)
2020     {
2021     // Have it a setup?
2022     if (m_pOptions == NULL)
2023     return false;
2024    
2025     // Aren't we already started, are we?
2026     if (m_pClient)
2027     return true;
2028    
2029     // Log prepare here.
2030     appendMessages(tr("Client connecting..."));
2031    
2032     // Create the client handle...
2033     m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2034     if (m_pClient == NULL) {
2035     // Is this the first try?
2036     // maybe we need to start a local server...
2037     if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
2038     appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2039     else
2040     startServer();
2041     // This is always a failure.
2042     stabilizeForm();
2043     return false;
2044     }
2045     // Just set receive timeout value, blindly.
2046     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2047     appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2048    
2049 capela 662 // Subscribe to channel info change notifications...
2050     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2051     appendMessagesClient("lscp_client_subscribe");
2052    
2053 capela 109 // We may stop scheduling around.
2054     stopSchedule();
2055    
2056     // We'll accept drops from now on...
2057     setAcceptDrops(true);
2058    
2059     // Log success here.
2060     appendMessages(tr("Client connected."));
2061    
2062 capela 428 // Hard-notify device configuration form,
2063     // if visible, that we're ready...
2064     if (m_pDeviceForm && m_pDeviceForm->isVisible())
2065     m_pDeviceForm->setClient(m_pClient);
2066 capela 662
2067 capela 109 // Is any session pending to be loaded?
2068     if (!m_pOptions->sSessionFile.isEmpty()) {
2069     // Just load the prabably startup session...
2070     if (loadSessionFile(m_pOptions->sSessionFile)) {
2071     m_pOptions->sSessionFile = QString::null;
2072     return true;
2073     }
2074     }
2075    
2076     // Make a new session
2077     return newSession();
2078     }
2079    
2080    
2081     // Stop client...
2082     void qsamplerMainForm::stopClient (void)
2083     {
2084     if (m_pClient == NULL)
2085     return;
2086    
2087 capela 428 // Hard-notify device configuration form,
2088     // if visible, that we're running out...
2089     if (m_pDeviceForm && m_pDeviceForm->isVisible())
2090     m_pDeviceForm->setClient(NULL);
2091    
2092 capela 109 // Log prepare here.
2093     appendMessages(tr("Client disconnecting..."));
2094    
2095     // Clear timer counters...
2096     stopSchedule();
2097    
2098     // We'll reject drops from now on...
2099     setAcceptDrops(false);
2100    
2101     // Force any channel strips around, but
2102     // but avoid removing the corresponding
2103     // channels from the back-end server.
2104     m_iDirtyCount = 0;
2105     closeSession(false);
2106 capela 586
2107 capela 109 // Close us as a client...
2108 capela 662 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2109     ::lscp_client_destroy(m_pClient);
2110 capela 109 m_pClient = NULL;
2111    
2112     // Log final here.
2113     appendMessages(tr("Client disconnected."));
2114    
2115     // Make visible status.
2116     stabilizeForm();
2117     }
2118    
2119    
2120     // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC