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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC