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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC