/[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 753 - (show annotations) (download) (as text)
Wed Aug 24 17:44:59 2005 UTC (18 years, 8 months ago) by capela
File MIME type: text/x-c++hdr
File size: 66343 byte(s)
Attempt to bring those aging autoconf templates to date;
  sample SPEC file for RPM build is now being included and
  generated at configure time.

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

  ViewVC Help
Powered by ViewVC