/[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 454 - (show annotations) (download) (as text)
Mon Mar 14 10:59:57 2005 UTC (19 years ago) by capela
File MIME type: text/x-c++hdr
File size: 61667 byte(s)
New keep child windows always on top option; device icon retouching;

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

  ViewVC Help
Powered by ViewVC