/[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 969 - (show annotations) (download) (as text)
Wed Dec 6 19:38:02 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 70004 byte(s)
* Fixed MIDI instrument volume setting.
* Enforcing uniqueness of bank and program key pair
  while editing the MIDI instrument map.

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

  ViewVC Help
Powered by ViewVC