/[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 987 - (show annotations) (download) (as text)
Tue Dec 19 11:19:55 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 72566 byte(s)
* Revised error verbosity in general and on session load/save;
  hour-glass wait cursor is now displayed on session load/save;
  keyboard shortcuts changed on MIDI instruments view context;
  improved channel strip arrangement on session open/load;
  instrument map entry removal confirmation (as optional);
  corrected some tooltip text strings.

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

  ViewVC Help
Powered by ViewVC