/[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 433 - (show annotations) (download) (as text)
Wed Mar 9 16:44:04 2005 UTC (13 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 61305 byte(s)
* Device configuration is now partially functional.

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

  ViewVC Help
Powered by ViewVC