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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1509 - (hide annotations) (download)
Thu Nov 22 11:10:44 2007 UTC (16 years, 5 months ago) by capela
File size: 74325 byte(s)
* Audio routing table is initially hidden in the dialog, when
  creating a new sampler channel.

* README requirements and configuration notes update.

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

  ViewVC Help
Powered by ViewVC