/[svn]/qsampler/trunk/src/qsamplerMainForm.cpp
ViewVC logotype

Contents of /qsampler/trunk/src/qsamplerMainForm.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1461 - (show annotations) (download)
Sun Oct 28 23:30:36 2007 UTC (16 years, 5 months ago) by schoenebeck
File size: 76027 byte(s)
* started to port QSampler to Qt4 (NOTE: this version is yet broken, use
  the latest tarball release 0.1.5 until the Qt4 port is completed)

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

  ViewVC Help
Powered by ViewVC