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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1464 - (show annotations) (download)
Thu Nov 1 17:14:21 2007 UTC (12 years, 8 months ago) by capela
File size: 77126 byte(s)
- Qt4 migration: missing copyright headers update.

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 // Always make the latest message visible.
1738 if (m_pMessages)
1739 m_pMessages->scrollToBottom();
1740 }
1741
1742
1743 // Global volume change receiver slot.
1744 void MainForm::volumeChanged ( int iVolume )
1745 {
1746 #ifdef CONFIG_VOLUME
1747
1748 if (m_iVolumeChanging > 0)
1749 return;
1750
1751 m_iVolumeChanging++;
1752
1753 // Update the toolbar widgets...
1754 if (m_pVolumeSlider->value() != iVolume)
1755 m_pVolumeSlider->setValue(iVolume);
1756 if (m_pVolumeSpinBox->value() != iVolume)
1757 m_pVolumeSpinBox->setValue(iVolume);
1758
1759 // Do it as commanded...
1760 float fVolume = 0.01f * float(iVolume);
1761 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
1762 appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
1763 else
1764 appendMessagesClient("lscp_set_volume");
1765
1766 m_iVolumeChanging--;
1767
1768 m_iDirtyCount++;
1769 stabilizeForm();
1770
1771 #endif
1772 }
1773
1774
1775 // Channel change receiver slot.
1776 void MainForm::channelStripChanged(ChannelStrip* pChannelStrip)
1777 {
1778 // Add this strip to the changed list...
1779 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1780 m_changedStrips.append(pChannelStrip);
1781 pChannelStrip->resetErrorCount();
1782 }
1783
1784 // Just mark the dirty form.
1785 m_iDirtyCount++;
1786 // and update the form status...
1787 stabilizeForm();
1788 }
1789
1790
1791 // Grab and restore current sampler channels session.
1792 void MainForm::updateSession (void)
1793 {
1794 #ifdef CONFIG_VOLUME
1795 int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
1796 m_iVolumeChanging++;
1797 m_pVolumeSlider->setValue(iVolume);
1798 m_pVolumeSpinBox->setValue(iVolume);
1799 m_iVolumeChanging--;
1800 #endif
1801 #ifdef CONFIG_MIDI_INSTRUMENT
1802 // FIXME: Make some room for default instrument maps...
1803 int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
1804 if (iMaps < 0)
1805 appendMessagesClient("lscp_get_midi_instrument_maps");
1806 else if (iMaps < 1) {
1807 ::lscp_add_midi_instrument_map(m_pClient, tr("Chromatic").latin1());
1808 ::lscp_add_midi_instrument_map(m_pClient, tr("Drum Kits").latin1());
1809 }
1810 #endif
1811
1812 // Retrieve the current channel list.
1813 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1814 if (piChannelIDs == NULL) {
1815 if (::lscp_client_get_errno(m_pClient)) {
1816 appendMessagesClient("lscp_list_channels");
1817 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1818 }
1819 } else {
1820 // Try to (re)create each channel.
1821 m_pWorkspace->setUpdatesEnabled(false);
1822 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1823 // Check if theres already a channel strip for this one...
1824 if (!channelStrip(piChannelIDs[iChannel]))
1825 createChannelStrip(new qsamplerChannel(piChannelIDs[iChannel]));
1826 }
1827 m_pWorkspace->setUpdatesEnabled(true);
1828 }
1829
1830 // Do we auto-arrange?
1831 if (m_pOptions && m_pOptions->bAutoArrange)
1832 channelsArrange();
1833
1834 // Remember to refresh devices and instruments...
1835 if (m_pInstrumentListForm)
1836 m_pInstrumentListForm->refreshInstruments();
1837 if (m_pDeviceForm)
1838 m_pDeviceForm->refreshDevices();
1839 }
1840
1841
1842 // Update the recent files list and menu.
1843 void MainForm::updateRecentFiles ( const QString& sFilename )
1844 {
1845 if (m_pOptions == NULL)
1846 return;
1847
1848 // Remove from list if already there (avoid duplicates)
1849 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1850 if (iter != m_pOptions->recentFiles.end())
1851 m_pOptions->recentFiles.remove(iter);
1852 // Put it to front...
1853 m_pOptions->recentFiles.push_front(sFilename);
1854
1855 // May update the menu.
1856 updateRecentFilesMenu();
1857 }
1858
1859
1860 // Update the recent files list and menu.
1861 void MainForm::updateRecentFilesMenu (void)
1862 {
1863 if (m_pOptions == NULL)
1864 return;
1865
1866 // Time to keep the list under limits.
1867 int iRecentFiles = m_pOptions->recentFiles.count();
1868 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1869 m_pOptions->recentFiles.pop_back();
1870 iRecentFiles--;
1871 }
1872
1873 // rebuild the recent files menu...
1874 m_pRecentFilesMenu->clear();
1875 for (int i = 0; i < iRecentFiles; i++) {
1876 const QString& sFilename = m_pOptions->recentFiles[i];
1877 if (QFileInfo(sFilename).exists()) {
1878 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1879 .arg(i + 1).arg(sessionName(sFilename)),
1880 this, SLOT(fileOpenRecent(int)), 0, i);
1881 }
1882 }
1883 }
1884
1885
1886 // Force update of the channels instrument names mode.
1887 void MainForm::updateInstrumentNames (void)
1888 {
1889 // Full channel list update...
1890 QWidgetList wlist = m_pWorkspace->windowList();
1891 if (wlist.isEmpty())
1892 return;
1893
1894 m_pWorkspace->setUpdatesEnabled(false);
1895 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1896 ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
1897 if (pChannelStrip)
1898 pChannelStrip->updateInstrumentName(true);
1899 }
1900 m_pWorkspace->setUpdatesEnabled(true);
1901 }
1902
1903
1904 // Force update of the channels display font.
1905 void MainForm::updateDisplayFont (void)
1906 {
1907 if (m_pOptions == NULL)
1908 return;
1909
1910 // Check if display font is legal.
1911 if (m_pOptions->sDisplayFont.isEmpty())
1912 return;
1913 // Realize it.
1914 QFont font;
1915 if (!font.fromString(m_pOptions->sDisplayFont))
1916 return;
1917
1918 // Full channel list update...
1919 QWidgetList wlist = m_pWorkspace->windowList();
1920 if (wlist.isEmpty())
1921 return;
1922
1923 m_pWorkspace->setUpdatesEnabled(false);
1924 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1925 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1926 if (pChannelStrip)
1927 pChannelStrip->setDisplayFont(font);
1928 }
1929 m_pWorkspace->setUpdatesEnabled(true);
1930 }
1931
1932
1933 // Update channel strips background effect.
1934 void MainForm::updateDisplayEffect (void)
1935 {
1936 QPixmap pm;
1937 if (m_pOptions->bDisplayEffect)
1938 pm = QPixmap(":/qsampler/pixmaps/displaybg1.png");
1939
1940 // Full channel list update...
1941 QWidgetList wlist = m_pWorkspace->windowList();
1942 if (wlist.isEmpty())
1943 return;
1944
1945 m_pWorkspace->setUpdatesEnabled(false);
1946 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1947 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1948 if (pChannelStrip)
1949 pChannelStrip->setDisplayBackground(pm);
1950 }
1951 m_pWorkspace->setUpdatesEnabled(true);
1952 }
1953
1954
1955 // Force update of the channels maximum volume setting.
1956 void MainForm::updateMaxVolume (void)
1957 {
1958 if (m_pOptions == NULL)
1959 return;
1960
1961 #ifdef CONFIG_VOLUME
1962 m_iVolumeChanging++;
1963 m_pVolumeSlider->setMaxValue(m_pOptions->iMaxVolume);
1964 m_pVolumeSpinBox->setMaxValue(m_pOptions->iMaxVolume);
1965 m_iVolumeChanging--;
1966 #endif
1967
1968 // Full channel list update...
1969 QWidgetList wlist = m_pWorkspace->windowList();
1970 if (wlist.isEmpty())
1971 return;
1972
1973 m_pWorkspace->setUpdatesEnabled(false);
1974 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1975 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
1976 if (pChannelStrip)
1977 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1978 }
1979 m_pWorkspace->setUpdatesEnabled(true);
1980 }
1981
1982
1983 //-------------------------------------------------------------------------
1984 // qsamplerMainForm -- Messages window form handlers.
1985
1986 // Messages output methods.
1987 void MainForm::appendMessages( const QString& s )
1988 {
1989 if (m_pMessages)
1990 m_pMessages->appendMessages(s);
1991
1992 statusBar()->message(s, 3000);
1993 }
1994
1995 void MainForm::appendMessagesColor( const QString& s, const QString& c )
1996 {
1997 if (m_pMessages)
1998 m_pMessages->appendMessagesColor(s, c);
1999
2000 statusBar()->message(s, 3000);
2001 }
2002
2003 void MainForm::appendMessagesText( const QString& s )
2004 {
2005 if (m_pMessages)
2006 m_pMessages->appendMessagesText(s);
2007 }
2008
2009 void MainForm::appendMessagesError( const QString& s )
2010 {
2011 if (m_pMessages)
2012 m_pMessages->show();
2013
2014 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
2015
2016 // Make it look responsive...:)
2017 QApplication::processEvents(QEventLoop::ExcludeUserInput);
2018
2019 QMessageBox::critical(this,
2020 QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
2021 }
2022
2023
2024 // This is a special message format, just for client results.
2025 void MainForm::appendMessagesClient( const QString& s )
2026 {
2027 if (m_pClient == NULL)
2028 return;
2029
2030 appendMessagesColor(s + QString(": %1 (errno=%2)")
2031 .arg(::lscp_client_get_result(m_pClient))
2032 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2033
2034 // Make it look responsive...:)
2035 QApplication::processEvents(QEventLoop::ExcludeUserInput);
2036 }
2037
2038
2039 // Force update of the messages font.
2040 void MainForm::updateMessagesFont (void)
2041 {
2042 if (m_pOptions == NULL)
2043 return;
2044
2045 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2046 QFont font;
2047 if (font.fromString(m_pOptions->sMessagesFont))
2048 m_pMessages->setMessagesFont(font);
2049 }
2050 }
2051
2052
2053 // Update messages window line limit.
2054 void MainForm::updateMessagesLimit (void)
2055 {
2056 if (m_pOptions == NULL)
2057 return;
2058
2059 if (m_pMessages) {
2060 if (m_pOptions->bMessagesLimit)
2061 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2062 else
2063 m_pMessages->setMessagesLimit(-1);
2064 }
2065 }
2066
2067
2068 // Enablement of the messages capture feature.
2069 void MainForm::updateMessagesCapture (void)
2070 {
2071 if (m_pOptions == NULL)
2072 return;
2073
2074 if (m_pMessages)
2075 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2076 }
2077
2078
2079 //-------------------------------------------------------------------------
2080 // qsamplerMainForm -- MDI channel strip management.
2081
2082 // The channel strip creation executive.
2083 ChannelStrip* MainForm::createChannelStrip(qsamplerChannel* pChannel)
2084 {
2085 if (m_pClient == NULL || pChannel == NULL)
2086 return NULL;
2087
2088 // Prepare for auto-arrange?
2089 ChannelStrip* pChannelStrip = NULL;
2090 int y = 0;
2091 if (m_pOptions && m_pOptions->bAutoArrange) {
2092 QWidgetList wlist = m_pWorkspace->windowList();
2093 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2094 pChannelStrip = (ChannelStrip *) wlist.at(iChannel);
2095 if (pChannelStrip) {
2096 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
2097 y += pChannelStrip->parentWidget()->frameGeometry().height();
2098 }
2099 }
2100 }
2101
2102 // Add a new channel itema...
2103 Qt::WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
2104 pChannelStrip = new ChannelStrip(m_pWorkspace, wflags);
2105 if (pChannelStrip == NULL)
2106 return NULL;
2107
2108 // Actual channel strip setup...
2109 pChannelStrip->setup(pChannel);
2110 QObject::connect(pChannelStrip,
2111 SIGNAL(channelChanged(qsamplerChannelStrip *)),
2112 SLOT(channelStripChanged(qsamplerChannelStrip *)));
2113 // Set some initial aesthetic options...
2114 if (m_pOptions) {
2115 // Background display effect...
2116 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2117 // We'll need a display font.
2118 QFont font;
2119 if (font.fromString(m_pOptions->sDisplayFont))
2120 pChannelStrip->setDisplayFont(font);
2121 // Maximum allowed volume setting.
2122 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2123 }
2124
2125 // Now we show up us to the world.
2126 pChannelStrip->show();
2127 // Only then, we'll auto-arrange...
2128 if (m_pOptions && m_pOptions->bAutoArrange) {
2129 int iWidth = m_pWorkspace->width();
2130 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
2131 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height(); pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
2132 }
2133
2134 // This is pretty new, so we'll watch for it closely.
2135 channelStripChanged(pChannelStrip);
2136
2137 // Return our successful reference...
2138 return pChannelStrip;
2139 }
2140
2141
2142 // Retrieve the active channel strip.
2143 ChannelStrip* MainForm::activeChannelStrip (void)
2144 {
2145 return (ChannelStrip*) m_pWorkspace->activeWindow();
2146 }
2147
2148
2149 // Retrieve a channel strip by index.
2150 ChannelStrip* MainForm::channelStripAt ( int iChannel )
2151 {
2152 QWidgetList wlist = m_pWorkspace->windowList();
2153 if (wlist.isEmpty())
2154 return NULL;
2155
2156 return (ChannelStrip*) wlist.at(iChannel);
2157 }
2158
2159
2160 // Retrieve a channel strip by sampler channel id.
2161 ChannelStrip* MainForm::channelStrip ( int iChannelID )
2162 {
2163 QWidgetList wlist = m_pWorkspace->windowList();
2164 if (wlist.isEmpty())
2165 return NULL;
2166
2167 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2168 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2169 if (pChannelStrip) {
2170 qsamplerChannel *pChannel = pChannelStrip->channel();
2171 if (pChannel && pChannel->channelID() == iChannelID)
2172 return pChannelStrip;
2173 }
2174 }
2175
2176 // Not found.
2177 return NULL;
2178 }
2179
2180
2181 // Construct the windows menu.
2182 void MainForm::channelsMenuAboutToShow (void)
2183 {
2184 ui.channelsMenu->clear();
2185 ui.channelsArrangeAction->addTo(ui.channelsMenu);
2186 ui.channelsAutoArrangeAction->addTo(ui.channelsMenu);
2187
2188 QWidgetList wlist = m_pWorkspace->windowList();
2189 if (!wlist.isEmpty()) {
2190 ui.channelsMenu->insertSeparator();
2191 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2192 ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel);
2193 if (pChannelStrip) {
2194 int iItemID = ui.channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
2195 ui.channelsMenu->setItemParameter(iItemID, iChannel);
2196 ui.channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
2197 }
2198 }
2199 }
2200 }
2201
2202
2203 // Windows menu activation slot
2204 void MainForm::channelsMenuActivated ( int iChannel )
2205 {
2206 ChannelStrip* pChannelStrip = channelStripAt(iChannel);
2207 if (pChannelStrip)
2208 pChannelStrip->showNormal();
2209 pChannelStrip->setFocus();
2210 }
2211
2212
2213 //-------------------------------------------------------------------------
2214 // qsamplerMainForm -- Timer stuff.
2215
2216 // Set the pseudo-timer delay schedule.
2217 void MainForm::startSchedule ( int iStartDelay )
2218 {
2219 m_iStartDelay = 1 + (iStartDelay * 1000);
2220 m_iTimerDelay = 0;
2221 }
2222
2223 // Suspend the pseudo-timer delay schedule.
2224 void MainForm::stopSchedule (void)
2225 {
2226 m_iStartDelay = 0;
2227 m_iTimerDelay = 0;
2228 }
2229
2230 // Timer slot funtion.
2231 void MainForm::timerSlot (void)
2232 {
2233 if (m_pOptions == NULL)
2234 return;
2235
2236 // Is it the first shot on server start after a few delay?
2237 if (m_iTimerDelay < m_iStartDelay) {
2238 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2239 if (m_iTimerDelay >= m_iStartDelay) {
2240 // If we cannot start it now, maybe a lil'mo'later ;)
2241 if (!startClient()) {
2242 m_iStartDelay += m_iTimerDelay;
2243 m_iTimerDelay = 0;
2244 }
2245 }
2246 }
2247
2248 if (m_pClient) {
2249 // Update the channel information for each pending strip...
2250 if (m_changedStrips.count() > 0) {
2251 for (ChannelStrip* pChannelStrip = m_changedStrips.first();
2252 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
2253 // If successfull, remove from pending list...
2254 if (pChannelStrip->updateChannelInfo())
2255 m_changedStrips.remove(pChannelStrip);
2256 }
2257 }
2258 // Refresh each channel usage, on each period...
2259 if (m_pOptions->bAutoRefresh) {
2260 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2261 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2262 m_iTimerSlot = 0;
2263 // Update the channel stream usage for each strip...
2264 QWidgetList wlist = m_pWorkspace->windowList();
2265 for (int iChannel = 0;
2266 iChannel < (int) wlist.count(); iChannel++) {
2267 ChannelStrip* pChannelStrip
2268 = (ChannelStrip*) wlist.at(iChannel);
2269 if (pChannelStrip && pChannelStrip->isVisible())
2270 pChannelStrip->updateChannelUsage();
2271 }
2272 }
2273 }
2274 }
2275
2276 // Register the next timer slot.
2277 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2278 }
2279
2280
2281 //-------------------------------------------------------------------------
2282 // qsamplerMainForm -- Server stuff.
2283
2284 // Start linuxsampler server...
2285 void MainForm::startServer (void)
2286 {
2287 if (m_pOptions == NULL)
2288 return;
2289
2290 // Aren't already a client, are we?
2291 if (!m_pOptions->bServerStart || m_pClient)
2292 return;
2293
2294 // Is the server process instance still here?
2295 if (m_pServer) {
2296 switch (QMessageBox::warning(this,
2297 QSAMPLER_TITLE ": " + tr("Warning"),
2298 tr("Could not start the LinuxSampler server.\n\n"
2299 "Maybe it ss already started."),
2300 tr("Stop"), tr("Kill"), tr("Cancel"))) {
2301 case 0:
2302 m_pServer->terminate();
2303 break;
2304 case 1:
2305 m_pServer->kill();
2306 break;
2307 }
2308 return;
2309 }
2310
2311 // Reset our timer counters...
2312 stopSchedule();
2313
2314 // OK. Let's build the startup process...
2315 m_pServer = new QProcess(this);
2316
2317 // Setup stdout/stderr capture...
2318 // if (m_pOptions->bStdoutCapture) {
2319 //m_pServer->setProcessChannelMode(
2320 // QProcess::StandardOutput);
2321 QObject::connect(m_pServer,
2322 SIGNAL(readyReadStdout()),
2323 SLOT(readServerStdout()));
2324 QObject::connect(m_pServer,
2325 SIGNAL(readyReadStderr()),
2326 SLOT(readServerStdout()));
2327 // }
2328 // The unforgiveable signal communication...
2329 QObject::connect(m_pServer,
2330 SIGNAL(processExited()),
2331 SLOT(processServerExit()));
2332
2333 // Build process arguments...
2334 QStringList serverCmdLine = QStringList::split(' ', m_pOptions->sServerCmdLine);
2335
2336 appendMessages(tr("Server is starting..."));
2337 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2338
2339
2340
2341 const QString prog = (serverCmdLine.size() > 0) ? serverCmdLine[0] : QString();
2342 const QStringList args = serverCmdLine.mid(1);
2343
2344 // Go jack, go...
2345 m_pServer->start(prog, args);
2346 if (!m_pServer->waitForStarted()) {
2347 appendMessagesError(tr("Could not start server.\n\nSorry."));
2348 processServerExit();
2349 return;
2350 }
2351
2352 // Show startup results...
2353 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->pid()));
2354
2355 // Reset (yet again) the timer counters,
2356 // but this time is deferred as the user opted.
2357 startSchedule(m_pOptions->iStartDelay);
2358 stabilizeForm();
2359 }
2360
2361
2362 // Stop linuxsampler server...
2363 void MainForm::stopServer (void)
2364 {
2365 // Stop client code.
2366 stopClient();
2367
2368 // And try to stop server.
2369 if (m_pServer) {
2370 appendMessages(tr("Server is stopping..."));
2371 if (m_pServer->state() == QProcess::Running)
2372 m_pServer->terminate();
2373 }
2374
2375 // Give it some time to terminate gracefully and stabilize...
2376 QTime t;
2377 t.start();
2378 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2379 QApplication::processEvents(QEventLoop::ExcludeUserInput);
2380
2381 // Do final processing anyway.
2382 processServerExit();
2383 }
2384
2385
2386 // Stdout handler...
2387 void MainForm::readServerStdout (void)
2388 {
2389 if (m_pMessages)
2390 m_pMessages->appendStdoutBuffer(m_pServer->readAllStandardOutput());
2391 }
2392
2393
2394 // Linuxsampler server cleanup.
2395 void MainForm::processServerExit (void)
2396 {
2397 // Force client code cleanup.
2398 stopClient();
2399
2400 // Flush anything that maybe pending...
2401 if (m_pMessages)
2402 m_pMessages->flushStdoutBuffer();
2403
2404 if (m_pServer) {
2405 // Force final server shutdown...
2406 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
2407 m_pServer->terminate();
2408 if (!m_pServer->waitForFinished(2000))
2409 m_pServer->kill();
2410 // Destroy it.
2411 delete m_pServer;
2412 m_pServer = NULL;
2413 }
2414
2415 // Again, make status visible stable.
2416 stabilizeForm();
2417 }
2418
2419
2420 //-------------------------------------------------------------------------
2421 // qsamplerMainForm -- Client stuff.
2422
2423 // The LSCP client callback procedure.
2424 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2425 {
2426 MainForm* pMainForm = (MainForm *) pvData;
2427 if (pMainForm == NULL)
2428 return LSCP_FAILED;
2429
2430 // ATTN: DO NOT EVER call any GUI code here,
2431 // as this is run under some other thread context.
2432 // A custom event must be posted here...
2433 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2434
2435 return LSCP_OK;
2436 }
2437
2438
2439 // Start our almighty client...
2440 bool MainForm::startClient (void)
2441 {
2442 // Have it a setup?
2443 if (m_pOptions == NULL)
2444 return false;
2445
2446 // Aren't we already started, are we?
2447 if (m_pClient)
2448 return true;
2449
2450 // Log prepare here.
2451 appendMessages(tr("Client connecting..."));
2452
2453 // Create the client handle...
2454 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2455 if (m_pClient == NULL) {
2456 // Is this the first try?
2457 // maybe we need to start a local server...
2458 if ((m_pServer && m_pServer->state() == QProcess::Running) || !m_pOptions->bServerStart)
2459 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2460 else
2461 startServer();
2462 // This is always a failure.
2463 stabilizeForm();
2464 return false;
2465 }
2466 // Just set receive timeout value, blindly.
2467 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2468 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2469
2470 // Subscribe to channel info change notifications...
2471 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2472 appendMessagesClient("lscp_client_subscribe");
2473
2474 // We may stop scheduling around.
2475 stopSchedule();
2476
2477 // We'll accept drops from now on...
2478 setAcceptDrops(true);
2479
2480 // Log success here.
2481 appendMessages(tr("Client connected."));
2482
2483 // Hard-notify instrumnet and device configuration forms,
2484 // if visible, that we're ready...
2485 if (m_pInstrumentListForm)
2486 m_pInstrumentListForm->refreshInstruments();
2487 if (m_pDeviceForm)
2488 m_pDeviceForm->refreshDevices();
2489
2490 // Is any session pending to be loaded?
2491 if (!m_pOptions->sSessionFile.isEmpty()) {
2492 // Just load the prabably startup session...
2493 if (loadSessionFile(m_pOptions->sSessionFile)) {
2494 m_pOptions->sSessionFile = QString::null;
2495 return true;
2496 }
2497 }
2498
2499 // Make a new session
2500 return newSession();
2501 }
2502
2503
2504 // Stop client...
2505 void MainForm::stopClient (void)
2506 {
2507 if (m_pClient == NULL)
2508 return;
2509
2510 // Log prepare here.
2511 appendMessages(tr("Client disconnecting..."));
2512
2513 // Clear timer counters...
2514 stopSchedule();
2515
2516 // We'll reject drops from now on...
2517 setAcceptDrops(false);
2518
2519 // Force any channel strips around, but
2520 // but avoid removing the corresponding
2521 // channels from the back-end server.
2522 m_iDirtyCount = 0;
2523 closeSession(false);
2524
2525 // Close us as a client...
2526 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2527 ::lscp_client_destroy(m_pClient);
2528 m_pClient = NULL;
2529
2530 // Hard-notify instrumnet and device configuration forms,
2531 // if visible, that we're running out...
2532 if (m_pInstrumentListForm)
2533 m_pInstrumentListForm->refreshInstruments();
2534 if (m_pDeviceForm)
2535 m_pDeviceForm->refreshDevices();
2536
2537 // Log final here.
2538 appendMessages(tr("Client disconnected."));
2539
2540 // Make visible status.
2541 stabilizeForm();
2542 }
2543
2544 } // namespace QSampler
2545
2546
2547 // end of qsamplerMainForm.cpp

  ViewVC Help
Powered by ViewVC