/[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 757 - (show annotations) (download) (as text)
Fri Aug 26 23:04:32 2005 UTC (18 years, 7 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66454 byte(s)
* All widget captions changed to include proper application title prefix.

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

  ViewVC Help
Powered by ViewVC