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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2717 - (hide annotations) (download)
Wed Jan 21 13:19:51 2015 UTC (9 years, 2 months ago) by schoenebeck
File size: 86120 byte(s)
* When closing qsampler and showing the user the dialog whether to stop the
  LinuxSampler backend, set the default selection to "Yes".

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

  ViewVC Help
Powered by ViewVC