/[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 1372 - (show annotations) (download) (as text)
Wed Oct 3 11:34:30 2007 UTC (16 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 77776 byte(s)
* The new channel instrument editor tool is now also accessible
  from the main menu and toolbar.

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 // Edit current sampler channel.
1304 void qsamplerMainForm::editEditChannel (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->channelEdit();
1315 }
1316
1317
1318 // Reset current sampler channel.
1319 void qsamplerMainForm::editResetChannel (void)
1320 {
1321 if (m_pClient == NULL)
1322 return;
1323
1324 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1325 if (pChannelStrip == NULL)
1326 return;
1327
1328 // Just invoque the channel strip procedure.
1329 pChannelStrip->channelReset();
1330 }
1331
1332
1333 // Reset all sampler channels.
1334 void qsamplerMainForm::editResetAllChannels (void)
1335 {
1336 if (m_pClient == NULL)
1337 return;
1338
1339 // Invoque the channel strip procedure,
1340 // for all channels out there...
1341 m_pWorkspace->setUpdatesEnabled(false);
1342 QWidgetList wlist = m_pWorkspace->windowList();
1343 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1344 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1345 if (pChannelStrip)
1346 pChannelStrip->channelReset();
1347 }
1348 m_pWorkspace->setUpdatesEnabled(true);
1349 }
1350
1351
1352 //-------------------------------------------------------------------------
1353 // qsamplerMainForm -- View Action slots.
1354
1355 // Show/hide the main program window menubar.
1356 void qsamplerMainForm::viewMenubar ( bool bOn )
1357 {
1358 if (bOn)
1359 MenuBar->show();
1360 else
1361 MenuBar->hide();
1362 }
1363
1364
1365 // Show/hide the main program window toolbar.
1366 void qsamplerMainForm::viewToolbar ( bool bOn )
1367 {
1368 if (bOn) {
1369 fileToolbar->show();
1370 editToolbar->show();
1371 channelsToolbar->show();
1372 } else {
1373 fileToolbar->hide();
1374 editToolbar->hide();
1375 channelsToolbar->hide();
1376 }
1377 }
1378
1379
1380 // Show/hide the main program window statusbar.
1381 void qsamplerMainForm::viewStatusbar ( bool bOn )
1382 {
1383 if (bOn)
1384 statusBar()->show();
1385 else
1386 statusBar()->hide();
1387 }
1388
1389
1390 // Show/hide the messages window logger.
1391 void qsamplerMainForm::viewMessages ( bool bOn )
1392 {
1393 if (bOn)
1394 m_pMessages->show();
1395 else
1396 m_pMessages->hide();
1397 }
1398
1399
1400 // Show/hide the MIDI instrument list-view form.
1401 void qsamplerMainForm::viewInstruments (void)
1402 {
1403 if (m_pOptions == NULL)
1404 return;
1405
1406 if (m_pInstrumentListForm) {
1407 m_pOptions->saveWidgetGeometry(m_pInstrumentListForm);
1408 if (m_pInstrumentListForm->isVisible()) {
1409 m_pInstrumentListForm->hide();
1410 } else {
1411 m_pInstrumentListForm->show();
1412 m_pInstrumentListForm->raise();
1413 m_pInstrumentListForm->setActiveWindow();
1414 }
1415 }
1416 }
1417
1418
1419 // Show/hide the device configurator form.
1420 void qsamplerMainForm::viewDevices (void)
1421 {
1422 if (m_pOptions == NULL)
1423 return;
1424
1425 if (m_pDeviceForm) {
1426 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1427 if (m_pDeviceForm->isVisible()) {
1428 m_pDeviceForm->hide();
1429 } else {
1430 m_pDeviceForm->show();
1431 m_pDeviceForm->raise();
1432 m_pDeviceForm->setActiveWindow();
1433 }
1434 }
1435 }
1436
1437
1438 // Show options dialog.
1439 void qsamplerMainForm::viewOptions (void)
1440 {
1441 if (m_pOptions == NULL)
1442 return;
1443
1444 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1445 if (pOptionsForm) {
1446 // Check out some initial nullities(tm)...
1447 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1448 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1449 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1450 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1451 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1452 // To track down deferred or immediate changes.
1453 QString sOldServerHost = m_pOptions->sServerHost;
1454 int iOldServerPort = m_pOptions->iServerPort;
1455 int iOldServerTimeout = m_pOptions->iServerTimeout;
1456 bool bOldServerStart = m_pOptions->bServerStart;
1457 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1458 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1459 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1460 int iOldMaxVolume = m_pOptions->iMaxVolume;
1461 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1462 bool bOldKeepOnTop = m_pOptions->bKeepOnTop;
1463 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1464 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1465 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1466 bool bOldCompletePath = m_pOptions->bCompletePath;
1467 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1468 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1469 // Load the current setup settings.
1470 pOptionsForm->setup(m_pOptions);
1471 // Show the setup dialog...
1472 if (pOptionsForm->exec()) {
1473 // Warn if something will be only effective on next run.
1474 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1475 (!bOldStdoutCapture && m_pOptions->bStdoutCapture) ||
1476 ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) ||
1477 (!bOldKeepOnTop && m_pOptions->bKeepOnTop)) {
1478 QMessageBox::information(this,
1479 QSAMPLER_TITLE ": " + tr("Information"),
1480 tr("Some settings may be only effective\n"
1481 "next time you start this program."), tr("OK"));
1482 updateMessagesCapture();
1483 }
1484 // Check wheather something immediate has changed.
1485 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1486 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1487 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1488 updateRecentFilesMenu();
1489 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1490 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1491 updateInstrumentNames();
1492 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1493 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1494 updateDisplayEffect();
1495 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1496 updateDisplayFont();
1497 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1498 updateMaxVolume();
1499 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1500 updateMessagesFont();
1501 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1502 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1503 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1504 updateMessagesLimit();
1505 // And now the main thing, whether we'll do client/server recycling?
1506 if ((sOldServerHost != m_pOptions->sServerHost) ||
1507 (iOldServerPort != m_pOptions->iServerPort) ||
1508 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1509 ( bOldServerStart && !m_pOptions->bServerStart) ||
1510 (!bOldServerStart && m_pOptions->bServerStart) ||
1511 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1512 fileRestart();
1513 }
1514 // Done.
1515 delete pOptionsForm;
1516 }
1517
1518 // This makes it.
1519 stabilizeForm();
1520 }
1521
1522
1523 //-------------------------------------------------------------------------
1524 // qsamplerMainForm -- Channels action slots.
1525
1526 // Arrange channel strips.
1527 void qsamplerMainForm::channelsArrange (void)
1528 {
1529 // Full width vertical tiling
1530 QWidgetList wlist = m_pWorkspace->windowList();
1531 if (wlist.isEmpty())
1532 return;
1533
1534 m_pWorkspace->setUpdatesEnabled(false);
1535 int y = 0;
1536 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1537 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1538 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1539 // Prevent flicker...
1540 pChannelStrip->hide();
1541 pChannelStrip->showNormal();
1542 } */
1543 pChannelStrip->adjustSize();
1544 int iWidth = m_pWorkspace->width();
1545 if (iWidth < pChannelStrip->width())
1546 iWidth = pChannelStrip->width();
1547 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1548 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1549 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1550 y += iHeight;
1551 }
1552 m_pWorkspace->setUpdatesEnabled(true);
1553
1554 stabilizeForm();
1555 }
1556
1557
1558 // Auto-arrange channel strips.
1559 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1560 {
1561 if (m_pOptions == NULL)
1562 return;
1563
1564 // Toggle the auto-arrange flag.
1565 m_pOptions->bAutoArrange = bOn;
1566
1567 // If on, update whole workspace...
1568 if (m_pOptions->bAutoArrange)
1569 channelsArrange();
1570 }
1571
1572
1573 //-------------------------------------------------------------------------
1574 // qsamplerMainForm -- Help Action slots.
1575
1576 // Show information about the Qt toolkit.
1577 void qsamplerMainForm::helpAboutQt (void)
1578 {
1579 QMessageBox::aboutQt(this);
1580 }
1581
1582
1583 // Show information about application program.
1584 void qsamplerMainForm::helpAbout (void)
1585 {
1586 // Stuff the about box text...
1587 QString sText = "<p>\n";
1588 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1589 sText += "<br />\n";
1590 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1591 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1592 #ifdef CONFIG_DEBUG
1593 sText += "<small><font color=\"red\">";
1594 sText += tr("Debugging option enabled.");
1595 sText += "</font></small><br />";
1596 #endif
1597 #ifndef CONFIG_LIBGIG
1598 sText += "<small><font color=\"red\">";
1599 sText += tr("GIG (libgig) file support disabled.");
1600 sText += "</font></small><br />";
1601 #endif
1602 #ifndef CONFIG_INSTRUMENT_NAME
1603 sText += "<small><font color=\"red\">";
1604 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1605 sText += "</font></small><br />";
1606 #endif
1607 #ifndef CONFIG_MUTE_SOLO
1608 sText += "<small><font color=\"red\">";
1609 sText += tr("Sampler channel Mute/Solo support disabled.");
1610 sText += "</font></small><br />";
1611 #endif
1612 #ifndef CONFIG_AUDIO_ROUTING
1613 sText += "<small><font color=\"red\">";
1614 sText += tr("LSCP (liblscp) audio_routing support disabled.");
1615 sText += "</font></small><br />";
1616 #endif
1617 #ifndef CONFIG_FXSEND
1618 sText += "<small><font color=\"red\">";
1619 sText += tr("Sampler channel Effect Sends support disabled.");
1620 sText += "</font></small><br />";
1621 #endif
1622 #ifndef CONFIG_VOLUME
1623 sText += "<small><font color=\"red\">";
1624 sText += tr("Global volume support disabled.");
1625 sText += "</font></small><br />";
1626 #endif
1627 #ifndef CONFIG_MIDI_INSTRUMENT
1628 sText += "<small><font color=\"red\">";
1629 sText += tr("MIDI instrument mapping support disabled.");
1630 sText += "</font></small><br />";
1631 #endif
1632 #ifndef CONFIG_EDIT_INSTRUMENT
1633 sText += "<small><font color=\"red\">";
1634 sText += tr("Instrument editing support disabled.");
1635 sText += "</font></small><br />";
1636 #endif
1637 sText += "<br />\n";
1638 sText += tr("Using") + ": ";
1639 sText += ::lscp_client_package();
1640 sText += " ";
1641 sText += ::lscp_client_version();
1642 #ifdef CONFIG_LIBGIG
1643 sText += ", ";
1644 sText += gig::libraryName().c_str();
1645 sText += " ";
1646 sText += gig::libraryVersion().c_str();
1647 #endif
1648 sText += "<br />\n";
1649 sText += "<br />\n";
1650 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1651 sText += "<br />\n";
1652 sText += "<small>";
1653 sText += QSAMPLER_COPYRIGHT "<br />\n";
1654 sText += "<br />\n";
1655 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1656 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1657 sText += "</small>";
1658 sText += "</p>\n";
1659
1660 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1661 }
1662
1663
1664 //-------------------------------------------------------------------------
1665 // qsamplerMainForm -- Main window stabilization.
1666
1667 void qsamplerMainForm::stabilizeForm (void)
1668 {
1669 // Update the main application caption...
1670 QString sSessionName = sessionName(m_sFilename);
1671 if (m_iDirtyCount > 0)
1672 sSessionName += " *";
1673 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1674
1675 // Update the main menu state...
1676 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1677 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1678 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1679 fileNewAction->setEnabled(bHasClient);
1680 fileOpenAction->setEnabled(bHasClient);
1681 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1682 fileSaveAsAction->setEnabled(bHasClient);
1683 fileResetAction->setEnabled(bHasClient);
1684 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1685 editAddChannelAction->setEnabled(bHasClient);
1686 editRemoveChannelAction->setEnabled(bHasChannel);
1687 editSetupChannelAction->setEnabled(bHasChannel);
1688 #ifdef CONFIG_EDIT_INSTRUMENT
1689 editEditChannelAction->setEnabled(bHasChannel);
1690 #else
1691 editEditChannelAction->setEnabled(false);
1692 #endif
1693 editResetChannelAction->setEnabled(bHasChannel);
1694 editResetAllChannelsAction->setEnabled(bHasChannel);
1695 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1696 #ifdef CONFIG_MIDI_INSTRUMENT
1697 viewInstrumentsAction->setOn(m_pInstrumentListForm
1698 && m_pInstrumentListForm->isVisible());
1699 viewInstrumentsAction->setEnabled(bHasClient);
1700 #else
1701 viewInstrumentsAction->setEnabled(false);
1702 #endif
1703 viewDevicesAction->setOn(m_pDeviceForm
1704 && m_pDeviceForm->isVisible());
1705 viewDevicesAction->setEnabled(bHasClient);
1706 channelsArrangeAction->setEnabled(bHasChannel);
1707
1708 #ifdef CONFIG_VOLUME
1709 // Toolbar widgets are also affected...
1710 m_pVolumeSlider->setEnabled(bHasClient);
1711 m_pVolumeSpinBox->setEnabled(bHasClient);
1712 #endif
1713
1714 // Client/Server status...
1715 if (bHasClient) {
1716 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1717 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1718 } else {
1719 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1720 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1721 }
1722 // Channel status...
1723 if (bHasChannel)
1724 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1725 else
1726 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1727 // Session status...
1728 if (m_iDirtyCount > 0)
1729 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1730 else
1731 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1732
1733 // Recent files menu.
1734 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1735
1736 // Always make the latest message visible.
1737 if (m_pMessages)
1738 m_pMessages->scrollToBottom();
1739 }
1740
1741
1742 // Global volume change receiver slot.
1743 void qsamplerMainForm::volumeChanged ( int iVolume )
1744 {
1745 #ifdef CONFIG_VOLUME
1746
1747 if (m_iVolumeChanging > 0)
1748 return;
1749
1750 m_iVolumeChanging++;
1751
1752 // Update the toolbar widgets...
1753 if (m_pVolumeSlider->value() != iVolume)
1754 m_pVolumeSlider->setValue(iVolume);
1755 if (m_pVolumeSpinBox->value() != iVolume)
1756 m_pVolumeSpinBox->setValue(iVolume);
1757
1758 // Do it as commanded...
1759 float fVolume = 0.01f * float(iVolume);
1760 if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK)
1761 appendMessages(QObject::tr("Volume: %1.").arg(fVolume));
1762 else
1763 appendMessagesClient("lscp_set_volume");
1764
1765 m_iVolumeChanging--;
1766
1767 m_iDirtyCount++;
1768 stabilizeForm();
1769
1770 #endif
1771 }
1772
1773
1774 // Channel change receiver slot.
1775 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1776 {
1777 // Add this strip to the changed list...
1778 if (m_changedStrips.containsRef(pChannelStrip) == 0) {
1779 m_changedStrips.append(pChannelStrip);
1780 pChannelStrip->resetErrorCount();
1781 }
1782
1783 // Just mark the dirty form.
1784 m_iDirtyCount++;
1785 // and update the form status...
1786 stabilizeForm();
1787 }
1788
1789
1790 // Grab and restore current sampler channels session.
1791 void qsamplerMainForm::updateSession (void)
1792 {
1793 #ifdef CONFIG_VOLUME
1794 int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient));
1795 m_iVolumeChanging++;
1796 m_pVolumeSlider->setValue(iVolume);
1797 m_pVolumeSpinBox->setValue(iVolume);
1798 m_iVolumeChanging--;
1799 #endif
1800 #ifdef CONFIG_MIDI_INSTRUMENT
1801 // FIXME: Make some room for default instrument maps...
1802 int iMaps = ::lscp_get_midi_instrument_maps(m_pClient);
1803 if (iMaps < 0)
1804 appendMessagesClient("lscp_get_midi_instrument_maps");
1805 else if (iMaps < 1) {
1806 ::lscp_add_midi_instrument_map(m_pClient, tr("Chromatic").latin1());
1807 ::lscp_add_midi_instrument_map(m_pClient, tr("Drum Kits").latin1());
1808 }
1809 #endif
1810
1811 // Retrieve the current channel list.
1812 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1813 if (piChannelIDs == NULL) {
1814 if (::lscp_client_get_errno(m_pClient)) {
1815 appendMessagesClient("lscp_list_channels");
1816 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1817 }
1818 } else {
1819 // Try to (re)create each channel.
1820 m_pWorkspace->setUpdatesEnabled(false);
1821 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1822 // Check if theres already a channel strip for this one...
1823 if (!channelStrip(piChannelIDs[iChannel]))
1824 createChannelStrip(new qsamplerChannel(piChannelIDs[iChannel]));
1825 }
1826 m_pWorkspace->setUpdatesEnabled(true);
1827 }
1828
1829 // Do we auto-arrange?
1830 if (m_pOptions && m_pOptions->bAutoArrange)
1831 channelsArrange();
1832
1833 // Remember to refresh devices and instruments...
1834 if (m_pInstrumentListForm)
1835 m_pInstrumentListForm->refreshInstruments();
1836 if (m_pDeviceForm)
1837 m_pDeviceForm->refreshDevices();
1838 }
1839
1840
1841 // Update the recent files list and menu.
1842 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1843 {
1844 if (m_pOptions == NULL)
1845 return;
1846
1847 // Remove from list if already there (avoid duplicates)
1848 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1849 if (iter != m_pOptions->recentFiles.end())
1850 m_pOptions->recentFiles.remove(iter);
1851 // Put it to front...
1852 m_pOptions->recentFiles.push_front(sFilename);
1853
1854 // May update the menu.
1855 updateRecentFilesMenu();
1856 }
1857
1858
1859 // Update the recent files list and menu.
1860 void qsamplerMainForm::updateRecentFilesMenu (void)
1861 {
1862 if (m_pOptions == NULL)
1863 return;
1864
1865 // Time to keep the list under limits.
1866 int iRecentFiles = m_pOptions->recentFiles.count();
1867 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1868 m_pOptions->recentFiles.pop_back();
1869 iRecentFiles--;
1870 }
1871
1872 // rebuild the recent files menu...
1873 m_pRecentFilesMenu->clear();
1874 for (int i = 0; i < iRecentFiles; i++) {
1875 const QString& sFilename = m_pOptions->recentFiles[i];
1876 if (QFileInfo(sFilename).exists()) {
1877 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1878 .arg(i + 1).arg(sessionName(sFilename)),
1879 this, SLOT(fileOpenRecent(int)), 0, i);
1880 }
1881 }
1882 }
1883
1884
1885 // Force update of the channels instrument names mode.
1886 void qsamplerMainForm::updateInstrumentNames (void)
1887 {
1888 // Full channel list update...
1889 QWidgetList wlist = m_pWorkspace->windowList();
1890 if (wlist.isEmpty())
1891 return;
1892
1893 m_pWorkspace->setUpdatesEnabled(false);
1894 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1895 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1896 if (pChannelStrip)
1897 pChannelStrip->updateInstrumentName(true);
1898 }
1899 m_pWorkspace->setUpdatesEnabled(true);
1900 }
1901
1902
1903 // Force update of the channels display font.
1904 void qsamplerMainForm::updateDisplayFont (void)
1905 {
1906 if (m_pOptions == NULL)
1907 return;
1908
1909 // Check if display font is legal.
1910 if (m_pOptions->sDisplayFont.isEmpty())
1911 return;
1912 // Realize it.
1913 QFont font;
1914 if (!font.fromString(m_pOptions->sDisplayFont))
1915 return;
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->setDisplayFont(font);
1927 }
1928 m_pWorkspace->setUpdatesEnabled(true);
1929 }
1930
1931
1932 // Update channel strips background effect.
1933 void qsamplerMainForm::updateDisplayEffect (void)
1934 {
1935 QPixmap pm;
1936 if (m_pOptions->bDisplayEffect)
1937 pm = QPixmap::fromMimeSource("displaybg1.png");
1938
1939 // Full channel list update...
1940 QWidgetList wlist = m_pWorkspace->windowList();
1941 if (wlist.isEmpty())
1942 return;
1943
1944 m_pWorkspace->setUpdatesEnabled(false);
1945 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1946 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1947 if (pChannelStrip)
1948 pChannelStrip->setDisplayBackground(pm);
1949 }
1950 m_pWorkspace->setUpdatesEnabled(true);
1951 }
1952
1953
1954 // Force update of the channels maximum volume setting.
1955 void qsamplerMainForm::updateMaxVolume (void)
1956 {
1957 if (m_pOptions == NULL)
1958 return;
1959
1960 #ifdef CONFIG_VOLUME
1961 m_iVolumeChanging++;
1962 m_pVolumeSlider->setMaxValue(m_pOptions->iMaxVolume);
1963 m_pVolumeSpinBox->setMaxValue(m_pOptions->iMaxVolume);
1964 m_iVolumeChanging--;
1965 #endif
1966
1967 // Full channel list update...
1968 QWidgetList wlist = m_pWorkspace->windowList();
1969 if (wlist.isEmpty())
1970 return;
1971
1972 m_pWorkspace->setUpdatesEnabled(false);
1973 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1974 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1975 if (pChannelStrip)
1976 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1977 }
1978 m_pWorkspace->setUpdatesEnabled(true);
1979 }
1980
1981
1982 //-------------------------------------------------------------------------
1983 // qsamplerMainForm -- Messages window form handlers.
1984
1985 // Messages output methods.
1986 void qsamplerMainForm::appendMessages( const QString& s )
1987 {
1988 if (m_pMessages)
1989 m_pMessages->appendMessages(s);
1990
1991 statusBar()->message(s, 3000);
1992 }
1993
1994 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1995 {
1996 if (m_pMessages)
1997 m_pMessages->appendMessagesColor(s, c);
1998
1999 statusBar()->message(s, 3000);
2000 }
2001
2002 void qsamplerMainForm::appendMessagesText( const QString& s )
2003 {
2004 if (m_pMessages)
2005 m_pMessages->appendMessagesText(s);
2006 }
2007
2008 void qsamplerMainForm::appendMessagesError( const QString& s )
2009 {
2010 if (m_pMessages)
2011 m_pMessages->show();
2012
2013 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
2014
2015 // Make it look responsive...:)
2016 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
2017
2018 QMessageBox::critical(this,
2019 QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel"));
2020 }
2021
2022
2023 // This is a special message format, just for client results.
2024 void qsamplerMainForm::appendMessagesClient( const QString& s )
2025 {
2026 if (m_pClient == NULL)
2027 return;
2028
2029 appendMessagesColor(s + QString(": %1 (errno=%2)")
2030 .arg(::lscp_client_get_result(m_pClient))
2031 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
2032
2033 // Make it look responsive...:)
2034 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
2035 }
2036
2037
2038 // Force update of the messages font.
2039 void qsamplerMainForm::updateMessagesFont (void)
2040 {
2041 if (m_pOptions == NULL)
2042 return;
2043
2044 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
2045 QFont font;
2046 if (font.fromString(m_pOptions->sMessagesFont))
2047 m_pMessages->setMessagesFont(font);
2048 }
2049 }
2050
2051
2052 // Update messages window line limit.
2053 void qsamplerMainForm::updateMessagesLimit (void)
2054 {
2055 if (m_pOptions == NULL)
2056 return;
2057
2058 if (m_pMessages) {
2059 if (m_pOptions->bMessagesLimit)
2060 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
2061 else
2062 m_pMessages->setMessagesLimit(-1);
2063 }
2064 }
2065
2066
2067 // Enablement of the messages capture feature.
2068 void qsamplerMainForm::updateMessagesCapture (void)
2069 {
2070 if (m_pOptions == NULL)
2071 return;
2072
2073 if (m_pMessages)
2074 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
2075 }
2076
2077
2078 //-------------------------------------------------------------------------
2079 // qsamplerMainForm -- MDI channel strip management.
2080
2081 // The channel strip creation executive.
2082 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
2083 {
2084 if (m_pClient == NULL || pChannel == NULL)
2085 return NULL;
2086
2087 // Prepare for auto-arrange?
2088 qsamplerChannelStrip *pChannelStrip = NULL;
2089 int y = 0;
2090 if (m_pOptions && m_pOptions->bAutoArrange) {
2091 QWidgetList wlist = m_pWorkspace->windowList();
2092 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2093 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
2094 if (pChannelStrip) {
2095 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
2096 y += pChannelStrip->parentWidget()->frameGeometry().height();
2097 }
2098 }
2099 }
2100
2101 // Add a new channel itema...
2102 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
2103 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
2104 if (pChannelStrip == NULL)
2105 return NULL;
2106
2107 // Actual channel strip setup...
2108 pChannelStrip->setup(pChannel);
2109 QObject::connect(pChannelStrip,
2110 SIGNAL(channelChanged(qsamplerChannelStrip *)),
2111 SLOT(channelStripChanged(qsamplerChannelStrip *)));
2112 // Set some initial aesthetic options...
2113 if (m_pOptions) {
2114 // Background display effect...
2115 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
2116 // We'll need a display font.
2117 QFont font;
2118 if (font.fromString(m_pOptions->sDisplayFont))
2119 pChannelStrip->setDisplayFont(font);
2120 // Maximum allowed volume setting.
2121 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
2122 }
2123
2124 // Now we show up us to the world.
2125 pChannelStrip->show();
2126 // Only then, we'll auto-arrange...
2127 if (m_pOptions && m_pOptions->bAutoArrange) {
2128 int iWidth = m_pWorkspace->width();
2129 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
2130 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
2131 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
2132 }
2133
2134 // This is pretty new, so we'll watch for it closely.
2135 channelStripChanged(pChannelStrip);
2136
2137 // Return our successful reference...
2138 return pChannelStrip;
2139 }
2140
2141
2142 // Retrieve the active channel strip.
2143 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
2144 {
2145 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
2146 }
2147
2148
2149 // Retrieve a channel strip by index.
2150 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
2151 {
2152 QWidgetList wlist = m_pWorkspace->windowList();
2153 if (wlist.isEmpty())
2154 return NULL;
2155
2156 return (qsamplerChannelStrip *) wlist.at(iChannel);
2157 }
2158
2159
2160 // Retrieve a channel strip by sampler channel id.
2161 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
2162 {
2163 QWidgetList wlist = m_pWorkspace->windowList();
2164 if (wlist.isEmpty())
2165 return NULL;
2166
2167 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2168 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
2169 if (pChannelStrip) {
2170 qsamplerChannel *pChannel = pChannelStrip->channel();
2171 if (pChannel && pChannel->channelID() == iChannelID)
2172 return pChannelStrip;
2173 }
2174 }
2175
2176 // Not found.
2177 return NULL;
2178 }
2179
2180
2181 // Construct the windows menu.
2182 void qsamplerMainForm::channelsMenuAboutToShow (void)
2183 {
2184 channelsMenu->clear();
2185 channelsArrangeAction->addTo(channelsMenu);
2186 channelsAutoArrangeAction->addTo(channelsMenu);
2187
2188 QWidgetList wlist = m_pWorkspace->windowList();
2189 if (!wlist.isEmpty()) {
2190 channelsMenu->insertSeparator();
2191 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
2192 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
2193 if (pChannelStrip) {
2194 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
2195 channelsMenu->setItemParameter(iItemID, iChannel);
2196 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
2197 }
2198 }
2199 }
2200 }
2201
2202
2203 // Windows menu activation slot
2204 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
2205 {
2206 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
2207 if (pChannelStrip)
2208 pChannelStrip->showNormal();
2209 pChannelStrip->setFocus();
2210 }
2211
2212
2213 //-------------------------------------------------------------------------
2214 // qsamplerMainForm -- Timer stuff.
2215
2216 // Set the pseudo-timer delay schedule.
2217 void qsamplerMainForm::startSchedule ( int iStartDelay )
2218 {
2219 m_iStartDelay = 1 + (iStartDelay * 1000);
2220 m_iTimerDelay = 0;
2221 }
2222
2223 // Suspend the pseudo-timer delay schedule.
2224 void qsamplerMainForm::stopSchedule (void)
2225 {
2226 m_iStartDelay = 0;
2227 m_iTimerDelay = 0;
2228 }
2229
2230 // Timer slot funtion.
2231 void qsamplerMainForm::timerSlot (void)
2232 {
2233 if (m_pOptions == NULL)
2234 return;
2235
2236 // Is it the first shot on server start after a few delay?
2237 if (m_iTimerDelay < m_iStartDelay) {
2238 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
2239 if (m_iTimerDelay >= m_iStartDelay) {
2240 // If we cannot start it now, maybe a lil'mo'later ;)
2241 if (!startClient()) {
2242 m_iStartDelay += m_iTimerDelay;
2243 m_iTimerDelay = 0;
2244 }
2245 }
2246 }
2247
2248 if (m_pClient) {
2249 // Update the channel information for each pending strip...
2250 if (m_changedStrips.count() > 0) {
2251 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
2252 pChannelStrip; pChannelStrip = m_changedStrips.next()) {
2253 // If successfull, remove from pending list...
2254 if (pChannelStrip->updateChannelInfo())
2255 m_changedStrips.remove(pChannelStrip);
2256 }
2257 }
2258 // Refresh each channel usage, on each period...
2259 if (m_pOptions->bAutoRefresh) {
2260 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
2261 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) {
2262 m_iTimerSlot = 0;
2263 // Update the channel stream usage for each strip...
2264 QWidgetList wlist = m_pWorkspace->windowList();
2265 for (int iChannel = 0;
2266 iChannel < (int) wlist.count(); iChannel++) {
2267 qsamplerChannelStrip *pChannelStrip
2268 = (qsamplerChannelStrip *) wlist.at(iChannel);
2269 if (pChannelStrip && pChannelStrip->isVisible())
2270 pChannelStrip->updateChannelUsage();
2271 }
2272 }
2273 }
2274 }
2275
2276 // Register the next timer slot.
2277 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
2278 }
2279
2280
2281 //-------------------------------------------------------------------------
2282 // qsamplerMainForm -- Server stuff.
2283
2284 // Start linuxsampler server...
2285 void qsamplerMainForm::startServer (void)
2286 {
2287 if (m_pOptions == NULL)
2288 return;
2289
2290 // Aren't already a client, are we?
2291 if (!m_pOptions->bServerStart || m_pClient)
2292 return;
2293
2294 // Is the server process instance still here?
2295 if (m_pServer) {
2296 switch (QMessageBox::warning(this,
2297 QSAMPLER_TITLE ": " + tr("Warning"),
2298 tr("Could not start the LinuxSampler server.\n\n"
2299 "Maybe it ss already started."),
2300 tr("Stop"), tr("Kill"), tr("Cancel"))) {
2301 case 0:
2302 m_pServer->tryTerminate();
2303 break;
2304 case 1:
2305 m_pServer->kill();
2306 break;
2307 }
2308 return;
2309 }
2310
2311 // Reset our timer counters...
2312 stopSchedule();
2313
2314 // OK. Let's build the startup process...
2315 m_pServer = new QProcess(this);
2316
2317 // Setup stdout/stderr capture...
2318 // if (m_pOptions->bStdoutCapture) {
2319 m_pServer->setCommunication(
2320 QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
2321 QObject::connect(m_pServer,
2322 SIGNAL(readyReadStdout()),
2323 SLOT(readServerStdout()));
2324 QObject::connect(m_pServer,
2325 SIGNAL(readyReadStderr()),
2326 SLOT(readServerStdout()));
2327 // }
2328 // The unforgiveable signal communication...
2329 QObject::connect(m_pServer,
2330 SIGNAL(processExited()),
2331 SLOT(processServerExit()));
2332
2333 // Build process arguments...
2334 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
2335
2336 appendMessages(tr("Server is starting..."));
2337 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
2338
2339 // Go jack, go...
2340 if (!m_pServer->start()) {
2341 appendMessagesError(tr("Could not start server.\n\nSorry."));
2342 processServerExit();
2343 return;
2344 }
2345
2346 // Show startup results...
2347 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
2348
2349 // Reset (yet again) the timer counters,
2350 // but this time is deferred as the user opted.
2351 startSchedule(m_pOptions->iStartDelay);
2352 stabilizeForm();
2353 }
2354
2355
2356 // Stop linuxsampler server...
2357 void qsamplerMainForm::stopServer (void)
2358 {
2359 // Stop client code.
2360 stopClient();
2361
2362 // And try to stop server.
2363 if (m_pServer) {
2364 appendMessages(tr("Server is stopping..."));
2365 if (m_pServer->isRunning())
2366 m_pServer->tryTerminate();
2367 }
2368
2369 // Give it some time to terminate gracefully and stabilize...
2370 QTime t;
2371 t.start();
2372 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
2373 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
2374
2375 // Do final processing anyway.
2376 processServerExit();
2377 }
2378
2379
2380 // Stdout handler...
2381 void qsamplerMainForm::readServerStdout (void)
2382 {
2383 if (m_pMessages)
2384 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
2385 }
2386
2387
2388 // Linuxsampler server cleanup.
2389 void qsamplerMainForm::processServerExit (void)
2390 {
2391 // Force client code cleanup.
2392 stopClient();
2393
2394 // Flush anything that maybe pending...
2395 if (m_pMessages)
2396 m_pMessages->flushStdoutBuffer();
2397
2398 if (m_pServer) {
2399 // Force final server shutdown...
2400 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
2401 if (!m_pServer->normalExit())
2402 m_pServer->kill();
2403 // Destroy it.
2404 delete m_pServer;
2405 m_pServer = NULL;
2406 }
2407
2408 // Again, make status visible stable.
2409 stabilizeForm();
2410 }
2411
2412
2413 //-------------------------------------------------------------------------
2414 // qsamplerMainForm -- Client stuff.
2415
2416 // The LSCP client callback procedure.
2417 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
2418 {
2419 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
2420 if (pMainForm == NULL)
2421 return LSCP_FAILED;
2422
2423 // ATTN: DO NOT EVER call any GUI code here,
2424 // as this is run under some other thread context.
2425 // A custom event must be posted here...
2426 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
2427
2428 return LSCP_OK;
2429 }
2430
2431
2432 // Start our almighty client...
2433 bool qsamplerMainForm::startClient (void)
2434 {
2435 // Have it a setup?
2436 if (m_pOptions == NULL)
2437 return false;
2438
2439 // Aren't we already started, are we?
2440 if (m_pClient)
2441 return true;
2442
2443 // Log prepare here.
2444 appendMessages(tr("Client connecting..."));
2445
2446 // Create the client handle...
2447 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
2448 if (m_pClient == NULL) {
2449 // Is this the first try?
2450 // maybe we need to start a local server...
2451 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
2452 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
2453 else
2454 startServer();
2455 // This is always a failure.
2456 stabilizeForm();
2457 return false;
2458 }
2459 // Just set receive timeout value, blindly.
2460 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
2461 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
2462
2463 // Subscribe to channel info change notifications...
2464 if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK)
2465 appendMessagesClient("lscp_client_subscribe");
2466
2467 // We may stop scheduling around.
2468 stopSchedule();
2469
2470 // We'll accept drops from now on...
2471 setAcceptDrops(true);
2472
2473 // Log success here.
2474 appendMessages(tr("Client connected."));
2475
2476 // Hard-notify instrumnet and device configuration forms,
2477 // if visible, that we're ready...
2478 if (m_pInstrumentListForm)
2479 m_pInstrumentListForm->refreshInstruments();
2480 if (m_pDeviceForm)
2481 m_pDeviceForm->refreshDevices();
2482
2483 // Is any session pending to be loaded?
2484 if (!m_pOptions->sSessionFile.isEmpty()) {
2485 // Just load the prabably startup session...
2486 if (loadSessionFile(m_pOptions->sSessionFile)) {
2487 m_pOptions->sSessionFile = QString::null;
2488 return true;
2489 }
2490 }
2491
2492 // Make a new session
2493 return newSession();
2494 }
2495
2496
2497 // Stop client...
2498 void qsamplerMainForm::stopClient (void)
2499 {
2500 if (m_pClient == NULL)
2501 return;
2502
2503 // Log prepare here.
2504 appendMessages(tr("Client disconnecting..."));
2505
2506 // Clear timer counters...
2507 stopSchedule();
2508
2509 // We'll reject drops from now on...
2510 setAcceptDrops(false);
2511
2512 // Force any channel strips around, but
2513 // but avoid removing the corresponding
2514 // channels from the back-end server.
2515 m_iDirtyCount = 0;
2516 closeSession(false);
2517
2518 // Close us as a client...
2519 ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO);
2520 ::lscp_client_destroy(m_pClient);
2521 m_pClient = NULL;
2522
2523 // Hard-notify instrumnet and device configuration forms,
2524 // if visible, that we're running out...
2525 if (m_pInstrumentListForm)
2526 m_pInstrumentListForm->refreshInstruments();
2527 if (m_pDeviceForm)
2528 m_pDeviceForm->refreshDevices();
2529
2530 // Log final here.
2531 appendMessages(tr("Client disconnected."));
2532
2533 // Make visible status.
2534 stabilizeForm();
2535 }
2536
2537
2538 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC