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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC