/[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 986 - (show annotations) (download) (as text)
Mon Dec 18 16:51:50 2006 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 71412 byte(s)
* Fixed session file save, correcting MIDI instrument map (re)numbering.

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

  ViewVC Help
Powered by ViewVC