/[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 1013 - (show annotations) (download) (as text)
Mon Jan 8 16:52:48 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 72568 byte(s)
* Instruments window gets its own toolbar (and statusbar too);
  also introducing MIDI instrument map selection to the view;
  MIDI instrument item editing now allows changing map, bank
  or program key values (were previously disabled).

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

  ViewVC Help
Powered by ViewVC