/[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 428 - (show annotations) (download) (as text)
Mon Mar 7 17:05:55 2005 UTC (19 years ago) by capela
File MIME type: text/x-c++hdr
File size: 61268 byte(s)
More device configuration interface preparations.

1 // qsamplerMainForm.ui.h
2 //
3 // ui.h extension file, included from the uic-generated form implementation.
4 /****************************************************************************
5 Copyright (C) 2004-2005, 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
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 *****************************************************************************/
22
23 #include <qapplication.h>
24 #include <qeventloop.h>
25 #include <qworkspace.h>
26 #include <qprocess.h>
27 #include <qmessagebox.h>
28 #include <qdragobject.h>
29 #include <qregexp.h>
30 #include <qfiledialog.h>
31 #include <qfileinfo.h>
32 #include <qfile.h>
33 #include <qtextstream.h>
34 #include <qstatusbar.h>
35 #include <qlabel.h>
36 #include <qtimer.h>
37
38 #include "qsamplerAbout.h"
39 #include "qsamplerOptions.h"
40 #include "qsamplerChannel.h"
41 #include "qsamplerMessages.h"
42
43 #include "qsamplerChannelStrip.h"
44
45 #include "qsamplerDeviceForm.h"
46 #include "qsamplerOptionsForm.h"
47
48 #include "config.h"
49
50 #ifdef HAVE_SIGNAL_H
51 #include <signal.h>
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 1000
74
75 class qsamplerCustomEvent : public QCustomEvent
76 {
77 public:
78
79 // Constructor.
80 qsamplerCustomEvent(lscp_event_t event, const char *pchData, int cchData)
81 : QCustomEvent(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 // Kind of constructor.
104 void qsamplerMainForm::init (void)
105 {
106 // Initialize some pointer references.
107 m_pOptions = NULL;
108
109 // All child forms are to be created later, not earlier than setup.
110 m_pMessages = NULL;
111 m_pDeviceForm = NULL;
112
113 // We'll start clean.
114 m_iUntitled = 0;
115 m_iDirtyCount = 0;
116
117 m_pServer = NULL;
118 m_pClient = NULL;
119
120 m_iStartDelay = 0;
121 m_iTimerDelay = 0;
122
123 m_iTimerSlot = 0;
124
125 #ifdef HAVE_SIGNAL_H
126 // Set to ignore any fatal "Broken pipe" signals.
127 ::signal(SIGPIPE, SIG_IGN);
128 #endif
129
130 // Make it an MDI workspace.
131 m_pWorkspace = new QWorkspace(this);
132 m_pWorkspace->setScrollBarsEnabled(true);
133 // Set the activation connection.
134 QObject::connect(m_pWorkspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(stabilizeForm()));
135 // Make it shine :-)
136 setCentralWidget(m_pWorkspace);
137
138 // Create some statusbar labels...
139 QLabel *pLabel;
140 // Client status.
141 pLabel = new QLabel(tr("Connected"), this);
142 pLabel->setAlignment(Qt::AlignLeft);
143 pLabel->setMinimumSize(pLabel->sizeHint());
144 m_statusItem[QSAMPLER_STATUS_CLIENT] = pLabel;
145 statusBar()->addWidget(pLabel);
146 // Server address.
147 pLabel = new QLabel(this);
148 pLabel->setAlignment(Qt::AlignLeft);
149 m_statusItem[QSAMPLER_STATUS_SERVER] = pLabel;
150 statusBar()->addWidget(pLabel, 1);
151 // Channel title.
152 pLabel = new QLabel(this);
153 pLabel->setAlignment(Qt::AlignLeft);
154 m_statusItem[QSAMPLER_STATUS_CHANNEL] = pLabel;
155 statusBar()->addWidget(pLabel, 2);
156 // Session modification status.
157 pLabel = new QLabel(tr("MOD"), this);
158 pLabel->setAlignment(Qt::AlignHCenter);
159 pLabel->setMinimumSize(pLabel->sizeHint());
160 m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel;
161 statusBar()->addWidget(pLabel);
162
163 // Create the recent files sub-menu.
164 m_pRecentFilesMenu = new QPopupMenu(this);
165 fileMenu->insertSeparator(4);
166 fileMenu->insertItem(tr("Recent &Files"), m_pRecentFilesMenu, 0, 5);
167
168 #if defined(WIN32)
169 WSAStartup(MAKEWORD(1, 1), &_wsaData);
170 #endif
171 }
172
173
174 // Kind of destructor.
175 void qsamplerMainForm::destroy (void)
176 {
177 // Do final processing anyway.
178 processServerExit();
179
180 #if defined(WIN32)
181 WSACleanup();
182 #endif
183
184 // Finally drop any widgets around...
185 if (m_pDeviceForm)
186 delete m_pDeviceForm;
187 if (m_pMessages)
188 delete m_pMessages;
189 if (m_pWorkspace)
190 delete m_pWorkspace;
191
192 // Delete status item labels one by one.
193 if (m_statusItem[QSAMPLER_STATUS_CLIENT])
194 delete m_statusItem[QSAMPLER_STATUS_CLIENT];
195 if (m_statusItem[QSAMPLER_STATUS_SERVER])
196 delete m_statusItem[QSAMPLER_STATUS_SERVER];
197 if (m_statusItem[QSAMPLER_STATUS_CHANNEL])
198 delete m_statusItem[QSAMPLER_STATUS_CHANNEL];
199 if (m_statusItem[QSAMPLER_STATUS_SESSION])
200 delete m_statusItem[QSAMPLER_STATUS_SESSION];
201
202 // Delete recentfiles menu.
203 if (m_pRecentFilesMenu)
204 delete m_pRecentFilesMenu;
205 }
206
207
208 // Make and set a proper setup options step.
209 void qsamplerMainForm::setup ( qsamplerOptions *pOptions )
210 {
211 // We got options?
212 m_pOptions = pOptions;
213
214 // Some child forms are to be created right now.
215 m_pMessages = new qsamplerMessages(this);
216 m_pDeviceForm = new qsamplerDeviceForm(this);
217 // Set message defaults...
218 updateMessagesFont();
219 updateMessagesLimit();
220 updateMessagesCapture();
221 // Set the visibility signal.
222 QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), this, SLOT(stabilizeForm()));
223
224 // Initial decorations toggle state.
225 viewMenubarAction->setOn(m_pOptions->bMenubar);
226 viewToolbarAction->setOn(m_pOptions->bToolbar);
227 viewStatusbarAction->setOn(m_pOptions->bStatusbar);
228 channelsAutoArrangeAction->setOn(m_pOptions->bAutoArrange);
229
230 // Initial decorations visibility state.
231 viewMenubar(m_pOptions->bMenubar);
232 viewToolbar(m_pOptions->bToolbar);
233 viewStatusbar(m_pOptions->bStatusbar);
234
235 // Restore whole dock windows state.
236 QString sDockables = m_pOptions->settings().readEntry("/Layout/DockWindows" , QString::null);
237 if (sDockables.isEmpty()) {
238 // Message window is forced to dock on the bottom.
239 moveDockWindow(m_pMessages, Qt::DockBottom);
240 } else {
241 // Make it as the last time.
242 QTextIStream istr(&sDockables);
243 istr >> *this;
244 }
245 // Try to restore old window positioning and initial visibility.
246 m_pOptions->loadWidgetGeometry(this);
247 m_pOptions->loadWidgetGeometry(m_pDeviceForm);
248
249 // Final startup stabilization...
250 updateRecentFilesMenu();
251 stabilizeForm();
252
253 // Make it ready :-)
254 statusBar()->message(tr("Ready"), 3000);
255
256 // We'll try to start immediately...
257 startSchedule(0);
258
259 // Register the first timer slot.
260 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
261 }
262
263
264 // Window close event handlers.
265 bool qsamplerMainForm::queryClose (void)
266 {
267 bool bQueryClose = closeSession(false);
268
269 // Try to save current general state...
270 if (m_pOptions) {
271 // Some windows default fonts is here on demand too.
272 if (bQueryClose && m_pMessages)
273 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
274 // Other windows just need asking if it can close gracefully...
275 if (bQueryClose && m_pDeviceForm)
276 bQueryClose = m_pDeviceForm->queryClose();
277 // Try to save current positioning.
278 if (bQueryClose) {
279 // Save decorations state.
280 m_pOptions->bMenubar = MenuBar->isVisible();
281 m_pOptions->bToolbar = (fileToolbar->isVisible() || editToolbar->isVisible() || channelsToolbar->isVisible());
282 m_pOptions->bStatusbar = statusBar()->isVisible();
283 // Save the dock windows state.
284 QString sDockables;
285 QTextOStream ostr(&sDockables);
286 ostr << *this;
287 m_pOptions->settings().writeEntry("/Layout/DockWindows", sDockables);
288 // And the children, and the main windows state,.
289 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
290 m_pOptions->saveWidgetGeometry(this);
291 // Close popup widgets.
292 if (m_pDeviceForm)
293 m_pDeviceForm->close();
294 // Stop client and/or server, gracefully.
295 stopServer();
296 }
297 }
298
299 return bQueryClose;
300 }
301
302
303 void qsamplerMainForm::closeEvent ( QCloseEvent *pCloseEvent )
304 {
305 if (queryClose())
306 pCloseEvent->accept();
307 else
308 pCloseEvent->ignore();
309 }
310
311
312 // Drag'n'drop file handler.
313 bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
314 {
315 bool bDecode = false;
316
317 if (QTextDrag::canDecode(pEvent)) {
318 QString sText;
319 bDecode = QTextDrag::decode(pEvent, sText);
320 if (bDecode) {
321 files = QStringList::split('\n', sText);
322 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
323 *iter = QUrl((*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null)).path();
324 }
325 }
326
327 return bDecode;
328 }
329
330
331 // Window drag-n-drop event handlers.
332 void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
333 {
334 QStringList files;
335 pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
336 }
337
338
339 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
340 {
341 QStringList files;
342
343 if (!decodeDragFiles(pDropEvent, files))
344 return;
345
346 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
347 const QString& sPath = *iter;
348 if (qsamplerChannel::isInstrumentFile(sPath)) {
349 // Try to create a new channel from instrument file...
350 qsamplerChannel *pChannel = new qsamplerChannel(this);
351 if (pChannel == NULL)
352 return;
353 // Start setting the instrument filename...
354 pChannel->setInstrument(sPath, 0);
355 // Before we show it up, may be we'll
356 // better ask for some initial values?
357 if (!pChannel->channelSetup(this)) {
358 delete pChannel;
359 return;
360 }
361 // Finally, give it to a new channel strip...
362 if (!createChannelStrip(pChannel)) {
363 delete pChannel;
364 return;
365 }
366 // Make that an overall update.
367 m_iDirtyCount++;
368 stabilizeForm();
369 } // Otherwise, load an usual session file (LSCP script)...
370 else if (closeSession(true))
371 loadSessionFile(sPath);
372 // Make it look responsive...:)
373 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
374 }
375 }
376
377
378 // Custome event handler.
379 void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
380 {
381 // For the time being, just pump it to messages.
382 if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
383 qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
384 appendMessagesColor(tr("Notify event: %1 data: %2")
385 .arg(::lscp_event_to_text(pEvent->event()))
386 .arg(pEvent->data()), "#996699");
387 }
388 }
389
390
391 // Context menu event handler.
392 void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
393 {
394 stabilizeForm();
395
396 editMenu->exec(pEvent->globalPos());
397 }
398
399
400 //-------------------------------------------------------------------------
401 // qsamplerMainForm -- Brainless public property accessors.
402
403 // The global options settings property.
404 qsamplerOptions *qsamplerMainForm::options (void)
405 {
406 return m_pOptions;
407 }
408
409 // The LSCP client descriptor property.
410 lscp_client_t *qsamplerMainForm::client (void)
411 {
412 return m_pClient;
413 }
414
415
416 //-------------------------------------------------------------------------
417 // qsamplerMainForm -- Session file stuff.
418
419 // Format the displayable session filename.
420 QString qsamplerMainForm::sessionName ( const QString& sFilename )
421 {
422 bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
423 QString sSessionName = sFilename;
424 if (sSessionName.isEmpty())
425 sSessionName = tr("Untitled") + QString::number(m_iUntitled);
426 else if (!bCompletePath)
427 sSessionName = QFileInfo(sSessionName).fileName();
428 return sSessionName;
429 }
430
431
432 // Create a new session file from scratch.
433 bool qsamplerMainForm::newSession (void)
434 {
435 // Check if we can do it.
436 if (!closeSession(true))
437 return false;
438
439 // Give us what the server has, right now...
440 updateSession();
441
442 // Ok increment untitled count.
443 m_iUntitled++;
444
445 // Stabilize form.
446 m_sFilename = QString::null;
447 m_iDirtyCount = 0;
448 appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename)));
449 stabilizeForm();
450
451 return true;
452 }
453
454
455 // Open an existing sampler session.
456 bool qsamplerMainForm::openSession (void)
457 {
458 if (m_pOptions == NULL)
459 return false;
460
461 // Ask for the filename to open...
462 QString sFilename = QFileDialog::getOpenFileName(
463 m_pOptions->sSessionDir, // Start here.
464 tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
465 this, 0, // Parent and name (none)
466 tr("Open Session") // Caption.
467 );
468
469 // Have we cancelled?
470 if (sFilename.isEmpty())
471 return false;
472
473 // Check if we're going to discard safely the current one...
474 if (!closeSession(true))
475 return false;
476
477 // Load it right away.
478 return loadSessionFile(sFilename);
479 }
480
481
482 // Save current sampler session with another name.
483 bool qsamplerMainForm::saveSession ( bool bPrompt )
484 {
485 if (m_pOptions == NULL)
486 return false;
487
488 QString sFilename = m_sFilename;
489
490 // Ask for the file to save, if there's none...
491 if (bPrompt || sFilename.isEmpty()) {
492 // If none is given, assume default directory.
493 if (sFilename.isEmpty())
494 sFilename = m_pOptions->sSessionDir;
495 // Prompt the guy...
496 sFilename = QFileDialog::getSaveFileName(
497 sFilename, // Start here.
498 tr("LSCP Session files") + " (*.lscp)", // Filter (LSCP files)
499 this, 0, // Parent and name (none)
500 tr("Save Session") // Caption.
501 );
502 // Have we cancelled it?
503 if (sFilename.isEmpty())
504 return false;
505 // Enforce .lscp extension...
506 if (QFileInfo(sFilename).extension().isEmpty())
507 sFilename += ".lscp";
508 // Check if already exists...
509 if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) {
510 if (QMessageBox::warning(this, tr("Warning"),
511 tr("The file already exists:\n\n"
512 "\"%1\"\n\n"
513 "Do you want to replace it?")
514 .arg(sFilename),
515 tr("Replace"), tr("Cancel")) > 0)
516 return false;
517 }
518 }
519
520 // Save it right away.
521 return saveSessionFile(sFilename);
522 }
523
524
525 // Close current session.
526 bool qsamplerMainForm::closeSession ( bool bForce )
527 {
528 bool bClose = true;
529
530 // Are we dirty enough to prompt it?
531 if (m_iDirtyCount > 0) {
532 switch (QMessageBox::warning(this, tr("Warning"),
533 tr("The current session has been changed:\n\n"
534 "\"%1\"\n\n"
535 "Do you want to save the changes?")
536 .arg(sessionName(m_sFilename)),
537 tr("Save"), tr("Discard"), tr("Cancel"))) {
538 case 0: // Save...
539 bClose = saveSession(false);
540 // Fall thru....
541 case 1: // Discard
542 break;
543 default: // Cancel.
544 bClose = false;
545 break;
546 }
547 }
548
549 // If we may close it, dot it.
550 if (bClose) {
551 // Remove all channel strips from sight...
552 m_pWorkspace->setUpdatesEnabled(false);
553 QWidgetList wlist = m_pWorkspace->windowList();
554 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
555 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
556 if (pChannelStrip) {
557 qsamplerChannel *pChannel = pChannelStrip->channel();
558 if (bForce && pChannel)
559 pChannel->removeChannel();
560 delete pChannelStrip;
561 }
562 }
563 m_pWorkspace->setUpdatesEnabled(true);
564 // We're now clean, for sure.
565 m_iDirtyCount = 0;
566 }
567
568 return bClose;
569 }
570
571
572 // Load a session from specific file path.
573 bool qsamplerMainForm::loadSessionFile ( const QString& sFilename )
574 {
575 if (m_pClient == NULL)
576 return false;
577
578 // Open and read from real file.
579 QFile file(sFilename);
580 if (!file.open(IO_ReadOnly)) {
581 appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
582 return false;
583 }
584
585 // Read the file.
586 int iErrors = 0;
587 QTextStream ts(&file);
588 while (!ts.atEnd()) {
589 // Read the line.
590 QString sCommand = ts.readLine().simplifyWhiteSpace();
591 // If not empty, nor a comment, call the server...
592 if (!sCommand.isEmpty() && sCommand[0] != '#') {
593 appendMessagesColor(sCommand, "#996633");
594 // Remember that, no matter what,
595 // all LSCP commands are CR/LF terminated.
596 sCommand += "\r\n";
597 if (::lscp_client_query(m_pClient, sCommand.latin1()) != LSCP_OK) {
598 appendMessagesClient("lscp_client_query");
599 iErrors++;
600 break;
601 }
602 }
603 // Try to make it snappy :)
604 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
605 }
606
607 // Ok. we've read it.
608 file.close();
609
610 // Have we any errors?
611 if (iErrors > 0)
612 appendMessagesError(tr("Session could not be loaded\nfrom \"%1\".\n\nSorry.").arg(sFilename));
613
614 // Now we'll try to create (update) the whole GUI session.
615 updateSession();
616
617 // Save as default session directory.
618 if (m_pOptions)
619 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
620 // We're not dirty anymore.
621 m_iDirtyCount = 0;
622 // Stabilize form...
623 m_sFilename = sFilename;
624 updateRecentFiles(sFilename);
625 appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
626
627 // Make that an overall update.
628 stabilizeForm();
629 return true;
630 }
631
632
633 // Save current session to specific file path.
634 bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
635 {
636 // Open and write into real file.
637 QFile file(sFilename);
638 if (!file.open(IO_WriteOnly | IO_Truncate)) {
639 appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
640 return false;
641 }
642
643 // Write the file.
644 int iErrors = 0;
645 QTextStream ts(&file);
646 ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
647 ts << "# " << tr("Version")
648 << ": " QSAMPLER_VERSION << endl;
649 ts << "# " << tr("Build")
650 << ": " __DATE__ " " __TIME__ << endl;
651 ts << "#" << endl;
652 ts << "# " << tr("File")
653 << ": " << QFileInfo(sFilename).fileName() << endl;
654 ts << "# " << tr("Date")
655 << ": " << QDate::currentDate().toString("MMMM dd yyyy")
656 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
657 ts << "#" << endl;
658 ts << endl;
659 QWidgetList wlist = m_pWorkspace->windowList();
660 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
661 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
662 if (pChannelStrip) {
663 qsamplerChannel *pChannel = pChannelStrip->channel();
664 if (pChannel) {
665 ts << "# Channel " << iChannel << endl;
666 ts << "ADD CHANNEL" << endl;
667 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel << " " << pChannel->audioDriver() << endl;
668 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel << " " << pChannel->midiDriver() << endl;
669 // ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel << " " << pChannel->midiPort() << endl;
670 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " ";
671 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
672 ts << "ALL";
673 else
674 ts << pChannel->midiChannel();
675 ts << endl;
676 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannel << endl;
677 ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannel << endl;
678 ts << "SET CHANNEL VOLUME " << iChannel << " " << pChannel->volume() << endl;
679 ts << endl;
680 }
681 }
682 // Try to keep it snappy :)
683 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
684 }
685
686 // Ok. we've wrote it.
687 file.close();
688
689 // Have we any errors?
690 if (iErrors > 0)
691 appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
692
693 // Save as default session directory.
694 if (m_pOptions)
695 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
696 // We're not dirty anymore.
697 m_iDirtyCount = 0;
698 // Stabilize form...
699 m_sFilename = sFilename;
700 updateRecentFiles(sFilename);
701 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
702 stabilizeForm();
703 return true;
704 }
705
706
707 //-------------------------------------------------------------------------
708 // qsamplerMainForm -- File Action slots.
709
710 // Create a new sampler session.
711 void qsamplerMainForm::fileNew (void)
712 {
713 // Of course we'll start clean new.
714 newSession();
715 }
716
717
718 // Open an existing sampler session.
719 void qsamplerMainForm::fileOpen (void)
720 {
721 // Open it right away.
722 openSession();
723 }
724
725
726 // Open a recent file session.
727 void qsamplerMainForm::fileOpenRecent ( int iIndex )
728 {
729 // Check if we can safely close the current session...
730 if (m_pOptions && closeSession(true)) {
731 QString sFilename = m_pOptions->recentFiles[iIndex];
732 loadSessionFile(sFilename);
733 }
734 }
735
736
737 // Save current sampler session.
738 void qsamplerMainForm::fileSave (void)
739 {
740 // Save it right away.
741 saveSession(false);
742 }
743
744
745 // Save current sampler session with another name.
746 void qsamplerMainForm::fileSaveAs (void)
747 {
748 // Save it right away, maybe with another name.
749 saveSession(true);
750 }
751
752
753 // Reset the sampler instance.
754 void qsamplerMainForm::fileReset (void)
755 {
756 if (m_pClient == NULL)
757 return;
758
759 // Ask user whether he/she want's an internal sampler reset...
760 if (QMessageBox::warning(this, tr("Warning"),
761 tr("Resetting the sampler instance will close\n"
762 "all device and channel configurations.\n\n"
763 "Please note that this operation may cause\n"
764 "temporary MIDI and Audio disruption\n\n"
765 "Do you want to reset the sampler engine now?"),
766 tr("Reset"), tr("Cancel")) > 0)
767 return;
768
769 // Just do the reset, after closing down current session...
770 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
771 appendMessagesClient("lscp_reset_sampler");
772 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
773 return;
774 }
775
776 // Log this.
777 appendMessages(tr("Sampler reset."));
778
779 // Make it a new session...
780 newSession();
781 }
782
783
784 // Restart the client/server instance.
785 void qsamplerMainForm::fileRestart (void)
786 {
787 if (m_pOptions == NULL)
788 return;
789
790 bool bRestart = true;
791
792 // Ask user whether he/she want's a complete restart...
793 // (if we're currently up and running)
794 if (bRestart && m_pClient) {
795 bRestart = (QMessageBox::warning(this, tr("Warning"),
796 tr("New settings will be effective after\n"
797 "restarting the client/server connection.\n\n"
798 "Please note that this operation may cause\n"
799 "temporary MIDI and Audio disruption\n\n"
800 "Do you want to restart the connection now?"),
801 tr("Restart"), tr("Cancel")) == 0);
802 }
803
804 // Are we still for it?
805 if (bRestart && closeSession(true)) {
806 // Stop server, it will force the client too.
807 stopServer();
808 // Reschedule a restart...
809 startSchedule(m_pOptions->iStartDelay);
810 }
811 }
812
813
814 // Exit application program.
815 void qsamplerMainForm::fileExit (void)
816 {
817 // Go for close the whole thing.
818 close();
819 }
820
821
822 //-------------------------------------------------------------------------
823 // qsamplerMainForm -- Edit Action slots.
824
825 // Add a new sampler channel.
826 void qsamplerMainForm::editAddChannel (void)
827 {
828 if (m_pClient == NULL)
829 return;
830
831 // Just create the channel instance...
832 qsamplerChannel *pChannel = new qsamplerChannel(this);
833 if (pChannel == NULL)
834 return;
835
836 // Before we show it up, may be we'll
837 // better ask for some initial values?
838 if (!pChannel->channelSetup(this)) {
839 delete pChannel;
840 return;
841 }
842
843 // And give it to the strip (will own the channel instance, if successful).
844 if (!createChannelStrip(pChannel)) {
845 delete pChannel;
846 return;
847 }
848
849 // Make that an overall update.
850 m_iDirtyCount++;
851 stabilizeForm();
852 }
853
854
855 // Remove current sampler channel.
856 void qsamplerMainForm::editRemoveChannel (void)
857 {
858 if (m_pClient == NULL)
859 return;
860
861 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
862 if (pChannelStrip == NULL)
863 return;
864
865 qsamplerChannel *pChannel = pChannelStrip->channel();
866 if (pChannel == NULL)
867 return;
868
869 // Prompt user if he/she's sure about this...
870 if (m_pOptions && m_pOptions->bConfirmRemove) {
871 if (QMessageBox::warning(this, tr("Warning"),
872 tr("About to remove channel:\n\n"
873 "%1\n\n"
874 "Are you sure?")
875 .arg(pChannelStrip->caption()),
876 tr("OK"), tr("Cancel")) > 0)
877 return;
878 }
879
880 // Remove the existing sampler channel.
881 if (!pChannel->removeChannel())
882 return;
883
884 // Just delete the channel strip.
885 delete pChannelStrip;
886
887 // Do we auto-arrange?
888 if (m_pOptions && m_pOptions->bAutoArrange)
889 channelsArrange();
890
891 // We'll be dirty, for sure...
892 m_iDirtyCount++;
893 stabilizeForm();
894 }
895
896
897 // Setup current sampler channel.
898 void qsamplerMainForm::editSetupChannel (void)
899 {
900 if (m_pClient == NULL)
901 return;
902
903 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
904 if (pChannelStrip == NULL)
905 return;
906
907 // Just invoque the channel strip procedure.
908 pChannelStrip->channelSetup();
909 }
910
911
912 // Reset current sampler channel.
913 void qsamplerMainForm::editResetChannel (void)
914 {
915 if (m_pClient == NULL)
916 return;
917
918 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
919 if (pChannelStrip == NULL)
920 return;
921
922 // Just invoque the channel strip procedure.
923 pChannelStrip->channelReset();
924 }
925
926
927 // Reset all sampler channels.
928 void qsamplerMainForm::editResetAllChannels (void)
929 {
930 if (m_pClient == NULL)
931 return;
932
933 // Invoque the channel strip procedure,
934 // for all channels out there...
935 m_pWorkspace->setUpdatesEnabled(false);
936 QWidgetList wlist = m_pWorkspace->windowList();
937 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
938 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
939 if (pChannelStrip)
940 pChannelStrip->channelReset();
941 }
942 m_pWorkspace->setUpdatesEnabled(true);
943 }
944
945
946 //-------------------------------------------------------------------------
947 // qsamplerMainForm -- View Action slots.
948
949 // Show/hide the main program window menubar.
950 void qsamplerMainForm::viewMenubar ( bool bOn )
951 {
952 if (bOn)
953 MenuBar->show();
954 else
955 MenuBar->hide();
956 }
957
958
959 // Show/hide the main program window toolbar.
960 void qsamplerMainForm::viewToolbar ( bool bOn )
961 {
962 if (bOn) {
963 fileToolbar->show();
964 editToolbar->show();
965 channelsToolbar->show();
966 } else {
967 fileToolbar->hide();
968 editToolbar->hide();
969 channelsToolbar->hide();
970 }
971 }
972
973
974 // Show/hide the main program window statusbar.
975 void qsamplerMainForm::viewStatusbar ( bool bOn )
976 {
977 if (bOn)
978 statusBar()->show();
979 else
980 statusBar()->hide();
981 }
982
983
984 // Show/hide the messages window logger.
985 void qsamplerMainForm::viewMessages ( bool bOn )
986 {
987 if (bOn)
988 m_pMessages->show();
989 else
990 m_pMessages->hide();
991 }
992
993
994 // Show/hide the device configurator form.
995 void qsamplerMainForm::viewDevices (void)
996 {
997 if (m_pOptions == NULL)
998 return;
999
1000 if (m_pDeviceForm) {
1001 m_pOptions->saveWidgetGeometry(m_pDeviceForm);
1002 m_pDeviceForm->setClient(m_pClient);
1003 if (m_pDeviceForm->isVisible()) {
1004 m_pDeviceForm->hide();
1005 } else {
1006 m_pDeviceForm->show();
1007 m_pDeviceForm->raise();
1008 m_pDeviceForm->setActiveWindow();
1009 }
1010 }
1011 }
1012
1013
1014 // Show options dialog.
1015 void qsamplerMainForm::viewOptions (void)
1016 {
1017 if (m_pOptions == NULL)
1018 return;
1019
1020 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
1021 if (pOptionsForm) {
1022 // Check out some initial nullities(tm)...
1023 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1024 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
1025 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
1026 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
1027 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
1028 // To track down deferred or immediate changes.
1029 QString sOldServerHost = m_pOptions->sServerHost;
1030 int iOldServerPort = m_pOptions->iServerPort;
1031 int iOldServerTimeout = m_pOptions->iServerTimeout;
1032 bool bOldServerStart = m_pOptions->bServerStart;
1033 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
1034 QString sOldDisplayFont = m_pOptions->sDisplayFont;
1035 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
1036 int iOldMaxVolume = m_pOptions->iMaxVolume;
1037 QString sOldMessagesFont = m_pOptions->sMessagesFont;
1038 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
1039 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
1040 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
1041 bool bOldCompletePath = m_pOptions->bCompletePath;
1042 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
1043 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
1044 // Load the current setup settings.
1045 pOptionsForm->setup(m_pOptions);
1046 // Show the setup dialog...
1047 if (pOptionsForm->exec()) {
1048 // Warn if something will be only effective on next run.
1049 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
1050 (!bOldStdoutCapture && m_pOptions->bStdoutCapture)) {
1051 QMessageBox::information(this, tr("Information"),
1052 tr("Some settings may be only effective\n"
1053 "next time you start this program."), tr("OK"));
1054 updateMessagesCapture();
1055 }
1056 // Check wheather something immediate has changed.
1057 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1058 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1059 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1060 updateRecentFilesMenu();
1061 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1062 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1063 updateInstrumentNames();
1064 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1065 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1066 updateDisplayEffect();
1067 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1068 updateDisplayFont();
1069 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1070 updateMaxVolume();
1071 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1072 updateMessagesFont();
1073 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1074 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1075 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1076 updateMessagesLimit();
1077 // And now the main thing, whether we'll do client/server recycling?
1078 if ((sOldServerHost != m_pOptions->sServerHost) ||
1079 (iOldServerPort != m_pOptions->iServerPort) ||
1080 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1081 ( bOldServerStart && !m_pOptions->bServerStart) ||
1082 (!bOldServerStart && m_pOptions->bServerStart) ||
1083 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1084 fileRestart();
1085 }
1086 // Done.
1087 delete pOptionsForm;
1088 }
1089
1090 // This makes it.
1091 stabilizeForm();
1092 }
1093
1094
1095 //-------------------------------------------------------------------------
1096 // qsamplerMainForm -- Channels action slots.
1097
1098 // Arrange channel strips.
1099 void qsamplerMainForm::channelsArrange (void)
1100 {
1101 // Full width vertical tiling
1102 QWidgetList wlist = m_pWorkspace->windowList();
1103 if (wlist.isEmpty())
1104 return;
1105
1106 m_pWorkspace->setUpdatesEnabled(false);
1107 int y = 0;
1108 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1109 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1110 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1111 // Prevent flicker...
1112 pChannelStrip->hide();
1113 pChannelStrip->showNormal();
1114 } */
1115 pChannelStrip->adjustSize();
1116 int iWidth = m_pWorkspace->width();
1117 if (iWidth < pChannelStrip->width())
1118 iWidth = pChannelStrip->width();
1119 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1120 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1121 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1122 y += iHeight;
1123 }
1124 m_pWorkspace->setUpdatesEnabled(true);
1125
1126 stabilizeForm();
1127 }
1128
1129
1130 // Auto-arrange channel strips.
1131 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1132 {
1133 if (m_pOptions == NULL)
1134 return;
1135
1136 // Toggle the auto-arrange flag.
1137 m_pOptions->bAutoArrange = bOn;
1138
1139 // If on, update whole workspace...
1140 if (m_pOptions->bAutoArrange)
1141 channelsArrange();
1142 }
1143
1144
1145 //-------------------------------------------------------------------------
1146 // qsamplerMainForm -- Help Action slots.
1147
1148 // Show information about the Qt toolkit.
1149 void qsamplerMainForm::helpAboutQt (void)
1150 {
1151 QMessageBox::aboutQt(this);
1152 }
1153
1154
1155 // Show information about application program.
1156 void qsamplerMainForm::helpAbout (void)
1157 {
1158 // Stuff the about box text...
1159 QString sText = "<p>\n";
1160 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1161 sText += "<br />\n";
1162 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1163 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1164 #ifdef CONFIG_DEBUG
1165 sText += "<small><font color=\"red\">";
1166 sText += tr("Debugging option enabled.");
1167 sText += "</font></small><br />";
1168 #endif
1169 #ifndef CONFIG_LIBGIG
1170 sText += "<small><font color=\"red\">";
1171 sText += tr("GIG (libgig) file support disabled.");
1172 sText += "</font></small><br />";
1173 #endif
1174 #ifndef CONFIG_INSTRUMENT_NAME
1175 sText += "<small><font color=\"red\">";
1176 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1177 sText += "</font></small><br />";
1178 #endif
1179 sText += "<br />\n";
1180 sText += tr("Using") + ": ";
1181 sText += ::lscp_client_package();
1182 sText += " ";
1183 sText += ::lscp_client_version();
1184 sText += "<br />\n";
1185 sText += "<br />\n";
1186 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1187 sText += "<br />\n";
1188 sText += "<small>";
1189 sText += QSAMPLER_COPYRIGHT "<br />\n";
1190 sText += "<br />\n";
1191 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1192 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1193 sText += "</small>";
1194 sText += "</p>\n";
1195
1196 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1197 }
1198
1199
1200 //-------------------------------------------------------------------------
1201 // qsamplerMainForm -- Main window stabilization.
1202
1203 void qsamplerMainForm::stabilizeForm (void)
1204 {
1205 // Update the main application caption...
1206 QString sSessionName = sessionName(m_sFilename);
1207 if (m_iDirtyCount > 0)
1208 sSessionName += '*';
1209 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName));
1210
1211 // Update the main menu state...
1212 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1213 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1214 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1215 fileNewAction->setEnabled(bHasClient);
1216 fileOpenAction->setEnabled(bHasClient);
1217 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1218 fileSaveAsAction->setEnabled(bHasClient);
1219 fileResetAction->setEnabled(bHasClient);
1220 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1221 editAddChannelAction->setEnabled(bHasClient);
1222 editRemoveChannelAction->setEnabled(bHasChannel);
1223 editSetupChannelAction->setEnabled(bHasChannel);
1224 editResetChannelAction->setEnabled(bHasChannel);
1225 editResetAllChannelsAction->setEnabled(bHasChannel);
1226 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1227 viewDevicesAction->setOn(m_pDeviceForm && m_pDeviceForm->isVisible());
1228 viewDevicesAction->setEnabled(bHasClient);
1229 channelsArrangeAction->setEnabled(bHasChannel);
1230
1231 // Client/Server status...
1232 if (bHasClient) {
1233 m_statusItem[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1234 m_statusItem[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1235 } else {
1236 m_statusItem[QSAMPLER_STATUS_CLIENT]->clear();
1237 m_statusItem[QSAMPLER_STATUS_SERVER]->clear();
1238 }
1239 // Channel status...
1240 if (bHasChannel)
1241 m_statusItem[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1242 else
1243 m_statusItem[QSAMPLER_STATUS_CHANNEL]->clear();
1244 // Session status...
1245 if (m_iDirtyCount > 0)
1246 m_statusItem[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1247 else
1248 m_statusItem[QSAMPLER_STATUS_SESSION]->clear();
1249
1250 // Recent files menu.
1251 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1252
1253 // Always make the latest message visible.
1254 if (m_pMessages)
1255 m_pMessages->scrollToBottom();
1256 }
1257
1258
1259 // Channel change receiver slot.
1260 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1261 {
1262 // Add this strip to the changed list...
1263 if (m_changedStrips.containsRef(pChannelStrip) == 0)
1264 m_changedStrips.append(pChannelStrip);
1265
1266 // Just mark the dirty form.
1267 m_iDirtyCount++;
1268 // and update the form status...
1269 stabilizeForm();
1270 }
1271
1272
1273 // Grab and restore current sampler channels session.
1274 void qsamplerMainForm::updateSession (void)
1275 {
1276 // Retrieve the current channel list.
1277 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1278 if (piChannelIDs == NULL) {
1279 if (::lscp_client_get_errno(m_pClient)) {
1280 appendMessagesClient("lscp_list_channels");
1281 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1282 }
1283 return;
1284 }
1285
1286 // Try to (re)create each channel.
1287 m_pWorkspace->setUpdatesEnabled(false);
1288 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1289 // Check if theres already a channel strip for this one...
1290 if (!channelStrip(piChannelIDs[iChannel]))
1291 createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1292 // Make it visibly responsive...
1293 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1294 }
1295 m_pWorkspace->setUpdatesEnabled(true);
1296 }
1297
1298
1299 // Update the recent files list and menu.
1300 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1301 {
1302 if (m_pOptions == NULL)
1303 return;
1304
1305 // Remove from list if already there (avoid duplicates)
1306 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1307 if (iter != m_pOptions->recentFiles.end())
1308 m_pOptions->recentFiles.remove(iter);
1309 // Put it to front...
1310 m_pOptions->recentFiles.push_front(sFilename);
1311
1312 // May update the menu.
1313 updateRecentFilesMenu();
1314 }
1315
1316
1317 // Update the recent files list and menu.
1318 void qsamplerMainForm::updateRecentFilesMenu (void)
1319 {
1320 if (m_pOptions == NULL)
1321 return;
1322
1323 // Time to keep the list under limits.
1324 int iRecentFiles = m_pOptions->recentFiles.count();
1325 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1326 m_pOptions->recentFiles.pop_back();
1327 iRecentFiles--;
1328 }
1329
1330 // rebuild the recent files menu...
1331 m_pRecentFilesMenu->clear();
1332 for (int i = 0; i < iRecentFiles; i++) {
1333 const QString& sFilename = m_pOptions->recentFiles[i];
1334 if (QFileInfo(sFilename).exists()) {
1335 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1336 .arg(i + 1).arg(sessionName(sFilename)),
1337 this, SLOT(fileOpenRecent(int)), 0, i);
1338 }
1339 }
1340 }
1341
1342
1343 // Force update of the channels instrument names mode.
1344 void qsamplerMainForm::updateInstrumentNames (void)
1345 {
1346 // Full channel list update...
1347 QWidgetList wlist = m_pWorkspace->windowList();
1348 if (wlist.isEmpty())
1349 return;
1350
1351 m_pWorkspace->setUpdatesEnabled(false);
1352 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1353 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1354 if (pChannelStrip)
1355 pChannelStrip->updateInstrumentName(true);
1356 }
1357 m_pWorkspace->setUpdatesEnabled(true);
1358 }
1359
1360
1361 // Force update of the channels display font.
1362 void qsamplerMainForm::updateDisplayFont (void)
1363 {
1364 if (m_pOptions == NULL)
1365 return;
1366
1367 // Check if display font is legal.
1368 if (m_pOptions->sDisplayFont.isEmpty())
1369 return;
1370 // Realize it.
1371 QFont font;
1372 if (!font.fromString(m_pOptions->sDisplayFont))
1373 return;
1374
1375 // Full channel list update...
1376 QWidgetList wlist = m_pWorkspace->windowList();
1377 if (wlist.isEmpty())
1378 return;
1379
1380 m_pWorkspace->setUpdatesEnabled(false);
1381 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1382 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1383 if (pChannelStrip)
1384 pChannelStrip->setDisplayFont(font);
1385 }
1386 m_pWorkspace->setUpdatesEnabled(true);
1387 }
1388
1389
1390 // Update channel strips background effect.
1391 void qsamplerMainForm::updateDisplayEffect (void)
1392 {
1393 QPixmap pm;
1394 if (m_pOptions->bDisplayEffect)
1395 pm = QPixmap::fromMimeSource("displaybg1.png");
1396
1397 // Full channel list update...
1398 QWidgetList wlist = m_pWorkspace->windowList();
1399 if (wlist.isEmpty())
1400 return;
1401
1402 m_pWorkspace->setUpdatesEnabled(false);
1403 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1404 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1405 if (pChannelStrip)
1406 pChannelStrip->setDisplayBackground(pm);
1407 }
1408 m_pWorkspace->setUpdatesEnabled(true);
1409 }
1410
1411
1412 // Force update of the channels maximum volume setting.
1413 void qsamplerMainForm::updateMaxVolume (void)
1414 {
1415 if (m_pOptions == NULL)
1416 return;
1417
1418 // Full channel list update...
1419 QWidgetList wlist = m_pWorkspace->windowList();
1420 if (wlist.isEmpty())
1421 return;
1422
1423 m_pWorkspace->setUpdatesEnabled(false);
1424 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1425 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1426 if (pChannelStrip)
1427 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1428 }
1429 m_pWorkspace->setUpdatesEnabled(true);
1430 }
1431
1432
1433 //-------------------------------------------------------------------------
1434 // qsamplerMainForm -- Messages window form handlers.
1435
1436 // Messages output methods.
1437 void qsamplerMainForm::appendMessages( const QString& s )
1438 {
1439 if (m_pMessages)
1440 m_pMessages->appendMessages(s);
1441
1442 statusBar()->message(s, 3000);
1443 }
1444
1445 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1446 {
1447 if (m_pMessages)
1448 m_pMessages->appendMessagesColor(s, c);
1449
1450 statusBar()->message(s, 3000);
1451 }
1452
1453 void qsamplerMainForm::appendMessagesText( const QString& s )
1454 {
1455 if (m_pMessages)
1456 m_pMessages->appendMessagesText(s);
1457 }
1458
1459 void qsamplerMainForm::appendMessagesError( const QString& s )
1460 {
1461 if (m_pMessages)
1462 m_pMessages->show();
1463
1464 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1465
1466 QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1467 }
1468
1469
1470 // This is a special message format, just for client results.
1471 void qsamplerMainForm::appendMessagesClient( const QString& s )
1472 {
1473 if (m_pClient == NULL)
1474 return;
1475
1476 appendMessagesColor(s + QString(": %1 (errno=%2)")
1477 .arg(::lscp_client_get_result(m_pClient))
1478 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1479 }
1480
1481
1482 // Force update of the messages font.
1483 void qsamplerMainForm::updateMessagesFont (void)
1484 {
1485 if (m_pOptions == NULL)
1486 return;
1487
1488 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1489 QFont font;
1490 if (font.fromString(m_pOptions->sMessagesFont))
1491 m_pMessages->setMessagesFont(font);
1492 }
1493 }
1494
1495
1496 // Update messages window line limit.
1497 void qsamplerMainForm::updateMessagesLimit (void)
1498 {
1499 if (m_pOptions == NULL)
1500 return;
1501
1502 if (m_pMessages) {
1503 if (m_pOptions->bMessagesLimit)
1504 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1505 else
1506 m_pMessages->setMessagesLimit(0);
1507 }
1508 }
1509
1510
1511 // Enablement of the messages capture feature.
1512 void qsamplerMainForm::updateMessagesCapture (void)
1513 {
1514 if (m_pOptions == NULL)
1515 return;
1516
1517 if (m_pMessages)
1518 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1519 }
1520
1521
1522 //-------------------------------------------------------------------------
1523 // qsamplerMainForm -- MDI channel strip management.
1524
1525 // The channel strip creation executive.
1526 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1527 {
1528 if (m_pClient == NULL || pChannel == NULL)
1529 return NULL;
1530
1531 // Prepare for auto-arrange?
1532 qsamplerChannelStrip *pChannelStrip = NULL;
1533 int y = 0;
1534 if (m_pOptions && m_pOptions->bAutoArrange) {
1535 QWidgetList wlist = m_pWorkspace->windowList();
1536 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1537 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1538 if (pChannelStrip) {
1539 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1540 y += pChannelStrip->parentWidget()->frameGeometry().height();
1541 }
1542 }
1543 }
1544
1545 // Add a new channel itema...
1546 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1547 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1548 if (pChannelStrip == NULL)
1549 return NULL;
1550
1551 // Actual channel strip setup...
1552 pChannelStrip->setup(pChannel);
1553 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1554 // Set some initial aesthetic options...
1555 if (m_pOptions) {
1556 // Background display effect...
1557 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1558 // We'll need a display font.
1559 QFont font;
1560 if (font.fromString(m_pOptions->sDisplayFont))
1561 pChannelStrip->setDisplayFont(font);
1562 // Maximum allowed volume setting.
1563 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1564 }
1565
1566 // Now we show up us to the world.
1567 pChannelStrip->show();
1568 // Only then, we'll auto-arrange...
1569 if (m_pOptions && m_pOptions->bAutoArrange) {
1570 int iWidth = m_pWorkspace->width();
1571 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1572 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1573 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1574 }
1575
1576 // This is pretty new, so we'll watch for it closely.
1577 channelStripChanged(pChannelStrip);
1578
1579 // Return our successful reference...
1580 return pChannelStrip;
1581 }
1582
1583
1584 // Retrieve the active channel strip.
1585 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1586 {
1587 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1588 }
1589
1590
1591 // Retrieve a channel strip by index.
1592 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1593 {
1594 QWidgetList wlist = m_pWorkspace->windowList();
1595 if (wlist.isEmpty())
1596 return NULL;
1597
1598 return (qsamplerChannelStrip *) wlist.at(iChannel);
1599 }
1600
1601
1602 // Retrieve a channel strip by sampler channel id.
1603 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1604 {
1605 QWidgetList wlist = m_pWorkspace->windowList();
1606 if (wlist.isEmpty())
1607 return NULL;
1608
1609 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1610 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1611 if (pChannelStrip) {
1612 qsamplerChannel *pChannel = pChannelStrip->channel();
1613 if (pChannel && pChannel->channelID() == iChannelID)
1614 return pChannelStrip;
1615 }
1616 }
1617
1618 // Not found.
1619 return NULL;
1620 }
1621
1622
1623 // Construct the windows menu.
1624 void qsamplerMainForm::channelsMenuAboutToShow (void)
1625 {
1626 channelsMenu->clear();
1627 channelsArrangeAction->addTo(channelsMenu);
1628 channelsAutoArrangeAction->addTo(channelsMenu);
1629
1630 QWidgetList wlist = m_pWorkspace->windowList();
1631 if (!wlist.isEmpty()) {
1632 channelsMenu->insertSeparator();
1633 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1634 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1635 if (pChannelStrip) {
1636 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1637 channelsMenu->setItemParameter(iItemID, iChannel);
1638 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1639 }
1640 }
1641 }
1642 }
1643
1644
1645 // Windows menu activation slot
1646 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1647 {
1648 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1649 if (pChannelStrip)
1650 pChannelStrip->showNormal();
1651 pChannelStrip->setFocus();
1652 }
1653
1654
1655 //-------------------------------------------------------------------------
1656 // qsamplerMainForm -- Timer stuff.
1657
1658 // Set the pseudo-timer delay schedule.
1659 void qsamplerMainForm::startSchedule ( int iStartDelay )
1660 {
1661 m_iStartDelay = 1 + (iStartDelay * 1000);
1662 m_iTimerDelay = 0;
1663 }
1664
1665 // Suspend the pseudo-timer delay schedule.
1666 void qsamplerMainForm::stopSchedule (void)
1667 {
1668 m_iStartDelay = 0;
1669 m_iTimerDelay = 0;
1670 }
1671
1672 // Timer slot funtion.
1673 void qsamplerMainForm::timerSlot (void)
1674 {
1675 if (m_pOptions == NULL)
1676 return;
1677
1678 // Is it the first shot on server start after a few delay?
1679 if (m_iTimerDelay < m_iStartDelay) {
1680 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1681 if (m_iTimerDelay >= m_iStartDelay) {
1682 // If we cannot start it now, maybe a lil'mo'later ;)
1683 if (!startClient()) {
1684 m_iStartDelay += m_iTimerDelay;
1685 m_iTimerDelay = 0;
1686 }
1687 }
1688 }
1689
1690 // Refresh each channel usage, on each period...
1691 if (m_pClient && (m_changedStrips.count() > 0 || m_pOptions->bAutoRefresh)) {
1692 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1693 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1694 m_iTimerSlot = 0;
1695 // Update the channel information for each pending strip...
1696 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1697 pChannelStrip;
1698 pChannelStrip = m_changedStrips.next()) {
1699 // If successfull, remove from pending list...
1700 if (pChannelStrip->updateChannelInfo())
1701 m_changedStrips.remove(pChannelStrip);
1702 }
1703 // Update the channel stream usage for each strip...
1704 QWidgetList wlist = m_pWorkspace->windowList();
1705 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1706 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1707 if (pChannelStrip && pChannelStrip->isVisible())
1708 pChannelStrip->updateChannelUsage();
1709 }
1710 }
1711 }
1712
1713 // Register the next timer slot.
1714 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1715 }
1716
1717
1718 //-------------------------------------------------------------------------
1719 // qsamplerMainForm -- Server stuff.
1720
1721 // Start linuxsampler server...
1722 void qsamplerMainForm::startServer (void)
1723 {
1724 if (m_pOptions == NULL)
1725 return;
1726
1727 // Aren't already a client, are we?
1728 if (!m_pOptions->bServerStart || m_pClient)
1729 return;
1730
1731 // Is the server process instance still here?
1732 if (m_pServer) {
1733 switch (QMessageBox::warning(this, tr("Warning"),
1734 tr("Could not start the LinuxSampler server.\n\n"
1735 "Maybe it ss already started."),
1736 tr("Stop"), tr("Kill"), tr("Cancel"))) {
1737 case 0:
1738 m_pServer->tryTerminate();
1739 break;
1740 case 1:
1741 m_pServer->kill();
1742 break;
1743 }
1744 return;
1745 }
1746
1747 // Reset our timer counters...
1748 stopSchedule();
1749
1750 // OK. Let's build the startup process...
1751 m_pServer = new QProcess(this);
1752
1753 // Setup stdout/stderr capture...
1754 //if (m_pOptions->bStdoutCapture) {
1755 m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1756 QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1757 QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1758 //}
1759 // The unforgiveable signal communication...
1760 QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1761
1762 // Build process arguments...
1763 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1764
1765 appendMessages(tr("Server is starting..."));
1766 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1767
1768 // Go jack, go...
1769 if (!m_pServer->start()) {
1770 appendMessagesError(tr("Could not start server.\n\nSorry."));
1771 processServerExit();
1772 return;
1773 }
1774
1775 // Show startup results...
1776 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1777
1778 // Reset (yet again) the timer counters,
1779 // but this time is deferred as the user opted.
1780 startSchedule(m_pOptions->iStartDelay);
1781 stabilizeForm();
1782 }
1783
1784
1785 // Stop linuxsampler server...
1786 void qsamplerMainForm::stopServer (void)
1787 {
1788 // Stop client code.
1789 stopClient();
1790
1791 // And try to stop server.
1792 if (m_pServer) {
1793 appendMessages(tr("Server is stopping..."));
1794 if (m_pServer->isRunning())
1795 m_pServer->tryTerminate();
1796 }
1797
1798 // Give it some time to terminate gracefully and stabilize...
1799 QTime t;
1800 t.start();
1801 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1802 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1803
1804 // Do final processing anyway.
1805 processServerExit();
1806 }
1807
1808
1809 // Stdout handler...
1810 void qsamplerMainForm::readServerStdout (void)
1811 {
1812 if (m_pMessages)
1813 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1814 }
1815
1816
1817 // Linuxsampler server cleanup.
1818 void qsamplerMainForm::processServerExit (void)
1819 {
1820 // Force client code cleanup.
1821 stopClient();
1822
1823 // Flush anything that maybe pending...
1824 if (m_pMessages)
1825 m_pMessages->flushStdoutBuffer();
1826
1827 if (m_pServer) {
1828 // Force final server shutdown...
1829 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1830 if (!m_pServer->normalExit())
1831 m_pServer->kill();
1832 // Destroy it.
1833 delete m_pServer;
1834 m_pServer = NULL;
1835 }
1836
1837 // Again, make status visible stable.
1838 stabilizeForm();
1839 }
1840
1841
1842 //-------------------------------------------------------------------------
1843 // qsamplerMainForm -- Client stuff.
1844
1845 // The LSCP client callback procedure.
1846 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1847 {
1848 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1849 if (pMainForm == NULL)
1850 return LSCP_FAILED;
1851
1852 // ATTN: DO NOT EVER call any GUI code here,
1853 // as this is run under some other thread context.
1854 // A custom event must be posted here...
1855 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1856
1857 return LSCP_OK;
1858 }
1859
1860
1861 // Start our almighty client...
1862 bool qsamplerMainForm::startClient (void)
1863 {
1864 // Have it a setup?
1865 if (m_pOptions == NULL)
1866 return false;
1867
1868 // Aren't we already started, are we?
1869 if (m_pClient)
1870 return true;
1871
1872 // Log prepare here.
1873 appendMessages(tr("Client connecting..."));
1874
1875 // Create the client handle...
1876 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1877 if (m_pClient == NULL) {
1878 // Is this the first try?
1879 // maybe we need to start a local server...
1880 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1881 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1882 else
1883 startServer();
1884 // This is always a failure.
1885 stabilizeForm();
1886 return false;
1887 }
1888 // Just set receive timeout value, blindly.
1889 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1890 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1891
1892 // We may stop scheduling around.
1893 stopSchedule();
1894
1895 // We'll accept drops from now on...
1896 setAcceptDrops(true);
1897
1898 // Log success here.
1899 appendMessages(tr("Client connected."));
1900
1901 // Hard-notify device configuration form,
1902 // if visible, that we're ready...
1903 if (m_pDeviceForm && m_pDeviceForm->isVisible())
1904 m_pDeviceForm->setClient(m_pClient);
1905
1906 // Is any session pending to be loaded?
1907 if (!m_pOptions->sSessionFile.isEmpty()) {
1908 // Just load the prabably startup session...
1909 if (loadSessionFile(m_pOptions->sSessionFile)) {
1910 m_pOptions->sSessionFile = QString::null;
1911 return true;
1912 }
1913 }
1914
1915 // Make a new session
1916 return newSession();
1917 }
1918
1919
1920 // Stop client...
1921 void qsamplerMainForm::stopClient (void)
1922 {
1923 if (m_pClient == NULL)
1924 return;
1925
1926 // Hard-notify device configuration form,
1927 // if visible, that we're running out...
1928 if (m_pDeviceForm && m_pDeviceForm->isVisible())
1929 m_pDeviceForm->setClient(NULL);
1930
1931 // Log prepare here.
1932 appendMessages(tr("Client disconnecting..."));
1933
1934 // Clear timer counters...
1935 stopSchedule();
1936
1937 // We'll reject drops from now on...
1938 setAcceptDrops(false);
1939
1940 // Force any channel strips around, but
1941 // but avoid removing the corresponding
1942 // channels from the back-end server.
1943 m_iDirtyCount = 0;
1944 closeSession(false);
1945
1946 // Close us as a client...
1947 lscp_client_destroy(m_pClient);
1948 m_pClient = NULL;
1949
1950 // Log final here.
1951 appendMessages(tr("Client disconnected."));
1952
1953 // Make visible status.
1954 stabilizeForm();
1955 }
1956
1957
1958 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC