/[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 404 - (show annotations) (download) (as text)
Wed Feb 23 13:47:47 2005 UTC (13 years, 9 months ago) by capela
File MIME type: text/x-c++hdr
File size: 59744 byte(s)
* Added new menu and toolbar option: Reset All Channels.

* Channel setup changes are now properly filtered, as for
  only those settings that are actually changed gets applied;
  change information are now also posted to messages window.

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

  ViewVC Help
Powered by ViewVC