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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3509 - (hide annotations) (download)
Tue Apr 2 17:55:00 2019 UTC (11 months, 3 weeks ago) by capela
File size: 90701 byte(s)
- Removed duplicate #include <signal.h>.
1 capela 1464 // qsamplerMainForm.cpp
2     //
3     /****************************************************************************
4 capela 3508 Copyright (C) 2004-2019, rncbc aka Rui Nuno Capela. All rights reserved.
5 capela 2718 Copyright (C) 2007,2008,2015 Christian Schoenebeck
6 capela 1464
7     This program is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; either version 2
10     of the License, or (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License along
18     with this program; if not, write to the Free Software Foundation, Inc.,
19     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20    
21     *****************************************************************************/
22    
23 capela 1513 #include "qsamplerAbout.h"
24 schoenebeck 1461 #include "qsamplerMainForm.h"
25    
26     #include "qsamplerOptions.h"
27     #include "qsamplerChannel.h"
28     #include "qsamplerMessages.h"
29    
30     #include "qsamplerChannelStrip.h"
31     #include "qsamplerInstrumentList.h"
32    
33     #include "qsamplerInstrumentListForm.h"
34     #include "qsamplerDeviceForm.h"
35     #include "qsamplerOptionsForm.h"
36 schoenebeck 1698 #include "qsamplerDeviceStatusForm.h"
37 schoenebeck 1461
38 capela 2387 #include <QMdiArea>
39     #include <QMdiSubWindow>
40    
41 capela 1499 #include <QApplication>
42     #include <QProcess>
43     #include <QMessageBox>
44    
45     #include <QRegExp>
46     #include <QTextStream>
47     #include <QFileDialog>
48     #include <QFileInfo>
49     #include <QFile>
50     #include <QUrl>
51    
52     #include <QDragEnterEvent>
53    
54     #include <QStatusBar>
55     #include <QSpinBox>
56     #include <QSlider>
57     #include <QLabel>
58     #include <QTimer>
59     #include <QDateTime>
60    
61 capela 2387 #if QT_VERSION >= 0x050000
62     #include <QMimeData>
63     #endif
64    
65 capela 2038 #if QT_VERSION < 0x040500
66     namespace Qt {
67     const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000);
68     }
69     #endif
70 capela 1499
71 schoenebeck 1461 #ifdef 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     #endif
93    
94    
95 capela 2112 //-------------------------------------------------------------------------
96     // LADISH Level 1 support stuff.
97    
98 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
99 capela 2112
100     #include <QSocketNotifier>
101    
102     #include <sys/types.h>
103     #include <sys/socket.h>
104    
105     #include <signal.h>
106    
107     // File descriptor for SIGUSR1 notifier.
108 capela 3508 static int g_fdSigusr1[2];
109 capela 2112
110     // Unix SIGUSR1 signal handler.
111     static void qsampler_sigusr1_handler ( int /* signo */ )
112     {
113     char c = 1;
114    
115 capela 3508 (::write(g_fdSigusr1[0], &c, sizeof(c)) > 0);
116 capela 2112 }
117    
118 capela 3508 // File descriptor for SIGTERM notifier.
119     static int g_fdSigterm[2];
120    
121     // Unix SIGTERM signal handler.
122     static void qsampler_sigterm_handler ( int /* signo */ )
123     {
124     char c = 1;
125    
126     (::write(g_fdSigterm[0], &c, sizeof(c)) > 0);
127     }
128    
129 capela 2112 #endif // HAVE_SIGNAL_H
130    
131    
132     //-------------------------------------------------------------------------
133 capela 2979 // QSampler -- namespace
134 capela 2112
135    
136 capela 1558 namespace QSampler {
137    
138 schoenebeck 1461 // Timer constant stuff.
139     #define QSAMPLER_TIMER_MSECS 200
140    
141     // Status bar item indexes
142     #define QSAMPLER_STATUS_CLIENT 0 // Client connection state.
143     #define QSAMPLER_STATUS_SERVER 1 // Currenr server address (host:port)
144     #define QSAMPLER_STATUS_CHANNEL 2 // Active channel caption.
145     #define QSAMPLER_STATUS_SESSION 3 // Current session modification state.
146    
147    
148 capela 2050 // Specialties for thread-callback comunication.
149     #define QSAMPLER_LSCP_EVENT QEvent::Type(QEvent::User + 1)
150    
151    
152 schoenebeck 1461 //-------------------------------------------------------------------------
153 capela 2979 // QSampler::LscpEvent -- specialty for LSCP callback comunication.
154 schoenebeck 1461
155 capela 2050 class LscpEvent : public QEvent
156 schoenebeck 1461 {
157     public:
158    
159 capela 1509 // Constructor.
160 capela 2050 LscpEvent(lscp_event_t event, const char *pchData, int cchData)
161     : QEvent(QSAMPLER_LSCP_EVENT)
162 capela 1509 {
163     m_event = event;
164     m_data = QString::fromUtf8(pchData, cchData);
165     }
166 schoenebeck 1461
167 capela 1509 // Accessors.
168 capela 2979 lscp_event_t event() { return m_event; }
169     const QString& data() { return m_data; }
170 schoenebeck 1461
171     private:
172    
173 capela 1509 // The proper event type.
174     lscp_event_t m_event;
175     // The event data as a string.
176     QString m_data;
177 schoenebeck 1461 };
178    
179    
180     //-------------------------------------------------------------------------
181 capela 2979 // QSampler::Workspace -- Main window workspace (MDI Area) decl.
182 schoenebeck 1461
183 capela 2979 class Workspace : public QMdiArea
184     {
185     public:
186    
187     Workspace(MainForm *pMainForm) : QMdiArea(pMainForm) {}
188    
189     protected:
190    
191     void resizeEvent(QResizeEvent *)
192     {
193     MainForm *pMainForm = static_cast<MainForm *> (parentWidget());
194     if (pMainForm)
195     pMainForm->channelsArrangeAuto();
196     }
197     };
198    
199    
200     //-------------------------------------------------------------------------
201     // QSampler::MainForm -- Main window form implementation.
202    
203 schoenebeck 1461 // Kind of singleton reference.
204 capela 2979 MainForm *MainForm::g_pMainForm = NULL;
205 schoenebeck 1461
206 capela 1509 MainForm::MainForm ( QWidget *pParent )
207     : QMainWindow(pParent)
208     {
209     m_ui.setupUi(this);
210 schoenebeck 1461
211     // Pseudo-singleton reference setup.
212     g_pMainForm = this;
213    
214 capela 1509 // Initialize some pointer references.
215     m_pOptions = NULL;
216 schoenebeck 1461
217 capela 1509 // All child forms are to be created later, not earlier than setup.
218     m_pMessages = NULL;
219     m_pInstrumentListForm = NULL;
220     m_pDeviceForm = NULL;
221 schoenebeck 1461
222 capela 1509 // We'll start clean.
223     m_iUntitled = 0;
224 capela 2978 m_iDirtySetup = 0;
225 capela 1509 m_iDirtyCount = 0;
226 schoenebeck 1461
227 capela 1509 m_pServer = NULL;
228     m_pClient = NULL;
229 schoenebeck 1461
230 capela 1509 m_iStartDelay = 0;
231     m_iTimerDelay = 0;
232 schoenebeck 1461
233 capela 1509 m_iTimerSlot = 0;
234 schoenebeck 1461
235 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
236 capela 2112
237 schoenebeck 1461 // Set to ignore any fatal "Broken pipe" signals.
238     ::signal(SIGPIPE, SIG_IGN);
239 capela 2112
240 capela 2050 // LADISH Level 1 suport.
241 schoenebeck 1461
242 capela 2112 // Initialize file descriptors for SIGUSR1 socket notifier.
243 capela 3508 ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigusr1);
244     m_pSigusr1Notifier
245     = new QSocketNotifier(g_fdSigusr1[1], QSocketNotifier::Read, this);
246 capela 2112
247 capela 3508 QObject::connect(m_pSigusr1Notifier,
248 capela 2112 SIGNAL(activated(int)),
249     SLOT(handle_sigusr1()));
250    
251     // Install SIGUSR1 signal handler.
252 capela 3508 struct sigaction sigusr1;
253     sigusr1.sa_handler = qsampler_sigusr1_handler;
254     sigemptyset(&sigusr1.sa_mask);
255     sigusr1.sa_flags = 0;
256     sigusr1.sa_flags |= SA_RESTART;
257     ::sigaction(SIGUSR1, &sigusr1, NULL);
258 capela 2112
259 capela 3508 // Initialize file descriptors for SIGTERM socket notifier.
260     ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigterm);
261     m_pSigtermNotifier
262     = new QSocketNotifier(g_fdSigterm[1], QSocketNotifier::Read, this);
263    
264     QObject::connect(m_pSigtermNotifier,
265     SIGNAL(activated(int)),
266     SLOT(handle_sigterm()));
267    
268     // Install SIGTERM signal handler.
269     struct sigaction sigterm;
270     sigterm.sa_handler = qsampler_sigterm_handler;
271     ::sigemptyset(&sigterm.sa_mask);
272     sigterm.sa_flags = 0;
273     sigterm.sa_flags |= SA_RESTART;
274     ::sigaction(SIGTERM, &sigterm, NULL);
275     ::sigaction(SIGQUIT, &sigterm, NULL);
276    
277     // Ignore SIGHUP/SIGINT signals.
278     ::signal(SIGHUP, SIG_IGN);
279     ::signal(SIGINT, SIG_IGN);
280    
281 capela 2112 #else // HAVE_SIGNAL_H
282    
283 capela 3508 m_pSigusr1Notifier = NULL;
284     m_pSigtermNotifier = NULL;
285 capela 2112
286     #endif // !HAVE_SIGNAL_H
287    
288 schoenebeck 1461 #ifdef CONFIG_VOLUME
289 capela 1509 // Make some extras into the toolbar...
290 schoenebeck 1461 const QString& sVolumeText = tr("Master volume");
291     m_iVolumeChanging = 0;
292     // Volume slider...
293 capela 1509 m_ui.channelsToolbar->addSeparator();
294     m_pVolumeSlider = new QSlider(Qt::Horizontal, m_ui.channelsToolbar);
295 capela 1515 m_pVolumeSlider->setTickPosition(QSlider::TicksBothSides);
296 schoenebeck 1461 m_pVolumeSlider->setTickInterval(10);
297     m_pVolumeSlider->setPageStep(10);
298 capela 1499 m_pVolumeSlider->setSingleStep(10);
299     m_pVolumeSlider->setMinimum(0);
300     m_pVolumeSlider->setMaximum(100);
301 capela 1466 m_pVolumeSlider->setMaximumHeight(26);
302 schoenebeck 1461 m_pVolumeSlider->setMinimumWidth(160);
303 capela 1499 m_pVolumeSlider->setToolTip(sVolumeText);
304 schoenebeck 1461 QObject::connect(m_pVolumeSlider,
305     SIGNAL(valueChanged(int)),
306     SLOT(volumeChanged(int)));
307 capela 1509 m_ui.channelsToolbar->addWidget(m_pVolumeSlider);
308 schoenebeck 1461 // Volume spin-box
309 capela 1509 m_ui.channelsToolbar->addSeparator();
310     m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar);
311 schoenebeck 1461 m_pVolumeSpinBox->setSuffix(" %");
312 capela 1499 m_pVolumeSpinBox->setMinimum(0);
313     m_pVolumeSpinBox->setMaximum(100);
314     m_pVolumeSpinBox->setToolTip(sVolumeText);
315 schoenebeck 1461 QObject::connect(m_pVolumeSpinBox,
316     SIGNAL(valueChanged(int)),
317     SLOT(volumeChanged(int)));
318 capela 1509 m_ui.channelsToolbar->addWidget(m_pVolumeSpinBox);
319 schoenebeck 1461 #endif
320    
321 capela 1509 // Make it an MDI workspace.
322 capela 2979 m_pWorkspace = new Workspace(this);
323 capela 2441 m_pWorkspace->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
324     m_pWorkspace->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
325 schoenebeck 1461 // Set the activation connection.
326     QObject::connect(m_pWorkspace,
327 capela 2387 SIGNAL(subWindowActivated(QMdiSubWindow *)),
328     SLOT(activateStrip(QMdiSubWindow *)));
329 capela 1509 // Make it shine :-)
330     setCentralWidget(m_pWorkspace);
331 schoenebeck 1461
332 capela 1509 // Create some statusbar labels...
333     QLabel *pLabel;
334     // Client status.
335     pLabel = new QLabel(tr("Connected"), this);
336     pLabel->setAlignment(Qt::AlignLeft);
337     pLabel->setMinimumSize(pLabel->sizeHint());
338     m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
339     statusBar()->addWidget(pLabel);
340     // Server address.
341     pLabel = new QLabel(this);
342     pLabel->setAlignment(Qt::AlignLeft);
343     m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
344     statusBar()->addWidget(pLabel, 1);
345     // Channel title.
346     pLabel = new QLabel(this);
347     pLabel->setAlignment(Qt::AlignLeft);
348     m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
349     statusBar()->addWidget(pLabel, 2);
350     // Session modification status.
351     pLabel = new QLabel(tr("MOD"), this);
352     pLabel->setAlignment(Qt::AlignHCenter);
353     pLabel->setMinimumSize(pLabel->sizeHint());
354     m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
355     statusBar()->addWidget(pLabel);
356 schoenebeck 1461
357 capela 3358 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
358 capela 1509 WSAStartup(MAKEWORD(1, 1), &_wsaData);
359 schoenebeck 1461 #endif
360 capela 1466
361 capela 1512 // Some actions surely need those
362     // shortcuts firmly attached...
363     addAction(m_ui.viewMenubarAction);
364     addAction(m_ui.viewToolbarAction);
365    
366 capela 1509 QObject::connect(m_ui.fileNewAction,
367 capela 1499 SIGNAL(triggered()),
368 capela 1466 SLOT(fileNew()));
369 capela 1509 QObject::connect(m_ui.fileOpenAction,
370 capela 1499 SIGNAL(triggered()),
371 capela 1466 SLOT(fileOpen()));
372 capela 1509 QObject::connect(m_ui.fileSaveAction,
373 capela 1499 SIGNAL(triggered()),
374 capela 1466 SLOT(fileSave()));
375 capela 1509 QObject::connect(m_ui.fileSaveAsAction,
376 capela 1499 SIGNAL(triggered()),
377 capela 1466 SLOT(fileSaveAs()));
378 capela 1509 QObject::connect(m_ui.fileResetAction,
379 capela 1499 SIGNAL(triggered()),
380 capela 1466 SLOT(fileReset()));
381 capela 1509 QObject::connect(m_ui.fileRestartAction,
382 capela 1499 SIGNAL(triggered()),
383 capela 1466 SLOT(fileRestart()));
384 capela 1509 QObject::connect(m_ui.fileExitAction,
385 capela 1499 SIGNAL(triggered()),
386 capela 1466 SLOT(fileExit()));
387 capela 1509 QObject::connect(m_ui.editAddChannelAction,
388 capela 1499 SIGNAL(triggered()),
389 capela 1466 SLOT(editAddChannel()));
390 capela 1509 QObject::connect(m_ui.editRemoveChannelAction,
391 capela 1499 SIGNAL(triggered()),
392 capela 1466 SLOT(editRemoveChannel()));
393 capela 1509 QObject::connect(m_ui.editSetupChannelAction,
394 capela 1499 SIGNAL(triggered()),
395 capela 1466 SLOT(editSetupChannel()));
396 capela 1509 QObject::connect(m_ui.editEditChannelAction,
397 capela 1499 SIGNAL(triggered()),
398 capela 1466 SLOT(editEditChannel()));
399 capela 1509 QObject::connect(m_ui.editResetChannelAction,
400 capela 1499 SIGNAL(triggered()),
401 capela 1466 SLOT(editResetChannel()));
402 capela 1509 QObject::connect(m_ui.editResetAllChannelsAction,
403 capela 1499 SIGNAL(triggered()),
404 capela 1466 SLOT(editResetAllChannels()));
405 capela 1509 QObject::connect(m_ui.viewMenubarAction,
406 capela 1466 SIGNAL(toggled(bool)),
407     SLOT(viewMenubar(bool)));
408 capela 1509 QObject::connect(m_ui.viewToolbarAction,
409 capela 1466 SIGNAL(toggled(bool)),
410     SLOT(viewToolbar(bool)));
411 capela 1509 QObject::connect(m_ui.viewStatusbarAction,
412 capela 1466 SIGNAL(toggled(bool)),
413     SLOT(viewStatusbar(bool)));
414 capela 1509 QObject::connect(m_ui.viewMessagesAction,
415 capela 1466 SIGNAL(toggled(bool)),
416     SLOT(viewMessages(bool)));
417 capela 1509 QObject::connect(m_ui.viewInstrumentsAction,
418 capela 1499 SIGNAL(triggered()),
419 capela 1466 SLOT(viewInstruments()));
420 capela 1509 QObject::connect(m_ui.viewDevicesAction,
421 capela 1499 SIGNAL(triggered()),
422 capela 1466 SLOT(viewDevices()));
423 capela 1509 QObject::connect(m_ui.viewOptionsAction,
424 capela 1499 SIGNAL(triggered()),
425 capela 1466 SLOT(viewOptions()));
426 capela 1509 QObject::connect(m_ui.channelsArrangeAction,
427 capela 1499 SIGNAL(triggered()),
428 capela 1466 SLOT(channelsArrange()));
429 capela 1509 QObject::connect(m_ui.channelsAutoArrangeAction,
430 capela 1466 SIGNAL(toggled(bool)),
431     SLOT(channelsAutoArrange(bool)));
432 capela 1509 QObject::connect(m_ui.helpAboutAction,
433 capela 1499 SIGNAL(triggered()),
434 capela 1466 SLOT(helpAbout()));
435 capela 1509 QObject::connect(m_ui.helpAboutQtAction,
436 capela 1499 SIGNAL(triggered()),
437 capela 1466 SLOT(helpAboutQt()));
438 capela 1499
439 capela 1509 QObject::connect(m_ui.fileMenu,
440 capela 1499 SIGNAL(aboutToShow()),
441     SLOT(updateRecentFilesMenu()));
442 capela 1509 QObject::connect(m_ui.channelsMenu,
443 capela 1507 SIGNAL(aboutToShow()),
444     SLOT(channelsMenuAboutToShow()));
445 capela 2681 #ifdef CONFIG_VOLUME
446     QObject::connect(m_ui.channelsToolbar,
447     SIGNAL(orientationChanged(Qt::Orientation)),
448     SLOT(channelsToolbarOrientation(Qt::Orientation)));
449     #endif
450 schoenebeck 1461 }
451    
452     // Destructor.
453     MainForm::~MainForm()
454     {
455 capela 1509 // Do final processing anyway.
456     processServerExit();
457 schoenebeck 1461
458 capela 3358 #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32)
459 capela 1509 WSACleanup();
460 schoenebeck 1461 #endif
461    
462 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
463 capela 3508 if (m_pSigusr1Notifier)
464     delete m_pSigusr1Notifier;
465     if (m_pSigtermNotifier)
466     delete m_pSigtermNotifier;
467 capela 2112 #endif
468    
469 capela 1509 // Finally drop any widgets around...
470     if (m_pDeviceForm)
471     delete m_pDeviceForm;
472     if (m_pInstrumentListForm)
473     delete m_pInstrumentListForm;
474     if (m_pMessages)
475     delete m_pMessages;
476     if (m_pWorkspace)
477     delete m_pWorkspace;
478 schoenebeck 1461
479 capela 1509 // Delete status item labels one by one.
480     if (m_statusItem[QSAMPLER_STATUS_CLIENT])
481     delete m_statusItem[QSAMPLER_STATUS_CLIENT];
482     if (m_statusItem[QSAMPLER_STATUS_SERVER])
483     delete m_statusItem[QSAMPLER_STATUS_SERVER];
484     if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
485     delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
486     if (m_statusItem[QSAMPLER_STATUS_SESSION])
487     delete m_statusItem[QSAMPLER_STATUS_SESSION];
488 schoenebeck 1461
489     #ifdef CONFIG_VOLUME
490     delete m_pVolumeSpinBox;
491     delete m_pVolumeSlider;
492     #endif
493    
494     // Pseudo-singleton reference shut-down.
495     g_pMainForm = NULL;
496     }
497    
498    
499     // Make and set a proper setup options step.
500 capela 1558 void MainForm::setup ( Options *pOptions )
501 schoenebeck 1461 {
502 capela 1509 // We got options?
503     m_pOptions = pOptions;
504 schoenebeck 1461
505 capela 1509 // What style do we create these forms?
506 capela 1499 Qt::WindowFlags wflags = Qt::Window
507     | Qt::CustomizeWindowHint
508     | Qt::WindowTitleHint
509     | Qt::WindowSystemMenuHint
510 capela 2038 | Qt::WindowMinMaxButtonsHint
511     | Qt::WindowCloseButtonHint;
512 capela 1499 if (m_pOptions->bKeepOnTop)
513     wflags |= Qt::Tool;
514 capela 2038
515 capela 1509 // Some child forms are to be created right now.
516 capela 1558 m_pMessages = new Messages(this);
517 capela 1509 m_pDeviceForm = new DeviceForm(this, wflags);
518 schoenebeck 1461 #ifdef CONFIG_MIDI_INSTRUMENT
519 capela 1509 m_pInstrumentListForm = new InstrumentListForm(this, wflags);
520 schoenebeck 1461 #else
521 capela 2038 m_ui.viewInstrumentsAction->setEnabled(false);
522 schoenebeck 1461 #endif
523 capela 2038
524 capela 1739 // Setup messages logging appropriately...
525     m_pMessages->setLogging(
526     m_pOptions->bMessagesLog,
527     m_pOptions->sMessagesLogPath);
528 capela 2038
529 capela 1509 // Set message defaults...
530     updateMessagesFont();
531     updateMessagesLimit();
532     updateMessagesCapture();
533 capela 2978
534 capela 1509 // Set the visibility signal.
535 schoenebeck 1461 QObject::connect(m_pMessages,
536     SIGNAL(visibilityChanged(bool)),
537     SLOT(stabilizeForm()));
538    
539 capela 1509 // Initial decorations toggle state.
540     m_ui.viewMenubarAction->setChecked(m_pOptions->bMenubar);
541     m_ui.viewToolbarAction->setChecked(m_pOptions->bToolbar);
542     m_ui.viewStatusbarAction->setChecked(m_pOptions->bStatusbar);
543     m_ui.channelsAutoArrangeAction->setChecked(m_pOptions->bAutoArrange);
544 schoenebeck 1461
545 capela 1509 // Initial decorations visibility state.
546     viewMenubar(m_pOptions->bMenubar);
547     viewToolbar(m_pOptions->bToolbar);
548     viewStatusbar(m_pOptions->bStatusbar);
549 schoenebeck 1461
550 capela 1509 addDockWidget(Qt::BottomDockWidgetArea, m_pMessages);
551 schoenebeck 1461
552 capela 1499 // Restore whole dock windows state.
553     QByteArray aDockables = m_pOptions->settings().value(
554     "/Layout/DockWindows").toByteArray();
555     if (!aDockables.isEmpty()) {
556     restoreState(aDockables);
557     }
558    
559 capela 1509 // Try to restore old window positioning and initial visibility.
560 capela 2077 m_pOptions->loadWidgetGeometry(this, true);
561 capela 1509 m_pOptions->loadWidgetGeometry(m_pInstrumentListForm);
562     m_pOptions->loadWidgetGeometry(m_pDeviceForm);
563 schoenebeck 1461
564 capela 1509 // Final startup stabilization...
565     updateMaxVolume();
566     updateRecentFilesMenu();
567     stabilizeForm();
568 schoenebeck 1461
569 capela 1509 // Make it ready :-)
570     statusBar()->showMessage(tr("Ready"), 3000);
571 schoenebeck 1461
572 capela 1509 // We'll try to start immediately...
573     startSchedule(0);
574 schoenebeck 1461
575 capela 1509 // Register the first timer slot.
576     QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
577 schoenebeck 1461 }
578    
579    
580     // Window close event handlers.
581     bool MainForm::queryClose (void)
582     {
583 capela 1509 bool bQueryClose = closeSession(false);
584 schoenebeck 1461
585 capela 1509 // Try to save current general state...
586     if (m_pOptions) {
587     // Some windows default fonts is here on demand too.
588     if (bQueryClose && m_pMessages)
589     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
590     // Try to save current positioning.
591     if (bQueryClose) {
592     // Save decorations state.
593     m_pOptions->bMenubar = m_ui.MenuBar->isVisible();
594     m_pOptions->bToolbar = (m_ui.fileToolbar->isVisible()
595     || m_ui.editToolbar->isVisible()
596     || m_ui.channelsToolbar->isVisible());
597     m_pOptions->bStatusbar = statusBar()->isVisible();
598     // Save the dock windows state.
599 capela 1499 m_pOptions->settings().setValue("/Layout/DockWindows", saveState());
600 capela 1509 // And the children, and the main windows state,.
601 schoenebeck 1461 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
602     m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
603 capela 2077 m_pOptions->saveWidgetGeometry(this, true);
604 schoenebeck 1461 // Close popup widgets.
605     if (m_pInstrumentListForm)
606     m_pInstrumentListForm->close();
607     if (m_pDeviceForm)
608     m_pDeviceForm->close();
609 capela 1509 // Stop client and/or server, gracefully.
610 schoenebeck 1626 stopServer(true /*interactive*/);
611 capela 1509 }
612     }
613 schoenebeck 1461
614 capela 1509 return bQueryClose;
615 schoenebeck 1461 }
616    
617    
618     void MainForm::closeEvent ( QCloseEvent *pCloseEvent )
619     {
620 schoenebeck 1699 if (queryClose()) {
621     DeviceStatusForm::deleteAllInstances();
622 capela 1509 pCloseEvent->accept();
623 schoenebeck 1699 } else
624 capela 1509 pCloseEvent->ignore();
625 schoenebeck 1461 }
626    
627    
628     // Window drag-n-drop event handlers.
629     void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
630     {
631 capela 1499 // Accept external drags only...
632     if (pDragEnterEvent->source() == NULL
633     && pDragEnterEvent->mimeData()->hasUrls()) {
634     pDragEnterEvent->accept();
635     } else {
636     pDragEnterEvent->ignore();
637     }
638 schoenebeck 1461 }
639    
640    
641 capela 2978 void MainForm::dropEvent ( QDropEvent *pDropEvent )
642 schoenebeck 1461 {
643 capela 1499 // Accept externally originated drops only...
644     if (pDropEvent->source())
645     return;
646 schoenebeck 1461
647 capela 1499 const QMimeData *pMimeData = pDropEvent->mimeData();
648     if (pMimeData->hasUrls()) {
649     QListIterator<QUrl> iter(pMimeData->urls());
650     while (iter.hasNext()) {
651     const QString& sPath = iter.next().toLocalFile();
652 capela 2108 // if (Channel::isDlsInstrumentFile(sPath)) {
653     if (QFileInfo(sPath).exists()) {
654 capela 1499 // Try to create a new channel from instrument file...
655 capela 1558 Channel *pChannel = new Channel();
656 capela 1499 if (pChannel == NULL)
657     return;
658     // Start setting the instrument filename...
659     pChannel->setInstrument(sPath, 0);
660     // Before we show it up, may be we'll
661     // better ask for some initial values?
662     if (!pChannel->channelSetup(this)) {
663     delete pChannel;
664     return;
665     }
666     // Finally, give it to a new channel strip...
667     if (!createChannelStrip(pChannel)) {
668     delete pChannel;
669     return;
670     }
671     // Make that an overall update.
672     m_iDirtyCount++;
673     stabilizeForm();
674     } // Otherwise, load an usual session file (LSCP script)...
675     else if (closeSession(true)) {
676     loadSessionFile(sPath);
677     break;
678 schoenebeck 1461 }
679     }
680     // Make it look responsive...:)
681 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
682 schoenebeck 1461 }
683     }
684    
685 capela 1499
686 schoenebeck 1461 // Custome event handler.
687 capela 2050 void MainForm::customEvent ( QEvent* pEvent )
688 schoenebeck 1461 {
689 capela 1509 // For the time being, just pump it to messages.
690 capela 2050 if (pEvent->type() == QSAMPLER_LSCP_EVENT) {
691     LscpEvent *pLscpEvent = static_cast<LscpEvent *> (pEvent);
692     switch (pLscpEvent->event()) {
693 schoenebeck 1702 case LSCP_EVENT_CHANNEL_COUNT:
694     updateAllChannelStrips(true);
695     break;
696 schoenebeck 1691 case LSCP_EVENT_CHANNEL_INFO: {
697 capela 2978 const int iChannelID = pLscpEvent->data().toInt();
698 schoenebeck 1691 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
699     if (pChannelStrip)
700     channelStripChanged(pChannelStrip);
701     break;
702     }
703 schoenebeck 1698 case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT:
704 schoenebeck 1699 if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
705 schoenebeck 1698 DeviceStatusForm::onDevicesChanged();
706     updateViewMidiDeviceStatusMenu();
707 schoenebeck 1699 break;
708     case LSCP_EVENT_MIDI_INPUT_DEVICE_INFO: {
709     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
710 capela 2050 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
711 schoenebeck 1699 DeviceStatusForm::onDeviceChanged(iDeviceID);
712     break;
713     }
714 schoenebeck 1702 case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT:
715     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
716     break;
717     case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO:
718     if (m_pDeviceForm) m_pDeviceForm->refreshDevices();
719     break;
720 capela 2036 #if CONFIG_EVENT_CHANNEL_MIDI
721 schoenebeck 1691 case LSCP_EVENT_CHANNEL_MIDI: {
722 capela 2050 const int iChannelID = pLscpEvent->data().section(' ', 0, 0).toInt();
723 schoenebeck 1691 ChannelStrip *pChannelStrip = channelStrip(iChannelID);
724     if (pChannelStrip)
725 capela 2036 pChannelStrip->midiActivityLedOn();
726 schoenebeck 1691 break;
727     }
728 capela 2036 #endif
729     #if CONFIG_EVENT_DEVICE_MIDI
730 schoenebeck 1698 case LSCP_EVENT_DEVICE_MIDI: {
731 capela 2050 const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt();
732     const int iPortID = pLscpEvent->data().section(' ', 1, 1).toInt();
733 capela 2036 DeviceStatusForm *pDeviceStatusForm
734     = DeviceStatusForm::getInstance(iDeviceID);
735 schoenebeck 1698 if (pDeviceStatusForm)
736     pDeviceStatusForm->midiArrived(iPortID);
737     break;
738     }
739 capela 2036 #endif
740 schoenebeck 1691 default:
741 capela 2050 appendMessagesColor(tr("LSCP Event: %1 data: %2")
742     .arg(::lscp_event_to_text(pLscpEvent->event()))
743     .arg(pLscpEvent->data()), "#996699");
744 schoenebeck 1461 }
745 capela 2112 }
746     }
747    
748    
749     // LADISH Level 1 -- SIGUSR1 signal handler.
750     void MainForm::handle_sigusr1 (void)
751     {
752 persson 2144 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
753 capela 2113
754 capela 2112 char c;
755    
756 capela 3508 if (::read(g_fdSigusr1[1], &c, sizeof(c)) > 0)
757 capela 2050 saveSession(false);
758 capela 2113
759     #endif
760 schoenebeck 1461 }
761    
762 capela 2038
763 capela 3508 void MainForm::handle_sigterm (void)
764     {
765     #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H)
766    
767     char c;
768    
769     if (::read(g_fdSigterm[1], &c, sizeof(c)) > 0)
770     close();
771    
772     #endif
773     }
774    
775    
776 capela 2038 void MainForm::updateViewMidiDeviceStatusMenu (void)
777     {
778 schoenebeck 1698 m_ui.viewMidiDeviceStatusMenu->clear();
779 capela 2038 const std::map<int, DeviceStatusForm *> statusForms
780     = DeviceStatusForm::getInstances();
781     std::map<int, DeviceStatusForm *>::const_iterator iter
782     = statusForms.begin();
783     for ( ; iter != statusForms.end(); ++iter) {
784     DeviceStatusForm *pStatusForm = iter->second;
785 schoenebeck 1698 m_ui.viewMidiDeviceStatusMenu->addAction(
786 capela 2038 pStatusForm->visibleAction());
787 schoenebeck 1698 }
788     }
789    
790 capela 2038
791 schoenebeck 1461 // Context menu event handler.
792     void MainForm::contextMenuEvent( QContextMenuEvent *pEvent )
793     {
794 capela 1509 stabilizeForm();
795 schoenebeck 1461
796 capela 1509 m_ui.editMenu->exec(pEvent->globalPos());
797 schoenebeck 1461 }
798    
799    
800     //-------------------------------------------------------------------------
801 capela 2979 // QSampler::MainForm -- Brainless public property accessors.
802 schoenebeck 1461
803     // The global options settings property.
804 capela 1558 Options *MainForm::options (void) const
805 schoenebeck 1461 {
806 capela 1509 return m_pOptions;
807 schoenebeck 1461 }
808    
809    
810     // The LSCP client descriptor property.
811 capela 1509 lscp_client_t *MainForm::client (void) const
812 schoenebeck 1461 {
813 capela 1509 return m_pClient;
814 schoenebeck 1461 }
815    
816    
817     // The pseudo-singleton instance accessor.
818     MainForm *MainForm::getInstance (void)
819     {
820     return g_pMainForm;
821     }
822    
823    
824     //-------------------------------------------------------------------------
825 capela 2979 // QSampler::MainForm -- Session file stuff.
826 schoenebeck 1461
827     // Format the displayable session filename.
828     QString MainForm::sessionName ( const QString& sFilename )
829     {
830 capela 2978 const bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
831 capela 1509 QString sSessionName = sFilename;
832     if (sSessionName.isEmpty())
833     sSessionName = tr("Untitled") + QString::number(m_iUntitled);
834     else if (!bCompletePath)
835     sSessionName = QFileInfo(sSessionName).fileName();
836     return sSessionName;
837 schoenebeck 1461 }
838    
839    
840     // Create a new session file from scratch.
841     bool MainForm::newSession (void)
842     {
843 capela 1509 // Check if we can do it.
844     if (!closeSession(true))
845     return false;
846 schoenebeck 1461
847     // Give us what the server has, right now...
848     updateSession();
849    
850 capela 1509 // Ok increment untitled count.
851     m_iUntitled++;
852 schoenebeck 1461
853 capela 1509 // Stabilize form.
854     m_sFilename = QString::null;
855     m_iDirtyCount = 0;
856     appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
857     stabilizeForm();
858 schoenebeck 1461
859 capela 1509 return true;
860 schoenebeck 1461 }
861    
862    
863     // Open an existing sampler session.
864     bool MainForm::openSession (void)
865     {
866 capela 1509 if (m_pOptions == NULL)
867     return false;
868 schoenebeck 1461
869 capela 1509 // Ask for the filename to open...
870 capela 1499 QString sFilename = QFileDialog::getOpenFileName(this,
871     QSAMPLER_TITLE ": " + tr("Open Session"), // Caption.
872     m_pOptions->sSessionDir, // Start here.
873     tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
874     );
875 schoenebeck 1461
876 capela 1509 // Have we cancelled?
877     if (sFilename.isEmpty())
878     return false;
879 schoenebeck 1461
880 capela 1509 // Check if we're going to discard safely the current one...
881     if (!closeSession(true))
882     return false;
883 schoenebeck 1461
884 capela 1509 // Load it right away.
885     return loadSessionFile(sFilename);
886 schoenebeck 1461 }
887    
888    
889     // Save current sampler session with another name.
890     bool MainForm::saveSession ( bool bPrompt )
891     {
892 capela 1509 if (m_pOptions == NULL)
893     return false;
894 schoenebeck 1461
895 capela 1509 QString sFilename = m_sFilename;
896 schoenebeck 1461
897 capela 1509 // Ask for the file to save, if there's none...
898     if (bPrompt || sFilename.isEmpty()) {
899     // If none is given, assume default directory.
900     if (sFilename.isEmpty())
901     sFilename = m_pOptions->sSessionDir;
902     // Prompt the guy...
903 capela 1499 sFilename = QFileDialog::getSaveFileName(this,
904     QSAMPLER_TITLE ": " + tr("Save Session"), // Caption.
905     sFilename, // Start here.
906     tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files)
907     );
908 capela 1509 // Have we cancelled it?
909     if (sFilename.isEmpty())
910     return false;
911     // Enforce .lscp extension...
912     if (QFileInfo(sFilename).suffix().isEmpty())
913     sFilename += ".lscp";
914 capela 3437 #if 0
915 capela 1509 // Check if already exists...
916     if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
917     if (QMessageBox::warning(this,
918 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
919 capela 1509 tr("The file already exists:\n\n"
920     "\"%1\"\n\n"
921     "Do you want to replace it?")
922     .arg(sFilename),
923 capela 1840 QMessageBox::Yes | QMessageBox::No)
924     == QMessageBox::No)
925 capela 1509 return false;
926     }
927 capela 3437 #endif
928 capela 1509 }
929 schoenebeck 1461
930 capela 1509 // Save it right away.
931     return saveSessionFile(sFilename);
932 schoenebeck 1461 }
933    
934    
935     // Close current session.
936     bool MainForm::closeSession ( bool bForce )
937     {
938 capela 1509 bool bClose = true;
939 schoenebeck 1461
940 capela 1509 // Are we dirty enough to prompt it?
941     if (m_iDirtyCount > 0) {
942     switch (QMessageBox::warning(this,
943 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Warning"),
944 capela 1509 tr("The current session has been changed:\n\n"
945     "\"%1\"\n\n"
946     "Do you want to save the changes?")
947     .arg(sessionName(m_sFilename)),
948 capela 1840 QMessageBox::Save |
949     QMessageBox::Discard |
950     QMessageBox::Cancel)) {
951     case QMessageBox::Save:
952 capela 1509 bClose = saveSession(false);
953     // Fall thru....
954 capela 1840 case QMessageBox::Discard:
955 capela 1509 break;
956     default: // Cancel.
957     bClose = false;
958     break;
959     }
960     }
961 schoenebeck 1461
962 capela 1509 // If we may close it, dot it.
963     if (bClose) {
964     // Remove all channel strips from sight...
965     m_pWorkspace->setUpdatesEnabled(false);
966 capela 2978 const QList<QMdiSubWindow *>& wlist
967     = m_pWorkspace->subWindowList();
968     const int iStripCount = wlist.count();
969     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
970 capela 2387 ChannelStrip *pChannelStrip = NULL;
971 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
972 capela 2387 if (pMdiSubWindow)
973     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
974 capela 1509 if (pChannelStrip) {
975 capela 1558 Channel *pChannel = pChannelStrip->channel();
976 capela 1509 if (bForce && pChannel)
977     pChannel->removeChannel();
978     delete pChannelStrip;
979     }
980 capela 2441 if (pMdiSubWindow)
981     delete pMdiSubWindow;
982 capela 1509 }
983     m_pWorkspace->setUpdatesEnabled(true);
984     // We're now clean, for sure.
985     m_iDirtyCount = 0;
986     }
987 schoenebeck 1461
988 capela 1509 return bClose;
989 schoenebeck 1461 }
990    
991    
992     // Load a session from specific file path.
993     bool MainForm::loadSessionFile ( const QString& sFilename )
994     {
995 capela 1509 if (m_pClient == NULL)
996     return false;
997 schoenebeck 1461
998 capela 1509 // Open and read from real file.
999     QFile file(sFilename);
1000     if (!file.open(QIODevice::ReadOnly)) {
1001     appendMessagesError(
1002     tr("Could not open \"%1\" session file.\n\nSorry.")
1003     .arg(sFilename));
1004     return false;
1005     }
1006 schoenebeck 1461
1007     // Tell the world we'll take some time...
1008     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1009    
1010 capela 1509 // Read the file.
1011 schoenebeck 1461 int iLine = 0;
1012 capela 1509 int iErrors = 0;
1013     QTextStream ts(&file);
1014     while (!ts.atEnd()) {
1015     // Read the line.
1016     QString sCommand = ts.readLine().trimmed();
1017 schoenebeck 1461 iLine++;
1018 capela 1509 // If not empty, nor a comment, call the server...
1019     if (!sCommand.isEmpty() && sCommand[0] != '#') {
1020 schoenebeck 1461 // Remember that, no matter what,
1021     // all LSCP commands are CR/LF terminated.
1022     sCommand += "\r\n";
1023 capela 1499 if (::lscp_client_query(m_pClient, sCommand.toUtf8().constData())
1024     != LSCP_OK) {
1025 schoenebeck 1461 appendMessagesColor(QString("%1(%2): %3")
1026     .arg(QFileInfo(sFilename).fileName()).arg(iLine)
1027 capela 1499 .arg(sCommand.simplified()), "#996633");
1028 schoenebeck 1461 appendMessagesClient("lscp_client_query");
1029     iErrors++;
1030     }
1031 capela 1509 }
1032     // Try to make it snappy :)
1033     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1034     }
1035 schoenebeck 1461
1036 capela 1509 // Ok. we've read it.
1037     file.close();
1038 schoenebeck 1461
1039     // Now we'll try to create (update) the whole GUI session.
1040     updateSession();
1041    
1042     // We're fornerly done.
1043     QApplication::restoreOverrideCursor();
1044    
1045     // Have we any errors?
1046 capela 1509 if (iErrors > 0) {
1047     appendMessagesError(
1048     tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.")
1049     .arg(sFilename));
1050     }
1051 schoenebeck 1461
1052 capela 1509 // Save as default session directory.
1053     if (m_pOptions)
1054     m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
1055 schoenebeck 1461 // We're not dirty anymore, if loaded without errors,
1056     m_iDirtyCount = iErrors;
1057 capela 1509 // Stabilize form...
1058     m_sFilename = sFilename;
1059     updateRecentFiles(sFilename);
1060     appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
1061 schoenebeck 1461
1062 capela 1509 // Make that an overall update.
1063     stabilizeForm();
1064     return true;
1065 schoenebeck 1461 }
1066    
1067    
1068     // Save current session to specific file path.
1069     bool MainForm::saveSessionFile ( const QString& sFilename )
1070     {
1071     if (m_pClient == NULL)
1072     return false;
1073    
1074     // Check whether server is apparently OK...
1075     if (::lscp_get_channels(m_pClient) < 0) {
1076     appendMessagesClient("lscp_get_channels");
1077     return false;
1078     }
1079    
1080 capela 1509 // Open and write into real file.
1081     QFile file(sFilename);
1082     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
1083     appendMessagesError(
1084     tr("Could not open \"%1\" session file.\n\nSorry.")
1085     .arg(sFilename));
1086     return false;
1087     }
1088 schoenebeck 1461
1089     // Tell the world we'll take some time...
1090     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1091    
1092 capela 1509 // Write the file.
1093 capela 2978 int iErrors = 0;
1094 capela 1509 QTextStream ts(&file);
1095     ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
1096 capela 3049 ts << "# " << tr("Version") << ": " CONFIG_BUILD_VERSION << endl;
1097     // ts << "# " << tr("Build") << ": " CONFIG_BUILD_DATE << endl;
1098 capela 1509 ts << "#" << endl;
1099     ts << "# " << tr("File")
1100     << ": " << QFileInfo(sFilename).fileName() << endl;
1101     ts << "# " << tr("Date")
1102     << ": " << QDate::currentDate().toString("MMM dd yyyy")
1103     << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
1104     ts << "#" << endl;
1105     ts << endl;
1106 schoenebeck 1461
1107     // It is assumed that this new kind of device+session file
1108     // will be loaded from a complete initialized server...
1109     int *piDeviceIDs;
1110 capela 3437 int i, iDevice;
1111 schoenebeck 1461 ts << "RESET" << endl;
1112    
1113     // Audio device mapping.
1114 capela 3437 QMap<int, int> audioDeviceMap; iDevice = 0;
1115 capela 1558 piDeviceIDs = Device::getDevices(m_pClient, Device::Audio);
1116 capela 3437 for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) {
1117     Device device(Device::Audio, piDeviceIDs[i]);
1118     // Avoid plug-in driver devices...
1119     if (device.driverName().toUpper() == "PLUGIN")
1120     continue;
1121     // Audio device specification...
1122 schoenebeck 1461 ts << endl;
1123     ts << "# " << device.deviceTypeName() << " " << device.driverName()
1124     << " " << tr("Device") << " " << iDevice << endl;
1125     ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
1126 capela 1558 DeviceParamMap::ConstIterator deviceParam;
1127 schoenebeck 1461 for (deviceParam = device.params().begin();
1128     deviceParam != device.params().end();
1129     ++deviceParam) {
1130 capela 1558 const DeviceParam& param = deviceParam.value();
1131 schoenebeck 1461 if (param.value.isEmpty()) ts << "# ";
1132     ts << " " << deviceParam.key() << "='" << param.value << "'";
1133     }
1134     ts << endl;
1135     // Audio channel parameters...
1136     int iPort = 0;
1137 capela 1558 QListIterator<DevicePort *> iter(device.ports());
1138 capela 1499 while (iter.hasNext()) {
1139 capela 1558 DevicePort *pPort = iter.next();
1140     DeviceParamMap::ConstIterator portParam;
1141 schoenebeck 1461 for (portParam = pPort->params().begin();
1142     portParam != pPort->params().end();
1143     ++portParam) {
1144 capela 1558 const DeviceParam& param = portParam.value();
1145 schoenebeck 1461 if (param.fix || param.value.isEmpty()) ts << "# ";
1146     ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
1147     << " " << iPort << " " << portParam.key()
1148     << "='" << param.value << "'" << endl;
1149     }
1150 capela 1499 iPort++;
1151 schoenebeck 1461 }
1152     // Audio device index/id mapping.
1153 capela 3437 audioDeviceMap.insert(device.deviceID(), iDevice++);
1154 schoenebeck 1461 // Try to keep it snappy :)
1155 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1156 schoenebeck 1461 }
1157    
1158     // MIDI device mapping.
1159 capela 3437 QMap<int, int> midiDeviceMap; iDevice = 0;
1160 capela 1558 piDeviceIDs = Device::getDevices(m_pClient, Device::Midi);
1161 capela 3437 for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) {
1162     Device device(Device::Midi, piDeviceIDs[i]);
1163     // Avoid plug-in driver devices...
1164     if (device.driverName().toUpper() == "PLUGIN")
1165     continue;
1166     // MIDI device specification...
1167 schoenebeck 1461 ts << endl;
1168     ts << "# " << device.deviceTypeName() << " " << device.driverName()
1169     << " " << tr("Device") << " " << iDevice << endl;
1170     ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
1171 capela 1558 DeviceParamMap::ConstIterator deviceParam;
1172 schoenebeck 1461 for (deviceParam = device.params().begin();
1173     deviceParam != device.params().end();
1174     ++deviceParam) {
1175 capela 1558 const DeviceParam& param = deviceParam.value();
1176 schoenebeck 1461 if (param.value.isEmpty()) ts << "# ";
1177     ts << " " << deviceParam.key() << "='" << param.value << "'";
1178     }
1179     ts << endl;
1180     // MIDI port parameters...
1181     int iPort = 0;
1182 capela 1558 QListIterator<DevicePort *> iter(device.ports());
1183 capela 1499 while (iter.hasNext()) {
1184 capela 1558 DevicePort *pPort = iter.next();
1185     DeviceParamMap::ConstIterator portParam;
1186 schoenebeck 1461 for (portParam = pPort->params().begin();
1187     portParam != pPort->params().end();
1188     ++portParam) {
1189 capela 1558 const DeviceParam& param = portParam.value();
1190 schoenebeck 1461 if (param.fix || param.value.isEmpty()) ts << "# ";
1191     ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
1192 capela 1509 << " " << iPort << " " << portParam.key()
1193     << "='" << param.value << "'" << endl;
1194 schoenebeck 1461 }
1195 capela 1499 iPort++;
1196 schoenebeck 1461 }
1197     // MIDI device index/id mapping.
1198 capela 3437 midiDeviceMap.insert(device.deviceID(), iDevice++);
1199 schoenebeck 1461 // Try to keep it snappy :)
1200 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1201 schoenebeck 1461 }
1202     ts << endl;
1203    
1204     #ifdef CONFIG_MIDI_INSTRUMENT
1205     // MIDI instrument mapping...
1206     QMap<int, int> midiInstrumentMap;
1207     int *piMaps = ::lscp_list_midi_instrument_maps(m_pClient);
1208     for (int iMap = 0; piMaps && piMaps[iMap] >= 0; iMap++) {
1209 capela 2978 const int iMidiMap = piMaps[iMap];
1210 schoenebeck 1461 const char *pszMapName
1211     = ::lscp_get_midi_instrument_map_name(m_pClient, iMidiMap);
1212     ts << "# " << tr("MIDI instrument map") << " " << iMap;
1213     if (pszMapName)
1214     ts << " - " << pszMapName;
1215     ts << endl;
1216     ts << "ADD MIDI_INSTRUMENT_MAP";
1217     if (pszMapName)
1218     ts << " '" << pszMapName << "'";
1219     ts << endl;
1220     // MIDI instrument mapping...
1221     lscp_midi_instrument_t *pInstrs
1222     = ::lscp_list_midi_instruments(m_pClient, iMidiMap);
1223     for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; iInstr++) {
1224     lscp_midi_instrument_info_t *pInstrInfo
1225     = ::lscp_get_midi_instrument_info(m_pClient, &pInstrs[iInstr]);
1226     if (pInstrInfo) {
1227     ts << "MAP MIDI_INSTRUMENT "
1228     << iMap << " "
1229     << pInstrs[iInstr].bank << " "
1230     << pInstrs[iInstr].prog << " "
1231     << pInstrInfo->engine_name << " '"
1232     << pInstrInfo->instrument_file << "' "
1233     << pInstrInfo->instrument_nr << " "
1234     << pInstrInfo->volume << " ";
1235     switch (pInstrInfo->load_mode) {
1236     case LSCP_LOAD_PERSISTENT:
1237     ts << "PERSISTENT";
1238     break;
1239     case LSCP_LOAD_ON_DEMAND_HOLD:
1240     ts << "ON_DEMAND_HOLD";
1241     break;
1242     case LSCP_LOAD_ON_DEMAND:
1243     case LSCP_LOAD_DEFAULT:
1244     default:
1245     ts << "ON_DEMAND";
1246     break;
1247     }
1248     if (pInstrInfo->name)
1249     ts << " '" << pInstrInfo->name << "'";
1250     ts << endl;
1251     } // Check for errors...
1252     else if (::lscp_client_get_errno(m_pClient)) {
1253     appendMessagesClient("lscp_get_midi_instrument_info");
1254     iErrors++;
1255     }
1256     // Try to keep it snappy :)
1257 capela 1499 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1258 schoenebeck 1461 }
1259     ts << endl;
1260     // Check for errors...
1261     if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) {
1262     appendMessagesClient("lscp_list_midi_instruments");
1263     iErrors++;
1264     }
1265     // MIDI strument index/id mapping.
1266 capela 3437 midiInstrumentMap.insert(iMidiMap, iMap);
1267 schoenebeck 1461 }
1268     // Check for errors...
1269     if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) {
1270     appendMessagesClient("lscp_list_midi_instrument_maps");
1271     iErrors++;
1272     }
1273     #endif // CONFIG_MIDI_INSTRUMENT
1274    
1275 capela 3437 // Sampler channel mapping...
1276     int iChannelID = 0;
1277 capela 2978 const QList<QMdiSubWindow *>& wlist
1278     = m_pWorkspace->subWindowList();
1279     const int iStripCount = wlist.count();
1280     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1281 capela 2387 ChannelStrip *pChannelStrip = NULL;
1282 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1283 capela 2387 if (pMdiSubWindow)
1284     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1285 capela 1509 if (pChannelStrip) {
1286 capela 1558 Channel *pChannel = pChannelStrip->channel();
1287 capela 1509 if (pChannel) {
1288 capela 3437 // Avoid "artifial" plug-in devices...
1289     const int iAudioDevice = pChannel->audioDevice();
1290     if (!audioDeviceMap.contains(iAudioDevice))
1291     continue;
1292     const int iMidiDevice = pChannel->midiDevice();
1293     if (!midiDeviceMap.contains(iMidiDevice))
1294     continue;
1295     // Go for regular, canonical devices...
1296 capela 2978 ts << "# " << tr("Channel") << " " << iChannelID << endl;
1297 capela 1509 ts << "ADD CHANNEL" << endl;
1298 schoenebeck 1461 if (audioDeviceMap.isEmpty()) {
1299 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID
1300 schoenebeck 1461 << " " << pChannel->audioDriver() << endl;
1301     } else {
1302 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannelID
1303 capela 3437 << " " << audioDeviceMap.value(iAudioDevice) << endl;
1304 schoenebeck 1461 }
1305     if (midiDeviceMap.isEmpty()) {
1306 capela 2978 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID
1307 schoenebeck 1461 << " " << pChannel->midiDriver() << endl;
1308     } else {
1309 capela 2978 ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannelID
1310 capela 3437 << " " << midiDeviceMap.value(iMidiDevice) << endl;
1311 schoenebeck 1461 }
1312 capela 2978 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID
1313 schoenebeck 1461 << " " << pChannel->midiPort() << endl;
1314 capela 2978 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " ";
1315 capela 1509 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
1316     ts << "ALL";
1317     else
1318     ts << pChannel->midiChannel();
1319     ts << endl;
1320     ts << "LOAD ENGINE " << pChannel->engineName()
1321 capela 2978 << " " << iChannelID << endl;
1322 schoenebeck 1461 if (pChannel->instrumentStatus() < 100) ts << "# ";
1323 capela 1509 ts << "LOAD INSTRUMENT NON_MODAL '"
1324     << pChannel->instrumentFile() << "' "
1325 capela 2978 << pChannel->instrumentNr() << " " << iChannelID << endl;
1326 capela 1558 ChannelRoutingMap::ConstIterator audioRoute;
1327 schoenebeck 1461 for (audioRoute = pChannel->audioRouting().begin();
1328     audioRoute != pChannel->audioRouting().end();
1329     ++audioRoute) {
1330 capela 2978 ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannelID
1331 schoenebeck 1461 << " " << audioRoute.key()
1332 capela 1499 << " " << audioRoute.value() << endl;
1333 schoenebeck 1461 }
1334 capela 2978 ts << "SET CHANNEL VOLUME " << iChannelID
1335 schoenebeck 1461 << " " << pChannel->volume() << endl;
1336     if (pChannel->channelMute())
1337 capela 2978 ts << "SET CHANNEL MUTE " << iChannelID << " 1" << endl;
1338 schoenebeck 1461 if (pChannel->channelSolo())
1339 capela 2978 ts << "SET CHANNEL SOLO " << iChannelID << " 1" << endl;
1340 capela 2441 #ifdef CONFIG_MIDI_INSTRUMENT
1341 capela 3437 const int iMidiMap = pChannel->midiMap();
1342     if (midiInstrumentMap.contains(iMidiMap)) {
1343 capela 2978 ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannelID
1344 capela 3437 << " " << midiInstrumentMap.value(iMidiMap) << endl;
1345 schoenebeck 1461 }
1346 capela 2441 #endif
1347     #ifdef CONFIG_FXSEND
1348 schoenebeck 1461 int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID);
1349     for (int iFxSend = 0;
1350     piFxSends && piFxSends[iFxSend] >= 0;
1351     iFxSend++) {
1352     lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info(
1353     m_pClient, iChannelID, piFxSends[iFxSend]);
1354     if (pFxSendInfo) {
1355 capela 2978 ts << "CREATE FX_SEND " << iChannelID
1356 schoenebeck 1461 << " " << pFxSendInfo->midi_controller;
1357     if (pFxSendInfo->name)
1358     ts << " '" << pFxSendInfo->name << "'";
1359     ts << endl;
1360     int *piRouting = pFxSendInfo->audio_routing;
1361     for (int iAudioSrc = 0;
1362     piRouting && piRouting[iAudioSrc] >= 0;
1363     iAudioSrc++) {
1364     ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL "
1365 capela 2978 << iChannelID
1366 schoenebeck 1461 << " " << iFxSend
1367     << " " << iAudioSrc
1368     << " " << piRouting[iAudioSrc] << endl;
1369     }
1370 capela 2441 #ifdef CONFIG_FXSEND_LEVEL
1371 capela 2978 ts << "SET FX_SEND LEVEL " << iChannelID
1372 schoenebeck 1461 << " " << iFxSend
1373     << " " << pFxSendInfo->level << endl;
1374 capela 2441 #endif
1375 schoenebeck 1461 } // Check for errors...
1376     else if (::lscp_client_get_errno(m_pClient)) {
1377     appendMessagesClient("lscp_get_fxsend_info");
1378     iErrors++;
1379     }
1380     }
1381 capela 2441 #endif
1382 capela 1509 ts << endl;
1383 capela 3437 // Go for next channel...
1384     ++iChannelID;
1385 capela 1509 }
1386     }
1387     // Try to keep it snappy :)
1388     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
1389     }
1390 schoenebeck 1461
1391     #ifdef CONFIG_VOLUME
1392     ts << "# " << tr("Global volume level") << endl;
1393     ts << "SET VOLUME " << ::lscp_get_volume(m_pClient) << endl;
1394     ts << endl;
1395     #endif
1396    
1397 capela 1509 // Ok. we've wrote it.
1398     file.close();
1399 schoenebeck 1461
1400     // We're fornerly done.
1401     QApplication::restoreOverrideCursor();
1402    
1403 capela 1509 // Have we any errors?
1404     if (iErrors > 0) {
1405     appendMessagesError(
1406     tr("Some settings could not be saved\n"
1407     "to \"%1\" session file.\n\nSorry.")
1408     .arg(sFilename));
1409     }
1410 schoenebeck 1461
1411 capela 1509 // Save as default session directory.
1412     if (m_pOptions)
1413     m_pOptions->sSessionDir = QFileInfo(sFilename).dir().absolutePath();
1414     // We're not dirty anymore.
1415     m_iDirtyCount = 0;
1416     // Stabilize form...
1417     m_sFilename = sFilename;
1418     updateRecentFiles(sFilename);
1419     appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
1420     stabilizeForm();
1421     return true;
1422 schoenebeck 1461 }
1423    
1424    
1425     // Session change receiver slot.
1426     void MainForm::sessionDirty (void)
1427     {
1428 capela 1509 // Just mark the dirty form.
1429     m_iDirtyCount++;
1430     // and update the form status...
1431     stabilizeForm();
1432 schoenebeck 1461 }
1433    
1434    
1435     //-------------------------------------------------------------------------
1436 capela 2979 // QSampler::MainForm -- File Action slots.
1437 schoenebeck 1461
1438     // Create a new sampler session.
1439     void MainForm::fileNew (void)
1440     {
1441 capela 1509 // Of course we'll start clean new.
1442     newSession();
1443 schoenebeck 1461 }
1444    
1445    
1446     // Open an existing sampler session.
1447     void MainForm::fileOpen (void)
1448     {
1449 capela 1509 // Open it right away.
1450     openSession();
1451 schoenebeck 1461 }
1452    
1453    
1454     // Open a recent file session.
1455 capela 1499 void MainForm::fileOpenRecent (void)
1456 schoenebeck 1461 {
1457 capela 1499 // Retrive filename index from action data...
1458     QAction *pAction = qobject_cast<QAction *> (sender());
1459     if (pAction && m_pOptions) {
1460 capela 2978 const int iIndex = pAction->data().toInt();
1461 capela 1499 if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) {
1462     QString sFilename = m_pOptions->recentFiles[iIndex];
1463     // Check if we can safely close the current session...
1464     if (!sFilename.isEmpty() && closeSession(true))
1465     loadSessionFile(sFilename);
1466     }
1467     }
1468 schoenebeck 1461 }
1469    
1470    
1471     // Save current sampler session.
1472     void MainForm::fileSave (void)
1473     {
1474 capela 1509 // Save it right away.
1475     saveSession(false);
1476 schoenebeck 1461 }
1477    
1478    
1479     // Save current sampler session with another name.
1480     void MainForm::fileSaveAs (void)
1481     {
1482 capela 1509 // Save it right away, maybe with another name.
1483     saveSession(true);
1484 schoenebeck 1461 }
1485    
1486    
1487     // Reset the sampler instance.
1488     void MainForm::fileReset (void)
1489     {
1490 capela 1509 if (m_pClient == NULL)
1491     return;
1492 schoenebeck 1461
1493 capela 1509 // Ask user whether he/she want's an internal sampler reset...
1494 capela 2722 if (m_pOptions && m_pOptions->bConfirmReset) {
1495     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1496     const QString& sText = tr(
1497     "Resetting the sampler instance will close\n"
1498     "all device and channel configurations.\n\n"
1499     "Please note that this operation may cause\n"
1500     "temporary MIDI and Audio disruption.\n\n"
1501     "Do you want to reset the sampler engine now?");
1502     #if 0
1503     if (QMessageBox::warning(this, sTitle, sText,
1504     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1505     return;
1506     #else
1507     QMessageBox mbox(this);
1508     mbox.setIcon(QMessageBox::Warning);
1509     mbox.setWindowTitle(sTitle);
1510     mbox.setText(sText);
1511     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1512     QCheckBox cbox(tr("Don't ask this again"));
1513     cbox.setChecked(false);
1514     cbox.blockSignals(true);
1515     mbox.addButton(&cbox, QMessageBox::ActionRole);
1516     if (mbox.exec() == QMessageBox::Cancel)
1517     return;
1518     if (cbox.isChecked())
1519     m_pOptions->bConfirmReset = false;
1520     #endif
1521     }
1522 schoenebeck 1461
1523     // Trye closing the current session, first...
1524     if (!closeSession(true))
1525     return;
1526    
1527 capela 1509 // Just do the reset, after closing down current session...
1528 schoenebeck 1461 // Do the actual sampler reset...
1529     if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1530     appendMessagesClient("lscp_reset_sampler");
1531     appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1532     return;
1533     }
1534    
1535 capela 1509 // Log this.
1536     appendMessages(tr("Sampler reset."));
1537 schoenebeck 1461
1538     // Make it a new session...
1539     newSession();
1540     }
1541    
1542    
1543     // Restart the client/server instance.
1544     void MainForm::fileRestart (void)
1545     {
1546 capela 1509 if (m_pOptions == NULL)
1547     return;
1548 schoenebeck 1461
1549 capela 1509 bool bRestart = true;
1550 schoenebeck 1461
1551 capela 1509 // Ask user whether he/she want's a complete restart...
1552     // (if we're currently up and running)
1553 capela 2722 if (m_pOptions && m_pOptions->bConfirmRestart) {
1554     const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1555     const QString& sText = tr(
1556     "New settings will be effective after\n"
1557 capela 1509 "restarting the client/server connection.\n\n"
1558     "Please note that this operation may cause\n"
1559     "temporary MIDI and Audio disruption.\n\n"
1560 capela 2722 "Do you want to restart the connection now?");
1561     #if 0
1562     if (QMessageBox::warning(this, sTitle, sText,
1563     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1564     bRestart = false;
1565     #else
1566     QMessageBox mbox(this);
1567     mbox.setIcon(QMessageBox::Warning);
1568     mbox.setWindowTitle(sTitle);
1569     mbox.setText(sText);
1570     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1571     QCheckBox cbox(tr("Don't ask this again"));
1572     cbox.setChecked(false);
1573     cbox.blockSignals(true);
1574     mbox.addButton(&cbox, QMessageBox::ActionRole);
1575     if (mbox.exec() == QMessageBox::Cancel)
1576     bRestart = false;
1577     else
1578     if (cbox.isChecked())
1579     m_pOptions->bConfirmRestart = false;
1580     #endif
1581 capela 1509 }
1582 schoenebeck 1461
1583 capela 1509 // Are we still for it?
1584     if (bRestart && closeSession(true)) {
1585     // Stop server, it will force the client too.
1586     stopServer();
1587     // Reschedule a restart...
1588     startSchedule(m_pOptions->iStartDelay);
1589     }
1590 schoenebeck 1461 }
1591    
1592    
1593     // Exit application program.
1594     void MainForm::fileExit (void)
1595     {
1596 capela 1509 // Go for close the whole thing.
1597     close();
1598 schoenebeck 1461 }
1599    
1600    
1601     //-------------------------------------------------------------------------
1602 capela 2979 // QSampler::MainForm -- Edit Action slots.
1603 schoenebeck 1461
1604     // Add a new sampler channel.
1605     void MainForm::editAddChannel (void)
1606     {
1607 capela 2978 ++m_iDirtySetup;
1608     addChannelStrip();
1609     --m_iDirtySetup;
1610     }
1611    
1612     void MainForm::addChannelStrip (void)
1613     {
1614 capela 1509 if (m_pClient == NULL)
1615     return;
1616 schoenebeck 1461
1617 capela 1509 // Just create the channel instance...
1618 capela 1558 Channel *pChannel = new Channel();
1619 capela 1509 if (pChannel == NULL)
1620     return;
1621 schoenebeck 1461
1622 capela 1509 // Before we show it up, may be we'll
1623     // better ask for some initial values?
1624     if (!pChannel->channelSetup(this)) {
1625     delete pChannel;
1626     return;
1627     }
1628 schoenebeck 1461
1629 capela 1509 // And give it to the strip...
1630     // (will own the channel instance, if successful).
1631     if (!createChannelStrip(pChannel)) {
1632     delete pChannel;
1633     return;
1634     }
1635 schoenebeck 1461
1636 capela 1515 // Do we auto-arrange?
1637 capela 2979 channelsArrangeAuto();
1638 capela 1515
1639 capela 1509 // Make that an overall update.
1640     m_iDirtyCount++;
1641     stabilizeForm();
1642 schoenebeck 1461 }
1643    
1644    
1645     // Remove current sampler channel.
1646     void MainForm::editRemoveChannel (void)
1647     {
1648 capela 2978 ++m_iDirtySetup;
1649     removeChannelStrip();
1650     --m_iDirtySetup;
1651     }
1652    
1653     void MainForm::removeChannelStrip (void)
1654     {
1655 capela 1509 if (m_pClient == NULL)
1656     return;
1657 schoenebeck 1461
1658 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1659 capela 1509 if (pChannelStrip == NULL)
1660     return;
1661 schoenebeck 1461
1662 capela 1558 Channel *pChannel = pChannelStrip->channel();
1663 capela 1509 if (pChannel == NULL)
1664     return;
1665 schoenebeck 1461
1666 capela 1509 // Prompt user if he/she's sure about this...
1667     if (m_pOptions && m_pOptions->bConfirmRemove) {
1668 capela 2722 const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning");
1669     const QString& sText = tr(
1670     "About to remove channel:\n\n"
1671 capela 1509 "%1\n\n"
1672     "Are you sure?")
1673 capela 2722 .arg(pChannelStrip->windowTitle());
1674     #if 0
1675     if (QMessageBox::warning(this, sTitle, sText,
1676     QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
1677 capela 1509 return;
1678 capela 2722 #else
1679     QMessageBox mbox(this);
1680     mbox.setIcon(QMessageBox::Warning);
1681     mbox.setWindowTitle(sTitle);
1682     mbox.setText(sText);
1683     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1684     QCheckBox cbox(tr("Don't ask this again"));
1685     cbox.setChecked(false);
1686     cbox.blockSignals(true);
1687     mbox.addButton(&cbox, QMessageBox::ActionRole);
1688     if (mbox.exec() == QMessageBox::Cancel)
1689     return;
1690     if (cbox.isChecked())
1691     m_pOptions->bConfirmRemove = false;
1692     #endif
1693 capela 1509 }
1694 schoenebeck 1461
1695 capela 1509 // Remove the existing sampler channel.
1696     if (!pChannel->removeChannel())
1697     return;
1698 schoenebeck 1461
1699 capela 2978 // Just delete the channel strip.
1700     destroyChannelStrip(pChannelStrip);
1701    
1702 capela 1509 // We'll be dirty, for sure...
1703     m_iDirtyCount++;
1704 capela 2978 stabilizeForm();
1705 schoenebeck 1461 }
1706    
1707    
1708     // Setup current sampler channel.
1709     void MainForm::editSetupChannel (void)
1710     {
1711 capela 1509 if (m_pClient == NULL)
1712     return;
1713 schoenebeck 1461
1714 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1715 capela 1509 if (pChannelStrip == NULL)
1716     return;
1717 schoenebeck 1461
1718 capela 1509 // Just invoque the channel strip procedure.
1719     pChannelStrip->channelSetup();
1720 schoenebeck 1461 }
1721    
1722    
1723     // Edit current sampler channel.
1724     void MainForm::editEditChannel (void)
1725     {
1726 capela 1509 if (m_pClient == NULL)
1727     return;
1728 schoenebeck 1461
1729 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1730 capela 1509 if (pChannelStrip == NULL)
1731     return;
1732 schoenebeck 1461
1733 capela 1509 // Just invoque the channel strip procedure.
1734     pChannelStrip->channelEdit();
1735 schoenebeck 1461 }
1736    
1737    
1738     // Reset current sampler channel.
1739     void MainForm::editResetChannel (void)
1740     {
1741 capela 1509 if (m_pClient == NULL)
1742     return;
1743 schoenebeck 1461
1744 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1745 capela 1509 if (pChannelStrip == NULL)
1746     return;
1747 schoenebeck 1461
1748 capela 1509 // Just invoque the channel strip procedure.
1749     pChannelStrip->channelReset();
1750 schoenebeck 1461 }
1751    
1752    
1753     // Reset all sampler channels.
1754     void MainForm::editResetAllChannels (void)
1755     {
1756     if (m_pClient == NULL)
1757     return;
1758    
1759     // Invoque the channel strip procedure,
1760     // for all channels out there...
1761     m_pWorkspace->setUpdatesEnabled(false);
1762 capela 2978 const QList<QMdiSubWindow *>& wlist
1763     = m_pWorkspace->subWindowList();
1764     const int iStripCount = wlist.count();
1765     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1766 capela 2387 ChannelStrip *pChannelStrip = NULL;
1767 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1768 capela 2387 if (pMdiSubWindow)
1769     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
1770 schoenebeck 1461 if (pChannelStrip)
1771     pChannelStrip->channelReset();
1772     }
1773     m_pWorkspace->setUpdatesEnabled(true);
1774     }
1775    
1776    
1777     //-------------------------------------------------------------------------
1778 capela 2979 // QSampler::MainForm -- View Action slots.
1779 schoenebeck 1461
1780     // Show/hide the main program window menubar.
1781     void MainForm::viewMenubar ( bool bOn )
1782     {
1783 capela 1509 if (bOn)
1784     m_ui.MenuBar->show();
1785     else
1786     m_ui.MenuBar->hide();
1787 schoenebeck 1461 }
1788    
1789    
1790     // Show/hide the main program window toolbar.
1791     void MainForm::viewToolbar ( bool bOn )
1792     {
1793 capela 1509 if (bOn) {
1794     m_ui.fileToolbar->show();
1795     m_ui.editToolbar->show();
1796     m_ui.channelsToolbar->show();
1797     } else {
1798     m_ui.fileToolbar->hide();
1799     m_ui.editToolbar->hide();
1800     m_ui.channelsToolbar->hide();
1801     }
1802 schoenebeck 1461 }
1803    
1804    
1805     // Show/hide the main program window statusbar.
1806     void MainForm::viewStatusbar ( bool bOn )
1807     {
1808 capela 1509 if (bOn)
1809     statusBar()->show();
1810     else
1811     statusBar()->hide();
1812 schoenebeck 1461 }
1813    
1814    
1815     // Show/hide the messages window logger.
1816     void MainForm::viewMessages ( bool bOn )
1817     {
1818 capela 1509 if (bOn)
1819     m_pMessages->show();
1820     else
1821     m_pMessages->hide();
1822 schoenebeck 1461 }
1823    
1824    
1825     // Show/hide the MIDI instrument list-view form.
1826     void MainForm::viewInstruments (void)
1827     {
1828     if (m_pOptions == NULL)
1829     return;
1830    
1831     if (m_pInstrumentListForm) {
1832     m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1833     if (m_pInstrumentListForm->isVisible()) {
1834     m_pInstrumentListForm->hide();
1835     } else {
1836     m_pInstrumentListForm->show();
1837     m_pInstrumentListForm->raise();
1838 capela 1499 m_pInstrumentListForm->activateWindow();
1839 schoenebeck 1461 }
1840     }
1841     }
1842    
1843    
1844     // Show/hide the device configurator form.
1845     void MainForm::viewDevices (void)
1846     {
1847     if (m_pOptions == NULL)
1848     return;
1849    
1850     if (m_pDeviceForm) {
1851     m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1852     if (m_pDeviceForm->isVisible()) {
1853     m_pDeviceForm->hide();
1854     } else {
1855     m_pDeviceForm->show();
1856     m_pDeviceForm->raise();
1857 capela 1499 m_pDeviceForm->activateWindow();
1858 schoenebeck 1461 }
1859     }
1860     }
1861    
1862    
1863     // Show options dialog.
1864     void MainForm::viewOptions (void)
1865     {
1866 capela 1509 if (m_pOptions == NULL)
1867     return;
1868 schoenebeck 1461
1869 capela 1509 OptionsForm* pOptionsForm = new OptionsForm(this);
1870     if (pOptionsForm) {
1871     // Check out some initial nullities(tm)...
1872 capela 2387 ChannelStrip *pChannelStrip = activeChannelStrip();
1873 capela 1509 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1874     m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1875     if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1876     m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1877     // To track down deferred or immediate changes.
1878 capela 2978 const QString sOldServerHost = m_pOptions->sServerHost;
1879     const int iOldServerPort = m_pOptions->iServerPort;
1880     const int iOldServerTimeout = m_pOptions->iServerTimeout;
1881     const bool bOldServerStart = m_pOptions->bServerStart;
1882     const QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1883     const bool bOldMessagesLog = m_pOptions->bMessagesLog;
1884     const QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath;
1885     const QString sOldDisplayFont = m_pOptions->sDisplayFont;
1886     const bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1887     const int iOldMaxVolume = m_pOptions->iMaxVolume;
1888     const QString sOldMessagesFont = m_pOptions->sMessagesFont;
1889     const bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1890     const bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1891     const int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1892     const int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1893     const bool bOldCompletePath = m_pOptions->bCompletePath;
1894     const bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1895     const int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1896     const int iOldBaseFontSize = m_pOptions->iBaseFontSize;
1897 capela 1509 // Load the current setup settings.
1898     pOptionsForm->setup(m_pOptions);
1899     // Show the setup dialog...
1900     if (pOptionsForm->exec()) {
1901     // Warn if something will be only effective on next run.
1902     if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1903     (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1904     ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1905 capela 1749 (!bOldKeepOnTop && m_pOptions->bKeepOnTop) ||
1906     (iOldBaseFontSize != m_pOptions->iBaseFontSize)) {
1907 capela 1509 QMessageBox::information(this,
1908 schoenebeck 1461 QSAMPLER_TITLE ": " + tr("Information"),
1909 capela 1509 tr("Some settings may be only effective\n"
1910 capela 1840 "next time you start this program."));
1911 capela 1509 updateMessagesCapture();
1912     }
1913     // Check wheather something immediate has changed.
1914 capela 1738 if (( bOldMessagesLog && !m_pOptions->bMessagesLog) ||
1915     (!bOldMessagesLog && m_pOptions->bMessagesLog) ||
1916     (sOldMessagesLogPath != m_pOptions->sMessagesLogPath))
1917     m_pMessages->setLogging(
1918     m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath);
1919 capela 1509 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1920     (!bOldCompletePath && m_pOptions->bCompletePath) ||
1921     (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1922     updateRecentFilesMenu();
1923     if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1924     (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1925     updateInstrumentNames();
1926     if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1927     (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1928     updateDisplayEffect();
1929     if (sOldDisplayFont != m_pOptions->sDisplayFont)
1930     updateDisplayFont();
1931     if (iOldMaxVolume != m_pOptions->iMaxVolume)
1932     updateMaxVolume();
1933     if (sOldMessagesFont != m_pOptions->sMessagesFont)
1934     updateMessagesFont();
1935     if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1936     (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1937     (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1938     updateMessagesLimit();
1939     // And now the main thing, whether we'll do client/server recycling?
1940     if ((sOldServerHost != m_pOptions->sServerHost) ||
1941     (iOldServerPort != m_pOptions->iServerPort) ||
1942     (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1943     ( bOldServerStart && !m_pOptions->bServerStart) ||
1944     (!bOldServerStart && m_pOptions->bServerStart) ||
1945     (sOldServerCmdLine != m_pOptions->sServerCmdLine
1946     && m_pOptions->bServerStart))
1947     fileRestart();
1948     }
1949     // Done.
1950     delete pOptionsForm;
1951     }
1952 schoenebeck 1461
1953 capela 1509 // This makes it.
1954     stabilizeForm();
1955 schoenebeck 1461 }
1956    
1957    
1958     //-------------------------------------------------------------------------
1959 capela 2979 // QSampler::MainForm -- Channels action slots.
1960 schoenebeck 1461
1961     // Arrange channel strips.
1962     void MainForm::channelsArrange (void)
1963     {
1964 capela 1509 // Full width vertical tiling
1965 capela 2978 const QList<QMdiSubWindow *>& wlist
1966     = m_pWorkspace->subWindowList();
1967 capela 1509 if (wlist.isEmpty())
1968     return;
1969 schoenebeck 1461
1970 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
1971     int y = 0;
1972 capela 2978 const int iStripCount = wlist.count();
1973     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
1974     QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
1975     if (pMdiSubWindow) {
1976     pMdiSubWindow->adjustSize();
1977     int iWidth = m_pWorkspace->width();
1978     if (iWidth < pMdiSubWindow->width())
1979     iWidth = pMdiSubWindow->width();
1980     const int iHeight = pMdiSubWindow->frameGeometry().height();
1981     pMdiSubWindow->setGeometry(0, y, iWidth, iHeight);
1982 capela 2387 y += iHeight;
1983     }
1984 capela 1509 }
1985     m_pWorkspace->setUpdatesEnabled(true);
1986 schoenebeck 1461
1987 capela 1509 stabilizeForm();
1988 schoenebeck 1461 }
1989    
1990    
1991     // Auto-arrange channel strips.
1992     void MainForm::channelsAutoArrange ( bool bOn )
1993     {
1994 capela 1509 if (m_pOptions == NULL)
1995     return;
1996 schoenebeck 1461
1997 capela 1509 // Toggle the auto-arrange flag.
1998     m_pOptions->bAutoArrange = bOn;
1999 schoenebeck 1461
2000 capela 1509 // If on, update whole workspace...
2001 capela 2979 channelsArrangeAuto();
2002     }
2003    
2004    
2005     void MainForm::channelsArrangeAuto (void)
2006     {
2007     if (m_pOptions && m_pOptions->bAutoArrange)
2008 capela 1509 channelsArrange();
2009 schoenebeck 1461 }
2010    
2011    
2012     //-------------------------------------------------------------------------
2013 capela 2979 // QSampler::MainForm -- Help Action slots.
2014 schoenebeck 1461
2015     // Show information about the Qt toolkit.
2016     void MainForm::helpAboutQt (void)
2017     {
2018 capela 1509 QMessageBox::aboutQt(this);
2019 schoenebeck 1461 }
2020    
2021    
2022     // Show information about application program.
2023     void MainForm::helpAbout (void)
2024     {
2025 capela 3049 QStringList list;
2026 schoenebeck 1461 #ifdef CONFIG_DEBUG
2027 capela 3049 list << tr("Debugging option enabled.");
2028 schoenebeck 1461 #endif
2029     #ifndef CONFIG_LIBGIG
2030 capela 3049 list << tr("GIG (libgig) file support disabled.");
2031 schoenebeck 1461 #endif
2032     #ifndef CONFIG_INSTRUMENT_NAME
2033 capela 3049 list << tr("LSCP (liblscp) instrument_name support disabled.");
2034 schoenebeck 1461 #endif
2035     #ifndef CONFIG_MUTE_SOLO
2036 capela 3049 list << tr("Sampler channel Mute/Solo support disabled.");
2037 schoenebeck 1461 #endif
2038     #ifndef CONFIG_AUDIO_ROUTING
2039 capela 3049 list << tr("LSCP (liblscp) audio_routing support disabled.");
2040 schoenebeck 1461 #endif
2041     #ifndef CONFIG_FXSEND
2042 capela 3049 list << tr("Sampler channel Effect Sends support disabled.");
2043 schoenebeck 1461 #endif
2044     #ifndef CONFIG_VOLUME
2045 capela 3049 list << tr("Global volume support disabled.");
2046 schoenebeck 1461 #endif
2047     #ifndef CONFIG_MIDI_INSTRUMENT
2048 capela 3049 list << tr("MIDI instrument mapping support disabled.");
2049 schoenebeck 1461 #endif
2050     #ifndef CONFIG_EDIT_INSTRUMENT
2051 capela 3049 list << tr("Instrument editing support disabled.");
2052 schoenebeck 1461 #endif
2053 capela 1815 #ifndef CONFIG_EVENT_CHANNEL_MIDI
2054 capela 3049 list << tr("Channel MIDI event support disabled.");
2055 capela 1815 #endif
2056     #ifndef CONFIG_EVENT_DEVICE_MIDI
2057 capela 3049 list << tr("Device MIDI event support disabled.");
2058 capela 1815 #endif
2059     #ifndef CONFIG_MAX_VOICES
2060 capela 3049 list << tr("Runtime max. voices / disk streams support disabled.");
2061 capela 1815 #endif
2062 capela 3049
2063     // Stuff the about box text...
2064     QString sText = "<p>\n";
2065     sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
2066 capela 1509 sText += "<br />\n";
2067 capela 3049 sText += tr("Version") + ": <b>" CONFIG_BUILD_VERSION "</b><br />\n";
2068     // sText += "<small>" + tr("Build") + ": " CONFIG_BUILD_DATE "</small><br />\n";
2069     if (!list.isEmpty()) {
2070     sText += "<small><font color=\"red\">";
2071     sText += list.join("<br />\n");
2072     sText += "</font></small>";
2073     }
2074     sText += "<br />\n";
2075 capela 1509 sText += tr("Using") + ": ";
2076     sText += ::lscp_client_package();
2077     sText += " ";
2078     sText += ::lscp_client_version();
2079 schoenebeck 1461 #ifdef CONFIG_LIBGIG
2080 capela 1509 sText += ", ";
2081     sText += gig::libraryName().c_str();
2082     sText += " ";
2083     sText += gig::libraryVersion().c_str();
2084 schoenebeck 1461 #endif
2085 capela 1509 sText += "<br />\n";
2086     sText += "<br />\n";
2087     sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
2088     sText += "<br />\n";
2089     sText += "<small>";
2090     sText += QSAMPLER_COPYRIGHT "<br />\n";
2091     sText += QSAMPLER_COPYRIGHT2 "<br />\n";
2092     sText += "<br />\n";
2093     sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
2094     sText += tr("under the terms of the GNU General Public License version 2 or later.");
2095     sText += "</small>";
2096     sText += "</p>\n";
2097 schoenebeck 1461
2098 capela 1509 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
2099 schoenebeck 1461 }
2100    
2101    
2102     //-------------------------------------------------------------------------
2103 capela 2979 // QSampler::MainForm -- Main window stabilization.
2104 schoenebeck 1461
2105     void MainForm::stabilizeForm (void)
2106     {
2107 capela 1509 // Update the main application caption...
2108     QString sSessionName = sessionName(m_sFilename);
2109     if (m_iDirtyCount > 0)
2110     sSessionName += " *";
2111     setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
2112 schoenebeck 1461
2113 capela 1509 // Update the main menu state...
2114 capela 2038 ChannelStrip *pChannelStrip = activeChannelStrip();
2115 capela 2978 const QList<QMdiSubWindow *>& wlist = m_pWorkspace->subWindowList();
2116     const bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
2117     const bool bHasChannel = (bHasClient && pChannelStrip != NULL);
2118     const bool bHasChannels = (bHasClient && wlist.count() > 0);
2119 capela 1509 m_ui.fileNewAction->setEnabled(bHasClient);
2120     m_ui.fileOpenAction->setEnabled(bHasClient);
2121     m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
2122     m_ui.fileSaveAsAction->setEnabled(bHasClient);
2123     m_ui.fileResetAction->setEnabled(bHasClient);
2124     m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
2125     m_ui.editAddChannelAction->setEnabled(bHasClient);
2126     m_ui.editRemoveChannelAction->setEnabled(bHasChannel);
2127     m_ui.editSetupChannelAction->setEnabled(bHasChannel);
2128 schoenebeck 1461 #ifdef CONFIG_EDIT_INSTRUMENT
2129 capela 1509 m_ui.editEditChannelAction->setEnabled(bHasChannel);
2130 schoenebeck 1461 #else
2131 capela 1509 m_ui.editEditChannelAction->setEnabled(false);
2132 schoenebeck 1461 #endif
2133 capela 1509 m_ui.editResetChannelAction->setEnabled(bHasChannel);
2134 capela 2038 m_ui.editResetAllChannelsAction->setEnabled(bHasChannels);
2135 capela 1509 m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible());
2136 schoenebeck 1461 #ifdef CONFIG_MIDI_INSTRUMENT
2137 capela 1509 m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm
2138 schoenebeck 1461 && m_pInstrumentListForm->isVisible());
2139 capela 1509 m_ui.viewInstrumentsAction->setEnabled(bHasClient);
2140 schoenebeck 1461 #else
2141 capela 1509 m_ui.viewInstrumentsAction->setEnabled(false);
2142 schoenebeck 1461 #endif
2143 capela 1509 m_ui.viewDevicesAction->setChecked(m_pDeviceForm
2144 schoenebeck 1461 && m_pDeviceForm->isVisible());
2145 capela 1509 m_ui.viewDevicesAction->setEnabled(bHasClient);
2146 capela 2038 m_ui.viewMidiDeviceStatusMenu->setEnabled(
2147     DeviceStatusForm::getInstances().size() > 0);
2148     m_ui.channelsArrangeAction->setEnabled(bHasChannels);
2149 schoenebeck 1461
2150     #ifdef CONFIG_VOLUME
2151     // Toolbar widgets are also affected...
2152 capela 1509 m_pVolumeSlider->setEnabled(bHasClient);
2153     m_pVolumeSpinBox->setEnabled(bHasClient);
2154 schoenebeck 1461 #endif
2155    
2156 capela 1509 // Client/Server status...
2157     if (bHasClient) {
2158     m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
2159     m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost
2160     + ':' + QString::number(m_pOptions->iServerPort));
2161     } else {
2162     m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
2163     m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
2164     }
2165     // Channel status...
2166     if (bHasChannel)
2167     m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->windowTitle());
2168     else
2169     m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
2170     // Session status...
2171     if (m_iDirtyCount > 0)
2172     m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
2173     else
2174     m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
2175 schoenebeck 1461
2176 capela 1509 // Recent files menu.
2177     m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0);
2178 schoenebeck 1461 }
2179    
2180    
2181     // Global volume change receiver slot.
2182     void MainForm::volumeChanged ( int iVolume )
2183     {
2184     #ifdef CONFIG_VOLUME
2185    
2186     if (m_iVolumeChanging > 0)
2187     return;
2188    
2189     m_iVolumeChanging++;
2190    
2191     // Update the toolbar widgets...
2192     if (m_pVolumeSlider->value() != iVolume)
2193     m_pVolumeSlider->setValue(iVolume);
2194     if (m_pVolumeSpinBox->value() != iVolume)
2195     m_pVolumeSpinBox->setValue(iVolume);
2196    
2197     // Do it as commanded...
2198 capela 2978 const float fVolume = 0.01f * float(iVolume);
2199 schoenebeck 1461 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
2200     appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
2201     else
2202     appendMessagesClient("lscp_set_volume");
2203    
2204     m_iVolumeChanging--;
2205    
2206     m_iDirtyCount++;
2207     stabilizeForm();
2208    
2209     #endif
2210     }
2211    
2212    
2213     // Channel change receiver slot.
2214 capela 2387 void MainForm::channelStripChanged ( ChannelStrip *pChannelStrip )
2215 schoenebeck 1461 {
2216     // Add this strip to the changed list...
2217 capela 1499 if (!m_changedStrips.contains(pChannelStrip)) {
2218 schoenebeck 1461 m_changedStrips.append(pChannelStrip);
2219     pChannelStrip->resetErrorCount();
2220     }
2221    
2222 capela 1509 // Just mark the dirty form.
2223     m_iDirtyCount++;
2224     // and update the form status...
2225     stabilizeForm();
2226 schoenebeck 1461 }
2227    
2228    
2229     // Grab and restore current sampler channels session.
2230     void MainForm::updateSession (void)
2231     {
2232     #ifdef CONFIG_VOLUME
2233 capela 2978 const int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
2234 schoenebeck 1461 m_iVolumeChanging++;
2235     m_pVolumeSlider->setValue(iVolume);
2236     m_pVolumeSpinBox->setValue(iVolume);
2237     m_iVolumeChanging--;
2238     #endif
2239     #ifdef CONFIG_MIDI_INSTRUMENT
2240     // FIXME: Make some room for default instrument maps...
2241 capela 2978 const int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
2242 schoenebeck 1461 if (iMaps < 0)
2243     appendMessagesClient("lscp_get_midi_instrument_maps");
2244     else if (iMaps < 1) {
2245 capela 1499 ::lscp_add_midi_instrument_map(m_pClient,
2246     tr("Chromatic").toUtf8().constData());
2247     ::lscp_add_midi_instrument_map(m_pClient,
2248     tr("Drum Kits").toUtf8().constData());
2249 schoenebeck 1461 }
2250     #endif
2251    
2252 schoenebeck 1702 updateAllChannelStrips(false);
2253    
2254     // Do we auto-arrange?
2255 capela 2979 channelsArrangeAuto();
2256 schoenebeck 1702
2257     // Remember to refresh devices and instruments...
2258     if (m_pInstrumentListForm)
2259     m_pInstrumentListForm->refreshInstruments();
2260     if (m_pDeviceForm)
2261     m_pDeviceForm->refreshDevices();
2262     }
2263    
2264 capela 2387
2265     void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips )
2266     {
2267 capela 2978 // Skip if setting up a new channel strip...
2268     if (m_iDirtySetup > 0)
2269     return;
2270    
2271 schoenebeck 1461 // Retrieve the current channel list.
2272     int *piChannelIDs = ::lscp_list_channels(m_pClient);
2273     if (piChannelIDs == NULL) {
2274     if (::lscp_client_get_errno(m_pClient)) {
2275     appendMessagesClient("lscp_list_channels");
2276 capela 1509 appendMessagesError(
2277     tr("Could not get current list of channels.\n\nSorry."));
2278 schoenebeck 1461 }
2279     } else {
2280     // Try to (re)create each channel.
2281     m_pWorkspace->setUpdatesEnabled(false);
2282 capela 2387 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2283 schoenebeck 1461 // Check if theres already a channel strip for this one...
2284     if (!channelStrip(piChannelIDs[iChannel]))
2285 capela 1558 createChannelStrip(new Channel(piChannelIDs[iChannel]));
2286 schoenebeck 1461 }
2287 schoenebeck 1702 // Do we auto-arrange?
2288 capela 2979 channelsArrangeAuto();
2289 schoenebeck 1702 // remove dead channel strips
2290     if (bRemoveDeadStrips) {
2291 capela 2978 const QList<QMdiSubWindow *>& wlist
2292     = m_pWorkspace->subWindowList();
2293     const int iStripCount = wlist.count();
2294     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2295 capela 2387 ChannelStrip *pChannelStrip = NULL;
2296 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2297 capela 2387 if (pMdiSubWindow)
2298     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2299     if (pChannelStrip) {
2300     bool bExists = false;
2301 capela 2978 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) {
2302     Channel *pChannel = pChannelStrip->channel();
2303     if (pChannel == NULL)
2304 capela 2387 break;
2305 capela 2978 if (piChannelIDs[iChannel] == pChannel->channelID()) {
2306 capela 2387 // strip exists, don't touch it
2307     bExists = true;
2308     break;
2309     }
2310 schoenebeck 1702 }
2311 capela 2387 if (!bExists)
2312     destroyChannelStrip(pChannelStrip);
2313 schoenebeck 1702 }
2314     }
2315     }
2316 schoenebeck 1461 m_pWorkspace->setUpdatesEnabled(true);
2317     }
2318 capela 2387
2319     stabilizeForm();
2320 schoenebeck 1461 }
2321    
2322 capela 2387
2323 schoenebeck 1461 // Update the recent files list and menu.
2324     void MainForm::updateRecentFiles ( const QString& sFilename )
2325     {
2326 capela 1509 if (m_pOptions == NULL)
2327     return;
2328 schoenebeck 1461
2329 capela 1509 // Remove from list if already there (avoid duplicates)
2330 capela 2978 const int iIndex = m_pOptions->recentFiles.indexOf(sFilename);
2331 capela 1509 if (iIndex >= 0)
2332     m_pOptions->recentFiles.removeAt(iIndex);
2333     // Put it to front...
2334     m_pOptions->recentFiles.push_front(sFilename);
2335 schoenebeck 1461 }
2336    
2337    
2338     // Update the recent files list and menu.
2339     void MainForm::updateRecentFilesMenu (void)
2340     {
2341 capela 1499 if (m_pOptions == NULL)
2342     return;
2343 schoenebeck 1461
2344 capela 1499 // Time to keep the list under limits.
2345     int iRecentFiles = m_pOptions->recentFiles.count();
2346     while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
2347     m_pOptions->recentFiles.pop_back();
2348     iRecentFiles--;
2349     }
2350 schoenebeck 1461
2351 capela 1499 // Rebuild the recent files menu...
2352 capela 1509 m_ui.fileOpenRecentMenu->clear();
2353 capela 1499 for (int i = 0; i < iRecentFiles; i++) {
2354     const QString& sFilename = m_pOptions->recentFiles[i];
2355     if (QFileInfo(sFilename).exists()) {
2356 capela 1509 QAction *pAction = m_ui.fileOpenRecentMenu->addAction(
2357 capela 1499 QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)),
2358     this, SLOT(fileOpenRecent()));
2359     pAction->setData(i);
2360     }
2361     }
2362 schoenebeck 1461 }
2363    
2364    
2365     // Force update of the channels instrument names mode.
2366     void MainForm::updateInstrumentNames (void)
2367     {
2368 capela 1509 // Full channel list update...
2369 capela 2978 const QList<QMdiSubWindow *>& wlist
2370     = m_pWorkspace->subWindowList();
2371 capela 1509 if (wlist.isEmpty())
2372     return;
2373 schoenebeck 1461
2374 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2375 capela 2978 const int iStripCount = wlist.count();
2376     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2377     ChannelStrip *pChannelStrip = NULL;
2378     QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2379     if (pMdiSubWindow)
2380     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2381 capela 1509 if (pChannelStrip)
2382     pChannelStrip->updateInstrumentName(true);
2383     }
2384     m_pWorkspace->setUpdatesEnabled(true);
2385 schoenebeck 1461 }
2386    
2387    
2388     // Force update of the channels display font.
2389     void MainForm::updateDisplayFont (void)
2390     {
2391 capela 1509 if (m_pOptions == NULL)
2392     return;
2393 schoenebeck 1461
2394 capela 1509 // Check if display font is legal.
2395     if (m_pOptions->sDisplayFont.isEmpty())
2396     return;
2397 capela 2978
2398 capela 1509 // Realize it.
2399     QFont font;
2400     if (!font.fromString(m_pOptions->sDisplayFont))
2401     return;
2402 schoenebeck 1461
2403 capela 1509 // Full channel list update...
2404 capela 2978 const QList<QMdiSubWindow *>& wlist
2405     = m_pWorkspace->subWindowList();
2406 capela 1509 if (wlist.isEmpty())
2407     return;
2408 schoenebeck 1461
2409 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2410 capela 2978 const int iStripCount = wlist.count();
2411     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2412 capela 2387 ChannelStrip *pChannelStrip = NULL;
2413 capela 2978 QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip);
2414 capela 2387 if (pMdiSubWindow)
2415     pChannelStrip = static_cast<ChannelStrip *> (pMdiSubWindow->widget());
2416 capela 1509 if (pChannelStrip)
2417     pChannelStrip->setDisplayFont(font);
2418     }
2419     m_pWorkspace->setUpdatesEnabled(true);
2420 schoenebeck 1461 }
2421    
2422    
2423     // Update channel strips background effect.
2424     void MainForm::updateDisplayEffect (void)
2425     {
2426 capela 1509 // Full channel list update...
2427 capela 2978 const QList<QMdiSubWindow *>& wlist
2428     = m_pWorkspace->subWindowList();
2429 capela 1509 if (wlist.isEmpty())
2430     return;
2431 schoenebeck 1461
2432 capela 1509 m_pWorkspace->setUpdatesEnabled(false);
2433 capela 2978 const int iStripCount = wlist.count();
2434     for (int iStrip = 0; iStrip < iStripCount; ++iStrip) {
2435 capela 2387 ChannelStrip *pChannelStrip = NULL;