/[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 1367 - (show annotations) (download) (as text)
Mon Oct 1 22:03:35 2007 UTC (16 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 77262 byte(s)
- Added new button Edit to the channel strips, which probably
  does exactly what you think it does: it opens an appropriate
  instrument editor application (FIX).

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

  ViewVC Help
Powered by ViewVC