/[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 680 - (show annotations) (download) (as text)
Thu Jun 23 22:54:14 2005 UTC (18 years, 9 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65936 byte(s)
* Release 0.1.2: Even minor workings needs a rest.

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

  ViewVC Help
Powered by ViewVC