/[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 961 - (show annotations) (download) (as text)
Sun Dec 3 18:26:13 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 69980 byte(s)
- Adding preliminary MIDI instrument mapping support; now
  with an instrument list widget and editing capabilities.

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

  ViewVC Help
Powered by ViewVC