/[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 751 - (show annotations) (download) (as text)
Fri Aug 19 17:10:16 2005 UTC (18 years, 8 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66364 byte(s)
* Added MUTE/SOLO buttons to individual channel strips.

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

  ViewVC Help
Powered by ViewVC