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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC