/[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 400 - (show annotations) (download) (as text)
Mon Feb 21 15:02:58 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59104 byte(s)
Major internal switch regarding channel strip change management.

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 //-------------------------------------------------------------------------
914 // qsamplerMainForm -- View Action slots.
915
916 // Show/hide the main program window menubar.
917 void qsamplerMainForm::viewMenubar ( bool bOn )
918 {
919 if (bOn)
920 MenuBar->show();
921 else
922 MenuBar->hide();
923 }
924
925
926 // Show/hide the main program window toolbar.
927 void qsamplerMainForm::viewToolbar ( bool bOn )
928 {
929 if (bOn) {
930 fileToolbar->show();
931 editToolbar->show();
932 channelsToolbar->show();
933 } else {
934 fileToolbar->hide();
935 editToolbar->hide();
936 channelsToolbar->hide();
937 }
938 }
939
940
941 // Show/hide the main program window statusbar.
942 void qsamplerMainForm::viewStatusbar ( bool bOn )
943 {
944 if (bOn)
945 statusBar()->show();
946 else
947 statusBar()->hide();
948 }
949
950
951 // Show/hide the messages window logger.
952 void qsamplerMainForm::viewMessages ( bool bOn )
953 {
954 if (bOn)
955 m_pMessages->show();
956 else
957 m_pMessages->hide();
958 }
959
960
961 // Show options dialog.
962 void qsamplerMainForm::viewOptions (void)
963 {
964 if (m_pOptions == NULL)
965 return;
966
967 qsamplerOptionsForm *pOptionsForm = new qsamplerOptionsForm(this);
968 if (pOptionsForm) {
969 // Check out some initial nullities(tm)...
970 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
971 if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip)
972 m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString();
973 if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages)
974 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
975 // To track down deferred or immediate changes.
976 QString sOldServerHost = m_pOptions->sServerHost;
977 int iOldServerPort = m_pOptions->iServerPort;
978 int iOldServerTimeout = m_pOptions->iServerTimeout;
979 bool bOldServerStart = m_pOptions->bServerStart;
980 QString sOldServerCmdLine = m_pOptions->sServerCmdLine;
981 QString sOldDisplayFont = m_pOptions->sDisplayFont;
982 bool bOldDisplayEffect = m_pOptions->bDisplayEffect;
983 int iOldMaxVolume = m_pOptions->iMaxVolume;
984 QString sOldMessagesFont = m_pOptions->sMessagesFont;
985 bool bOldStdoutCapture = m_pOptions->bStdoutCapture;
986 int bOldMessagesLimit = m_pOptions->bMessagesLimit;
987 int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines;
988 bool bOldCompletePath = m_pOptions->bCompletePath;
989 bool bOldInstrumentNames = m_pOptions->bInstrumentNames;
990 int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles;
991 // Load the current setup settings.
992 pOptionsForm->setup(m_pOptions);
993 // Show the setup dialog...
994 if (pOptionsForm->exec()) {
995 // Warn if something will be only effective on next run.
996 if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) ||
997 (!bOldStdoutCapture && m_pOptions->bStdoutCapture)) {
998 QMessageBox::information(this, tr("Information"),
999 tr("Some settings may be only effective\n"
1000 "next time you start this program."), tr("OK"));
1001 updateMessagesCapture();
1002 }
1003 // Check wheather something immediate has changed.
1004 if (( bOldCompletePath && !m_pOptions->bCompletePath) ||
1005 (!bOldCompletePath && m_pOptions->bCompletePath) ||
1006 (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles))
1007 updateRecentFilesMenu();
1008 if (( bOldInstrumentNames && !m_pOptions->bInstrumentNames) ||
1009 (!bOldInstrumentNames && m_pOptions->bInstrumentNames))
1010 updateInstrumentNames();
1011 if (( bOldDisplayEffect && !m_pOptions->bDisplayEffect) ||
1012 (!bOldDisplayEffect && m_pOptions->bDisplayEffect))
1013 updateDisplayEffect();
1014 if (sOldDisplayFont != m_pOptions->sDisplayFont)
1015 updateDisplayFont();
1016 if (iOldMaxVolume != m_pOptions->iMaxVolume)
1017 updateMaxVolume();
1018 if (sOldMessagesFont != m_pOptions->sMessagesFont)
1019 updateMessagesFont();
1020 if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) ||
1021 (!bOldMessagesLimit && m_pOptions->bMessagesLimit) ||
1022 (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines))
1023 updateMessagesLimit();
1024 // And now the main thing, whether we'll do client/server recycling?
1025 if ((sOldServerHost != m_pOptions->sServerHost) ||
1026 (iOldServerPort != m_pOptions->iServerPort) ||
1027 (iOldServerTimeout != m_pOptions->iServerTimeout) ||
1028 ( bOldServerStart && !m_pOptions->bServerStart) ||
1029 (!bOldServerStart && m_pOptions->bServerStart) ||
1030 (sOldServerCmdLine != m_pOptions->sServerCmdLine && m_pOptions->bServerStart))
1031 fileRestart();
1032 }
1033 // Done.
1034 delete pOptionsForm;
1035 }
1036
1037 // This makes it.
1038 stabilizeForm();
1039 }
1040
1041
1042 //-------------------------------------------------------------------------
1043 // qsamplerMainForm -- Channels action slots.
1044
1045 // Arrange channel strips.
1046 void qsamplerMainForm::channelsArrange (void)
1047 {
1048 // Full width vertical tiling
1049 QWidgetList wlist = m_pWorkspace->windowList();
1050 if (wlist.isEmpty())
1051 return;
1052
1053 m_pWorkspace->setUpdatesEnabled(false);
1054 int y = 0;
1055 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1056 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1057 /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) {
1058 // Prevent flicker...
1059 pChannelStrip->hide();
1060 pChannelStrip->showNormal();
1061 } */
1062 pChannelStrip->adjustSize();
1063 int iWidth = m_pWorkspace->width();
1064 if (iWidth < pChannelStrip->width())
1065 iWidth = pChannelStrip->width();
1066 // int iHeight = pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1067 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1068 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1069 y += iHeight;
1070 }
1071 m_pWorkspace->setUpdatesEnabled(true);
1072
1073 stabilizeForm();
1074 }
1075
1076
1077 // Auto-arrange channel strips.
1078 void qsamplerMainForm::channelsAutoArrange ( bool bOn )
1079 {
1080 if (m_pOptions == NULL)
1081 return;
1082
1083 // Toggle the auto-arrange flag.
1084 m_pOptions->bAutoArrange = bOn;
1085
1086 // If on, update whole workspace...
1087 if (m_pOptions->bAutoArrange)
1088 channelsArrange();
1089 }
1090
1091
1092 //-------------------------------------------------------------------------
1093 // qsamplerMainForm -- Help Action slots.
1094
1095 // Show information about the Qt toolkit.
1096 void qsamplerMainForm::helpAboutQt (void)
1097 {
1098 QMessageBox::aboutQt(this);
1099 }
1100
1101
1102 // Show information about application program.
1103 void qsamplerMainForm::helpAbout (void)
1104 {
1105 // Stuff the about box text...
1106 QString sText = "<p>\n";
1107 sText += "<b>" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "</b><br />\n";
1108 sText += "<br />\n";
1109 sText += tr("Version") + ": <b>" QSAMPLER_VERSION "</b><br />\n";
1110 sText += "<small>" + tr("Build") + ": " __DATE__ " " __TIME__ "</small><br />\n";
1111 #ifdef CONFIG_DEBUG
1112 sText += "<small><font color=\"red\">";
1113 sText += tr("Debugging option enabled.");
1114 sText += "</font></small><br />";
1115 #endif
1116 #ifndef CONFIG_LIBGIG
1117 sText += "<small><font color=\"red\">";
1118 sText += tr("GIG (libgig) file support disabled.");
1119 sText += "</font></small><br />";
1120 #endif
1121 #ifndef CONFIG_INSTRUMENT_NAME
1122 sText += "<small><font color=\"red\">";
1123 sText += tr("LSCP (liblscp) instrument_name support disabled.");
1124 sText += "</font></small><br />";
1125 #endif
1126 sText += "<br />\n";
1127 sText += tr("Using") + ": ";
1128 sText += ::lscp_client_package();
1129 sText += " ";
1130 sText += ::lscp_client_version();
1131 sText += "<br />\n";
1132 sText += "<br />\n";
1133 sText += tr("Website") + ": <a href=\"" QSAMPLER_WEBSITE "\">" QSAMPLER_WEBSITE "</a><br />\n";
1134 sText += "<br />\n";
1135 sText += "<small>";
1136 sText += QSAMPLER_COPYRIGHT "<br />\n";
1137 sText += "<br />\n";
1138 sText += tr("This program is free software; you can redistribute it and/or modify it") + "<br />\n";
1139 sText += tr("under the terms of the GNU General Public License version 2 or later.");
1140 sText += "</small>";
1141 sText += "</p>\n";
1142
1143 QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText);
1144 }
1145
1146
1147 //-------------------------------------------------------------------------
1148 // qsamplerMainForm -- Main window stabilization.
1149
1150 void qsamplerMainForm::stabilizeForm (void)
1151 {
1152 // Update the main application caption...
1153 QString sSessioName = sessionName(m_sFilename);
1154 if (m_iDirtyCount > 0)
1155 sSessioName += '*';
1156 setCaption(tr(QSAMPLER_TITLE " - [%1]").arg(sSessioName));
1157
1158 // Update the main menu state...
1159 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
1160 bool bHasClient = (m_pOptions != NULL && m_pClient != NULL);
1161 bool bHasChannel = (bHasClient && pChannelStrip != NULL);
1162 fileNewAction->setEnabled(bHasClient);
1163 fileOpenAction->setEnabled(bHasClient);
1164 fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0);
1165 fileSaveAsAction->setEnabled(bHasClient);
1166 fileResetAction->setEnabled(bHasClient);
1167 fileRestartAction->setEnabled(bHasClient || m_pServer == NULL);
1168 editAddChannelAction->setEnabled(bHasClient);
1169 editRemoveChannelAction->setEnabled(bHasChannel);
1170 editSetupChannelAction->setEnabled(bHasChannel);
1171 editResetChannelAction->setEnabled(bHasChannel);
1172 channelsArrangeAction->setEnabled(bHasChannel);
1173 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1174
1175 // Client/Server status...
1176 if (bHasClient) {
1177 m_status[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1178 m_status[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1179 } else {
1180 m_status[QSAMPLER_STATUS_CLIENT]->clear();
1181 m_status[QSAMPLER_STATUS_SERVER]->clear();
1182 }
1183 // Channel status...
1184 if (bHasChannel)
1185 m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1186 else
1187 m_status[QSAMPLER_STATUS_CHANNEL]->clear();
1188 // Session status...
1189 if (m_iDirtyCount > 0)
1190 m_status[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1191 else
1192 m_status[QSAMPLER_STATUS_SESSION]->clear();
1193
1194 // Recent files menu.
1195 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1196
1197 // Always make the latest message visible.
1198 if (m_pMessages)
1199 m_pMessages->scrollToBottom();
1200 }
1201
1202
1203 // Channel change receiver slot.
1204 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip *pChannelStrip )
1205 {
1206 // Add this strip to the changed list...
1207 if (m_changedStrips.containsRef(pChannelStrip) == 0)
1208 m_changedStrips.append(pChannelStrip);
1209
1210 // Just mark the dirty form.
1211 m_iDirtyCount++;
1212 // and update the form status...
1213 stabilizeForm();
1214 }
1215
1216
1217 // Grab and restore current sampler channels session.
1218 void qsamplerMainForm::updateSession (void)
1219 {
1220 // Retrieve the current channel list.
1221 int *piChannelIDs = ::lscp_list_channels(m_pClient);
1222 if (piChannelIDs == NULL) {
1223 if (::lscp_client_get_errno(m_pClient)) {
1224 appendMessagesClient("lscp_list_channels");
1225 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
1226 }
1227 return;
1228 }
1229
1230 // Try to (re)create each channel.
1231 m_pWorkspace->setUpdatesEnabled(false);
1232 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
1233 // Check if theres already a channel strip for this one...
1234 if (!channelStrip(piChannelIDs[iChannel]))
1235 createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
1236 // Make it visibly responsive...
1237 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1238 }
1239 m_pWorkspace->setUpdatesEnabled(true);
1240 }
1241
1242
1243 // Update the recent files list and menu.
1244 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1245 {
1246 if (m_pOptions == NULL)
1247 return;
1248
1249 // Remove from list if already there (avoid duplicates)
1250 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1251 if (iter != m_pOptions->recentFiles.end())
1252 m_pOptions->recentFiles.remove(iter);
1253 // Put it to front...
1254 m_pOptions->recentFiles.push_front(sFilename);
1255
1256 // May update the menu.
1257 updateRecentFilesMenu();
1258 }
1259
1260
1261 // Update the recent files list and menu.
1262 void qsamplerMainForm::updateRecentFilesMenu (void)
1263 {
1264 if (m_pOptions == NULL)
1265 return;
1266
1267 // Time to keep the list under limits.
1268 int iRecentFiles = m_pOptions->recentFiles.count();
1269 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1270 m_pOptions->recentFiles.pop_back();
1271 iRecentFiles--;
1272 }
1273
1274 // rebuild the recent files menu...
1275 m_pRecentFilesMenu->clear();
1276 for (int i = 0; i < iRecentFiles; i++) {
1277 const QString& sFilename = m_pOptions->recentFiles[i];
1278 if (QFileInfo(sFilename).exists()) {
1279 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1280 .arg(i + 1).arg(sessionName(sFilename)),
1281 this, SLOT(fileOpenRecent(int)), 0, i);
1282 }
1283 }
1284 }
1285
1286
1287 // Force update of the channels instrument names mode.
1288 void qsamplerMainForm::updateInstrumentNames (void)
1289 {
1290 // Full channel list update...
1291 QWidgetList wlist = m_pWorkspace->windowList();
1292 if (wlist.isEmpty())
1293 return;
1294
1295 m_pWorkspace->setUpdatesEnabled(false);
1296 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1297 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1298 if (pChannelStrip)
1299 pChannelStrip->updateInstrumentName(true);
1300 }
1301 m_pWorkspace->setUpdatesEnabled(true);
1302 }
1303
1304
1305 // Force update of the channels display font.
1306 void qsamplerMainForm::updateDisplayFont (void)
1307 {
1308 if (m_pOptions == NULL)
1309 return;
1310
1311 // Check if display font is legal.
1312 if (m_pOptions->sDisplayFont.isEmpty())
1313 return;
1314 // Realize it.
1315 QFont font;
1316 if (!font.fromString(m_pOptions->sDisplayFont))
1317 return;
1318
1319 // Full channel list update...
1320 QWidgetList wlist = m_pWorkspace->windowList();
1321 if (wlist.isEmpty())
1322 return;
1323
1324 m_pWorkspace->setUpdatesEnabled(false);
1325 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1326 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1327 if (pChannelStrip)
1328 pChannelStrip->setDisplayFont(font);
1329 }
1330 m_pWorkspace->setUpdatesEnabled(true);
1331 }
1332
1333
1334 // Update channel strips background effect.
1335 void qsamplerMainForm::updateDisplayEffect (void)
1336 {
1337 QPixmap pm;
1338 if (m_pOptions->bDisplayEffect)
1339 pm = QPixmap::fromMimeSource("displaybg1.png");
1340
1341 // Full channel list update...
1342 QWidgetList wlist = m_pWorkspace->windowList();
1343 if (wlist.isEmpty())
1344 return;
1345
1346 m_pWorkspace->setUpdatesEnabled(false);
1347 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1348 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1349 if (pChannelStrip)
1350 pChannelStrip->setDisplayBackground(pm);
1351 }
1352 m_pWorkspace->setUpdatesEnabled(true);
1353 }
1354
1355
1356 // Force update of the channels maximum volume setting.
1357 void qsamplerMainForm::updateMaxVolume (void)
1358 {
1359 if (m_pOptions == NULL)
1360 return;
1361
1362 // Full channel list update...
1363 QWidgetList wlist = m_pWorkspace->windowList();
1364 if (wlist.isEmpty())
1365 return;
1366
1367 m_pWorkspace->setUpdatesEnabled(false);
1368 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1369 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1370 if (pChannelStrip)
1371 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1372 }
1373 m_pWorkspace->setUpdatesEnabled(true);
1374 }
1375
1376
1377 //-------------------------------------------------------------------------
1378 // qsamplerMainForm -- Messages window form handlers.
1379
1380 // Messages output methods.
1381 void qsamplerMainForm::appendMessages( const QString& s )
1382 {
1383 if (m_pMessages)
1384 m_pMessages->appendMessages(s);
1385
1386 statusBar()->message(s, 3000);
1387 }
1388
1389 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1390 {
1391 if (m_pMessages)
1392 m_pMessages->appendMessagesColor(s, c);
1393
1394 statusBar()->message(s, 3000);
1395 }
1396
1397 void qsamplerMainForm::appendMessagesText( const QString& s )
1398 {
1399 if (m_pMessages)
1400 m_pMessages->appendMessagesText(s);
1401 }
1402
1403 void qsamplerMainForm::appendMessagesError( const QString& s )
1404 {
1405 if (m_pMessages)
1406 m_pMessages->show();
1407
1408 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1409
1410 QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1411 }
1412
1413
1414 // This is a special message format, just for client results.
1415 void qsamplerMainForm::appendMessagesClient( const QString& s )
1416 {
1417 if (m_pClient == NULL)
1418 return;
1419
1420 appendMessagesColor(s + QString(": %1 (errno=%2)")
1421 .arg(::lscp_client_get_result(m_pClient))
1422 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1423 }
1424
1425
1426 // Force update of the messages font.
1427 void qsamplerMainForm::updateMessagesFont (void)
1428 {
1429 if (m_pOptions == NULL)
1430 return;
1431
1432 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1433 QFont font;
1434 if (font.fromString(m_pOptions->sMessagesFont))
1435 m_pMessages->setMessagesFont(font);
1436 }
1437 }
1438
1439
1440 // Update messages window line limit.
1441 void qsamplerMainForm::updateMessagesLimit (void)
1442 {
1443 if (m_pOptions == NULL)
1444 return;
1445
1446 if (m_pMessages) {
1447 if (m_pOptions->bMessagesLimit)
1448 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1449 else
1450 m_pMessages->setMessagesLimit(0);
1451 }
1452 }
1453
1454
1455 // Enablement of the messages capture feature.
1456 void qsamplerMainForm::updateMessagesCapture (void)
1457 {
1458 if (m_pOptions == NULL)
1459 return;
1460
1461 if (m_pMessages)
1462 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1463 }
1464
1465
1466 //-------------------------------------------------------------------------
1467 // qsamplerMainForm -- MDI channel strip management.
1468
1469 // The channel strip creation executive.
1470 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1471 {
1472 if (m_pClient == NULL || pChannel == NULL)
1473 return NULL;
1474
1475 // Prepare for auto-arrange?
1476 qsamplerChannelStrip *pChannelStrip = NULL;
1477 int y = 0;
1478 if (m_pOptions && m_pOptions->bAutoArrange) {
1479 QWidgetList wlist = m_pWorkspace->windowList();
1480 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1481 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1482 if (pChannelStrip) {
1483 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1484 y += pChannelStrip->parentWidget()->frameGeometry().height();
1485 }
1486 }
1487 }
1488
1489 // Add a new channel itema...
1490 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1491 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1492 if (pChannelStrip == NULL)
1493 return NULL;
1494
1495 // Actual channel strip setup...
1496 pChannelStrip->setup(pChannel);
1497 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1498 // Set some initial aesthetic options...
1499 if (m_pOptions) {
1500 // Background display effect...
1501 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1502 // We'll need a display font.
1503 QFont font;
1504 if (font.fromString(m_pOptions->sDisplayFont))
1505 pChannelStrip->setDisplayFont(font);
1506 // Maximum allowed volume setting.
1507 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1508 }
1509
1510 // Now we show up us to the world.
1511 pChannelStrip->show();
1512 // Only then, we'll auto-arrange...
1513 if (m_pOptions && m_pOptions->bAutoArrange) {
1514 int iWidth = m_pWorkspace->width();
1515 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1516 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1517 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1518 }
1519
1520 // This is pretty new, so we'll watch for it closely.
1521 channelStripChanged(pChannelStrip);
1522
1523 // Return our successful reference...
1524 return pChannelStrip;
1525 }
1526
1527
1528 // Retrieve the active channel strip.
1529 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1530 {
1531 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1532 }
1533
1534
1535 // Retrieve a channel strip by index.
1536 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1537 {
1538 QWidgetList wlist = m_pWorkspace->windowList();
1539 if (wlist.isEmpty())
1540 return NULL;
1541
1542 return (qsamplerChannelStrip *) wlist.at(iChannel);
1543 }
1544
1545
1546 // Retrieve a channel strip by sampler channel id.
1547 qsamplerChannelStrip *qsamplerMainForm::channelStrip ( int iChannelID )
1548 {
1549 QWidgetList wlist = m_pWorkspace->windowList();
1550 if (wlist.isEmpty())
1551 return NULL;
1552
1553 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1554 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1555 if (pChannelStrip) {
1556 qsamplerChannel *pChannel = pChannelStrip->channel();
1557 if (pChannel && pChannel->channelID() == iChannelID)
1558 return pChannelStrip;
1559 }
1560 }
1561
1562 // Not found.
1563 return NULL;
1564 }
1565
1566
1567 // Construct the windows menu.
1568 void qsamplerMainForm::channelsMenuAboutToShow (void)
1569 {
1570 channelsMenu->clear();
1571 channelsArrangeAction->addTo(channelsMenu);
1572 channelsAutoArrangeAction->addTo(channelsMenu);
1573
1574 QWidgetList wlist = m_pWorkspace->windowList();
1575 if (!wlist.isEmpty()) {
1576 channelsMenu->insertSeparator();
1577 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1578 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1579 if (pChannelStrip) {
1580 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1581 channelsMenu->setItemParameter(iItemID, iChannel);
1582 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1583 }
1584 }
1585 }
1586 }
1587
1588
1589 // Windows menu activation slot
1590 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1591 {
1592 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1593 if (pChannelStrip)
1594 pChannelStrip->showNormal();
1595 pChannelStrip->setFocus();
1596 }
1597
1598
1599 //-------------------------------------------------------------------------
1600 // qsamplerMainForm -- Timer stuff.
1601
1602 // Set the pseudo-timer delay schedule.
1603 void qsamplerMainForm::startSchedule ( int iStartDelay )
1604 {
1605 m_iStartDelay = 1 + (iStartDelay * 1000);
1606 m_iTimerDelay = 0;
1607 }
1608
1609 // Suspend the pseudo-timer delay schedule.
1610 void qsamplerMainForm::stopSchedule (void)
1611 {
1612 m_iStartDelay = 0;
1613 m_iTimerDelay = 0;
1614 }
1615
1616 // Timer slot funtion.
1617 void qsamplerMainForm::timerSlot (void)
1618 {
1619 if (m_pOptions == NULL)
1620 return;
1621
1622 // Is it the first shot on server start after a few delay?
1623 if (m_iTimerDelay < m_iStartDelay) {
1624 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1625 if (m_iTimerDelay >= m_iStartDelay) {
1626 // If we cannot start it now, maybe a lil'mo'later ;)
1627 if (!startClient()) {
1628 m_iStartDelay += m_iTimerDelay;
1629 m_iTimerDelay = 0;
1630 }
1631 }
1632 }
1633
1634 // Refresh each channel usage, on each period...
1635 if (m_pClient && (m_changedStrips.count() > 0 || m_pOptions->bAutoRefresh)) {
1636 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1637 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1638 m_iTimerSlot = 0;
1639 // Update the channel information for each pending strip...
1640 for (qsamplerChannelStrip *pChannelStrip = m_changedStrips.first();
1641 pChannelStrip;
1642 pChannelStrip = m_changedStrips.next()) {
1643 // If successfull, remove from pending list...
1644 if (pChannelStrip->updateChannelInfo())
1645 m_changedStrips.remove(pChannelStrip);
1646 }
1647 // Update the channel stream usage for each strip...
1648 QWidgetList wlist = m_pWorkspace->windowList();
1649 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1650 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1651 if (pChannelStrip && pChannelStrip->isVisible())
1652 pChannelStrip->updateChannelUsage();
1653 }
1654 }
1655 }
1656
1657 // Register the next timer slot.
1658 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1659 }
1660
1661
1662 //-------------------------------------------------------------------------
1663 // qsamplerMainForm -- Server stuff.
1664
1665 // Start linuxsampler server...
1666 void qsamplerMainForm::startServer (void)
1667 {
1668 if (m_pOptions == NULL)
1669 return;
1670
1671 // Aren't already a client, are we?
1672 if (!m_pOptions->bServerStart || m_pClient)
1673 return;
1674
1675 // Is the server process instance still here?
1676 if (m_pServer) {
1677 switch (QMessageBox::warning(this, tr("Warning"),
1678 tr("Could not start the LinuxSampler server.\n\n"
1679 "Maybe it ss already started."),
1680 tr("Stop"), tr("Kill"), tr("Cancel"))) {
1681 case 0:
1682 m_pServer->tryTerminate();
1683 break;
1684 case 1:
1685 m_pServer->kill();
1686 break;
1687 }
1688 return;
1689 }
1690
1691 // Reset our timer counters...
1692 stopSchedule();
1693
1694 // OK. Let's build the startup process...
1695 m_pServer = new QProcess(this);
1696
1697 // Setup stdout/stderr capture...
1698 //if (m_pOptions->bStdoutCapture) {
1699 m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1700 QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1701 QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1702 //}
1703 // The unforgiveable signal communication...
1704 QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1705
1706 // Build process arguments...
1707 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1708
1709 appendMessages(tr("Server is starting..."));
1710 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1711
1712 // Go jack, go...
1713 if (!m_pServer->start()) {
1714 appendMessagesError(tr("Could not start server.\n\nSorry."));
1715 processServerExit();
1716 return;
1717 }
1718
1719 // Show startup results...
1720 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1721
1722 // Reset (yet again) the timer counters,
1723 // but this time is deferred as the user opted.
1724 startSchedule(m_pOptions->iStartDelay);
1725 stabilizeForm();
1726 }
1727
1728
1729 // Stop linuxsampler server...
1730 void qsamplerMainForm::stopServer (void)
1731 {
1732 // Stop client code.
1733 stopClient();
1734
1735 // And try to stop server.
1736 if (m_pServer) {
1737 appendMessages(tr("Server is stopping..."));
1738 if (m_pServer->isRunning())
1739 m_pServer->tryTerminate();
1740 }
1741
1742 // Give it some time to terminate gracefully and stabilize...
1743 QTime t;
1744 t.start();
1745 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1746 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1747
1748 // Do final processing anyway.
1749 processServerExit();
1750 }
1751
1752
1753 // Stdout handler...
1754 void qsamplerMainForm::readServerStdout (void)
1755 {
1756 if (m_pMessages)
1757 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1758 }
1759
1760
1761 // Linuxsampler server cleanup.
1762 void qsamplerMainForm::processServerExit (void)
1763 {
1764 // Force client code cleanup.
1765 stopClient();
1766
1767 // Flush anything that maybe pending...
1768 if (m_pMessages)
1769 m_pMessages->flushStdoutBuffer();
1770
1771 if (m_pServer) {
1772 // Force final server shutdown...
1773 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1774 if (!m_pServer->normalExit())
1775 m_pServer->kill();
1776 // Destroy it.
1777 delete m_pServer;
1778 m_pServer = NULL;
1779 }
1780
1781 // Again, make status visible stable.
1782 stabilizeForm();
1783 }
1784
1785
1786 //-------------------------------------------------------------------------
1787 // qsamplerMainForm -- Client stuff.
1788
1789 // The LSCP client callback procedure.
1790 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1791 {
1792 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1793 if (pMainForm == NULL)
1794 return LSCP_FAILED;
1795
1796 // ATTN: DO NOT EVER call any GUI code here,
1797 // as this is run under some other thread context.
1798 // A custom event must be posted here...
1799 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1800
1801 return LSCP_OK;
1802 }
1803
1804
1805 // Start our almighty client...
1806 bool qsamplerMainForm::startClient (void)
1807 {
1808 // Have it a setup?
1809 if (m_pOptions == NULL)
1810 return false;
1811
1812 // Aren't we already started, are we?
1813 if (m_pClient)
1814 return true;
1815
1816 // Log prepare here.
1817 appendMessages(tr("Client connecting..."));
1818
1819 // Create the client handle...
1820 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1821 if (m_pClient == NULL) {
1822 // Is this the first try?
1823 // maybe we need to start a local server...
1824 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1825 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1826 else
1827 startServer();
1828 // This is always a failure.
1829 stabilizeForm();
1830 return false;
1831 }
1832 // Just set receive timeout value, blindly.
1833 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1834 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1835
1836 // We may stop scheduling around.
1837 stopSchedule();
1838
1839 // We'll accept drops from now on...
1840 setAcceptDrops(true);
1841
1842 // Log success here.
1843 appendMessages(tr("Client connected."));
1844
1845 // Is any session pending to be loaded?
1846 if (!m_pOptions->sSessionFile.isEmpty()) {
1847 // Just load the prabably startup session...
1848 if (loadSessionFile(m_pOptions->sSessionFile)) {
1849 m_pOptions->sSessionFile = QString::null;
1850 return true;
1851 }
1852 }
1853
1854 // Make a new session
1855 return newSession();
1856 }
1857
1858
1859 // Stop client...
1860 void qsamplerMainForm::stopClient (void)
1861 {
1862 if (m_pClient == NULL)
1863 return;
1864
1865 // Log prepare here.
1866 appendMessages(tr("Client disconnecting..."));
1867
1868 // Clear timer counters...
1869 stopSchedule();
1870
1871 // We'll reject drops from now on...
1872 setAcceptDrops(false);
1873
1874 // Force any channel strips around, but
1875 // but avoid removing the corresponding
1876 // channels from the back-end server.
1877 m_iDirtyCount = 0;
1878 closeSession(false);
1879
1880 // Close us as a client...
1881 lscp_client_destroy(m_pClient);
1882 m_pClient = NULL;
1883
1884 // Log final here.
1885 appendMessages(tr("Client disconnected."));
1886
1887 // Make visible status.
1888 stabilizeForm();
1889 }
1890
1891
1892 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC