/[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 664 - (show annotations) (download) (as text)
Sat Jun 18 22:44:56 2005 UTC (18 years, 9 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65946 byte(s)
* Minor stream/voice usage auto-refresh cycle optimization.

1 // qsamplerMainForm.ui.h
2 //
3 // ui.h extension file, included from the uic-generated form implementation.
4 /****************************************************************************
5 Copyright (C) 2004-2005, 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
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
45 #include "qsamplerDeviceForm.h"
46 #include "qsamplerOptionsForm.h"
47
48 #include "config.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 constructor.
108 void qsamplerMainForm::init (void)
109 {
110 // Initialize some pointer references.
111 m_pOptions = NULL;
112
113 // All child forms are to be created later, not earlier than setup.
114 m_pMessages = NULL;
115 m_pDeviceForm = NULL;
116
117 // We'll start clean.
118 m_iUntitled = 0;
119 m_iDirtyCount = 0;
120
121 m_pServer = NULL;
122 m_pClient = NULL;
123
124 m_iStartDelay = 0;
125 m_iTimerDelay = 0;
126
127 m_iTimerSlot = 0;
128
129 #ifdef HAVE_SIGNAL_H
130 // Set to ignore any fatal "Broken pipe" signals.
131 ::signal(SIGPIPE, SIG_IGN);
132 #endif
133
134 // Make it an MDI workspace.
135 m_pWorkspace = new QWorkspace(this);
136 m_pWorkspace->setScrollBarsEnabled(true);
137 // Set the activation connection.
138 QObject::connect(m_pWorkspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(stabilizeForm()));
139 // Make it shine :-)
140 setCentralWidget(m_pWorkspace);
141
142 // Create some statusbar labels...
143 QLabel *pLabel;
144 // Client status.
145 pLabel = new QLabel(tr("Connected"), this);
146 pLabel->setAlignment(Qt::AlignLeft);
147 pLabel->setMinimumSize(pLabel->sizeHint());
148 m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
149 statusBar()->addWidget(pLabel);
150 // Server address.
151 pLabel = new QLabel(this);
152 pLabel->setAlignment(Qt::AlignLeft);
153 m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
154 statusBar()->addWidget(pLabel, 1);
155 // Channel title.
156 pLabel = new QLabel(this);
157 pLabel->setAlignment(Qt::AlignLeft);
158 m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
159 statusBar()->addWidget(pLabel, 2);
160 // Session modification status.
161 pLabel = new QLabel(tr("MOD"), this);
162 pLabel->setAlignment(Qt::AlignHCenter);
163 pLabel->setMinimumSize(pLabel->sizeHint());
164 m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
165 statusBar()->addWidget(pLabel);
166
167 // Create the recent files sub-menu.
168 m_pRecentFilesMenu = new QPopupMenu(this);
169 fileMenu->insertSeparator(4);
170 fileMenu->insertItem(tr("Recent &Files"), m_pRecentFilesMenu, 0, 5);
171
172 #if defined(WIN32)
173 WSAStartup(MAKEWORD(1, 1), &_wsaData);
174 #endif
175 }
176
177
178 // Kind of destructor.
179 void qsamplerMainForm::destroy (void)
180 {
181 // Do final processing anyway.
182 processServerExit();
183
184 #if defined(WIN32)
185 WSACleanup();
186 #endif
187
188 // Finally drop any widgets around...
189 if (m_pDeviceForm)
190 delete m_pDeviceForm;
191 if (m_pMessages)
192 delete m_pMessages;
193 if (m_pWorkspace)
194 delete m_pWorkspace;
195
196 // Delete status item labels one by one.
197 if (m_statusItem[QSAMPLER_STATUS_CLIENT])
198 delete m_statusItem[QSAMPLER_STATUS_CLIENT];
199 if (m_statusItem[QSAMPLER_STATUS_SERVER])
200 delete m_statusItem[QSAMPLER_STATUS_SERVER];
201 if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
202 delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
203 if (m_statusItem[QSAMPLER_STATUS_SESSION])
204 delete m_statusItem[QSAMPLER_STATUS_SESSION];
205
206 // Delete recentfiles menu.
207 if (m_pRecentFilesMenu)
208 delete m_pRecentFilesMenu;
209 }
210
211
212 // Make and set a proper setup options step.
213 void qsamplerMainForm::setup ( qsamplerOptions *pOptions )
214 {
215 // We got options?
216 m_pOptions = pOptions;
217
218 // What style do we create these forms?
219 WFlags wflags = Qt::WType_TopLevel;
220 if (m_pOptions->bKeepOnTop)
221 wflags |= Qt::WStyle_Tool;
222 // Some child forms are to be created right now.
223 m_pMessages = new qsamplerMessages(this);
224 m_pDeviceForm = new qsamplerDeviceForm(this, 0, wflags);
225 m_pDeviceForm->setMainForm(this); // An important life immutable!
226 // Set message defaults...
227 updateMessagesFont();
228 updateMessagesLimit();
229 updateMessagesCapture();
230 // Set the visibility signal.
231 QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), this, SLOT(stabilizeForm()));
232
233 // Initial decorations toggle state.
234 viewMenubarAction->setOn(m_pOptions->bMenubar);
235 viewToolbarAction->setOn(m_pOptions->bToolbar);
236 viewStatusbarAction->setOn(m_pOptions->bStatusbar);
237 channelsAutoArrangeAction->setOn(m_pOptions->bAutoArrange);
238
239 // Initial decorations visibility state.
240 viewMenubar(m_pOptions->bMenubar);
241 viewToolbar(m_pOptions->bToolbar);
242 viewStatusbar(m_pOptions->bStatusbar);
243
244 // Restore whole dock windows state.
245 QString sDockables = m_pOptions->settings().readEntry("/Layout/DockWindows" , QString::null);
246 if (sDockables.isEmpty()) {
247 // Message window is forced to dock on the bottom.
248 moveDockWindow(m_pMessages, Qt::DockBottom);
249 } else {
250 // Make it as the last time.
251 QTextIStream istr(&sDockables);
252 istr >> *this;
253 }
254 // Try to restore old window positioning and initial visibility.
255 m_pOptions->loadWidgetGeometry(this);
256 m_pOptions->loadWidgetGeometry(m_pDeviceForm);
257
258 // Final startup stabilization...
259 updateRecentFilesMenu();
260 stabilizeForm();
261
262 // Make it ready :-)
263 statusBar()->message(tr("Ready"), 3000);
264
265 // We'll try to start immediately...
266 startSchedule(0);
267
268 // Register the first timer slot.
269 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
270 }
271
272
273 // Window close event handlers.
274 bool qsamplerMainForm::queryClose (void)
275 {
276 bool bQueryClose = closeSession(false);
277
278 // Try to save current general state...
279 if (m_pOptions) {
280 // Some windows default fonts is here on demand too.
281 if (bQueryClose && m_pMessages)
282 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
283 // Try to save current positioning.
284 if (bQueryClose) {
285 // Save decorations state.
286 m_pOptions->bMenubar = MenuBar->isVisible();
287 m_pOptions->bToolbar = (fileToolbar->isVisible() || editToolbar->isVisible() || channelsToolbar->isVisible());
288 m_pOptions->bStatusbar = statusBar()->isVisible();
289 // Save the dock windows state.
290 QString sDockables;
291 QTextOStream ostr(&sDockables);
292 ostr << *this;
293 m_pOptions->settings().writeEntry("/Layout/DockWindows", sDockables);
294 // And the children, and the main windows state,.
295 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
296 m_pOptions->saveWidgetGeometry(this);
297 // Close popup widgets.
298 if (m_pDeviceForm)
299 m_pDeviceForm->close();
300 // Stop client and/or server, gracefully.
301 stopServer();
302 }
303 }
304
305 return bQueryClose;
306 }
307
308
309 void qsamplerMainForm::closeEvent ( QCloseEvent *pCloseEvent )
310 {
311 if (queryClose())
312 pCloseEvent->accept();
313 else
314 pCloseEvent->ignore();
315 }
316
317
318 // Drag'n'drop file handler.
319 bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
320 {
321 bool bDecode = false;
322
323 if (QTextDrag::canDecode(pEvent)) {
324 QString sText;
325 bDecode = QTextDrag::decode(pEvent, sText);
326 if (bDecode) {
327 files = QStringList::split('\n', sText);
328 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
329 *iter = QUrl((*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null)).path();
330 }
331 }
332
333 return bDecode;
334 }
335
336
337 // Window drag-n-drop event handlers.
338 void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
339 {
340 QStringList files;
341 pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
342 }
343
344
345 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
346 {
347 QStringList files;
348
349 if (!decodeDragFiles(pDropEvent, files))
350 return;
351
352 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
353 const QString& sPath = *iter;
354 if (qsamplerChannel::isInstrumentFile(sPath)) {
355 // Try to create a new channel from instrument file...
356 qsamplerChannel *pChannel = new qsamplerChannel(this);
357 if (pChannel == NULL)
358 return;
359 // Start setting the instrument filename...
360 pChannel->setInstrument(sPath, 0);
361 // Before we show it up, may be we'll
362 // better ask for some initial values?
363 if (!pChannel->channelSetup(this)) {
364 delete pChannel;
365 return;
366 }
367 // Finally, give it to a new channel strip...
368 if (!createChannelStrip(pChannel)) {
369 delete pChannel;
370 return;
371 }
372 // Make that an overall update.
373 m_iDirtyCount++;
374 stabilizeForm();
375 } // Otherwise, load an usual session file (LSCP script)...
376 else if (closeSession(true))
377 loadSessionFile(sPath);
378 // Make it look responsive...:)
379 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
380 }
381 }
382
383
384 // Custome event handler.
385 void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
386 {
387 // For the time being, just pump it to messages.
388 if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
389 qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
390 appendMessagesColor(tr("Notify event: %1 data: %2")
391 .arg(::lscp_event_to_text(pEvent->event()))
392 .arg(pEvent->data()), "#996699");
393 if (pEvent->event() == LSCP_EVENT_CHANNEL_INFO) {
394 int iChannelID = pEvent->data().toInt();
395 qsamplerChannelStrip *pChannelStrip = channelStrip(iChannelID);
396 if (pChannelStrip)
397 channelStripChanged(pChannelStrip);
398 }
399 }
400 }
401
402
403 // Context menu event handler.
404 void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
405 {
406 stabilizeForm();
407
408 editMenu->exec(pEvent->globalPos());
409 }
410
411
412 //-------------------------------------------------------------------------
413 // qsamplerMainForm -- Brainless public property accessors.
414
415 // The global options settings property.
416 qsamplerOptions *qsamplerMainForm::options (void)
417 {
418 return m_pOptions;
419 }
420
421 // The LSCP client descriptor property.
422 lscp_client_t *qsamplerMainForm::client (void)
423 {
424 return m_pClient;
425 }
426
427
428 //-------------------------------------------------------------------------
429 // qsamplerMainForm -- Session file stuff.
430
431 // Format the displayable session filename.
432 QString qsamplerMainForm::sessionName ( const QString& sFilename )
433 {
434 bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
435 QString sSessionName = sFilename;
436 if (sSessionName.isEmpty())
437 sSessionName = tr("Untitled") + QString::number(m_iUntitled);
438 else if (!bCompletePath)
439 sSessionName = QFileInfo(sSessionName).fileName();
440 return sSessionName;
441 }
442
443
444 // Create a new session file from scratch.
445 bool qsamplerMainForm::newSession (void)
446 {
447 // Check if we can do it.
448 if (!closeSession(true))
449 return false;
450
451 // Give us what the server has, right now...
452 updateSession();
453
454 // Ok increment untitled count.
455 m_iUntitled++;
456
457 // Stabilize form.
458 m_sFilename = QString::null;
459 m_iDirtyCount = 0;
460 appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
461 stabilizeForm();
462
463 return true;
464 }
465
466
467 // Open an existing sampler session.
468 bool qsamplerMainForm::openSession (void)
469 {
470 if (m_pOptions == NULL)
471 return false;
472
473 // Ask for the filename to open...
474 QString sFilename = QFileDialog::getOpenFileName(
475 m_pOptions->sSessionDir, // Start here.
476 tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
477 this, 0, // Parent and name (none)
478 tr("Open Session") // Caption.
479 );
480
481 // Have we cancelled?
482 if (sFilename.isEmpty())
483 return false;
484
485 // Check if we're going to discard safely the current one...
486 if (!closeSession(true))
487 return false;
488
489 // Load it right away.
490 return loadSessionFile(sFilename);
491 }
492
493
494 // Save current sampler session with another name.
495 bool qsamplerMainForm::saveSession ( bool bPrompt )
496 {
497 if (m_pOptions == NULL)
498 return false;
499
500 QString sFilename = m_sFilename;
501
502 // Ask for the file to save, if there's none...
503 if (bPrompt || sFilename.isEmpty()) {
504 // If none is given, assume default directory.
505 if (sFilename.isEmpty())
506 sFilename = m_pOptions->sSessionDir;
507 // Prompt the guy...
508 sFilename = QFileDialog::getSaveFileName(
509 sFilename, // Start here.
510 tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
511 this, 0, // Parent and name (none)
512 tr("Save Session") // Caption.
513 );
514 // Have we cancelled it?
515 if (sFilename.isEmpty())
516 return false;
517 // Enforce .lscp extension...
518 if (QFileInfo(sFilename).extension().isEmpty())
519 sFilename += ".lscp";
520 // Check if already exists...
521 if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
522 if (QMessageBox::warning(this, tr("Warning"),
523 tr("The file already exists:\n\n"
524 "\"%1\"\n\n"
525 "Do you want to replace it?")
526 .arg(sFilename),
527 tr("Replace"), tr("Cancel")) > 0)
528 return false;
529 }
530 }
531
532 // Save it right away.
533 return saveSessionFile(sFilename);
534 }
535
536
537 // Close current session.
538 bool qsamplerMainForm::closeSession ( bool bForce )
539 {
540 bool bClose = true;
541
542 // Are we dirty enough to prompt it?
543 if (m_iDirtyCount > 0) {
544 switch (QMessageBox::warning(this, tr("Warning"),
545 tr("The current session has been changed:\n\n"
546 "\"%1\"\n\n"
547 "Do you want to save the changes?")
548 .arg(sessionName(m_sFilename)),
549 tr("Save"), tr("Discard"), tr("Cancel"))) {
550 case 0: // Save...
551 bClose = saveSession(false);
552 // Fall thru....
553 case 1: // Discard
554 break;
555 default: // Cancel.
556 bClose = false;
557 break;
558 }
559 }
560
561 // If we may close it, dot it.
562 if (bClose) {
563 // Remove all channel strips from sight...
564 m_pWorkspace->setUpdatesEnabled(false);
565 QWidgetList wlist = m_pWorkspace->windowList();
566 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
567 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
568 if (pChannelStrip) {
569 qsamplerChannel *pChannel = pChannelStrip->channel();
570 if (bForce && pChannel)
571 pChannel->removeChannel();
572 delete pChannelStrip;
573 }
574 }
575 m_pWorkspace->setUpdatesEnabled(true);
576 // We're now clean, for sure.
577 m_iDirtyCount = 0;
578 }
579
580 return bClose;
581 }
582
583
584 // Load a session from specific file path.
585 bool qsamplerMainForm::loadSessionFile ( const QString& sFilename )
586 {
587 if (m_pClient == NULL)
588 return false;
589
590 // Open and read from real file.
591 QFile file(sFilename);
592 if (!file.open(IO_ReadOnly)) {
593 appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
594 return false;
595 }
596
597 // Read the file.
598 int iErrors = 0;
599 QTextStream ts(&file);
600 while (!ts.atEnd()) {
601 // Read the line.
602 QString sCommand = ts.readLine().simplifyWhiteSpace();
603 // If not empty, nor a comment, call the server...
604 if (!sCommand.isEmpty() && sCommand[0] != '#') {
605 appendMessagesColor(sCommand, "#996633");
606 // Remember that, no matter what,
607 // all LSCP commands are CR/LF terminated.
608 sCommand += "\r\n";
609 if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {
610 appendMessagesClient("lscp_client_query");
611 iErrors++;
612 }
613 }
614 // Try to make it snappy :)
615 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
616 }
617
618 // Ok. we've read it.
619 file.close();
620
621 // Now we'll try to create (update) the whole GUI session.
622 updateSession();
623
624 // Have we any errors?
625 if (iErrors > 0)
626 appendMessagesError(tr("Session loaded with errors\nfrom \"%1\".\n\nSorry.").arg(sFilename));
627
628 // Save as default session directory.
629 if (m_pOptions)
630 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
631 // We're not dirty anymore, if loaded without errors,
632 m_iDirtyCount = iErrors;
633 // Stabilize form...
634 m_sFilename = sFilename;
635 updateRecentFiles(sFilename);
636 appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
637
638 // Make that an overall update.
639 stabilizeForm();
640 return true;
641 }
642
643
644 // Save current session to specific file path.
645 bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
646 {
647 // Open and write into real file.
648 QFile file(sFilename);
649 if (!file.open(IO_WriteOnly | IO_Truncate)) {
650 appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
651 return false;
652 }
653
654 // Write the file.
655 int iErrors = 0;
656 QTextStream ts(&file);
657 ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
658 ts << "# " << tr("Version")
659 << ": " QSAMPLER_VERSION << endl;
660 ts << "# " << tr("Build")
661 << ": " __DATE__ " " __TIME__ << endl;
662 ts << "#" << endl;
663 ts << "# " << tr("File")
664 << ": " << QFileInfo(sFilename).fileName() << endl;
665 ts << "# " << tr("Date")
666 << ": " << QDate::currentDate().toString("MMM dd yyyy")
667 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
668 ts << "#" << endl;
669 ts << endl;
670 // It is assumed that this new kind of device+session file
671 // will be loaded from a complete
672 int *piDeviceIDs;
673 int iDevice;
674 ts << "RESET" << endl;
675 // Audio device mapping.
676 QMap<int, int> audioDeviceMap;
677 piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Audio);
678 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
679 ts << endl;
680 qsamplerDevice device(this, qsamplerDevice::Audio, piDeviceIDs[iDevice]);
681 // Audio device specification...
682 ts << "# " << device.deviceTypeName() << " " << device.driverName()
683 << " " << tr("Device") << " " << iDevice << endl;
684 ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName();
685 qsamplerDeviceParamMap::ConstIterator deviceParam;
686 for (deviceParam = device.params().begin();
687 deviceParam != device.params().end();
688 ++deviceParam) {
689 const qsamplerDeviceParam& param = deviceParam.data();
690 if (param.value.isEmpty()) ts << "# ";
691 ts << " " << deviceParam.key() << "='" << param.value << "'";
692 }
693 ts << endl;
694 // Audio channel parameters...
695 int iPort = 0;
696 for (qsamplerDevicePort *pPort = device.ports().first();
697 pPort;
698 pPort = device.ports().next(), ++iPort) {
699 qsamplerDeviceParamMap::ConstIterator portParam;
700 for (portParam = pPort->params().begin();
701 portParam != pPort->params().end();
702 ++portParam) {
703 const qsamplerDeviceParam& param = portParam.data();
704 if (param.fix || param.value.isEmpty()) ts << "# ";
705 ts << "SET AUDIO_OUTPUT_CHANNEL_PARAMETER " << iDevice
706 << " " << iPort << " " << portParam.key()
707 << "='" << param.value << "'" << endl;
708 }
709 }
710 // Audio device index/id mapping.
711 audioDeviceMap[device.deviceID()] = iDevice;
712 // Try to keep it snappy :)
713 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
714 }
715 // MIDI device mapping.
716 QMap<int, int> midiDeviceMap;
717 piDeviceIDs = qsamplerDevice::getDevices(m_pClient, qsamplerDevice::Midi);
718 for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) {
719 ts << endl;
720 qsamplerDevice device(this, qsamplerDevice::Midi, piDeviceIDs[iDevice]);
721 // MIDI device specification...
722 ts << "# " << device.deviceTypeName() << " " << device.driverName()
723 << " " << tr("Device") << " " << iDevice << endl;
724 ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName();
725 qsamplerDeviceParamMap::ConstIterator deviceParam;
726 for (deviceParam = device.params().begin();
727 deviceParam != device.params().end();
728 ++deviceParam) {
729 const qsamplerDeviceParam& param = deviceParam.data();
730 if (param.value.isEmpty()) ts << "# ";
731 ts << " " << deviceParam.key() << "='" << param.value << "'";
732 }
733 ts << endl;
734 // MIDI port parameters...
735 int iPort = 0;
736 for (qsamplerDevicePort *pPort = device.ports().first();
737 pPort;
738 pPort = device.ports().next(), ++iPort) {
739 qsamplerDeviceParamMap::ConstIterator portParam;
740 for (portParam = pPort->params().begin();
741 portParam != pPort->params().end();
742 ++portParam) {
743 const qsamplerDeviceParam& param = portParam.data();
744 if (param.fix || param.value.isEmpty()) ts << "# ";
745 ts << "SET MIDI_INPUT_PORT_PARAMETER " << iDevice
746 << " " << iPort << " " << portParam.key()
747 << "='" << param.value << "'" << endl;
748 }
749 }
750 // MIDI device index/id mapping.
751 midiDeviceMap[device.deviceID()] = iDevice;
752 // Try to keep it snappy :)
753 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
754 }
755 ts << endl;
756 // Sampler channel mapping.
757 QWidgetList wlist = m_pWorkspace->windowList();
758 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
759 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
760 if (pChannelStrip) {
761 qsamplerChannel *pChannel = pChannelStrip->channel();
762 if (pChannel) {
763 ts << "# " << tr("Channel") << " " << iChannel << endl;
764 ts << "ADD CHANNEL" << endl;
765 if (audioDeviceMap.isEmpty()) {
766 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel
767 << " " << pChannel->audioDriver() << endl;
768 } else {
769 ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel
770 << " " << audioDeviceMap[pChannel->audioDevice()] << endl;
771 }
772 if (midiDeviceMap.isEmpty()) {
773 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel
774 << " " << pChannel->midiDriver() << endl;
775 } else {
776 ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel
777 << " " << midiDeviceMap[pChannel->midiDevice()] << endl;
778 }
779 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel
780 << " " << pChannel->midiPort() << endl;
781 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
782 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
783 ts << "ALL";
784 else
785 ts << pChannel->midiChannel();
786 ts << endl;
787 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
788 ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl;
789 ts << "SET CHANNEL VOLUME " << iChannel << " " << pChannel->volume() << endl;
790 ts << endl;
791 }
792 }
793 // Try to keep it snappy :)
794 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
795 }
796
797 // Ok. we've wrote it.
798 file.close();
799
800 // Have we any errors?
801 if (iErrors > 0)
802 appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
803
804 // Save as default session directory.
805 if (m_pOptions)
806 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
807 // We're not dirty anymore.
808 m_iDirtyCount = 0;
809 // Stabilize form...
810 m_sFilename = sFilename;
811 updateRecentFiles(sFilename);
812 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
813 stabilizeForm();
814 return true;
815 }
816
817
818 // Session change receiver slot.
819 void qsamplerMainForm::sessionDirty (void)
820 {
821 // Just mark the dirty form.
822 m_iDirtyCount++;
823 // and update the form status...
824 stabilizeForm();
825 }
826
827
828 //-------------------------------------------------------------------------
829 // qsamplerMainForm -- File Action slots.
830
831 // Create a new sampler session.
832 void qsamplerMainForm::fileNew (void)
833 {
834 // Of course we'll start clean new.
835 newSession();
836 }
837
838
839 // Open an existing sampler session.
840 void qsamplerMainForm::fileOpen (void)
841 {
842 // Open it right away.
843 openSession();
844 }
845
846
847 // Open a recent file session.
848 void qsamplerMainForm::fileOpenRecent ( int iIndex )
849 {
850 // Check if we can safely close the current session...
851 if (m_pOptions && closeSession(true)) {
852 QString sFilename = m_pOptions->recentFiles[iIndex];
853 loadSessionFile(sFilename);
854 }
855 }
856
857
858 // Save current sampler session.
859 void qsamplerMainForm::fileSave (void)
860 {
861 // Save it right away.
862 saveSession(false);
863 }
864
865
866 // Save current sampler session with another name.
867 void qsamplerMainForm::fileSaveAs (void)
868 {
869 // Save it right away, maybe with another name.
870 saveSession(true);
871 }
872
873
874 // Reset the sampler instance.
875 void qsamplerMainForm::fileReset (void)
876 {
877 if (m_pClient == NULL)
878 return;
879
880 // Ask user whether he/she want's an internal sampler reset...
881 if (QMessageBox::warning(this, tr("Warning"),
882 tr("Resetting the sampler instance will close\n"
883 "all device and channel configurations.\n\n"
884 "Please note that this operation may cause\n"
885 "temporary MIDI and Audio disruption.\n\n"
886 "Do you want to reset the sampler engine now?"),
887 tr("Reset"), tr("Cancel")) > 0)
888 return;
889
890 // Just do the reset, after closing down current session...
891 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
892 appendMessagesClient("lscp_reset_sampler");
893 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
894 return;
895 }
896
897 // Log this.
898 appendMessages(tr("Sampler reset."));
899
900 // Make it a new session...
901 newSession();
902 }
903
904
905 // Restart the client/server instance.
906 void qsamplerMainForm::fileRestart (void)
907 {
908 if (m_pOptions == NULL)
909 return;
910
911 bool bRestart = true;
912
913 // Ask user whether he/she want's a complete restart...
914 // (if we're currently up and running)
915 if (bRestart && m_pClient) {
916 bRestart = (QMessageBox::warning(this, tr("Warning"),
917 tr("New settings will be effective after\n"
918 "restarting the client/server connection.\n\n"
919 "Please note that this operation may cause\n"
920 "temporary MIDI and Audio disruption.\n\n"
921 "Do you want to restart the connection now?"),
922 tr("Restart"), tr("Cancel")) == 0);
923 }
924
925 // Are we still for it?
926 if (bRestart && closeSession(true)) {
927 // Stop server, it will force the client too.
928 stopServer();
929 // Reschedule a restart...
930 startSchedule(m_pOptions->iStartDelay);
931 }
932 }
933
934
935 // Exit application program.
936 void qsamplerMainForm::fileExit (void)
937 {
938 // Go for close the whole thing.
939 close();
940 }
941
942
943 //-------------------------------------------------------------------------
944 // qsamplerMainForm -- Edit Action slots.
945
946 // Add a new sampler channel.
947 void qsamplerMainForm::editAddChannel (void)
948 {
949 if (m_pClient == NULL)
950 return;
951
952 // Just create the channel instance...
953 qsamplerChannel *pChannel = new qsamplerChannel(this);
954 if (pChannel == NULL)
955 return;
956
957 // Before we show it up, may be we'll
958 // better ask for some initial values?
959 if (!pChannel->channelSetup(this)) {
960 delete pChannel;
961 return;
962 }
963
964 // And give it to the strip (will own the channel instance, if successful).
965 if (!createChannelStrip(pChannel)) {
966 delete pChannel;
967 return;
968 }
969
970 // Make that an overall update.
971 m_iDirtyCount++;
972 stabilizeForm();
973 }
974
975
976 // Remove current sampler channel.
977 void qsamplerMainForm::editRemoveChannel (void)
978 {
979 if (m_pClient == NULL)
980 return;
981
982 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
983 if (pChannelStrip == NULL)
984 return;
985
986 qsamplerChannel *pChannel = pChannelStrip->channel();
987 if (pChannel == NULL)
988 return;
989
990 // Prompt user if he/she's sure about this...
991 if (m_pOptions && m_pOptions->bConfirmRemove) {
992 if (QMessageBox::warning(this, tr("Warning"),
993 tr("About to remove channel:\n\n"
994 "%1\n\n"
995 "Are you sure?")
996 .arg(pChannelStrip->caption()),
997 tr("OK"), tr("Cancel")) > 0)
998 return;
999 }
1000
1001 // Remove the existing sampler channel.
1002 if (!pChannel->removeChannel())
1003 return;
1004
1005 // Just delete the channel strip.
1006 delete pChannelStrip;
1007
1008 // Do we auto-arrange?
1009 if (m_pOptions && m_pOptions->bAutoArrange)
1010 channelsArrange();
1011
1012 // We'll be dirty, for sure...
1013 m_iDirtyCount++;
1014 stabilizeForm();
1015 }
1016
1017
1018 // Setup current sampler channel.
1019 void qsamplerMainForm::editSetupChannel (void)
1020 {
1021 if (m_pClient == NULL)
1022 return;
1023
1024 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1025 if (pChannelStrip == NULL)
1026 return;
1027
1028 // Just invoque the channel strip procedure.
1029 pChannelStrip->channelSetup();
1030 }
1031
1032
1033 // Reset current sampler channel.
1034 void qsamplerMainForm::editResetChannel (void)
1035 {
1036 if (m_pClient == NULL)
1037 return;
1038
1039 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1040 if (pChannelStrip == NULL)
1041 return;
1042
1043 // Just invoque the channel strip procedure.
1044 pChannelStrip->channelReset();
1045 }
1046
1047
1048 // Reset all sampler channels.
1049 void qsamplerMainForm::editResetAllChannels (void)
1050 {
1051 if (m_pClient == NULL)
1052 return;
1053
1054 // Invoque the channel strip procedure,
1055 // for all channels out there...
1056 m_pWorkspace->setUpdatesEnabled(false);
1057 QWidgetList wlist = m_pWorkspace->windowList();
1058 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1059 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1060 if (pChannelStrip)
1061 pChannelStrip->channelReset();
1062 }
1063 m_pWorkspace->setUpdatesEnabled(true);
1064 }
1065
1066
1067 //-------------------------------------------------------------------------
1068 // qsamplerMainForm -- View Action slots.
1069
1070 // Show/hide the main program window menubar.
1071 void qsamplerMainForm::viewMenubar ( bool bOn )
1072 {
1073 if (bOn)
1074 MenuBar->show();
1075 else
1076 MenuBar->hide();
1077 }
1078
1079
1080 // Show/hide the main program window toolbar.
1081 void qsamplerMainForm::viewToolbar ( bool bOn )
1082 {
1083 if (bOn) {
1084 fileToolbar->show();
1085 editToolbar->show();
1086 channelsToolbar->show();
1087 } else {
1088 fileToolbar->hide();
1089 editToolbar->hide();
1090 channelsToolbar->hide();
1091 }
1092 }
1093
1094
1095 // Show/hide the main program window statusbar.
1096 void qsamplerMainForm::viewStatusbar ( bool bOn )
1097 {
1098 if (bOn)
1099 statusBar()->show();
1100 else
1101 statusBar()->hide();
1102 }
1103
1104
1105 // Show/hide the messages window logger.
1106 void qsamplerMainForm::viewMessages ( bool bOn )
1107 {
1108 if (bOn)
1109 m_pMessages->show();
1110 else
1111 m_pMessages->hide();
1112 }
1113
1114
1115 // Show/hide the device configurator form.
1116 void qsamplerMainForm::viewDevices (void)
1117 {
1118 if (m_pOptions == NULL)
1119 return;
1120
1121 if (m_pDeviceForm) {
1122 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1123 m_pDeviceForm->setClient(m_pClient);
1124 if (m_pDeviceForm->isVisible()) {
1125 m_pDeviceForm->hide();
1126 } else {
1127 m_pDeviceForm->show();
1128 m_pDeviceForm->raise();
1129 m_pDeviceForm->setActiveWindow();
1130 }
1131 }
1132 }
1133
1134
1135 // Show options dialog.
1136 void qsamplerMainForm::viewOptions (void)
1137 {
1138 if (m_pOptions == NULL)
1139 return;
1140
1141 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1142 if (pOptionsForm) {
1143 // Check out some initial nullities(tm)...
1144 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1145 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1146 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1147 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1148 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1149 // To track down deferred or immediate changes.
1150 QString sOldServerHost = m_pOptions->sServerHost;
1151 int iOldServerPort = m_pOptions->iServerPort;
1152 int iOldServerTimeout = m_pOptions->iServerTimeout;
1153 bool bOldServerStart = m_pOptions->bServerStart;
1154 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1155 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1156 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1157 int iOldMaxVolume = m_pOptions->iMaxVolume;
1158 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1159 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1160 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1161 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1162 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1163 bool bOldCompletePath = m_pOptions->bCompletePath;
1164 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1165 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1166 // Load the current setup settings.
1167 pOptionsForm->setup(m_pOptions);
1168 // Show the setup dialog...
1169 if (pOptionsForm->exec()) {
1170 // Warn if something will be only effective on next run.
1171 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1172 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1173 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1174 (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1175 QMessageBox::information(this, tr("Information"),
1176 tr("Some settings may be only effective\n"
1177 "next time you start this program."), tr("OK"));
1178 updateMessagesCapture();
1179 }
1180 // Check wheather something immediate has changed.
1181 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1182 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1183 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1184 updateRecentFilesMenu();
1185 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1186 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1187 updateInstrumentNames();
1188 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1189 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1190 updateDisplayEffect();
1191 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1192 updateDisplayFont();
1193 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1194 updateMaxVolume();
1195 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1196 updateMessagesFont();
1197 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1198 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1199 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1200 updateMessagesLimit();
1201 // And now the main thing, whether we'll do client/server recycling?
1202 if ((sOldServerHost != m_pOptions->sServerHost) ||
1203 (iOldServerPort != m_pOptions->iServerPort) ||
1204 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1205 ( bOldServerStart && !m_pOptions->bServerStart) ||
1206 (!bOldServerStart && m_pOptions->bServerStart) ||
1207 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1208 fileRestart();
1209 }
1210 // Done.
1211 delete pOptionsForm;
1212 }
1213
1214 // This makes it.
1215 stabilizeForm();
1216 }
1217
1218
1219 //-------------------------------------------------------------------------
1220 // qsamplerMainForm -- Channels action slots.
1221
1222 // Arrange channel strips.
1223 void qsamplerMainForm::channelsArrange (void)
1224 {
1225 // Full width vertical tiling
1226 QWidgetList wlist = m_pWorkspace->windowList();
1227 if (wlist.isEmpty())
1228 return;
1229
1230 m_pWorkspace->setUpdatesEnabled(false);
1231 int y = 0;
1232 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1233 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1234 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1235 // Prevent flicker...
1236 pChannelStrip->hide();
1237 pChannelStrip->showNormal();
1238 } */
1239 pChannelStrip->adjustSize();
1240 int iWidth = m_pWorkspace->width();
1241 if (iWidth < pChannelStrip->width())
1242 iWidth = pChannelStrip->width();
1243 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1244 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1245 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1246 y += iHeight;
1247 }
1248 m_pWorkspace->setUpdatesEnabled(true);
1249
1250 stabilizeForm();
1251 }
1252
1253
1254 // Auto-arrange channel strips.
1255 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1256 {
1257 if (m_pOptions == NULL)
1258 return;
1259
1260 // Toggle the auto-arrange flag.
1261 m_pOptions->bAutoArrange = bOn;
1262
1263 // If on, update whole workspace...
1264 if (m_pOptions->bAutoArrange)
1265 channelsArrange();
1266 }
1267
1268
1269 //-------------------------------------------------------------------------
1270 // qsamplerMainForm -- Help Action slots.
1271
1272 // Show information about the Qt toolkit.
1273 void qsamplerMainForm::helpAboutQt (void)
1274 {
1275 QMessageBox::aboutQt(this);
1276 }
1277
1278
1279 // Show information about application program.
1280 void qsamplerMainForm::helpAbout (void)
1281 {
1282 // Stuff the about box text...
1283 QString sText = "<p>\n";
1284 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1285 sText += "<br />\n";
1286 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1287 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1288 #ifdef CONFIG_DEBUG
1289 sText += "<small><font color=\"red\">";
1290 sText += tr("Debugging option enabled.");
1291 sText += "</font></small><br />";
1292 #endif
1293 #ifndef CONFIG_LIBGIG
1294 sText += "<small><font color=\"red\">";
1295 sText += tr("GIG (libgig) file support disabled.");
1296 sText += "</font></small><br />";
1297 #endif
1298 #ifndef CONFIG_INSTRUMENT_NAME
1299 sText += "<small><font color=\"red\">";
1300 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1301 sText += "</font></small><br />";
1302 #endif
1303 sText += "<br />\n";
1304 sText += tr("Using") + ": ";
1305 sText += ::lscp_client_package();
1306 sText += " ";
1307 sText += ::lscp_client_version();
1308 #ifdef CONFIG_LIBGIG
1309 sText += ", ";
1310 sText += gig::libraryName();
1311 sText += " ";
1312 sText += gig::libraryVersion();
1313 #endif
1314 sText += "<br />\n";
1315 sText += "<br />\n";
1316 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1317 sText += "<br />\n";
1318 sText += "<small>";
1319 sText += QSAMPLER_COPYRIGHT "<br />\n";
1320 sText += "<br />\n";
1321 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1322 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1323 sText += "</small>";
1324 sText += "</p>\n";
1325
1326 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1327 }
1328
1329
1330 //-------------------------------------------------------------------------
1331 // qsamplerMainForm -- Main window stabilization.
1332
1333 void qsamplerMainForm::stabilizeForm (void)
1334 {
1335 // Update the main application caption...
1336 QString sSessionName = sessionName(m_sFilename);
1337 if (m_iDirtyCount > 0)
1338 sSessionName += '*';
1339 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1340
1341 // Update the main menu state...
1342 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1343 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1344 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1345 fileNewAction->setEnabled(bHasClient);
1346 fileOpenAction->setEnabled(bHasClient);
1347 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1348 fileSaveAsAction->setEnabled(bHasClient);
1349 fileResetAction->setEnabled(bHasClient);
1350 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1351 editAddChannelAction->setEnabled(bHasClient);
1352 editRemoveChannelAction->setEnabled(bHasChannel);
1353 editSetupChannelAction->setEnabled(bHasChannel);
1354 editResetChannelAction->setEnabled(bHasChannel);
1355 editResetAllChannelsAction->setEnabled(bHasChannel);
1356 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1357 viewDevicesAction->setOn(m_pDeviceForm && m_pDeviceForm->isVisible());
1358 viewDevicesAction->setEnabled(bHasClient);
1359 channelsArrangeAction->setEnabled(bHasChannel);
1360
1361 // Client/Server status...
1362 if (bHasClient) {
1363 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1364 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1365 } else {
1366 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1367 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1368 }
1369 // Channel status...
1370 if (bHasChannel)
1371 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1372 else
1373 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1374 // Session status...
1375 if (m_iDirtyCount > 0)
1376 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1377 else
1378 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1379
1380 // Recent files menu.
1381 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1382
1383 // Always make the latest message visible.
1384 if (m_pMessages)
1385 m_pMessages->scrollToBottom();
1386 }
1387
1388
1389 // Channel change receiver slot.
1390 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1391 {
1392 // Add this strip to the changed list...
1393 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1394 m_changedStrips.append(pChannelStrip);
1395 pChannelStrip->resetErrorCount();
1396 }
1397
1398 // Just mark the dirty form.
1399 m_iDirtyCount++;
1400 // and update the form status...
1401 stabilizeForm();
1402 }
1403
1404
1405 // Grab and restore current sampler channels session.
1406 void qsamplerMainForm::updateSession (void)
1407 {
1408 // Retrieve the current channel list.
1409 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1410 if (piChannelIDs == NULL) {
1411 if (::lscp_client_get_errno(m_pClient)) {
1412 appendMessagesClient("lscp_list_channels");
1413 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1414 }
1415 return;
1416 }
1417
1418 // Try to (re)create each channel.
1419 m_pWorkspace->setUpdatesEnabled(false);
1420 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1421 // Check if theres already a channel strip for this one...
1422 if (!channelStrip(piChannelIDs[iChannel]))
1423 createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1424 // Make it visibly responsive...
1425 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1426 }
1427 m_pWorkspace->setUpdatesEnabled(true);
1428
1429 // Remember to refresh devices
1430 if (m_pDeviceForm)
1431 m_pDeviceForm->refreshDevices();
1432 }
1433
1434
1435 // Update the recent files list and menu.
1436 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1437 {
1438 if (m_pOptions == NULL)
1439 return;
1440
1441 // Remove from list if already there (avoid duplicates)
1442 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1443 if (iter != m_pOptions->recentFiles.end())
1444 m_pOptions->recentFiles.remove(iter);
1445 // Put it to front...
1446 m_pOptions->recentFiles.push_front(sFilename);
1447
1448 // May update the menu.
1449 updateRecentFilesMenu();
1450 }
1451
1452
1453 // Update the recent files list and menu.
1454 void qsamplerMainForm::updateRecentFilesMenu (void)
1455 {
1456 if (m_pOptions == NULL)
1457 return;
1458
1459 // Time to keep the list under limits.
1460 int iRecentFiles = m_pOptions->recentFiles.count();
1461 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1462 m_pOptions->recentFiles.pop_back();
1463 iRecentFiles--;
1464 }
1465
1466 // rebuild the recent files menu...
1467 m_pRecentFilesMenu->clear();
1468 for (int i = 0; i < iRecentFiles; i++) {
1469 const QString& sFilename = m_pOptions->recentFiles[i];
1470 if (QFileInfo(sFilename).exists()) {
1471 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1472 .arg(i + 1).arg(sessionName(sFilename)),
1473 this, SLOT(fileOpenRecent(int)), 0, i);
1474 }
1475 }
1476 }
1477
1478
1479 // Force update of the channels instrument names mode.
1480 void qsamplerMainForm::updateInstrumentNames (void)
1481 {
1482 // Full channel list update...
1483 QWidgetList wlist = m_pWorkspace->windowList();
1484 if (wlist.isEmpty())
1485 return;
1486
1487 m_pWorkspace->setUpdatesEnabled(false);
1488 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1489 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1490 if (pChannelStrip)
1491 pChannelStrip->updateInstrumentName(true);
1492 }
1493 m_pWorkspace->setUpdatesEnabled(true);
1494 }
1495
1496
1497 // Force update of the channels display font.
1498 void qsamplerMainForm::updateDisplayFont (void)
1499 {
1500 if (m_pOptions == NULL)
1501 return;
1502
1503 // Check if display font is legal.
1504 if (m_pOptions->sDisplayFont.isEmpty())
1505 return;
1506 // Realize it.
1507 QFont font;
1508 if (!font.fromString(m_pOptions->sDisplayFont))
1509 return;
1510
1511 // Full channel list update...
1512 QWidgetList wlist = m_pWorkspace->windowList();
1513 if (wlist.isEmpty())
1514 return;
1515
1516 m_pWorkspace->setUpdatesEnabled(false);
1517 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1518 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1519 if (pChannelStrip)
1520 pChannelStrip->setDisplayFont(font);
1521 }
1522 m_pWorkspace->setUpdatesEnabled(true);
1523 }
1524
1525
1526 // Update channel strips background effect.
1527 void qsamplerMainForm::updateDisplayEffect (void)
1528 {
1529 QPixmap pm;
1530 if (m_pOptions->bDisplayEffect)
1531 pm = QPixmap::fromMimeSource("displaybg1.png");
1532
1533 // Full channel list update...
1534 QWidgetList wlist = m_pWorkspace->windowList();
1535 if (wlist.isEmpty())
1536 return;
1537
1538 m_pWorkspace->setUpdatesEnabled(false);
1539 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1540 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1541 if (pChannelStrip)
1542 pChannelStrip->setDisplayBackground(pm);
1543 }
1544 m_pWorkspace->setUpdatesEnabled(true);
1545 }
1546
1547
1548 // Force update of the channels maximum volume setting.
1549 void qsamplerMainForm::updateMaxVolume (void)
1550 {
1551 if (m_pOptions == NULL)
1552 return;
1553
1554 // Full channel list update...
1555 QWidgetList wlist = m_pWorkspace->windowList();
1556 if (wlist.isEmpty())
1557 return;
1558
1559 m_pWorkspace->setUpdatesEnabled(false);
1560 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1561 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1562 if (pChannelStrip)
1563 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1564 }
1565 m_pWorkspace->setUpdatesEnabled(true);
1566 }
1567
1568
1569 //-------------------------------------------------------------------------
1570 // qsamplerMainForm -- Messages window form handlers.
1571
1572 // Messages output methods.
1573 void qsamplerMainForm::appendMessages( const QString& s )
1574 {
1575 if (m_pMessages)
1576 m_pMessages->appendMessages(s);
1577
1578 statusBar()->message(s, 3000);
1579 }
1580
1581 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1582 {
1583 if (m_pMessages)
1584 m_pMessages->appendMessagesColor(s, c);
1585
1586 statusBar()->message(s, 3000);
1587 }
1588
1589 void qsamplerMainForm::appendMessagesText( const QString& s )
1590 {
1591 if (m_pMessages)
1592 m_pMessages->appendMessagesText(s);
1593 }
1594
1595 void qsamplerMainForm::appendMessagesError( const QString& s )
1596 {
1597 if (m_pMessages)
1598 m_pMessages->show();
1599
1600 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1601
1602 QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1603 }
1604
1605
1606 // This is a special message format, just for client results.
1607 void qsamplerMainForm::appendMessagesClient( const QString& s )
1608 {
1609 if (m_pClient == NULL)
1610 return;
1611
1612 appendMessagesColor(s + QString(": %1 (errno=%2)")
1613 .arg(::lscp_client_get_result(m_pClient))
1614 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1615 }
1616
1617
1618 // Force update of the messages font.
1619 void qsamplerMainForm::updateMessagesFont (void)
1620 {
1621 if (m_pOptions == NULL)
1622 return;
1623
1624 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1625 QFont font;
1626 if (font.fromString(m_pOptions->sMessagesFont))
1627 m_pMessages->setMessagesFont(font);
1628 }
1629 }
1630
1631
1632 // Update messages window line limit.
1633 void qsamplerMainForm::updateMessagesLimit (void)
1634 {
1635 if (m_pOptions == NULL)
1636 return;
1637
1638 if (m_pMessages) {
1639 if (m_pOptions->bMessagesLimit)
1640 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1641 else
1642 m_pMessages->setMessagesLimit(-1);
1643 }
1644 }
1645
1646
1647 // Enablement of the messages capture feature.
1648 void qsamplerMainForm::updateMessagesCapture (void)
1649 {
1650 if (m_pOptions == NULL)
1651 return;
1652
1653 if (m_pMessages)
1654 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1655 }
1656
1657
1658 //-------------------------------------------------------------------------
1659 // qsamplerMainForm -- MDI channel strip management.
1660
1661 // The channel strip creation executive.
1662 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1663 {
1664 if (m_pClient == NULL || pChannel == NULL)
1665 return NULL;
1666
1667 // Prepare for auto-arrange?
1668 qsamplerChannelStrip *pChannelStrip = NULL;
1669 int y = 0;
1670 if (m_pOptions && m_pOptions->bAutoArrange) {
1671 QWidgetList wlist = m_pWorkspace->windowList();
1672 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1673 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1674 if (pChannelStrip) {
1675 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1676 y += pChannelStrip->parentWidget()->frameGeometry().height();
1677 }
1678 }
1679 }
1680
1681 // Add a new channel itema...
1682 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1683 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1684 if (pChannelStrip == NULL)
1685 return NULL;
1686
1687 // Actual channel strip setup...
1688 pChannelStrip->setup(pChannel);
1689 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1690 // Set some initial aesthetic options...
1691 if (m_pOptions) {
1692 // Background display effect...
1693 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1694 // We'll need a display font.
1695 QFont font;
1696 if (font.fromString(m_pOptions->sDisplayFont))
1697 pChannelStrip->setDisplayFont(font);
1698 // Maximum allowed volume setting.
1699 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1700 }
1701
1702 // Now we show up us to the world.
1703 pChannelStrip->show();
1704 // Only then, we'll auto-arrange...
1705 if (m_pOptions && m_pOptions->bAutoArrange) {
1706 int iWidth = m_pWorkspace->width();
1707 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1708 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1709 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1710 }
1711
1712 // This is pretty new, so we'll watch for it closely.
1713 channelStripChanged(pChannelStrip);
1714
1715 // Return our successful reference...
1716 return pChannelStrip;
1717 }
1718
1719
1720 // Retrieve the active channel strip.
1721 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1722 {
1723 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1724 }
1725
1726
1727 // Retrieve a channel strip by index.
1728 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1729 {
1730 QWidgetList wlist = m_pWorkspace->windowList();
1731 if (wlist.isEmpty())
1732 return NULL;
1733
1734 return (qsamplerChannelStrip *) wlist.at(iChannel);
1735 }
1736
1737
1738 // Retrieve a channel strip by sampler channel id.
1739 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1740 {
1741 QWidgetList wlist = m_pWorkspace->windowList();
1742 if (wlist.isEmpty())
1743 return NULL;
1744
1745 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1746 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1747 if (pChannelStrip) {
1748 qsamplerChannel *pChannel = pChannelStrip->channel();
1749 if (pChannel && pChannel->channelID() == iChannelID)
1750 return pChannelStrip;
1751 }
1752 }
1753
1754 // Not found.
1755 return NULL;
1756 }
1757
1758
1759 // Construct the windows menu.
1760 void qsamplerMainForm::channelsMenuAboutToShow (void)
1761 {
1762 channelsMenu->clear();
1763 channelsArrangeAction->addTo(channelsMenu);
1764 channelsAutoArrangeAction->addTo(channelsMenu);
1765
1766 QWidgetList wlist = m_pWorkspace->windowList();
1767 if (!wlist.isEmpty()) {
1768 channelsMenu->insertSeparator();
1769 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1770 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1771 if (pChannelStrip) {
1772 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1773 channelsMenu->setItemParameter(iItemID, iChannel);
1774 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1775 }
1776 }
1777 }
1778 }
1779
1780
1781 // Windows menu activation slot
1782 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1783 {
1784 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1785 if (pChannelStrip)
1786 pChannelStrip->showNormal();
1787 pChannelStrip->setFocus();
1788 }
1789
1790
1791 //-------------------------------------------------------------------------
1792 // qsamplerMainForm -- Timer stuff.
1793
1794 // Set the pseudo-timer delay schedule.
1795 void qsamplerMainForm::startSchedule ( int iStartDelay )
1796 {
1797 m_iStartDelay = 1 + (iStartDelay * 1000);
1798 m_iTimerDelay = 0;
1799 }
1800
1801 // Suspend the pseudo-timer delay schedule.
1802 void qsamplerMainForm::stopSchedule (void)
1803 {
1804 m_iStartDelay = 0;
1805 m_iTimerDelay = 0;
1806 }
1807
1808 // Timer slot funtion.
1809 void qsamplerMainForm::timerSlot (void)
1810 {
1811 if (m_pOptions == NULL)
1812 return;
1813
1814 // Is it the first shot on server start after a few delay?
1815 if (m_iTimerDelay < m_iStartDelay) {
1816 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1817 if (m_iTimerDelay >= m_iStartDelay) {
1818 // If we cannot start it now, maybe a lil'mo'later ;)
1819 if (!startClient()) {
1820 m_iStartDelay += m_iTimerDelay;
1821 m_iTimerDelay = 0;
1822 }
1823 }
1824 }
1825
1826 if (m_pClient) {
1827 // Update the channel information for each pending strip...
1828 if (m_changedStrips.count() > 0) {
1829 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1830 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
1831 // If successfull, remove from pending list...
1832 if (pChannelStrip->updateChannelInfo())
1833 m_changedStrips.remove(pChannelStrip);
1834 }
1835 }
1836 // Refresh each channel usage, on each period...
1837 if (m_pOptions->bAutoRefresh) {
1838 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1839 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
1840 m_iTimerSlot = 0;
1841 // Update the channel stream usage for each strip...
1842 QWidgetList wlist = m_pWorkspace->windowList();
1843 for (int iChannel = 0;
1844 iChannel < (int) wlist.count(); iChannel++) {
1845 qsamplerChannelStrip *pChannelStrip
1846 = (qsamplerChannelStrip *) wlist.at(iChannel);
1847 if (pChannelStrip && pChannelStrip->isVisible())
1848 pChannelStrip->updateChannelUsage();
1849 }
1850 }
1851 }
1852 }
1853
1854 // Register the next timer slot.
1855 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1856 }
1857
1858
1859 //-------------------------------------------------------------------------
1860 // qsamplerMainForm -- Server stuff.
1861
1862 // Start linuxsampler server...
1863 void qsamplerMainForm::startServer (void)
1864 {
1865 if (m_pOptions == NULL)
1866 return;
1867
1868 // Aren't already a client, are we?
1869 if (!m_pOptions->bServerStart || m_pClient)
1870 return;
1871
1872 // Is the server process instance still here?
1873 if (m_pServer) {
1874 switch (QMessageBox::warning(this, tr("Warning"),
1875 tr("Could not start the LinuxSampler server.\n\n"
1876 "Maybe it ss already started."),
1877 tr("Stop"), tr("Kill"), tr("Cancel"))) {
1878 case 0:
1879 m_pServer->tryTerminate();
1880 break;
1881 case 1:
1882 m_pServer->kill();
1883 break;
1884 }
1885 return;
1886 }
1887
1888 // Reset our timer counters...
1889 stopSchedule();
1890
1891 // OK. Let's build the startup process...
1892 m_pServer = new QProcess(this);
1893
1894 // Setup stdout/stderr capture...
1895 //if (m_pOptions->bStdoutCapture) {
1896 m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1897 QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1898 QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1899 //}
1900 // The unforgiveable signal communication...
1901 QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1902
1903 // Build process arguments...
1904 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1905
1906 appendMessages(tr("Server is starting..."));
1907 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1908
1909 // Go jack, go...
1910 if (!m_pServer->start()) {
1911 appendMessagesError(tr("Could not start server.\n\nSorry."));
1912 processServerExit();
1913 return;
1914 }
1915
1916 // Show startup results...
1917 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1918
1919 // Reset (yet again) the timer counters,
1920 // but this time is deferred as the user opted.
1921 startSchedule(m_pOptions->iStartDelay);
1922 stabilizeForm();
1923 }
1924
1925
1926 // Stop linuxsampler server...
1927 void qsamplerMainForm::stopServer (void)
1928 {
1929 // Stop client code.
1930 stopClient();
1931
1932 // And try to stop server.
1933 if (m_pServer) {
1934 appendMessages(tr("Server is stopping..."));
1935 if (m_pServer->isRunning())
1936 m_pServer->tryTerminate();
1937 }
1938
1939 // Give it some time to terminate gracefully and stabilize...
1940 QTime t;
1941 t.start();
1942 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1943 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1944
1945 // Do final processing anyway.
1946 processServerExit();
1947 }
1948
1949
1950 // Stdout handler...
1951 void qsamplerMainForm::readServerStdout (void)
1952 {
1953 if (m_pMessages)
1954 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1955 }
1956
1957
1958 // Linuxsampler server cleanup.
1959 void qsamplerMainForm::processServerExit (void)
1960 {
1961 // Force client code cleanup.
1962 stopClient();
1963
1964 // Flush anything that maybe pending...
1965 if (m_pMessages)
1966 m_pMessages->flushStdoutBuffer();
1967
1968 if (m_pServer) {
1969 // Force final server shutdown...
1970 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1971 if (!m_pServer->normalExit())
1972 m_pServer->kill();
1973 // Destroy it.
1974 delete m_pServer;
1975 m_pServer = NULL;
1976 }
1977
1978 // Again, make status visible stable.
1979 stabilizeForm();
1980 }
1981
1982
1983 //-------------------------------------------------------------------------
1984 // qsamplerMainForm -- Client stuff.
1985
1986 // The LSCP client callback procedure.
1987 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1988 {
1989 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1990 if (pMainForm == NULL)
1991 return LSCP_FAILED;
1992
1993 // ATTN: DO NOT EVER call any GUI code here,
1994 // as this is run under some other thread context.
1995 // A custom event must be posted here...
1996 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1997
1998 return LSCP_OK;
1999 }
2000
2001
2002 // Start our almighty client...
2003 bool qsamplerMainForm::startClient (void)
2004 {
2005 // Have it a setup?
2006 if (m_pOptions == NULL)
2007 return false;
2008
2009 // Aren't we already started, are we?
2010 if (m_pClient)
2011 return true;
2012
2013 // Log prepare here.
2014 appendMessages(tr("Client connecting..."));
2015
2016 // Create the client handle...
2017 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2018 if (m_pClient == NULL) {
2019 // Is this the first try?
2020 // maybe we need to start a local server...
2021 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
2022 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2023 else
2024 startServer();
2025 // This is always a failure.
2026 stabilizeForm();
2027 return false;
2028 }
2029 // Just set receive timeout value, blindly.
2030 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2031 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2032
2033 // Subscribe to channel info change notifications...
2034 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2035 appendMessagesClient("lscp_client_subscribe");
2036
2037 // We may stop scheduling around.
2038 stopSchedule();
2039
2040 // We'll accept drops from now on...
2041 setAcceptDrops(true);
2042
2043 // Log success here.
2044 appendMessages(tr("Client connected."));
2045
2046 // Hard-notify device configuration form,
2047 // if visible, that we're ready...
2048 if (m_pDeviceForm && m_pDeviceForm->isVisible())
2049 m_pDeviceForm->setClient(m_pClient);
2050
2051 // Is any session pending to be loaded?
2052 if (!m_pOptions->sSessionFile.isEmpty()) {
2053 // Just load the prabably startup session...
2054 if (loadSessionFile(m_pOptions->sSessionFile)) {
2055 m_pOptions->sSessionFile = QString::null;
2056 return true;
2057 }
2058 }
2059
2060 // Make a new session
2061 return newSession();
2062 }
2063
2064
2065 // Stop client...
2066 void qsamplerMainForm::stopClient (void)
2067 {
2068 if (m_pClient == NULL)
2069 return;
2070
2071 // Hard-notify device configuration form,
2072 // if visible, that we're running out...
2073 if (m_pDeviceForm && m_pDeviceForm->isVisible())
2074 m_pDeviceForm->setClient(NULL);
2075
2076 // Log prepare here.
2077 appendMessages(tr("Client disconnecting..."));
2078
2079 // Clear timer counters...
2080 stopSchedule();
2081
2082 // We'll reject drops from now on...
2083 setAcceptDrops(false);
2084
2085 // Force any channel strips around, but
2086 // but avoid removing the corresponding
2087 // channels from the back-end server.
2088 m_iDirtyCount = 0;
2089 closeSession(false);
2090
2091 // Close us as a client...
2092 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2093 ::lscp_client_destroy(m_pClient);
2094 m_pClient = NULL;
2095
2096 // Log final here.
2097 appendMessages(tr("Client disconnected."));
2098
2099 // Make visible status.
2100 stabilizeForm();
2101 }
2102
2103
2104 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC