/[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 409 - (show annotations) (download) (as text)
Thu Feb 24 12:10:54 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59666 byte(s)
* Channels are now properly renumbered when saving to a
  session LSCP script, assuming that it should be always
  loaded from scratch (i.e. zero channels).

* Disabled MIDI port setting on channel dialog, and on
  session file save, as its use is still troublesome.

* Descrimination as for drag-and-drop of instrument files
  has been refined and fixed again.

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

  ViewVC Help
Powered by ViewVC