/[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 995 - (show annotations) (download) (as text)
Thu Dec 21 13:58:52 2006 UTC (17 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 73272 byte(s)
* Manual sampler reset also resets all MIDI instrument maps.

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

  ViewVC Help
Powered by ViewVC