/[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 1025 - (show annotations) (download) (as text)
Fri Jan 12 21:21:16 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 73948 byte(s)
- FX send numerical ID sequence fixed for session save.

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

  ViewVC Help
Powered by ViewVC