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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC