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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1465 - (show annotations) (download)
Thu Nov 1 17:49:27 2007 UTC (16 years, 5 months ago) by capela
File size: 77018 byte(s)
- Qt4 migration: main toolbars and messages dock-widget fix.

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

  ViewVC Help
Powered by ViewVC