/[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 1000 - (show annotations) (download) (as text)
Fri Dec 22 01:31:28 2006 UTC (17 years, 3 months ago) by capela
File MIME type: text/x-c++hdr
File size: 73559 byte(s)
* Fixed sampler channel MIDI instrument map assignment on session save.

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

  ViewVC Help
Powered by ViewVC