/[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 1033 - (show annotations) (download) (as text)
Mon Jan 15 11:27:22 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 74104 byte(s)
* Sampler channel FX send level is now also saved.

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 // Sampler channel mapping.
900 QWidgetList wlist = m_pWorkspace->windowList();
901 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
902 qsamplerChannelStrip *pChannelStrip
903 = static_cast<qsamplerChannelStrip *> (wlist.at(iChannel));
904 if (pChannelStrip) {
905 qsamplerChannel *pChannel = pChannelStrip->channel();
906 if (pChannel) {
907 ts << "# " << tr("Channel") << " " << iChannel << endl;
908 ts << "ADD CHANNEL" << endl;
909 if (audioDeviceMap.isEmpty()) {
910 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
911 << " " << pChannel->audioDriver() << endl;
912 } else {
913 ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
914 << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
915 }
916 if (midiDeviceMap.isEmpty()) {
917 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
918 << " " << pChannel->midiDriver() << endl;
919 } else {
920 ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
921 << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
922 }
923 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
924 << " " << pChannel->midiPort() << endl;
925 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
926 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
927 ts << "ALL";
928 else
929 ts << pChannel->midiChannel();
930 ts << endl;
931 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
932 if (pChannel->instrumentStatus() < 100) ts << "# ";
933 ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' "
934 << pChannel->instrumentNr() << " " << iChannel << endl;
935 qsamplerChannelRoutingMap::ConstIterator audioRoute;
936 for (audioRoute = pChannel->audioRouting().begin();
937 audioRoute != pChannel->audioRouting().end();
938 ++audioRoute) {
939 ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannel
940 << " " << audioRoute.key()
941 << " " << audioRoute.data() << endl;
942 }
943 ts << "SET CHANNEL VOLUME " << iChannel
944 << " " << pChannel->volume() << endl;
945 if (pChannel->channelMute())
946 ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl;
947 if (pChannel->channelSolo())
948 ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl;
949 #ifdef CONFIG_MIDI_INSTRUMENT
950 if (pChannel->midiMap() >= 0) {
951 ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannel
952 << " " << midiInstrumentMap[pChannel->midiMap()] << endl;
953 }
954 #endif
955 #ifdef CONFIG_FXSEND
956 int iChannelID = pChannel->channelID();
957 int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID);
958 for (int iFxSend = 0;
959 piFxSends && piFxSends[iFxSend] >= 0;
960 iFxSend++) {
961 lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info(
962 m_pClient, iChannelID, piFxSends[iFxSend]);
963 if (pFxSendInfo) {
964 ts << "CREATE FX_SEND " << iChannel
965 << " " << pFxSendInfo->midi_controller;
966 if (pFxSendInfo->name)
967 ts << " '" << pFxSendInfo->name << "'";
968 ts << endl;
969 int *piRouting = pFxSendInfo->audio_routing;
970 for (int iAudioSrc = 0;
971 piRouting && piRouting[iAudioSrc] >= 0;
972 iAudioSrc++) {
973 ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL "
974 << iChannel
975 << " " << iFxSend
976 << " " << iAudioSrc
977 << " " << piRouting[iAudioSrc] << endl;
978 }
979 #ifdef CONFIG_FXSEND_LEVEL
980 ts << "SET FX_SEND LEVEL " << iChannel
981 << " " << iFxSend
982 << " " << pFxSendInfo->level << endl;
983 #endif
984 } // Check for errors...
985 else if (::lscp_client_get_errno(m_pClient)) {
986 appendMessagesClient("lscp_get_fxsend_info");
987 iErrors++;
988 }
989 }
990 #endif
991 ts << endl;
992 }
993 }
994 // Try to keep it snappy :)
995 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
996 }
997
998 // Ok. we've wrote it.
999 file.close();
1000
1001 // We're fornerly done.
1002 QApplication::restoreOverrideCursor();
1003
1004 // Have we any errors?
1005 if (iErrors > 0)
1006 appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
1007
1008 // Save as default session directory.
1009 if (m_pOptions)
1010 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
1011 // We're not dirty anymore.
1012 m_iDirtyCount = 0;
1013 // Stabilize form...
1014 m_sFilename = sFilename;
1015 updateRecentFiles(sFilename);
1016 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
1017 stabilizeForm();
1018 return true;
1019 }
1020
1021
1022 // Session change receiver slot.
1023 void qsamplerMainForm::sessionDirty (void)
1024 {
1025 // Just mark the dirty form.
1026 m_iDirtyCount++;
1027 // and update the form status...
1028 stabilizeForm();
1029 }
1030
1031
1032 //-------------------------------------------------------------------------
1033 // qsamplerMainForm -- File Action slots.
1034
1035 // Create a new sampler session.
1036 void qsamplerMainForm::fileNew (void)
1037 {
1038 // Of course we'll start clean new.
1039 newSession();
1040 }
1041
1042
1043 // Open an existing sampler session.
1044 void qsamplerMainForm::fileOpen (void)
1045 {
1046 // Open it right away.
1047 openSession();
1048 }
1049
1050
1051 // Open a recent file session.
1052 void qsamplerMainForm::fileOpenRecent ( int iIndex )
1053 {
1054 // Check if we can safely close the current session...
1055 if (m_pOptions && closeSession(true)) {
1056 QString sFilename = m_pOptions->recentFiles[iIndex];
1057 loadSessionFile(sFilename);
1058 }
1059 }
1060
1061
1062 // Save current sampler session.
1063 void qsamplerMainForm::fileSave (void)
1064 {
1065 // Save it right away.
1066 saveSession(false);
1067 }
1068
1069
1070 // Save current sampler session with another name.
1071 void qsamplerMainForm::fileSaveAs (void)
1072 {
1073 // Save it right away, maybe with another name.
1074 saveSession(true);
1075 }
1076
1077
1078 // Reset the sampler instance.
1079 void qsamplerMainForm::fileReset (void)
1080 {
1081 if (m_pClient == NULL)
1082 return;
1083
1084 // Ask user whether he/she want's an internal sampler reset...
1085 if (QMessageBox::warning(this,
1086 QSAMPLER_TITLE ": " + tr("Warning"),
1087 tr("Resetting the sampler instance will close\n"
1088 "all device and channel configurations.\n\n"
1089 "Please note that this operation may cause\n"
1090 "temporary MIDI and Audio disruption.\n\n"
1091 "Do you want to reset the sampler engine now?"),
1092 tr("Reset"), tr("Cancel")) > 0)
1093 return;
1094
1095 // Trye closing the current session, first...
1096 if (!closeSession(true))
1097 return;
1098
1099 // Just do the reset, after closing down current session...
1100 // Do the actual sampler reset...
1101 if (::lscp_reset_sampler(m_pClient) != LSCP_OK) {
1102 appendMessagesClient("lscp_reset_sampler");
1103 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
1104 return;
1105 }
1106
1107 // Log this.
1108 appendMessages(tr("Sampler reset."));
1109
1110 // Make it a new session...
1111 newSession();
1112 }
1113
1114
1115 // Restart the client/server instance.
1116 void qsamplerMainForm::fileRestart (void)
1117 {
1118 if (m_pOptions == NULL)
1119 return;
1120
1121 bool bRestart = true;
1122
1123 // Ask user whether he/she want's a complete restart...
1124 // (if we're currently up and running)
1125 if (bRestart && m_pClient) {
1126 bRestart = (QMessageBox::warning(this,
1127 QSAMPLER_TITLE ": " + tr("Warning"),
1128 tr("New settings will be effective after\n"
1129 "restarting the client/server connection.\n\n"
1130 "Please note that this operation may cause\n"
1131 "temporary MIDI and Audio disruption.\n\n"
1132 "Do you want to restart the connection now?"),
1133 tr("Restart"), tr("Cancel")) == 0);
1134 }
1135
1136 // Are we still for it?
1137 if (bRestart && closeSession(true)) {
1138 // Stop server, it will force the client too.
1139 stopServer();
1140 // Reschedule a restart...
1141 startSchedule(m_pOptions->iStartDelay);
1142 }
1143 }
1144
1145
1146 // Exit application program.
1147 void qsamplerMainForm::fileExit (void)
1148 {
1149 // Go for close the whole thing.
1150 close();
1151 }
1152
1153
1154 //-------------------------------------------------------------------------
1155 // qsamplerMainForm -- Edit Action slots.
1156
1157 // Add a new sampler channel.
1158 void qsamplerMainForm::editAddChannel (void)
1159 {
1160 if (m_pClient == NULL)
1161 return;
1162
1163 // Just create the channel instance...
1164 qsamplerChannel *pChannel = new qsamplerChannel();
1165 if (pChannel == NULL)
1166 return;
1167
1168 // Before we show it up, may be we'll
1169 // better ask for some initial values?
1170 if (!pChannel->channelSetup(this)) {
1171 delete pChannel;
1172 return;
1173 }
1174
1175 // And give it to the strip (will own the channel instance, if successful).
1176 if (!createChannelStrip(pChannel)) {
1177 delete pChannel;
1178 return;
1179 }
1180
1181 // Make that an overall update.
1182 m_iDirtyCount++;
1183 stabilizeForm();
1184 }
1185
1186
1187 // Remove current sampler channel.
1188 void qsamplerMainForm::editRemoveChannel (void)
1189 {
1190 if (m_pClient == NULL)
1191 return;
1192
1193 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1194 if (pChannelStrip == NULL)
1195 return;
1196
1197 qsamplerChannel *pChannel = pChannelStrip->channel();
1198 if (pChannel == NULL)
1199 return;
1200
1201 // Prompt user if he/she's sure about this...
1202 if (m_pOptions && m_pOptions->bConfirmRemove) {
1203 if (QMessageBox::warning(this,
1204 QSAMPLER_TITLE ": " + tr("Warning"),
1205 tr("About to remove channel:\n\n"
1206 "%1\n\n"
1207 "Are you sure?")
1208 .arg(pChannelStrip->caption()),
1209 tr("OK"), tr("Cancel")) > 0)
1210 return;
1211 }
1212
1213 // Remove the existing sampler channel.
1214 if (!pChannel->removeChannel())
1215 return;
1216
1217 // Just delete the channel strip.
1218 delete pChannelStrip;
1219
1220 // Do we auto-arrange?
1221 if (m_pOptions && m_pOptions->bAutoArrange)
1222 channelsArrange();
1223
1224 // We'll be dirty, for sure...
1225 m_iDirtyCount++;
1226 stabilizeForm();
1227 }
1228
1229
1230 // Setup current sampler channel.
1231 void qsamplerMainForm::editSetupChannel (void)
1232 {
1233 if (m_pClient == NULL)
1234 return;
1235
1236 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1237 if (pChannelStrip == NULL)
1238 return;
1239
1240 // Just invoque the channel strip procedure.
1241 pChannelStrip->channelSetup();
1242 }
1243
1244
1245 // Reset current sampler channel.
1246 void qsamplerMainForm::editResetChannel (void)
1247 {
1248 if (m_pClient == NULL)
1249 return;
1250
1251 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1252 if (pChannelStrip == NULL)
1253 return;
1254
1255 // Just invoque the channel strip procedure.
1256 pChannelStrip->channelReset();
1257 }
1258
1259
1260 // Reset all sampler channels.
1261 void qsamplerMainForm::editResetAllChannels (void)
1262 {
1263 if (m_pClient == NULL)
1264 return;
1265
1266 // Invoque the channel strip procedure,
1267 // for all channels out there...
1268 m_pWorkspace->setUpdatesEnabled(false);
1269 QWidgetList wlist = m_pWorkspace->windowList();
1270 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1271 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1272 if (pChannelStrip)
1273 pChannelStrip->channelReset();
1274 }
1275 m_pWorkspace->setUpdatesEnabled(true);
1276 }
1277
1278
1279 //-------------------------------------------------------------------------
1280 // qsamplerMainForm -- View Action slots.
1281
1282 // Show/hide the main program window menubar.
1283 void qsamplerMainForm::viewMenubar ( bool bOn )
1284 {
1285 if (bOn)
1286 MenuBar->show();
1287 else
1288 MenuBar->hide();
1289 }
1290
1291
1292 // Show/hide the main program window toolbar.
1293 void qsamplerMainForm::viewToolbar ( bool bOn )
1294 {
1295 if (bOn) {
1296 fileToolbar->show();
1297 editToolbar->show();
1298 channelsToolbar->show();
1299 } else {
1300 fileToolbar->hide();
1301 editToolbar->hide();
1302 channelsToolbar->hide();
1303 }
1304 }
1305
1306
1307 // Show/hide the main program window statusbar.
1308 void qsamplerMainForm::viewStatusbar ( bool bOn )
1309 {
1310 if (bOn)
1311 statusBar()->show();
1312 else
1313 statusBar()->hide();
1314 }
1315
1316
1317 // Show/hide the messages window logger.
1318 void qsamplerMainForm::viewMessages ( bool bOn )
1319 {
1320 if (bOn)
1321 m_pMessages->show();
1322 else
1323 m_pMessages->hide();
1324 }
1325
1326
1327 // Show/hide the MIDI instrument list-view form.
1328 void qsamplerMainForm::viewInstruments (void)
1329 {
1330 if (m_pOptions == NULL)
1331 return;
1332
1333 if (m_pInstrumentListForm) {
1334 m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1335 if (m_pInstrumentListForm->isVisible()) {
1336 m_pInstrumentListForm->hide();
1337 } else {
1338 m_pInstrumentListForm->show();
1339 m_pInstrumentListForm->raise();
1340 m_pInstrumentListForm->setActiveWindow();
1341 }
1342 }
1343 }
1344
1345
1346 // Show/hide the device configurator form.
1347 void qsamplerMainForm::viewDevices (void)
1348 {
1349 if (m_pOptions == NULL)
1350 return;
1351
1352 if (m_pDeviceForm) {
1353 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1354 if (m_pDeviceForm->isVisible()) {
1355 m_pDeviceForm->hide();
1356 } else {
1357 m_pDeviceForm->show();
1358 m_pDeviceForm->raise();
1359 m_pDeviceForm->setActiveWindow();
1360 }
1361 }
1362 }
1363
1364
1365 // Show options dialog.
1366 void qsamplerMainForm::viewOptions (void)
1367 {
1368 if (m_pOptions == NULL)
1369 return;
1370
1371 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1372 if (pOptionsForm) {
1373 // Check out some initial nullities(tm)...
1374 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1375 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1376 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1377 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1378 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1379 // To track down deferred or immediate changes.
1380 QString sOldServerHost = m_pOptions->sServerHost;
1381 int iOldServerPort = m_pOptions->iServerPort;
1382 int iOldServerTimeout = m_pOptions->iServerTimeout;
1383 bool bOldServerStart = m_pOptions->bServerStart;
1384 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1385 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1386 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1387 int iOldMaxVolume = m_pOptions->iMaxVolume;
1388 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1389 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1390 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1391 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1392 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1393 bool bOldCompletePath = m_pOptions->bCompletePath;
1394 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1395 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1396 // Load the current setup settings.
1397 pOptionsForm->setup(m_pOptions);
1398 // Show the setup dialog...
1399 if (pOptionsForm->exec()) {
1400 // Warn if something will be only effective on next run.
1401 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1402 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1403 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1404 (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1405 QMessageBox::information(this,
1406 QSAMPLER_TITLE ": " + tr("Information"),
1407 tr("Some settings may be only effective\n"
1408 "next time you start this program."), tr("OK"));
1409 updateMessagesCapture();
1410 }
1411 // Check wheather something immediate has changed.
1412 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1413 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1414 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1415 updateRecentFilesMenu();
1416 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1417 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1418 updateInstrumentNames();
1419 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1420 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1421 updateDisplayEffect();
1422 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1423 updateDisplayFont();
1424 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1425 updateMaxVolume();
1426 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1427 updateMessagesFont();
1428 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1429 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1430 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1431 updateMessagesLimit();
1432 // And now the main thing, whether we'll do client/server recycling?
1433 if ((sOldServerHost != m_pOptions->sServerHost) ||
1434 (iOldServerPort != m_pOptions->iServerPort) ||
1435 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1436 ( bOldServerStart && !m_pOptions->bServerStart) ||
1437 (!bOldServerStart && m_pOptions->bServerStart) ||
1438 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1439 fileRestart();
1440 }
1441 // Done.
1442 delete pOptionsForm;
1443 }
1444
1445 // This makes it.
1446 stabilizeForm();
1447 }
1448
1449
1450 //-------------------------------------------------------------------------
1451 // qsamplerMainForm -- Channels action slots.
1452
1453 // Arrange channel strips.
1454 void qsamplerMainForm::channelsArrange (void)
1455 {
1456 // Full width vertical tiling
1457 QWidgetList wlist = m_pWorkspace->windowList();
1458 if (wlist.isEmpty())
1459 return;
1460
1461 m_pWorkspace->setUpdatesEnabled(false);
1462 int y = 0;
1463 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1464 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1465 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1466 // Prevent flicker...
1467 pChannelStrip->hide();
1468 pChannelStrip->showNormal();
1469 } */
1470 pChannelStrip->adjustSize();
1471 int iWidth = m_pWorkspace->width();
1472 if (iWidth < pChannelStrip->width())
1473 iWidth = pChannelStrip->width();
1474 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1475 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1476 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1477 y += iHeight;
1478 }
1479 m_pWorkspace->setUpdatesEnabled(true);
1480
1481 stabilizeForm();
1482 }
1483
1484
1485 // Auto-arrange channel strips.
1486 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1487 {
1488 if (m_pOptions == NULL)
1489 return;
1490
1491 // Toggle the auto-arrange flag.
1492 m_pOptions->bAutoArrange = bOn;
1493
1494 // If on, update whole workspace...
1495 if (m_pOptions->bAutoArrange)
1496 channelsArrange();
1497 }
1498
1499
1500 //-------------------------------------------------------------------------
1501 // qsamplerMainForm -- Help Action slots.
1502
1503 // Show information about the Qt toolkit.
1504 void qsamplerMainForm::helpAboutQt (void)
1505 {
1506 QMessageBox::aboutQt(this);
1507 }
1508
1509
1510 // Show information about application program.
1511 void qsamplerMainForm::helpAbout (void)
1512 {
1513 // Stuff the about box text...
1514 QString sText = "<p>\n";
1515 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1516 sText += "<br />\n";
1517 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1518 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1519 #ifdef CONFIG_DEBUG
1520 sText += "<small><font color=\"red\">";
1521 sText += tr("Debugging option enabled.");
1522 sText += "</font></small><br />";
1523 #endif
1524 #ifndef CONFIG_LIBGIG
1525 sText += "<small><font color=\"red\">";
1526 sText += tr("GIG (libgig) file support disabled.");
1527 sText += "</font></small><br />";
1528 #endif
1529 #ifndef CONFIG_INSTRUMENT_NAME
1530 sText += "<small><font color=\"red\">";
1531 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1532 sText += "</font></small><br />";
1533 #endif
1534 #ifndef CONFIG_MUTE_SOLO
1535 sText += "<small><font color=\"red\">";
1536 sText += tr("Sampler channel Mute/Solo support disabled.");
1537 sText += "</font></small><br />";
1538 #endif
1539 #ifndef CONFIG_AUDIO_ROUTING
1540 sText += "<small><font color=\"red\">";
1541 sText += tr("LSCP (liblscp) audio_routing support disabled.");
1542 sText += "</font></small><br />";
1543 #endif
1544 #ifndef CONFIG_FXSEND
1545 sText += "<small><font color=\"red\">";
1546 sText += tr("Sampler channel Effect Sends support disabled.");
1547 sText += "</font></small><br />";
1548 #endif
1549 #ifndef CONFIG_MIDI_INSTRUMENT
1550 sText += "<small><font color=\"red\">";
1551 sText += tr("MIDI instrument mapping support disabled.");
1552 sText += "</font></small><br />";
1553 #endif
1554 sText += "<br />\n";
1555 sText += tr("Using") + ": ";
1556 sText += ::lscp_client_package();
1557 sText += " ";
1558 sText += ::lscp_client_version();
1559 #ifdef CONFIG_LIBGIG
1560 sText += ", ";
1561 sText += gig::libraryName().c_str();
1562 sText += " ";
1563 sText += gig::libraryVersion().c_str();
1564 #endif
1565 sText += "<br />\n";
1566 sText += "<br />\n";
1567 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1568 sText += "<br />\n";
1569 sText += "<small>";
1570 sText += QSAMPLER_COPYRIGHT "<br />\n";
1571 sText += "<br />\n";
1572 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1573 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1574 sText += "</small>";
1575 sText += "</p>\n";
1576
1577 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1578 }
1579
1580
1581 //-------------------------------------------------------------------------
1582 // qsamplerMainForm -- Main window stabilization.
1583
1584 void qsamplerMainForm::stabilizeForm (void)
1585 {
1586 // Update the main application caption...
1587 QString sSessionName = sessionName(m_sFilename);
1588 if (m_iDirtyCount > 0)
1589 sSessionName += " *";
1590 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1591
1592 // Update the main menu state...
1593 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1594 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1595 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1596 fileNewAction->setEnabled(bHasClient);
1597 fileOpenAction->setEnabled(bHasClient);
1598 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1599 fileSaveAsAction->setEnabled(bHasClient);
1600 fileResetAction->setEnabled(bHasClient);
1601 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1602 editAddChannelAction->setEnabled(bHasClient);
1603 editRemoveChannelAction->setEnabled(bHasChannel);
1604 editSetupChannelAction->setEnabled(bHasChannel);
1605 editResetChannelAction->setEnabled(bHasChannel);
1606 editResetAllChannelsAction->setEnabled(bHasChannel);
1607 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1608 #ifdef CONFIG_MIDI_INSTRUMENT
1609 viewInstrumentsAction->setOn(m_pInstrumentListForm
1610 && m_pInstrumentListForm->isVisible());
1611 viewInstrumentsAction->setEnabled(bHasClient);
1612 #endif
1613 viewDevicesAction->setOn(m_pDeviceForm
1614 && m_pDeviceForm->isVisible());
1615 viewDevicesAction->setEnabled(bHasClient);
1616 channelsArrangeAction->setEnabled(bHasChannel);
1617
1618 // Client/Server status...
1619 if (bHasClient) {
1620 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1621 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1622 } else {
1623 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1624 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1625 }
1626 // Channel status...
1627 if (bHasChannel)
1628 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1629 else
1630 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1631 // Session status...
1632 if (m_iDirtyCount > 0)
1633 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1634 else
1635 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1636
1637 // Recent files menu.
1638 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1639
1640 // Always make the latest message visible.
1641 if (m_pMessages)
1642 m_pMessages->scrollToBottom();
1643 }
1644
1645
1646 // Channel change receiver slot.
1647 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1648 {
1649 // Add this strip to the changed list...
1650 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1651 m_changedStrips.append(pChannelStrip);
1652 pChannelStrip->resetErrorCount();
1653 }
1654
1655 // Just mark the dirty form.
1656 m_iDirtyCount++;
1657 // and update the form status...
1658 stabilizeForm();
1659 }
1660
1661
1662 // Grab and restore current sampler channels session.
1663 void qsamplerMainForm::updateSession (void)
1664 {
1665 #ifdef CONFIG_MIDI_INSTRUMENT
1666 // FIXME: Make some room for default instrument maps...
1667 int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
1668 if (iMaps < 0)
1669 appendMessagesClient("lscp_get_midi_instrument_maps");
1670 else if (iMaps < 1) {
1671 ::lscp_add_midi_instrument_map(m_pClient, tr("Chromatic").latin1());
1672 ::lscp_add_midi_instrument_map(m_pClient, tr("Drum Kits").latin1());
1673 }
1674 #endif
1675
1676 // Retrieve the current channel list.
1677 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1678 if (piChannelIDs == NULL) {
1679 if (::lscp_client_get_errno(m_pClient)) {
1680 appendMessagesClient("lscp_list_channels");
1681 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1682 }
1683 } else {
1684 // Try to (re)create each channel.
1685 m_pWorkspace->setUpdatesEnabled(false);
1686 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1687 // Check if theres already a channel strip for this one...
1688 if (!channelStrip(piChannelIDs[iChannel]))
1689 createChannelStrip(new qsamplerChannel(piChannelIDs[iChannel]));
1690 }
1691 m_pWorkspace->setUpdatesEnabled(true);
1692 }
1693
1694 // Do we auto-arrange?
1695 if (m_pOptions && m_pOptions->bAutoArrange)
1696 channelsArrange();
1697
1698 // Remember to refresh devices and instruments...
1699 if (m_pInstrumentListForm)
1700 m_pInstrumentListForm->refreshInstruments();
1701 if (m_pDeviceForm)
1702 m_pDeviceForm->refreshDevices();
1703 }
1704
1705
1706 // Update the recent files list and menu.
1707 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1708 {
1709 if (m_pOptions == NULL)
1710 return;
1711
1712 // Remove from list if already there (avoid duplicates)
1713 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1714 if (iter != m_pOptions->recentFiles.end())
1715 m_pOptions->recentFiles.remove(iter);
1716 // Put it to front...
1717 m_pOptions->recentFiles.push_front(sFilename);
1718
1719 // May update the menu.
1720 updateRecentFilesMenu();
1721 }
1722
1723
1724 // Update the recent files list and menu.
1725 void qsamplerMainForm::updateRecentFilesMenu (void)
1726 {
1727 if (m_pOptions == NULL)
1728 return;
1729
1730 // Time to keep the list under limits.
1731 int iRecentFiles = m_pOptions->recentFiles.count();
1732 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1733 m_pOptions->recentFiles.pop_back();
1734 iRecentFiles--;
1735 }
1736
1737 // rebuild the recent files menu...
1738 m_pRecentFilesMenu->clear();
1739 for (int i = 0; i < iRecentFiles; i++) {
1740 const QString& sFilename = m_pOptions->recentFiles[i];
1741 if (QFileInfo(sFilename).exists()) {
1742 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1743 .arg(i + 1).arg(sessionName(sFilename)),
1744 this, SLOT(fileOpenRecent(int)), 0, i);
1745 }
1746 }
1747 }
1748
1749
1750 // Force update of the channels instrument names mode.
1751 void qsamplerMainForm::updateInstrumentNames (void)
1752 {
1753 // Full channel list update...
1754 QWidgetList wlist = m_pWorkspace->windowList();
1755 if (wlist.isEmpty())
1756 return;
1757
1758 m_pWorkspace->setUpdatesEnabled(false);
1759 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1760 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1761 if (pChannelStrip)
1762 pChannelStrip->updateInstrumentName(true);
1763 }
1764 m_pWorkspace->setUpdatesEnabled(true);
1765 }
1766
1767
1768 // Force update of the channels display font.
1769 void qsamplerMainForm::updateDisplayFont (void)
1770 {
1771 if (m_pOptions == NULL)
1772 return;
1773
1774 // Check if display font is legal.
1775 if (m_pOptions->sDisplayFont.isEmpty())
1776 return;
1777 // Realize it.
1778 QFont font;
1779 if (!font.fromString(m_pOptions->sDisplayFont))
1780 return;
1781
1782 // Full channel list update...
1783 QWidgetList wlist = m_pWorkspace->windowList();
1784 if (wlist.isEmpty())
1785 return;
1786
1787 m_pWorkspace->setUpdatesEnabled(false);
1788 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1789 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1790 if (pChannelStrip)
1791 pChannelStrip->setDisplayFont(font);
1792 }
1793 m_pWorkspace->setUpdatesEnabled(true);
1794 }
1795
1796
1797 // Update channel strips background effect.
1798 void qsamplerMainForm::updateDisplayEffect (void)
1799 {
1800 QPixmap pm;
1801 if (m_pOptions->bDisplayEffect)
1802 pm = QPixmap::fromMimeSource("displaybg1.png");
1803
1804 // Full channel list update...
1805 QWidgetList wlist = m_pWorkspace->windowList();
1806 if (wlist.isEmpty())
1807 return;
1808
1809 m_pWorkspace->setUpdatesEnabled(false);
1810 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1811 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1812 if (pChannelStrip)
1813 pChannelStrip->setDisplayBackground(pm);
1814 }
1815 m_pWorkspace->setUpdatesEnabled(true);
1816 }
1817
1818
1819 // Force update of the channels maximum volume setting.
1820 void qsamplerMainForm::updateMaxVolume (void)
1821 {
1822 if (m_pOptions == NULL)
1823 return;
1824
1825 // Full channel list update...
1826 QWidgetList wlist = m_pWorkspace->windowList();
1827 if (wlist.isEmpty())
1828 return;
1829
1830 m_pWorkspace->setUpdatesEnabled(false);
1831 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1832 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1833 if (pChannelStrip)
1834 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1835 }
1836 m_pWorkspace->setUpdatesEnabled(true);
1837 }
1838
1839
1840 //-------------------------------------------------------------------------
1841 // qsamplerMainForm -- Messages window form handlers.
1842
1843 // Messages output methods.
1844 void qsamplerMainForm::appendMessages( const QString& s )
1845 {
1846 if (m_pMessages)
1847 m_pMessages->appendMessages(s);
1848
1849 statusBar()->message(s, 3000);
1850 }
1851
1852 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1853 {
1854 if (m_pMessages)
1855 m_pMessages->appendMessagesColor(s, c);
1856
1857 statusBar()->message(s, 3000);
1858 }
1859
1860 void qsamplerMainForm::appendMessagesText( const QString& s )
1861 {
1862 if (m_pMessages)
1863 m_pMessages->appendMessagesText(s);
1864 }
1865
1866 void qsamplerMainForm::appendMessagesError( const QString& s )
1867 {
1868 if (m_pMessages)
1869 m_pMessages->show();
1870
1871 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1872
1873 // Make it look responsive...:)
1874 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1875
1876 QMessageBox::critical(this,
1877 QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
1878 }
1879
1880
1881 // This is a special message format, just for client results.
1882 void qsamplerMainForm::appendMessagesClient( const QString& s )
1883 {
1884 if (m_pClient == NULL)
1885 return;
1886
1887 appendMessagesColor(s + QString(": %1 (errno=%2)")
1888 .arg(::lscp_client_get_result(m_pClient))
1889 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1890
1891 // Make it look responsive...:)
1892 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1893 }
1894
1895
1896 // Force update of the messages font.
1897 void qsamplerMainForm::updateMessagesFont (void)
1898 {
1899 if (m_pOptions == NULL)
1900 return;
1901
1902 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1903 QFont font;
1904 if (font.fromString(m_pOptions->sMessagesFont))
1905 m_pMessages->setMessagesFont(font);
1906 }
1907 }
1908
1909
1910 // Update messages window line limit.
1911 void qsamplerMainForm::updateMessagesLimit (void)
1912 {
1913 if (m_pOptions == NULL)
1914 return;
1915
1916 if (m_pMessages) {
1917 if (m_pOptions->bMessagesLimit)
1918 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1919 else
1920 m_pMessages->setMessagesLimit(-1);
1921 }
1922 }
1923
1924
1925 // Enablement of the messages capture feature.
1926 void qsamplerMainForm::updateMessagesCapture (void)
1927 {
1928 if (m_pOptions == NULL)
1929 return;
1930
1931 if (m_pMessages)
1932 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1933 }
1934
1935
1936 //-------------------------------------------------------------------------
1937 // qsamplerMainForm -- MDI channel strip management.
1938
1939 // The channel strip creation executive.
1940 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1941 {
1942 if (m_pClient == NULL || pChannel == NULL)
1943 return NULL;
1944
1945 // Prepare for auto-arrange?
1946 qsamplerChannelStrip *pChannelStrip = NULL;
1947 int y = 0;
1948 if (m_pOptions && m_pOptions->bAutoArrange) {
1949 QWidgetList wlist = m_pWorkspace->windowList();
1950 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1951 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1952 if (pChannelStrip) {
1953 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1954 y += pChannelStrip->parentWidget()->frameGeometry().height();
1955 }
1956 }
1957 }
1958
1959 // Add a new channel itema...
1960 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1961 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1962 if (pChannelStrip == NULL)
1963 return NULL;
1964
1965 // Actual channel strip setup...
1966 pChannelStrip->setup(pChannel);
1967 QObject::connect(pChannelStrip,
1968 SIGNAL(channelChanged(qsamplerChannelStrip *)),
1969 SLOT(channelStripChanged(qsamplerChannelStrip *)));
1970 // Set some initial aesthetic options...
1971 if (m_pOptions) {
1972 // Background display effect...
1973 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1974 // We'll need a display font.
1975 QFont font;
1976 if (font.fromString(m_pOptions->sDisplayFont))
1977 pChannelStrip->setDisplayFont(font);
1978 // Maximum allowed volume setting.
1979 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1980 }
1981
1982 // Now we show up us to the world.
1983 pChannelStrip->show();
1984 // Only then, we'll auto-arrange...
1985 if (m_pOptions && m_pOptions->bAutoArrange) {
1986 int iWidth = m_pWorkspace->width();
1987 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1988 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1989 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1990 }
1991
1992 // This is pretty new, so we'll watch for it closely.
1993 channelStripChanged(pChannelStrip);
1994
1995 // Return our successful reference...
1996 return pChannelStrip;
1997 }
1998
1999
2000 // Retrieve the active channel strip.
2001 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
2002 {
2003 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
2004 }
2005
2006
2007 // Retrieve a channel strip by index.
2008 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
2009 {
2010 QWidgetList wlist = m_pWorkspace->windowList();
2011 if (wlist.isEmpty())
2012 return NULL;
2013
2014 return (qsamplerChannelStrip *) wlist.at(iChannel);
2015 }
2016
2017
2018 // Retrieve a channel strip by sampler channel id.
2019 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
2020 {
2021 QWidgetList wlist = m_pWorkspace->windowList();
2022 if (wlist.isEmpty())
2023 return NULL;
2024
2025 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2026 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
2027 if (pChannelStrip) {
2028 qsamplerChannel *pChannel = pChannelStrip->channel();
2029 if (pChannel && pChannel->channelID() == iChannelID)
2030 return pChannelStrip;
2031 }
2032 }
2033
2034 // Not found.
2035 return NULL;
2036 }
2037
2038
2039 // Construct the windows menu.
2040 void qsamplerMainForm::channelsMenuAboutToShow (void)
2041 {
2042 channelsMenu->clear();
2043 channelsArrangeAction->addTo(channelsMenu);
2044 channelsAutoArrangeAction->addTo(channelsMenu);
2045
2046 QWidgetList wlist = m_pWorkspace->windowList();
2047 if (!wlist.isEmpty()) {
2048 channelsMenu->insertSeparator();
2049 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2050 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
2051 if (pChannelStrip) {
2052 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
2053 channelsMenu->setItemParameter(iItemID, iChannel);
2054 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
2055 }
2056 }
2057 }
2058 }
2059
2060
2061 // Windows menu activation slot
2062 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
2063 {
2064 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
2065 if (pChannelStrip)
2066 pChannelStrip->showNormal();
2067 pChannelStrip->setFocus();
2068 }
2069
2070
2071 //-------------------------------------------------------------------------
2072 // qsamplerMainForm -- Timer stuff.
2073
2074 // Set the pseudo-timer delay schedule.
2075 void qsamplerMainForm::startSchedule ( int iStartDelay )
2076 {
2077 m_iStartDelay = 1 + (iStartDelay * 1000);
2078 m_iTimerDelay = 0;
2079 }
2080
2081 // Suspend the pseudo-timer delay schedule.
2082 void qsamplerMainForm::stopSchedule (void)
2083 {
2084 m_iStartDelay = 0;
2085 m_iTimerDelay = 0;
2086 }
2087
2088 // Timer slot funtion.
2089 void qsamplerMainForm::timerSlot (void)
2090 {
2091 if (m_pOptions == NULL)
2092 return;
2093
2094 // Is it the first shot on server start after a few delay?
2095 if (m_iTimerDelay < m_iStartDelay) {
2096 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2097 if (m_iTimerDelay >= m_iStartDelay) {
2098 // If we cannot start it now, maybe a lil'mo'later ;)
2099 if (!startClient()) {
2100 m_iStartDelay += m_iTimerDelay;
2101 m_iTimerDelay = 0;
2102 }
2103 }
2104 }
2105
2106 if (m_pClient) {
2107 // Update the channel information for each pending strip...
2108 if (m_changedStrips.count() > 0) {
2109 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
2110 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
2111 // If successfull, remove from pending list...
2112 if (pChannelStrip->updateChannelInfo())
2113 m_changedStrips.remove(pChannelStrip);
2114 }
2115 }
2116 // Refresh each channel usage, on each period...
2117 if (m_pOptions->bAutoRefresh) {
2118 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2119 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2120 m_iTimerSlot = 0;
2121 // Update the channel stream usage for each strip...
2122 QWidgetList wlist = m_pWorkspace->windowList();
2123 for (int iChannel = 0;
2124 iChannel < (int) wlist.count(); iChannel++) {
2125 qsamplerChannelStrip *pChannelStrip
2126 = (qsamplerChannelStrip *) wlist.at(iChannel);
2127 if (pChannelStrip && pChannelStrip->isVisible())
2128 pChannelStrip->updateChannelUsage();
2129 }
2130 }
2131 }
2132 }
2133
2134 // Register the next timer slot.
2135 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2136 }
2137
2138
2139 //-------------------------------------------------------------------------
2140 // qsamplerMainForm -- Server stuff.
2141
2142 // Start linuxsampler server...
2143 void qsamplerMainForm::startServer (void)
2144 {
2145 if (m_pOptions == NULL)
2146 return;
2147
2148 // Aren't already a client, are we?
2149 if (!m_pOptions->bServerStart || m_pClient)
2150 return;
2151
2152 // Is the server process instance still here?
2153 if (m_pServer) {
2154 switch (QMessageBox::warning(this,
2155 QSAMPLER_TITLE ": " + tr("Warning"),
2156 tr("Could not start the LinuxSampler server.\n\n"
2157 "Maybe it ss already started."),
2158 tr("Stop"), tr("Kill"), tr("Cancel"))) {
2159 case 0:
2160 m_pServer->tryTerminate();
2161 break;
2162 case 1:
2163 m_pServer->kill();
2164 break;
2165 }
2166 return;
2167 }
2168
2169 // Reset our timer counters...
2170 stopSchedule();
2171
2172 // OK. Let's build the startup process...
2173 m_pServer = new QProcess(this);
2174
2175 // Setup stdout/stderr capture...
2176 // if (m_pOptions->bStdoutCapture) {
2177 m_pServer->setCommunication(
2178 QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
2179 QObject::connect(m_pServer,
2180 SIGNAL(readyReadStdout()),
2181 SLOT(readServerStdout()));
2182 QObject::connect(m_pServer,
2183 SIGNAL(readyReadStderr()),
2184 SLOT(readServerStdout()));
2185 // }
2186 // The unforgiveable signal communication...
2187 QObject::connect(m_pServer,
2188 SIGNAL(processExited()),
2189 SLOT(processServerExit()));
2190
2191 // Build process arguments...
2192 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
2193
2194 appendMessages(tr("Server is starting..."));
2195 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2196
2197 // Go jack, go...
2198 if (!m_pServer->start()) {
2199 appendMessagesError(tr("Could not start server.\n\nSorry."));
2200 processServerExit();
2201 return;
2202 }
2203
2204 // Show startup results...
2205 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
2206
2207 // Reset (yet again) the timer counters,
2208 // but this time is deferred as the user opted.
2209 startSchedule(m_pOptions->iStartDelay);
2210 stabilizeForm();
2211 }
2212
2213
2214 // Stop linuxsampler server...
2215 void qsamplerMainForm::stopServer (void)
2216 {
2217 // Stop client code.
2218 stopClient();
2219
2220 // And try to stop server.
2221 if (m_pServer) {
2222 appendMessages(tr("Server is stopping..."));
2223 if (m_pServer->isRunning())
2224 m_pServer->tryTerminate();
2225 }
2226
2227 // Give it some time to terminate gracefully and stabilize...
2228 QTime t;
2229 t.start();
2230 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2231 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
2232
2233 // Do final processing anyway.
2234 processServerExit();
2235 }
2236
2237
2238 // Stdout handler...
2239 void qsamplerMainForm::readServerStdout (void)
2240 {
2241 if (m_pMessages)
2242 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
2243 }
2244
2245
2246 // Linuxsampler server cleanup.
2247 void qsamplerMainForm::processServerExit (void)
2248 {
2249 // Force client code cleanup.
2250 stopClient();
2251
2252 // Flush anything that maybe pending...
2253 if (m_pMessages)
2254 m_pMessages->flushStdoutBuffer();
2255
2256 if (m_pServer) {
2257 // Force final server shutdown...
2258 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
2259 if (!m_pServer->normalExit())
2260 m_pServer->kill();
2261 // Destroy it.
2262 delete m_pServer;
2263 m_pServer = NULL;
2264 }
2265
2266 // Again, make status visible stable.
2267 stabilizeForm();
2268 }
2269
2270
2271 //-------------------------------------------------------------------------
2272 // qsamplerMainForm -- Client stuff.
2273
2274 // The LSCP client callback procedure.
2275 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2276 {
2277 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
2278 if (pMainForm == NULL)
2279 return LSCP_FAILED;
2280
2281 // ATTN: DO NOT EVER call any GUI code here,
2282 // as this is run under some other thread context.
2283 // A custom event must be posted here...
2284 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2285
2286 return LSCP_OK;
2287 }
2288
2289
2290 // Start our almighty client...
2291 bool qsamplerMainForm::startClient (void)
2292 {
2293 // Have it a setup?
2294 if (m_pOptions == NULL)
2295 return false;
2296
2297 // Aren't we already started, are we?
2298 if (m_pClient)
2299 return true;
2300
2301 // Log prepare here.
2302 appendMessages(tr("Client connecting..."));
2303
2304 // Create the client handle...
2305 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2306 if (m_pClient == NULL) {
2307 // Is this the first try?
2308 // maybe we need to start a local server...
2309 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
2310 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2311 else
2312 startServer();
2313 // This is always a failure.
2314 stabilizeForm();
2315 return false;
2316 }
2317 // Just set receive timeout value, blindly.
2318 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2319 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2320
2321 // Subscribe to channel info change notifications...
2322 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2323 appendMessagesClient("lscp_client_subscribe");
2324
2325 // We may stop scheduling around.
2326 stopSchedule();
2327
2328 // We'll accept drops from now on...
2329 setAcceptDrops(true);
2330
2331 // Log success here.
2332 appendMessages(tr("Client connected."));
2333
2334 // Hard-notify instrumnet and device configuration forms,
2335 // if visible, that we're ready...
2336 if (m_pInstrumentListForm)
2337 m_pInstrumentListForm->refreshInstruments();
2338 if (m_pDeviceForm)
2339 m_pDeviceForm->refreshDevices();
2340
2341 // Is any session pending to be loaded?
2342 if (!m_pOptions->sSessionFile.isEmpty()) {
2343 // Just load the prabably startup session...
2344 if (loadSessionFile(m_pOptions->sSessionFile)) {
2345 m_pOptions->sSessionFile = QString::null;
2346 return true;
2347 }
2348 }
2349
2350 // Make a new session
2351 return newSession();
2352 }
2353
2354
2355 // Stop client...
2356 void qsamplerMainForm::stopClient (void)
2357 {
2358 if (m_pClient == NULL)
2359 return;
2360
2361 // Log prepare here.
2362 appendMessages(tr("Client disconnecting..."));
2363
2364 // Clear timer counters...
2365 stopSchedule();
2366
2367 // We'll reject drops from now on...
2368 setAcceptDrops(false);
2369
2370 // Force any channel strips around, but
2371 // but avoid removing the corresponding
2372 // channels from the back-end server.
2373 m_iDirtyCount = 0;
2374 closeSession(false);
2375
2376 // Close us as a client...
2377 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2378 ::lscp_client_destroy(m_pClient);
2379 m_pClient = NULL;
2380
2381 // Hard-notify instrumnet and device configuration forms,
2382 // if visible, that we're running out...
2383 if (m_pInstrumentListForm)
2384 m_pInstrumentListForm->refreshInstruments();
2385 if (m_pDeviceForm)
2386 m_pDeviceForm->refreshDevices();
2387
2388 // Log final here.
2389 appendMessages(tr("Client disconnected."));
2390
2391 // Make visible status.
2392 stabilizeForm();
2393 }
2394
2395
2396 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC