~ubuntu-branches/ubuntu/karmic/qsampler/karmic

« back to all changes in this revision

Viewing changes to src/qsamplerMainForm.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Free Ekanayaka
  • Date: 2008-08-28 08:43:21 UTC
  • mfrom: (1.1.1 upstream) (2.1.2 hardy)
  • Revision ID: james.westby@ubuntu.com-20080828084321-guq8v04yg31co9gm
Tags: 0.2.1-1
* New upstream release
* Uploaded to Debian (Closes: #280576)

Show diffs side-by-side

added added

removed removed

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