/[svn]/qsampler/trunk/src/qsamplerMainForm.ui.h
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerMainForm.ui.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1034 - (show annotations) (download) (as text)
Mon Jan 15 16:21:01 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 76852 byte(s)
* Master sampler volume slider/spinbox combo is now featured.

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

  ViewVC Help
Powered by ViewVC