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

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

Parent Directory Parent Directory | Revision Log Revision Log


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