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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC