/[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 586 - (show annotations) (download) (as text)
Wed May 25 11:41:35 2005 UTC (18 years, 10 months ago) by capela
File MIME type: text/x-c++hdr
File size: 65470 byte(s)
* Fixed refresh cycle of channel strips in error state,
  which was preventing correct channel info updates.

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

  ViewVC Help
Powered by ViewVC