/[svn]/qsampler/trunk/src/qsamplerMainForm.cpp
ViewVC logotype

Annotation of /qsampler/trunk/src/qsamplerMainForm.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1465 - (hide annotations) (download)
Thu Nov 1 17:49:27 2007 UTC (16 years, 4 months ago) by capela
File size: 77018 byte(s)
- Qt4 migration: main toolbars and messages dock-widget fix.

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

  ViewVC Help
Powered by ViewVC