/[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 1024 - (show annotations) (download) (as text)
Fri Jan 12 10:56:31 2007 UTC (17 years, 2 months ago) by capela
File MIME type: text/x-c++hdr
File size: 73957 byte(s)
* Initial support for sampler channel FX sends, while saving
  the session state, only at code-level.

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

  ViewVC Help
Powered by ViewVC