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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC