/[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 980 - (show annotations) (download) (as text)
Sun Dec 17 22:29:29 2006 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 71291 byte(s)
* Revised and extended MIDI instrument mapping feature; this time
  two (2) MIDI maps are being implicitly created, ones designated
  as 'Chromatic' (0) and another as 'Drum Kits' (1), which can be
  assigned to each sampler channel. (ATTN: this commit elevates the
  requirements for liblscp >= 0.5.0, also on todays CVS and pending
  proper release very soon).

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

  ViewVC Help
Powered by ViewVC