/[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 968 - (show annotations) (download) (as text)
Tue Dec 5 12:37:06 2006 UTC (13 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 70008 byte(s)
* Fixed some compilation warnings due to suspicious type
  casting and unsused header macros.

* Changed deprecated copyright attribute to license
  and added ldconfig to post-(un)install steps
  to liblscp.spec (RPM).

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

  ViewVC Help
Powered by ViewVC