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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC