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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2722 - (hide annotations) (download)
Tue Mar 3 17:41:04 2015 UTC (9 years ago) by capela
File size: 88349 byte(s)
* Added this "Don't ask/show this again" option to some if not most
  of the nagging warning/error message boxes. (EXPERIMENTAL)
1 capela 1464 // qsamplerMainForm.cpp
2     //
3     /****************************************************************************
4 capela 2718 Copyright (C) 2004-2015, rncbc aka Rui Nuno Capela. All rights reserved.
5     Copyright (C) 2007,2008,2015 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 capela 2722 if (m_pOptions && m_pOptions->bConfirmReset) {
1409     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1410     const QString& sText = tr(
1411     "Resetting the sampler instance will close\n"
1412     "all device and channel configurations.\n\n"
1413     "Please note that this operation may cause\n"
1414     "temporary MIDI and Audio disruption.\n\n"
1415     "Do you want to reset the sampler engine now?");
1416     #if 0
1417     if (QMessageBox::warning(this, sTitle, sText,
1418     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1419     return;
1420     #else
1421     QMessageBox mbox(this);
1422     mbox.setIcon(QMessageBox::Warning);
1423     mbox.setWindowTitle(sTitle);
1424     mbox.setText(sText);
1425     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1426     QCheckBox cbox(tr("Don't ask this again"));
1427     cbox.setChecked(false);
1428     cbox.blockSignals(true);
1429     mbox.addButton(&cbox, QMessageBox::ActionRole);
1430     if (mbox.exec() == QMessageBox::Cancel)
1431     return;
1432     if (cbox.isChecked())
1433     m_pOptions->bConfirmReset = false;
1434     #endif
1435     }
1436 schoenebeck 1461
1437     // Trye closing the current session, first...
1438     if (!closeSession(true))
1439     return;
1440    
1441 capela 1509 // Just do the reset, after closing down current session...
1442 schoenebeck 1461 // Do the actual sampler reset...
1443     if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1444     appendMessagesClient("lscp_reset_sampler");
1445     appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1446     return;
1447     }
1448    
1449 capela 1509 // Log this.
1450     appendMessages(tr("Sampler reset."));
1451 schoenebeck 1461
1452     // Make it a new session...
1453     newSession();
1454     }
1455    
1456    
1457     // Restart the client/server instance.
1458     void MainForm::fileRestart (void)
1459     {
1460 capela 1509 if (m_pOptions == NULL)
1461     return;
1462 schoenebeck 1461
1463 capela 1509 bool bRestart = true;
1464 schoenebeck 1461
1465 capela 1509 // Ask user whether he/she want's a complete restart...
1466     // (if we're currently up and running)
1467 capela 2722 if (m_pOptions && m_pOptions->bConfirmRestart) {
1468     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1469     const QString& sText = tr(
1470     "New settings will be effective after\n"
1471 capela 1509 "restarting the client/server connection.\n\n"
1472     "Please note that this operation may cause\n"
1473     "temporary MIDI and Audio disruption.\n\n"
1474 capela 2722 "Do you want to restart the connection now?");
1475     #if 0
1476     if (QMessageBox::warning(this, sTitle, sText,
1477     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1478     bRestart = false;
1479     #else
1480     QMessageBox mbox(this);
1481     mbox.setIcon(QMessageBox::Warning);
1482     mbox.setWindowTitle(sTitle);
1483     mbox.setText(sText);
1484     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1485     QCheckBox cbox(tr("Don't ask this again"));
1486     cbox.setChecked(false);
1487     cbox.blockSignals(true);
1488     mbox.addButton(&cbox, QMessageBox::ActionRole);
1489     if (mbox.exec() == QMessageBox::Cancel)
1490     bRestart = false;
1491     else
1492     if (cbox.isChecked())
1493     m_pOptions->bConfirmRestart = false;
1494     #endif
1495 capela 1509 }
1496 schoenebeck 1461
1497 capela 1509 // Are we still for it?
1498     if (bRestart && closeSession(true)) {
1499     // Stop server, it will force the client too.
1500     stopServer();
1501     // Reschedule a restart...
1502     startSchedule(m_pOptions->iStartDelay);
1503     }
1504 schoenebeck 1461 }
1505    
1506    
1507     // Exit application program.
1508     void MainForm::fileExit (void)
1509     {
1510 capela 1509 // Go for close the whole thing.
1511     close();
1512 schoenebeck 1461 }
1513    
1514    
1515     //-------------------------------------------------------------------------
1516     // qsamplerMainForm -- Edit Action slots.
1517    
1518     // Add a new sampler channel.
1519     void MainForm::editAddChannel (void)
1520     {
1521 capela 1509 if (m_pClient == NULL)
1522     return;
1523 schoenebeck 1461
1524 capela 1509 // Just create the channel instance...
1525 capela 1558 Channel *pChannel = new Channel();
1526 capela 1509 if (pChannel == NULL)
1527     return;
1528 schoenebeck 1461
1529 capela 1509 // Before we show it up, may be we'll
1530     // better ask for some initial values?
1531     if (!pChannel->channelSetup(this)) {
1532     delete pChannel;
1533     return;
1534     }
1535 schoenebeck 1461
1536 capela 1509 // And give it to the strip...
1537     // (will own the channel instance, if successful).
1538     if (!createChannelStrip(pChannel)) {
1539     delete pChannel;
1540     return;
1541     }
1542 schoenebeck 1461
1543 capela 1515 // Do we auto-arrange?
1544     if (m_pOptions && m_pOptions->bAutoArrange)
1545     channelsArrange();
1546    
1547 capela 1509 // Make that an overall update.
1548     m_iDirtyCount++;
1549     stabilizeForm();
1550 schoenebeck 1461 }
1551    
1552    
1553     // Remove current sampler channel.
1554     void MainForm::editRemoveChannel (void)
1555     {
1556 capela 1509 if (m_pClient == NULL)
1557     return;
1558 schoenebeck 1461
1559 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1560 capela 1509 if (pChannelStrip == NULL)
1561     return;
1562 schoenebeck 1461
1563 capela 1558 Channel *pChannel = pChannelStrip->channel();
1564 capela 1509 if (pChannel == NULL)
1565     return;
1566 schoenebeck 1461
1567 capela 1509 // Prompt user if he/she's sure about this...
1568     if (m_pOptions && m_pOptions->bConfirmRemove) {
1569 capela 2722 const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1570     const QString& sText = tr(
1571     "About to remove channel:\n\n"
1572 capela 1509 "%1\n\n"
1573     "Are you sure?")
1574 capela 2722 .arg(pChannelStrip->windowTitle());
1575     #if 0
1576     if (QMessageBox::warning(this, sTitle, sText,
1577     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1578 capela 1509 return;
1579 capela 2722 #else
1580     QMessageBox mbox(this);
1581     mbox.setIcon(QMessageBox::Warning);
1582     mbox.setWindowTitle(sTitle);
1583     mbox.setText(sText);
1584     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1585     QCheckBox cbox(tr("Don't ask this again"));
1586     cbox.setChecked(false);
1587     cbox.blockSignals(true);
1588     mbox.addButton(&cbox, QMessageBox::ActionRole);
1589     if (mbox.exec() == QMessageBox::Cancel)
1590     return;
1591     if (cbox.isChecked())
1592     m_pOptions->bConfirmRemove = false;
1593     #endif
1594 capela 1509 }
1595 schoenebeck 1461
1596 capela 1509 // Remove the existing sampler channel.
1597     if (!pChannel->removeChannel())
1598     return;
1599 schoenebeck 1461
1600 capela 1509 // We'll be dirty, for sure...
1601     m_iDirtyCount++;
1602 capela 2441
1603     // Just delete the channel strip.
1604     destroyChannelStrip(pChannelStrip);
1605 schoenebeck 1461 }
1606    
1607    
1608     // Setup current sampler channel.
1609     void MainForm::editSetupChannel (void)
1610     {
1611 capela 1509 if (m_pClient == NULL)
1612     return;
1613 schoenebeck 1461
1614 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1615 capela 1509 if (pChannelStrip == NULL)
1616     return;
1617 schoenebeck 1461
1618 capela 1509 // Just invoque the channel strip procedure.
1619     pChannelStrip->channelSetup();
1620 schoenebeck 1461 }
1621    
1622    
1623     // Edit current sampler channel.
1624     void MainForm::editEditChannel (void)
1625     {
1626 capela 1509 if (m_pClient == NULL)
1627     return;
1628 schoenebeck 1461
1629 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1630 capela 1509 if (pChannelStrip == NULL)
1631     return;
1632 schoenebeck 1461
1633 capela 1509 // Just invoque the channel strip procedure.
1634     pChannelStrip->channelEdit();
1635 schoenebeck 1461 }
1636    
1637    
1638     // Reset current sampler channel.
1639     void MainForm::editResetChannel (void)
1640     {
1641 capela 1509 if (m_pClient == NULL)
1642     return;
1643 schoenebeck 1461
1644 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1645 capela 1509 if (pChannelStrip == NULL)
1646     return;
1647 schoenebeck 1461
1648 capela 1509 // Just invoque the channel strip procedure.
1649     pChannelStrip->channelReset();
1650 schoenebeck 1461 }
1651    
1652    
1653     // Reset all sampler channels.
1654     void MainForm::editResetAllChannels (void)
1655     {
1656     if (m_pClient == NULL)
1657     return;
1658    
1659     // Invoque the channel strip procedure,
1660     // for all channels out there...
1661     m_pWorkspace->setUpdatesEnabled(false);
1662 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
1663     for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
1664     ChannelStrip *pChannelStrip = NULL;
1665     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
1666     if (pMdiSubWindow)
1667     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1668 schoenebeck 1461 if (pChannelStrip)
1669     pChannelStrip->channelReset();
1670     }
1671     m_pWorkspace->setUpdatesEnabled(true);
1672     }
1673    
1674    
1675     //-------------------------------------------------------------------------
1676     // qsamplerMainForm -- View Action slots.
1677    
1678     // Show/hide the main program window menubar.
1679     void MainForm::viewMenubar ( bool bOn )
1680     {
1681 capela 1509 if (bOn)
1682     m_ui.MenuBar->show();
1683     else
1684     m_ui.MenuBar->hide();
1685 schoenebeck 1461 }
1686    
1687    
1688     // Show/hide the main program window toolbar.
1689     void MainForm::viewToolbar ( bool bOn )
1690     {
1691 capela 1509 if (bOn) {
1692     m_ui.fileToolbar->show();
1693     m_ui.editToolbar->show();
1694     m_ui.channelsToolbar->show();
1695     } else {
1696     m_ui.fileToolbar->hide();
1697     m_ui.editToolbar->hide();
1698     m_ui.channelsToolbar->hide();
1699     }
1700 schoenebeck 1461 }
1701    
1702    
1703     // Show/hide the main program window statusbar.
1704     void MainForm::viewStatusbar ( bool bOn )
1705     {
1706 capela 1509 if (bOn)
1707     statusBar()->show();
1708     else
1709     statusBar()->hide();
1710 schoenebeck 1461 }
1711    
1712    
1713     // Show/hide the messages window logger.
1714     void MainForm::viewMessages ( bool bOn )
1715     {
1716 capela 1509 if (bOn)
1717     m_pMessages->show();
1718     else
1719     m_pMessages->hide();
1720 schoenebeck 1461 }
1721    
1722    
1723     // Show/hide the MIDI instrument list-view form.
1724     void MainForm::viewInstruments (void)
1725     {
1726     if (m_pOptions == NULL)
1727     return;
1728    
1729     if (m_pInstrumentListForm) {
1730     m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1731     if (m_pInstrumentListForm->isVisible()) {
1732     m_pInstrumentListForm->hide();
1733     } else {
1734     m_pInstrumentListForm->show();
1735     m_pInstrumentListForm->raise();
1736 capela 1499 m_pInstrumentListForm->activateWindow();
1737 schoenebeck 1461 }
1738     }
1739     }
1740    
1741    
1742     // Show/hide the device configurator form.
1743     void MainForm::viewDevices (void)
1744     {
1745     if (m_pOptions == NULL)
1746     return;
1747    
1748     if (m_pDeviceForm) {
1749     m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1750     if (m_pDeviceForm->isVisible()) {
1751     m_pDeviceForm->hide();
1752     } else {
1753     m_pDeviceForm->show();
1754     m_pDeviceForm->raise();
1755 capela 1499 m_pDeviceForm->activateWindow();
1756 schoenebeck 1461 }
1757     }
1758     }
1759    
1760    
1761     // Show options dialog.
1762     void MainForm::viewOptions (void)
1763     {
1764 capela 1509 if (m_pOptions == NULL)
1765     return;
1766 schoenebeck 1461
1767 capela 1509 OptionsForm* pOptionsForm = new OptionsForm(this);
1768     if (pOptionsForm) {
1769     // Check out some initial nullities(tm)...
1770 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1771 capela 1509 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1772     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1773     if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1774     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1775     // To track down deferred or immediate changes.
1776     QString sOldServerHost = m_pOptions->sServerHost;
1777     int iOldServerPort = m_pOptions->iServerPort;
1778     int iOldServerTimeout = m_pOptions->iServerTimeout;
1779     bool bOldServerStart = m_pOptions->bServerStart;
1780     QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1781 schoenebeck 1803 bool bOldMessagesLog = m_pOptions->bMessagesLog;
1782 capela 1738 QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath;
1783 capela 1509 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1784     bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1785     int iOldMaxVolume = m_pOptions->iMaxVolume;
1786     QString sOldMessagesFont = m_pOptions->sMessagesFont;
1787     bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1788     bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1789     int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1790     int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1791     bool bOldCompletePath = m_pOptions->bCompletePath;
1792     bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1793     int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1794 capela 1749 int iOldBaseFontSize = m_pOptions->iBaseFontSize;
1795 capela 1509 // Load the current setup settings.
1796     pOptionsForm->setup(m_pOptions);
1797     // Show the setup dialog...
1798     if (pOptionsForm->exec()) {
1799     // Warn if something will be only effective on next run.
1800     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1801     (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1802     ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1803 capela 1749 (!bOldKeepOnTop && m_pOptions->bKeepOnTop) ||
1804     (iOldBaseFontSize != m_pOptions->iBaseFontSize)) {
1805 capela 1509 QMessageBox::information(this,
1806 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Information"),
1807 capela 1509 tr("Some settings may be only effective\n"
1808 capela 1840 "next time you start this program."));
1809 capela 1509 updateMessagesCapture();
1810     }
1811     // Check wheather something immediate has changed.
1812 capela 1738 if (( bOldMessagesLog && !m_pOptions->bMessagesLog) ||
1813     (!bOldMessagesLog && m_pOptions->bMessagesLog) ||
1814     (sOldMessagesLogPath != m_pOptions->sMessagesLogPath))
1815     m_pMessages->setLogging(
1816     m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath);
1817 capela 1509 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1818     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1819     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1820     updateRecentFilesMenu();
1821     if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1822     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1823     updateInstrumentNames();
1824     if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1825     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1826     updateDisplayEffect();
1827     if (sOldDisplayFont != m_pOptions->sDisplayFont)
1828     updateDisplayFont();
1829     if (iOldMaxVolume != m_pOptions->iMaxVolume)
1830     updateMaxVolume();
1831     if (sOldMessagesFont != m_pOptions->sMessagesFont)
1832     updateMessagesFont();
1833     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1834     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1835     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1836     updateMessagesLimit();
1837     // And now the main thing, whether we'll do client/server recycling?
1838     if ((sOldServerHost != m_pOptions->sServerHost) ||
1839     (iOldServerPort != m_pOptions->iServerPort) ||
1840     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1841     ( bOldServerStart && !m_pOptions->bServerStart) ||
1842     (!bOldServerStart && m_pOptions->bServerStart) ||
1843     (sOldServerCmdLine != m_pOptions->sServerCmdLine
1844     && m_pOptions->bServerStart))
1845     fileRestart();
1846     }
1847     // Done.
1848     delete pOptionsForm;
1849     }
1850 schoenebeck 1461
1851 capela 1509 // This makes it.
1852     stabilizeForm();
1853 schoenebeck 1461 }
1854    
1855    
1856     //-------------------------------------------------------------------------
1857     // qsamplerMainForm -- Channels action slots.
1858    
1859     // Arrange channel strips.
1860     void MainForm::channelsArrange (void)
1861     {
1862 capela 1509 // Full width vertical tiling
1863 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
1864 capela 1509 if (wlist.isEmpty())
1865     return;
1866 schoenebeck 1461
1867 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
1868     int y = 0;
1869 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
1870     ChannelStrip *pChannelStrip = NULL;
1871     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
1872     if (pMdiSubWindow)
1873     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1874     if (pChannelStrip) {
1875     /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1876     // Prevent flicker...
1877     pChannelStrip->hide();
1878     pChannelStrip->showNormal();
1879     } */
1880     pChannelStrip->adjustSize();
1881     int iWidth = m_pWorkspace->width();
1882     if (iWidth < pChannelStrip->width())
1883     iWidth = pChannelStrip->width();
1884     // int iHeight = pChannelStrip->height()
1885     // + pChannelStrip->parentWidget()->baseSize().height();
1886     int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1887     pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1888     y += iHeight;
1889     }
1890 capela 1509 }
1891     m_pWorkspace->setUpdatesEnabled(true);
1892 schoenebeck 1461
1893 capela 1509 stabilizeForm();
1894 schoenebeck 1461 }
1895    
1896    
1897     // Auto-arrange channel strips.
1898     void MainForm::channelsAutoArrange ( bool bOn )
1899     {
1900 capela 1509 if (m_pOptions == NULL)
1901     return;
1902 schoenebeck 1461
1903 capela 1509 // Toggle the auto-arrange flag.
1904     m_pOptions->bAutoArrange = bOn;
1905 schoenebeck 1461
1906 capela 1509 // If on, update whole workspace...
1907     if (m_pOptions->bAutoArrange)
1908     channelsArrange();
1909 schoenebeck 1461 }
1910    
1911    
1912     //-------------------------------------------------------------------------
1913     // qsamplerMainForm -- Help Action slots.
1914    
1915     // Show information about the Qt toolkit.
1916     void MainForm::helpAboutQt (void)
1917     {
1918 capela 1509 QMessageBox::aboutQt(this);
1919 schoenebeck 1461 }
1920    
1921    
1922     // Show information about application program.
1923     void MainForm::helpAbout (void)
1924     {
1925 capela 1509 // Stuff the about box text...
1926     QString sText = "<p>\n";
1927     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1928     sText += "<br />\n";
1929     sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1930     sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1931 schoenebeck 1461 #ifdef CONFIG_DEBUG
1932 capela 1509 sText += "<small><font color=\"red\">";
1933     sText += tr("Debugging option enabled.");
1934     sText += "</font></small><br />";
1935 schoenebeck 1461 #endif
1936     #ifndef CONFIG_LIBGIG
1937 capela 1509 sText += "<small><font color=\"red\">";
1938     sText += tr("GIG (libgig) file support disabled.");
1939     sText += "</font></small><br />";
1940 schoenebeck 1461 #endif
1941     #ifndef CONFIG_INSTRUMENT_NAME
1942 capela 1509 sText += "<small><font color=\"red\">";
1943     sText += tr("LSCP (liblscp) instrument_name support disabled.");
1944     sText += "</font></small><br />";
1945 schoenebeck 1461 #endif
1946     #ifndef CONFIG_MUTE_SOLO
1947 capela 1509 sText += "<small><font color=\"red\">";
1948     sText += tr("Sampler channel Mute/Solo support disabled.");
1949     sText += "</font></small><br />";
1950 schoenebeck 1461 #endif
1951     #ifndef CONFIG_AUDIO_ROUTING
1952 capela 1509 sText += "<small><font color=\"red\">";
1953     sText += tr("LSCP (liblscp) audio_routing support disabled.");
1954     sText += "</font></small><br />";
1955 schoenebeck 1461 #endif
1956     #ifndef CONFIG_FXSEND
1957 capela 1509 sText += "<small><font color=\"red\">";
1958     sText += tr("Sampler channel Effect Sends support disabled.");
1959     sText += "</font></small><br />";
1960 schoenebeck 1461 #endif
1961     #ifndef CONFIG_VOLUME
1962 capela 1509 sText += "<small><font color=\"red\">";
1963     sText += tr("Global volume support disabled.");
1964     sText += "</font></small><br />";
1965 schoenebeck 1461 #endif
1966     #ifndef CONFIG_MIDI_INSTRUMENT
1967 capela 1509 sText += "<small><font color=\"red\">";
1968     sText += tr("MIDI instrument mapping support disabled.");
1969     sText += "</font></small><br />";
1970 schoenebeck 1461 #endif
1971     #ifndef CONFIG_EDIT_INSTRUMENT
1972 capela 1509 sText += "<small><font color=\"red\">";
1973     sText += tr("Instrument editing support disabled.");
1974     sText += "</font></small><br />";
1975 schoenebeck 1461 #endif
1976 capela 1815 #ifndef CONFIG_EVENT_CHANNEL_MIDI
1977     sText += "<small><font color=\"red\">";
1978     sText += tr("Channel MIDI event support disabled.");
1979     sText += "</font></small><br />";
1980     #endif
1981     #ifndef CONFIG_EVENT_DEVICE_MIDI
1982     sText += "<small><font color=\"red\">";
1983     sText += tr("Device MIDI event support disabled.");
1984     sText += "</font></small><br />";
1985     #endif
1986     #ifndef CONFIG_MAX_VOICES
1987     sText += "<small><font color=\"red\">";
1988     sText += tr("Runtime max. voices / disk streams support disabled.");
1989     sText += "</font></small><br />";
1990     #endif
1991 capela 1509 sText += "<br />\n";
1992     sText += tr("Using") + ": ";
1993     sText += ::lscp_client_package();
1994     sText += " ";
1995     sText += ::lscp_client_version();
1996 schoenebeck 1461 #ifdef CONFIG_LIBGIG
1997 capela 1509 sText += ", ";
1998     sText += gig::libraryName().c_str();
1999     sText += " ";
2000     sText += gig::libraryVersion().c_str();
2001 schoenebeck 1461 #endif
2002 capela 1509 sText += "<br />\n";
2003     sText += "<br />\n";
2004     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
2005     sText += "<br />\n";
2006     sText += "<small>";
2007     sText += QSAMPLER_COPYRIGHT "<br />\n";
2008     sText += QSAMPLER_COPYRIGHT2 "<br />\n";
2009     sText += "<br />\n";
2010     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
2011     sText += tr("under the terms of the GNU General Public License version 2 or later.");
2012     sText += "</small>";
2013     sText += "</p>\n";
2014 schoenebeck 1461
2015 capela 1509 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
2016 schoenebeck 1461 }
2017    
2018    
2019     //-------------------------------------------------------------------------
2020     // qsamplerMainForm -- Main window stabilization.
2021    
2022     void MainForm::stabilizeForm (void)
2023     {
2024 capela 1509 // Update the main application caption...
2025     QString sSessionName = sessionName(m_sFilename);
2026     if (m_iDirtyCount > 0)
2027     sSessionName += " *";
2028     setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
2029 schoenebeck 1461
2030 capela 1509 // Update the main menu state...
2031 capela 2038 ChannelStrip *pChannelStrip = activeChannelStrip();
2032     bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
2033 capela 1509 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
2034 capela 2387 bool bHasChannels = (bHasClient && m_pWorkspace->subWindowList().count() > 0);
2035 capela 1509 m_ui.fileNewAction->setEnabled(bHasClient);
2036     m_ui.fileOpenAction->setEnabled(bHasClient);
2037     m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
2038     m_ui.fileSaveAsAction->setEnabled(bHasClient);
2039     m_ui.fileResetAction->setEnabled(bHasClient);
2040     m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
2041     m_ui.editAddChannelAction->setEnabled(bHasClient);
2042     m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
2043     m_ui.editSetupChannelAction->setEnabled(bHasChannel);
2044 schoenebeck 1461 #ifdef CONFIG_EDIT_INSTRUMENT
2045 capela 1509 m_ui.editEditChannelAction->setEnabled(bHasChannel);
2046 schoenebeck 1461 #else
2047 capela 1509 m_ui.editEditChannelAction->setEnabled(false);
2048 schoenebeck 1461 #endif
2049 capela 1509 m_ui.editResetChannelAction->setEnabled(bHasChannel);
2050 capela 2038 m_ui.editResetAllChannelsAction->setEnabled(bHasChannels);
2051 capela 1509 m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
2052 schoenebeck 1461 #ifdef CONFIG_MIDI_INSTRUMENT
2053 capela 1509 m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
2054 schoenebeck 1461 && m_pInstrumentListForm->isVisible());
2055 capela 1509 m_ui.viewInstrumentsAction->setEnabled(bHasClient);
2056 schoenebeck 1461 #else
2057 capela 1509 m_ui.viewInstrumentsAction->setEnabled(false);
2058 schoenebeck 1461 #endif
2059 capela 1509 m_ui.viewDevicesAction->setChecked(m_pDeviceForm
2060 schoenebeck 1461 && m_pDeviceForm->isVisible());
2061 capela 1509 m_ui.viewDevicesAction->setEnabled(bHasClient);
2062 capela 2038 m_ui.viewMidiDeviceStatusMenu->setEnabled(
2063     DeviceStatusForm::getInstances().size() > 0);
2064     m_ui.channelsArrangeAction->setEnabled(bHasChannels);
2065 schoenebeck 1461
2066     #ifdef CONFIG_VOLUME
2067     // Toolbar widgets are also affected...
2068 capela 1509 m_pVolumeSlider->setEnabled(bHasClient);
2069     m_pVolumeSpinBox->setEnabled(bHasClient);
2070 schoenebeck 1461 #endif
2071    
2072 capela 1509 // Client/Server status...
2073     if (bHasClient) {
2074     m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
2075     m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
2076     + ':' + QString::number(m_pOptions->iServerPort));
2077     } else {
2078     m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
2079     m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
2080     }
2081     // Channel status...
2082     if (bHasChannel)
2083     m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
2084     else
2085     m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
2086     // Session status...
2087     if (m_iDirtyCount > 0)
2088     m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
2089     else
2090     m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
2091 schoenebeck 1461
2092 capela 1509 // Recent files menu.
2093     m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
2094 schoenebeck 1461 }
2095    
2096    
2097     // Global volume change receiver slot.
2098     void MainForm::volumeChanged ( int iVolume )
2099     {
2100     #ifdef CONFIG_VOLUME
2101    
2102     if (m_iVolumeChanging > 0)
2103     return;
2104    
2105     m_iVolumeChanging++;
2106    
2107     // Update the toolbar widgets...
2108     if (m_pVolumeSlider->value() != iVolume)
2109     m_pVolumeSlider->setValue(iVolume);
2110     if (m_pVolumeSpinBox->value() != iVolume)
2111     m_pVolumeSpinBox->setValue(iVolume);
2112    
2113     // Do it as commanded...
2114     float fVolume = 0.01f * float(iVolume);
2115     if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
2116     appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
2117     else
2118     appendMessagesClient("lscp_set_volume");
2119    
2120     m_iVolumeChanging--;
2121    
2122     m_iDirtyCount++;
2123     stabilizeForm();
2124    
2125     #endif
2126     }
2127    
2128    
2129     // Channel change receiver slot.
2130 capela 2387 void MainForm::channelStripChanged ( ChannelStrip *pChannelStrip )
2131 schoenebeck 1461 {
2132     // Add this strip to the changed list...
2133 capela 1499 if (!m_changedStrips.contains(pChannelStrip)) {
2134 schoenebeck 1461 m_changedStrips.append(pChannelStrip);
2135     pChannelStrip->resetErrorCount();
2136     }
2137    
2138 capela 1509 // Just mark the dirty form.
2139     m_iDirtyCount++;
2140     // and update the form status...
2141     stabilizeForm();
2142 schoenebeck 1461 }
2143    
2144    
2145     // Grab and restore current sampler channels session.
2146     void MainForm::updateSession (void)
2147     {
2148     #ifdef CONFIG_VOLUME
2149     int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
2150     m_iVolumeChanging++;
2151     m_pVolumeSlider->setValue(iVolume);
2152     m_pVolumeSpinBox->setValue(iVolume);
2153     m_iVolumeChanging--;
2154     #endif
2155     #ifdef CONFIG_MIDI_INSTRUMENT
2156     // FIXME: Make some room for default instrument maps...
2157     int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
2158     if (iMaps < 0)
2159     appendMessagesClient("lscp_get_midi_instrument_maps");
2160     else if (iMaps < 1) {
2161 capela 1499 ::lscp_add_midi_instrument_map(m_pClient,
2162     tr("Chromatic").toUtf8().constData());
2163     ::lscp_add_midi_instrument_map(m_pClient,
2164     tr("Drum Kits").toUtf8().constData());
2165 schoenebeck 1461 }
2166     #endif
2167    
2168 schoenebeck 1702 updateAllChannelStrips(false);
2169    
2170     // Do we auto-arrange?
2171     if (m_pOptions && m_pOptions->bAutoArrange)
2172     channelsArrange();
2173    
2174     // Remember to refresh devices and instruments...
2175     if (m_pInstrumentListForm)
2176     m_pInstrumentListForm->refreshInstruments();
2177     if (m_pDeviceForm)
2178     m_pDeviceForm->refreshDevices();
2179     }
2180    
2181 capela 2387
2182     void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips )
2183     {
2184 schoenebeck 1461 // Retrieve the current channel list.
2185     int *piChannelIDs = ::lscp_list_channels(m_pClient);
2186     if (piChannelIDs == NULL) {
2187     if (::lscp_client_get_errno(m_pClient)) {
2188     appendMessagesClient("lscp_list_channels");
2189 capela 1509 appendMessagesError(
2190     tr("Could not get current list of channels.\n\nSorry."));
2191 schoenebeck 1461 }
2192     } else {
2193     // Try to (re)create each channel.
2194     m_pWorkspace->setUpdatesEnabled(false);
2195 capela 2387 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2196 schoenebeck 1461 // Check if theres already a channel strip for this one...
2197     if (!channelStrip(piChannelIDs[iChannel]))
2198 capela 1558 createChannelStrip(new Channel(piChannelIDs[iChannel]));
2199 schoenebeck 1461 }
2200 schoenebeck 1702 // Do we auto-arrange?
2201     if (m_pOptions && m_pOptions->bAutoArrange)
2202     channelsArrange();
2203     // remove dead channel strips
2204     if (bRemoveDeadStrips) {
2205 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2206     for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2207     ChannelStrip *pChannelStrip = NULL;
2208     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2209     if (pMdiSubWindow)
2210     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2211     if (pChannelStrip) {
2212     bool bExists = false;
2213     for (int j = 0; piChannelIDs[j] >= 0; ++j) {
2214     if (!pChannelStrip->channel())
2215     break;
2216     if (piChannelIDs[j] == pChannelStrip->channel()->channelID()) {
2217     // strip exists, don't touch it
2218     bExists = true;
2219     break;
2220     }
2221 schoenebeck 1702 }
2222 capela 2387 if (!bExists)
2223     destroyChannelStrip(pChannelStrip);
2224 schoenebeck 1702 }
2225     }
2226     }
2227 schoenebeck 1461 m_pWorkspace->setUpdatesEnabled(true);
2228     }
2229 capela 2387
2230     stabilizeForm();
2231 schoenebeck 1461 }
2232    
2233 capela 2387
2234 schoenebeck 1461 // Update the recent files list and menu.
2235     void MainForm::updateRecentFiles ( const QString& sFilename )
2236     {
2237 capela 1509 if (m_pOptions == NULL)
2238     return;
2239 schoenebeck 1461
2240 capela 1509 // Remove from list if already there (avoid duplicates)
2241     int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
2242     if (iIndex >= 0)
2243     m_pOptions->recentFiles.removeAt(iIndex);
2244     // Put it to front...
2245     m_pOptions->recentFiles.push_front(sFilename);
2246 schoenebeck 1461 }
2247    
2248    
2249     // Update the recent files list and menu.
2250     void MainForm::updateRecentFilesMenu (void)
2251     {
2252 capela 1499 if (m_pOptions == NULL)
2253     return;
2254 schoenebeck 1461
2255 capela 1499 // Time to keep the list under limits.
2256     int iRecentFiles = m_pOptions->recentFiles.count();
2257     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
2258     m_pOptions->recentFiles.pop_back();
2259     iRecentFiles--;
2260     }
2261 schoenebeck 1461
2262 capela 1499 // Rebuild the recent files menu...
2263 capela 1509 m_ui.fileOpenRecentMenu->clear();
2264 capela 1499 for (int i = 0; i < iRecentFiles; i++) {
2265     const QString& sFilename = m_pOptions->recentFiles[i];
2266     if (QFileInfo(sFilename).exists()) {
2267 capela 1509 QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
2268 capela 1499 QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
2269     this, SLOT(fileOpenRecent()));
2270     pAction->setData(i);
2271     }
2272     }
2273 schoenebeck 1461 }
2274    
2275    
2276     // Force update of the channels instrument names mode.
2277     void MainForm::updateInstrumentNames (void)
2278     {
2279 capela 1509 // Full channel list update...
2280 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2281 capela 1509 if (wlist.isEmpty())
2282     return;
2283 schoenebeck 1461
2284 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2285 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2286 capela 1509 ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
2287     if (pChannelStrip)
2288     pChannelStrip->updateInstrumentName(true);
2289     }
2290     m_pWorkspace->setUpdatesEnabled(true);
2291 schoenebeck 1461 }
2292    
2293    
2294     // Force update of the channels display font.
2295     void MainForm::updateDisplayFont (void)
2296     {
2297 capela 1509 if (m_pOptions == NULL)
2298     return;
2299 schoenebeck 1461
2300 capela 1509 // Check if display font is legal.
2301     if (m_pOptions->sDisplayFont.isEmpty())
2302     return;
2303     // Realize it.
2304     QFont font;
2305     if (!font.fromString(m_pOptions->sDisplayFont))
2306     return;
2307 schoenebeck 1461
2308 capela 1509 // Full channel list update...
2309 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2310 capela 1509 if (wlist.isEmpty())
2311     return;
2312 schoenebeck 1461
2313 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2314 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2315     ChannelStrip *pChannelStrip = NULL;
2316     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2317     if (pMdiSubWindow)
2318     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2319 capela 1509 if (pChannelStrip)
2320     pChannelStrip->setDisplayFont(font);
2321     }
2322     m_pWorkspace->setUpdatesEnabled(true);
2323 schoenebeck 1461 }
2324    
2325    
2326     // Update channel strips background effect.
2327     void MainForm::updateDisplayEffect (void)
2328     {
2329 capela 1509 // Full channel list update...
2330 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2331 capela 1509 if (wlist.isEmpty())
2332     return;
2333 schoenebeck 1461
2334 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2335 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2336     ChannelStrip *pChannelStrip = NULL;
2337     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2338     if (pMdiSubWindow)
2339     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2340 capela 1499 if (pChannelStrip)
2341     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2342 capela 1509 }
2343     m_pWorkspace->setUpdatesEnabled(true);
2344 schoenebeck 1461 }
2345    
2346    
2347     // Force update of the channels maximum volume setting.
2348     void MainForm::updateMaxVolume (void)
2349     {
2350 capela 1509 if (m_pOptions == NULL)
2351     return;
2352 schoenebeck 1461
2353     #ifdef CONFIG_VOLUME
2354     m_iVolumeChanging++;
2355 capela 1499 m_pVolumeSlider->setMaximum(m_pOptions->iMaxVolume);
2356     m_pVolumeSpinBox->setMaximum(m_pOptions->iMaxVolume);
2357 schoenebeck 1461 m_iVolumeChanging--;
2358     #endif
2359    
2360 capela 1509 // Full channel list update...
2361 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2362 capela 1509 if (wlist.isEmpty())
2363     return;
2364 schoenebeck 1461
2365 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2366 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2367     ChannelStrip *pChannelStrip = NULL;
2368     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2369     if (pMdiSubWindow)
2370     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2371 capela 1509 if (pChannelStrip)
2372     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2373     }
2374     m_pWorkspace->setUpdatesEnabled(true);
2375 schoenebeck 1461 }
2376    
2377    
2378     //-------------------------------------------------------------------------
2379     // qsamplerMainForm -- Messages window form handlers.
2380    
2381     // Messages output methods.
2382     void MainForm::appendMessages( const QString& s )
2383     {
2384 capela 1509 if (m_pMessages)
2385     m_pMessages->appendMessages(s);
2386 schoenebeck 1461
2387 capela 1509 statusBar()->showMessage(s, 3000);
2388 schoenebeck 1461 }
2389    
2390     void MainForm::appendMessagesColor( const QString& s, const QString& c )
2391     {
2392 capela 1509 if (m_pMessages)
2393     m_pMessages->appendMessagesColor(s, c);
2394 schoenebeck 1461
2395 capela 1509 statusBar()->showMessage(s, 3000);
2396 schoenebeck 1461 }
2397    
2398     void MainForm::appendMessagesText( const QString& s )
2399     {
2400 capela 1509 if (m_pMessages)
2401     m_pMessages->appendMessagesText(s);
2402 schoenebeck 1461 }
2403    
2404 capela 2722 void MainForm::appendMessagesError( const QString& sText )
2405 schoenebeck 1461 {
2406 capela 1509 if (m_pMessages)
2407     m_pMessages->show();
2408 schoenebeck 1461
2409 capela 2722 appendMessagesColor(sText.simplified(), "#ff0000");
2410 schoenebeck 1461
2411     // Make it look responsive...:)
2412 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2413 schoenebeck 1461
2414 capela 2722 if (m_pOptions && m_pOptions->bConfirmError) {
2415     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Error");
2416     #if 0
2417     QMessageBox::critical(this, sTitle, sText, QMessageBox::Cancel);
2418     #else
2419     QMessageBox mbox(this);
2420     mbox.setIcon(QMessageBox::Critical);
2421     mbox.setWindowTitle(sTitle);
2422     mbox.setText(sText);
2423     mbox.setStandardButtons(QMessageBox::Cancel);
2424     QCheckBox cbox(tr("Don't show this again"));
2425     cbox.setChecked(false);
2426     cbox.blockSignals(true);
2427     mbox.addButton(&cbox, QMessageBox::ActionRole);
2428     if (mbox.exec() && cbox.isChecked())
2429     m_pOptions->bConfirmError = false;
2430     #endif
2431     }
2432 schoenebeck 1461 }
2433    
2434    
2435     // This is a special message format, just for client results.
2436     void MainForm::appendMessagesClient( const QString& s )
2437     {
2438 capela 1509 if (m_pClient == NULL)
2439     return;
2440 schoenebeck 1461
2441 capela 1509 appendMessagesColor(s + QString(": %1 (errno=%2)")
2442     .arg(::lscp_client_get_result(m_pClient))
2443     .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2444 schoenebeck 1461
2445     // Make it look responsive...:)
2446 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2447 schoenebeck 1461 }
2448    
2449    
2450     // Force update of the messages font.
2451     void MainForm::updateMessagesFont (void)
2452     {
2453 capela 1509 if (m_pOptions == NULL)
2454     return;
2455 schoenebeck 1461
2456 capela 1509 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2457     QFont font;
2458     if (font.fromString(m_pOptions->sMessagesFont))
2459     m_pMessages->setMessagesFont(font);
2460     }
2461 schoenebeck 1461 }
2462    
2463    
2464     // Update messages window line limit.
2465     void MainForm::updateMessagesLimit (void)
2466     {
2467 capela 1509 if (m_pOptions == NULL)
2468     return;
2469 schoenebeck 1461
2470 capela 1509 if (m_pMessages) {
2471     if (m_pOptions->bMessagesLimit)
2472     m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2473     else
2474     m_pMessages->setMessagesLimit(-1);
2475     }
2476 schoenebeck 1461 }
2477    
2478    
2479     // Enablement of the messages capture feature.
2480     void MainForm::updateMessagesCapture (void)
2481     {
2482 capela 1509 if (m_pOptions == NULL)
2483     return;
2484 schoenebeck 1461
2485 capela 1509 if (m_pMessages)
2486     m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2487 schoenebeck 1461 }
2488    
2489    
2490     //-------------------------------------------------------------------------
2491     // qsamplerMainForm -- MDI channel strip management.
2492    
2493     // The channel strip creation executive.
2494 capela 2387 ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel )
2495 schoenebeck 1461 {
2496 capela 1509 if (m_pClient == NULL || pChannel == NULL)
2497     return NULL;
2498 schoenebeck 1461
2499 capela 1509 // Add a new channel itema...
2500 capela 1515 ChannelStrip *pChannelStrip = new ChannelStrip();
2501 capela 1509 if (pChannelStrip == NULL)
2502     return NULL;
2503 schoenebeck 1461
2504 capela 1515 // Set some initial channel strip options...
2505 capela 1509 if (m_pOptions) {
2506     // Background display effect...
2507     pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2508     // We'll need a display font.
2509     QFont font;
2510     if (font.fromString(m_pOptions->sDisplayFont))
2511     pChannelStrip->setDisplayFont(font);
2512     // Maximum allowed volume setting.
2513     pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2514     }
2515 schoenebeck 1461
2516 capela 1515 // Add it to workspace...
2517 capela 2441 m_pWorkspace->addSubWindow(pChannelStrip,
2518     Qt::SubWindow | Qt::FramelessWindowHint);
2519 capela 1515
2520     // Actual channel strip setup...
2521     pChannelStrip->setup(pChannel);
2522    
2523     QObject::connect(pChannelStrip,
2524 capela 2387 SIGNAL(channelChanged(ChannelStrip *)),
2525     SLOT(channelStripChanged(ChannelStrip *)));
2526 capela 1515
2527 capela 1509 // Now we show up us to the world.
2528     pChannelStrip->show();
2529 schoenebeck 1461
2530     // This is pretty new, so we'll watch for it closely.
2531     channelStripChanged(pChannelStrip);
2532    
2533 capela 1509 // Return our successful reference...
2534     return pChannelStrip;
2535 schoenebeck 1461 }
2536    
2537 capela 2387
2538     void MainForm::destroyChannelStrip ( ChannelStrip *pChannelStrip )
2539     {
2540 capela 2441 QMdiSubWindow *pMdiSubWindow
2541     = static_cast<QMdiSubWindow *> (pChannelStrip->parentWidget());
2542     if (pMdiSubWindow == NULL)
2543     return;
2544    
2545 schoenebeck 1702 // Just delete the channel strip.
2546     delete pChannelStrip;
2547 capela 2441 delete pMdiSubWindow;
2548 schoenebeck 1461
2549 schoenebeck 1702 // Do we auto-arrange?
2550     if (m_pOptions && m_pOptions->bAutoArrange)
2551     channelsArrange();
2552    
2553     stabilizeForm();
2554     }
2555    
2556 capela 2387
2557 schoenebeck 1461 // Retrieve the active channel strip.
2558 capela 2387 ChannelStrip *MainForm::activeChannelStrip (void)
2559 schoenebeck 1461 {
2560 capela 2387 QMdiSubWindow *pMdiSubWindow = m_pWorkspace->activeSubWindow();
2561     if (pMdiSubWindow)
2562     return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2563     else
2564     return NULL;
2565 schoenebeck 1461 }
2566    
2567    
2568     // Retrieve a channel strip by index.
2569 capela 2387 ChannelStrip *MainForm::channelStripAt ( int iChannel )
2570 schoenebeck 1461 {
2571 schoenebeck 1702 if (!m_pWorkspace) return NULL;
2572    
2573 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2574 capela 1509 if (wlist.isEmpty())
2575     return NULL;
2576 schoenebeck 1461
2577 schoenebeck 1702 if (iChannel < 0 || iChannel >= wlist.size())
2578     return NULL;
2579    
2580 capela 2387 QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2581     if (pMdiSubWindow)
2582     return static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2583     else
2584     return NULL;
2585 schoenebeck 1461 }
2586    
2587    
2588     // Retrieve a channel strip by sampler channel id.
2589 capela 2387 ChannelStrip *MainForm::channelStrip ( int iChannelID )
2590 schoenebeck 1461 {
2591 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2592 schoenebeck 1461 if (wlist.isEmpty())
2593     return NULL;
2594    
2595 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2596     ChannelStrip *pChannelStrip = NULL;
2597     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2598     if (pMdiSubWindow)
2599     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2600 schoenebeck 1461 if (pChannelStrip) {
2601 capela 1558 Channel *pChannel = pChannelStrip->channel();
2602 schoenebeck 1461 if (pChannel && pChannel->channelID() == iChannelID)
2603     return pChannelStrip;
2604     }
2605     }
2606    
2607     // Not found.
2608     return NULL;
2609     }
2610    
2611    
2612     // Construct the windows menu.
2613     void MainForm::channelsMenuAboutToShow (void)
2614     {
2615 capela 1509 m_ui.channelsMenu->clear();
2616     m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction);
2617     m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction);
2618 schoenebeck 1461
2619 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2620 capela 1509 if (!wlist.isEmpty()) {
2621     m_ui.channelsMenu->addSeparator();
2622 capela 2387 for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2623     ChannelStrip *pChannelStrip = NULL;
2624     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2625     if (pMdiSubWindow)
2626     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2627 capela 1499 if (pChannelStrip) {
2628 capela 1509 QAction *pAction = m_ui.channelsMenu->addAction(
2629     pChannelStrip->windowTitle(),
2630     this, SLOT(channelsMenuActivated()));
2631 capela 1507 pAction->setCheckable(true);
2632     pAction->setChecked(activeChannelStrip() == pChannelStrip);
2633 capela 1499 pAction->setData(iChannel);
2634     }
2635 capela 1509 }
2636     }
2637 schoenebeck 1461 }
2638    
2639    
2640     // Windows menu activation slot
2641 capela 1499 void MainForm::channelsMenuActivated (void)
2642 schoenebeck 1461 {
2643 capela 1499 // Retrive channel index from action data...
2644     QAction *pAction = qobject_cast<QAction *> (sender());
2645     if (pAction == NULL)
2646     return;
2647    
2648 capela 2387 ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt());
2649 capela 1499 if (pChannelStrip) {
2650     pChannelStrip->showNormal();
2651     pChannelStrip->setFocus();
2652     }
2653 schoenebeck 1461 }
2654    
2655    
2656     //-------------------------------------------------------------------------
2657     // qsamplerMainForm -- Timer stuff.
2658    
2659     // Set the pseudo-timer delay schedule.
2660     void MainForm::startSchedule ( int iStartDelay )
2661     {
2662 capela 1509 m_iStartDelay = 1 + (iStartDelay * 1000);
2663     m_iTimerDelay = 0;
2664 schoenebeck 1461 }
2665    
2666     // Suspend the pseudo-timer delay schedule.
2667     void MainForm::stopSchedule (void)
2668     {
2669 capela 1509 m_iStartDelay = 0;
2670     m_iTimerDelay = 0;
2671 schoenebeck 1461 }
2672    
2673     // Timer slot funtion.
2674     void MainForm::timerSlot (void)
2675     {
2676 capela 1509 if (m_pOptions == NULL)
2677     return;
2678 schoenebeck 1461
2679 capela 1509 // Is it the first shot on server start after a few delay?
2680     if (m_iTimerDelay < m_iStartDelay) {
2681     m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2682     if (m_iTimerDelay >= m_iStartDelay) {
2683     // If we cannot start it now, maybe a lil'mo'later ;)
2684     if (!startClient()) {
2685     m_iStartDelay += m_iTimerDelay;
2686     m_iTimerDelay = 0;
2687     }
2688     }
2689     }
2690 schoenebeck 1461
2691     if (m_pClient) {
2692     // Update the channel information for each pending strip...
2693 capela 1499 QListIterator<ChannelStrip *> iter(m_changedStrips);
2694     while (iter.hasNext()) {
2695     ChannelStrip *pChannelStrip = iter.next();
2696     // If successfull, remove from pending list...
2697     if (pChannelStrip->updateChannelInfo()) {
2698     int iChannelStrip = m_changedStrips.indexOf(pChannelStrip);
2699     if (iChannelStrip >= 0)
2700     m_changedStrips.removeAt(iChannelStrip);
2701 schoenebeck 1461 }
2702     }
2703     // Refresh each channel usage, on each period...
2704     if (m_pOptions->bAutoRefresh) {
2705     m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2706     if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2707     m_iTimerSlot = 0;
2708     // Update the channel stream usage for each strip...
2709 capela 2387 QList<QMdiSubWindow *> wlist = m_pWorkspace->subWindowList();
2710     for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) {
2711     ChannelStrip *pChannelStrip = NULL;
2712     QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel);
2713     if (pMdiSubWindow)
2714     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2715 schoenebeck 1461 if (pChannelStrip && pChannelStrip->isVisible())
2716     pChannelStrip->updateChannelUsage();
2717     }
2718     }
2719     }
2720     }
2721    
2722 capela 1509 // Register the next timer slot.
2723     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2724 schoenebeck 1461 }
2725    
2726    
2727     //-------------------------------------------------------------------------
2728     // qsamplerMainForm -- Server stuff.
2729    
2730     // Start linuxsampler server...
2731     void MainForm::startServer (void)
2732     {
2733 capela 1509 if (m_pOptions == NULL)
2734     return;
2735 schoenebeck 1461
2736 capela 1509 // Aren't already a client, are we?
2737     if (!m_pOptions->bServerStart || m_pClient)
2738     return;
2739 schoenebeck 1461
2740 capela 1509 // Is the server process instance still here?
2741     if (m_pServer) {
2742 capela 1840 if (QMessageBox::warning(this,
2743 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
2744 capela 1509 tr("Could not start the LinuxSampler server.\n\n"
2745 schoenebeck 1626 "Maybe it is already started."),
2746 capela 1840 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
2747 capela 1509 m_pServer->terminate();
2748     m_pServer->kill();
2749     }
2750     return;
2751     }
2752 schoenebeck 1461
2753 capela 1509 // Reset our timer counters...
2754     stopSchedule();
2755 schoenebeck 1461
2756 capela 1509 // Verify we have something to start with...
2757     if (m_pOptions->sServerCmdLine.isEmpty())
2758     return;
2759 schoenebeck 1461
2760 capela 1509 // OK. Let's build the startup process...
2761 schoenebeck 1626 m_pServer = new QProcess();
2762     bForceServerStop = true;
2763 capela 1509
2764     // Setup stdout/stderr capture...
2765     // if (m_pOptions->bStdoutCapture) {
2766 capela 1559 m_pServer->setProcessChannelMode(QProcess::ForwardedChannels);
2767 schoenebeck 1461 QObject::connect(m_pServer,
2768 schoenebeck 1470 SIGNAL(readyReadStandardOutput()),
2769 schoenebeck 1461 SLOT(readServerStdout()));
2770     QObject::connect(m_pServer,
2771 schoenebeck 1470 SIGNAL(readyReadStandardError()),
2772 schoenebeck 1461 SLOT(readServerStdout()));
2773 capela 1509 // }
2774    
2775 schoenebeck 1461 // The unforgiveable signal communication...
2776     QObject::connect(m_pServer,
2777 capela 1559 SIGNAL(finished(int, QProcess::ExitStatus)),
2778 schoenebeck 1461 SLOT(processServerExit()));
2779    
2780 capela 1509 // Build process arguments...
2781     QStringList args = m_pOptions->sServerCmdLine.split(' ');
2782     QString sCommand = args[0];
2783     args.removeAt(0);
2784 schoenebeck 1461
2785 capela 1509 appendMessages(tr("Server is starting..."));
2786     appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2787 schoenebeck 1461
2788 capela 1509 // Go linuxsampler, go...
2789     m_pServer->start(sCommand, args);
2790     if (!m_pServer->waitForStarted()) {
2791     appendMessagesError(tr("Could not start server.\n\nSorry."));
2792     processServerExit();
2793     return;
2794     }
2795 schoenebeck 1461
2796 capela 1509 // Show startup results...
2797     appendMessages(
2798     tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2799 schoenebeck 1461
2800 capela 1509 // Reset (yet again) the timer counters,
2801     // but this time is deferred as the user opted.
2802     startSchedule(m_pOptions->iStartDelay);
2803     stabilizeForm();
2804 schoenebeck 1461 }
2805    
2806    
2807     // Stop linuxsampler server...
2808 schoenebeck 1626 void MainForm::stopServer (bool bInteractive)
2809 schoenebeck 1461 {
2810 capela 1509 // Stop client code.
2811     stopClient();
2812 schoenebeck 1461
2813 schoenebeck 1626 if (m_pServer && bInteractive) {
2814     if (QMessageBox::question(this,
2815     QSAMPLER_TITLE ": " + tr("The backend's fate ..."),
2816     tr("You have the option to keep the sampler backend (LinuxSampler)\n"
2817     "running in the background. The sampler would continue to work\n"
2818     "according to your current sampler session and you could alter the\n"
2819     "sampler session at any time by relaunching QSampler.\n\n"
2820 capela 1890 "Do you want LinuxSampler to stop?"),
2821 schoenebeck 2717 QMessageBox::Yes | QMessageBox::No,
2822     QMessageBox::Yes) == QMessageBox::No)
2823 schoenebeck 1626 {
2824     bForceServerStop = false;
2825     }
2826     }
2827    
2828 capela 1509 // And try to stop server.
2829 schoenebeck 1626 if (m_pServer && bForceServerStop) {
2830 capela 1509 appendMessages(tr("Server is stopping..."));
2831 capela 1559 if (m_pServer->state() == QProcess::Running) {
2832 capela 2441 #if defined(WIN32)
2833 capela 1559 // Try harder...
2834     m_pServer->kill();
2835 capela 2441 #else
2836 capela 1559 // Try softly...
2837 capela 1509 m_pServer->terminate();
2838 capela 2441 #endif
2839 capela 1559 }
2840     } // Do final processing anyway.
2841     else processServerExit();
2842 schoenebeck 1461
2843 capela 1509 // Give it some time to terminate gracefully and stabilize...
2844     QTime t;
2845     t.start();
2846     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2847     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2848 schoenebeck 1461 }
2849    
2850    
2851     // Stdout handler...
2852     void MainForm::readServerStdout (void)
2853     {
2854 capela 1509 if (m_pMessages)
2855     m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2856 schoenebeck 1461 }
2857    
2858    
2859     // Linuxsampler server cleanup.
2860     void MainForm::processServerExit (void)
2861     {
2862 capela 1509 // Force client code cleanup.
2863     stopClient();
2864 schoenebeck 1461
2865 capela 1509 // Flush anything that maybe pending...
2866     if (m_pMessages)
2867     m_pMessages->flushStdoutBuffer();
2868 schoenebeck 1461
2869 schoenebeck 1626 if (m_pServer && bForceServerStop) {
2870 capela 1559 if (m_pServer->state() != QProcess::NotRunning) {
2871     appendMessages(tr("Server is being forced..."));
2872     // Force final server shutdown...
2873     m_pServer->kill();
2874     // Give it some time to terminate gracefully and stabilize...
2875     QTime t;
2876     t.start();
2877     while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2878     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
2879     }
2880 capela 1509 // Force final server shutdown...
2881     appendMessages(
2882     tr("Server was stopped with exit status %1.")
2883     .arg(m_pServer->exitStatus()));
2884     delete m_pServer;
2885     m_pServer = NULL;
2886     }
2887 schoenebeck 1461
2888 capela 1509 // Again, make status visible stable.
2889     stabilizeForm();
2890 schoenebeck 1461 }
2891    
2892    
2893     //-------------------------------------------------------------------------
2894     // qsamplerMainForm -- Client stuff.
2895    
2896     // The LSCP client callback procedure.
2897 capela 1509 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/,
2898     lscp_event_t event, const char *pchData, int cchData, void *pvData )
2899 schoenebeck 1461 {
2900 capela 1509 MainForm* pMainForm = (MainForm *) pvData;
2901     if (pMainForm == NULL)
2902     return LSCP_FAILED;
2903 schoenebeck 1461
2904 capela 1509 // ATTN: DO NOT EVER call any GUI code here,
2905     // as this is run under some other thread context.
2906     // A custom event must be posted here...
2907     QApplication::postEvent(pMainForm,
2908 capela 2050 new LscpEvent(event, pchData, cchData));
2909 schoenebeck 1461
2910 capela 1509 return LSCP_OK;
2911 schoenebeck 1461 }
2912    
2913    
2914     // Start our almighty client...
2915     bool MainForm::startClient (void)
2916     {
2917 capela 1509 // Have it a setup?
2918     if (m_pOptions == NULL)
2919     return false;
2920 schoenebeck 1461
2921 capela 1509 // Aren't we already started, are we?
2922     if (m_pClient)
2923     return true;
2924 schoenebeck 1461
2925 capela 1509 // Log prepare here.
2926     appendMessages(tr("Client connecting..."));
2927 schoenebeck 1461
2928 capela 1509 // Create the client handle...
2929 capela 1499 m_pClient = ::lscp_client_create(
2930     m_pOptions->sServerHost.toUtf8().constData(),
2931     m_pOptions->iServerPort, qsampler_client_callback, this);
2932 capela 1509 if (m_pClient == NULL) {
2933     // Is this the first try?
2934     // maybe we need to start a local server...
2935     if ((m_pServer && m_pServer->state() == QProcess::Running)
2936     || !m_pOptions->bServerStart) {
2937     appendMessagesError(
2938     tr("Could not connect to server as client.\n\nSorry."));
2939     } else {
2940     startServer();
2941     }
2942     // This is always a failure.
2943     stabilizeForm();
2944     return false;
2945     }
2946     // Just set receive timeout value, blindly.
2947     ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2948     appendMessages(
2949     tr("Client receive timeout is set to %1 msec.")
2950     .arg(::lscp_client_get_timeout(m_pClient)));
2951 schoenebeck 1461
2952     // Subscribe to channel info change notifications...
2953 schoenebeck 1702 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK)
2954     appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)");
2955 schoenebeck 1461 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2956 schoenebeck 1691 appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)");
2957 schoenebeck 1461
2958 schoenebeck 1698 DeviceStatusForm::onDevicesChanged(); // initialize
2959     updateViewMidiDeviceStatusMenu();
2960     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK)
2961     appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)");
2962 schoenebeck 1699 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK)
2963     appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)");
2964 schoenebeck 1702 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK)
2965     appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)");
2966     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK)
2967     appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)");
2968 schoenebeck 1698
2969 capela 1704 #if CONFIG_EVENT_CHANNEL_MIDI
2970 schoenebeck 1691 // Subscribe to channel MIDI data notifications...
2971     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK)
2972     appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)");
2973     #endif
2974    
2975 capela 1704 #if CONFIG_EVENT_DEVICE_MIDI
2976 schoenebeck 1698 // Subscribe to channel MIDI data notifications...
2977     if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK)
2978     appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)");
2979     #endif
2980    
2981 capela 1509 // We may stop scheduling around.
2982     stopSchedule();
2983 schoenebeck 1461
2984 capela 1509 // We'll accept drops from now on...
2985     setAcceptDrops(true);
2986 schoenebeck 1461
2987 capela 1509 // Log success here.
2988     appendMessages(tr("Client connected."));
2989 schoenebeck 1461
2990     // Hard-notify instrumnet and device configuration forms,
2991     // if visible, that we're ready...
2992     if (m_pInstrumentListForm)
2993 capela 1509 m_pInstrumentListForm->refreshInstruments();
2994 schoenebeck 1461 if (m_pDeviceForm)
2995 capela 1509 m_pDeviceForm->refreshDevices();
2996 schoenebeck 1461
2997 capela 1509 // Is any session pending to be loaded?
2998     if (!m_pOptions->sSessionFile.isEmpty()) {
2999     // Just load the prabably startup session...
3000     if (loadSessionFile(m_pOptions->sSessionFile)) {
3001     m_pOptions->sSessionFile = QString::null;
3002     return true;
3003     }
3004     }
3005 schoenebeck 1461
3006 schoenebeck 1803 // send the current / loaded fine tuning settings to the sampler
3007     m_pOptions->sendFineTuningSettings();
3008    
3009 capela 1509 // Make a new session
3010     return newSession();
3011 schoenebeck 1461 }
3012    
3013    
3014     // Stop client...
3015     void MainForm::stopClient (void)
3016     {
3017 capela 1509 if (m_pClient == NULL)
3018     return;
3019 schoenebeck 1461
3020 capela 1509 // Log prepare here.
3021     appendMessages(tr("Client disconnecting..."));
3022 schoenebeck 1461
3023 capela 1509 // Clear timer counters...
3024     stopSchedule();
3025 schoenebeck 1461
3026 capela 1509 // We'll reject drops from now on...
3027     setAcceptDrops(false);
3028 schoenebeck 1461
3029 capela 1509 // Force any channel strips around, but
3030     // but avoid removing the corresponding
3031     // channels from the back-end server.
3032     m_iDirtyCount = 0;
3033     closeSession(false);
3034 schoenebeck 1461
3035 capela 1509 // Close us as a client...
3036 capela 1704 #if CONFIG_EVENT_DEVICE_MIDI
3037 schoenebeck 1698 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI);
3038     #endif
3039 capela 1704 #if CONFIG_EVENT_CHANNEL_MIDI
3040 schoenebeck 1691 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI);
3041     #endif
3042 schoenebeck 1702 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO);
3043     ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT);
3044 schoenebeck 1699 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO);
3045 schoenebeck 1698 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT);
3046 schoenebeck 1461 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
3047 schoenebeck 1702 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT);
3048 capela 1509 ::lscp_client_destroy(m_pClient);
3049     m_pClient = NULL;
3050 schoenebeck 1461
3051     // Hard-notify instrumnet and device configuration forms,
3052     // if visible, that we're running out...
3053     if (m_pInstrumentListForm)
3054 capela 1509 m_pInstrumentListForm->refreshInstruments();
3055 schoenebeck 1461 if (m_pDeviceForm)
3056 capela 1509 m_pDeviceForm->refreshDevices();
3057 schoenebeck 1461
3058 capela 1509 // Log final here.
3059     appendMessages(tr("Client disconnected."));
3060 schoenebeck 1461
3061 capela 1509 // Make visible status.
3062     stabilizeForm();
3063 schoenebeck 1461 }
3064    
3065 capela 1514
3066     // Channel strip activation/selection.
3067 capela 2387 void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow )
3068 capela 1514 {
3069 capela 2387 ChannelStrip *pChannelStrip = NULL;
3070     if (pMdiSubWindow)
3071     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
3072 capela 1514 if (pChannelStrip)
3073     pChannelStrip->setSelected(true);
3074    
3075     stabilizeForm();
3076     }
3077    
3078    
3079 capela 2681 // Channel toolbar orientation change.
3080     void MainForm::channelsToolbarOrientation ( Qt::Orientation orientation )
3081     {
3082     #ifdef CONFIG_VOLUME
3083     m_pVolumeSlider->setOrientation(orientation);
3084     if (orientation == Qt::Horizontal) {
3085     m_pVolumeSlider->setMinimumHeight(24);
3086     m_pVolumeSlider->setMaximumHeight(32);
3087     m_pVolumeSlider->setMinimumWidth(120);
3088     m_pVolumeSlider->setMaximumWidth(640);
3089     m_pVolumeSpinBox->setMaximumWidth(64);
3090     m_pVolumeSpinBox->setButtonSymbols(QSpinBox::UpDownArrows);
3091     } else {
3092     m_pVolumeSlider->setMinimumHeight(120);
3093     m_pVolumeSlider->setMaximumHeight(480);
3094     m_pVolumeSlider->setMinimumWidth(24);
3095     m_pVolumeSlider->setMaximumWidth(32);
3096     m_pVolumeSpinBox->setMaximumWidth(32);
3097     m_pVolumeSpinBox->setButtonSymbols(QSpinBox::NoButtons);
3098     }
3099     #endif
3100     }
3101    
3102    
3103 schoenebeck 1461 } // namespace QSampler
3104 capela 1464
3105    
3106     // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC