/[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 1018 - (show annotations) (download) (as text)
Wed Jan 10 19:39:00 2007 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 72526 byte(s)
* Regression on sampler channel strip display,
 MIDI port / channel is now back.

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

  ViewVC Help
Powered by ViewVC