/[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 920 - (hide annotations) (download) (as text)
Sun Sep 24 12:47:51 2006 UTC (17 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66736 byte(s)
GPL address update.

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

  ViewVC Help
Powered by ViewVC