/[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 391 - (show annotations) (download) (as text)
Fri Feb 18 10:28:45 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 57852 byte(s)
* Drag-and-drop of instrument files now fixed.

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 m_iChangeCount = 0;
114
115 m_pServer = NULL;
116 m_pClient = NULL;
117
118 m_iStartDelay = 0;
119 m_iTimerDelay = 0;
120
121 m_iTimerSlot = 0;
122
123 #ifdef HAVE_SIGNAL_H
124 // Set to ignore any fatal "Broken pipe" signals.
125 ::signal(SIGPIPE, SIG_IGN);
126 #endif
127
128 // Make it an MDI workspace.
129 m_pWorkspace = new QWorkspace(this);
130 m_pWorkspace->setScrollBarsEnabled(true);
131 // Set the activation connection.
132 QObject::connect(m_pWorkspace, SIGNAL(windowActivated(QWidget *)), this, SLOT(stabilizeForm()));
133 // Make it shine :-)
134 setCentralWidget(m_pWorkspace);
135
136 // Create some statusbar labels...
137 QLabel *pLabel;
138 // Client status.
139 pLabel = new QLabel(tr("Connected"), this);
140 pLabel->setAlignment(Qt::AlignLeft);
141 pLabel->setMinimumSize(pLabel->sizeHint());
142 m_status[QSAMPLER_STATUS_CLIENT] = pLabel;
143 statusBar()->addWidget(pLabel);
144 // Server address.
145 pLabel = new QLabel(this);
146 pLabel->setAlignment(Qt::AlignLeft);
147 m_status[QSAMPLER_STATUS_SERVER] = pLabel;
148 statusBar()->addWidget(pLabel, 1);
149 // Channel title.
150 pLabel = new QLabel(this);
151 pLabel->setAlignment(Qt::AlignLeft);
152 m_status[QSAMPLER_STATUS_CHANNEL] = pLabel;
153 statusBar()->addWidget(pLabel, 2);
154 // Session modification status.
155 pLabel = new QLabel(tr("MOD"), this);
156 pLabel->setAlignment(Qt::AlignHCenter);
157 pLabel->setMinimumSize(pLabel->sizeHint());
158 m_status[QSAMPLER_STATUS_SESSION] = pLabel;
159 statusBar()->addWidget(pLabel);
160
161 // Create the recent files sub-menu.
162 m_pRecentFilesMenu = new QPopupMenu(this);
163 fileMenu->insertSeparator(4);
164 fileMenu->insertItem(tr("Recent &Files"), m_pRecentFilesMenu, 0, 5);
165
166 #if defined(WIN32)
167 WSAStartup(MAKEWORD(1, 1), &_wsaData);
168 #endif
169 }
170
171
172 // Kind of destructor.
173 void qsamplerMainForm::destroy (void)
174 {
175 // Do final processing anyway.
176 processServerExit();
177
178 // Delete recentfiles menu.
179 if (m_pRecentFilesMenu)
180 delete m_pRecentFilesMenu;
181 // Delete status item labels one by one.
182 if (m_status[QSAMPLER_STATUS_CLIENT])
183 delete m_status[QSAMPLER_STATUS_CLIENT];
184 if (m_status[QSAMPLER_STATUS_SERVER])
185 delete m_status[QSAMPLER_STATUS_SERVER];
186 if (m_status[QSAMPLER_STATUS_CHANNEL])
187 delete m_status[QSAMPLER_STATUS_CHANNEL];
188 if (m_status[QSAMPLER_STATUS_SESSION])
189 delete m_status[QSAMPLER_STATUS_SESSION];
190
191 // Finally drop any widgets around...
192 if (m_pMessages)
193 delete m_pMessages;
194 if (m_pWorkspace)
195 delete m_pWorkspace;
196
197 #if defined(WIN32)
198 WSACleanup();
199 #endif
200 }
201
202
203 // Make and set a proper setup options step.
204 void qsamplerMainForm::setup ( qsamplerOptions *pOptions )
205 {
206 // We got options?
207 m_pOptions = pOptions;
208
209 // Some child forms are to be created right now.
210 m_pMessages = new qsamplerMessages(this);
211 // Set message defaults...
212 updateMessagesFont();
213 updateMessagesLimit();
214 updateMessagesCapture();
215 // Set the visibility signal.
216 QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), this, SLOT(stabilizeForm()));
217
218 // Initial decorations toggle state.
219 viewMenubarAction->setOn(m_pOptions->bMenubar);
220 viewToolbarAction->setOn(m_pOptions->bToolbar);
221 viewStatusbarAction->setOn(m_pOptions->bStatusbar);
222 channelsAutoArrangeAction->setOn(m_pOptions->bAutoArrange);
223
224 // Initial decorations visibility state.
225 viewMenubar(m_pOptions->bMenubar);
226 viewToolbar(m_pOptions->bToolbar);
227 viewStatusbar(m_pOptions->bStatusbar);
228
229 // Restore whole dock windows state.
230 QString sDockables = m_pOptions->settings().readEntry("/Layout/DockWindows" , QString::null);
231 if (sDockables.isEmpty()) {
232 // Message window is forced to dock on the bottom.
233 moveDockWindow(m_pMessages, Qt::DockBottom);
234 } else {
235 // Make it as the last time.
236 QTextIStream istr(&sDockables);
237 istr >> *this;
238 }
239 // Try to restore old window positioning.
240 m_pOptions->loadWidgetGeometry(this);
241
242 // Final startup stabilization...
243 updateRecentFilesMenu();
244 stabilizeForm();
245
246 // Make it ready :-)
247 statusBar()->message(tr("Ready"), 3000);
248
249 // We'll try to start immediately...
250 startSchedule(0);
251
252 // Register the first timer slot.
253 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
254 }
255
256
257 // Window close event handlers.
258 bool qsamplerMainForm::queryClose (void)
259 {
260 bool bQueryClose = closeSession(false);
261
262 // Try to save current general state...
263 if (m_pOptions) {
264 // Some windows default fonts is here on demand too.
265 if (bQueryClose && m_pMessages)
266 m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString();
267 // Try to save current positioning.
268 if (bQueryClose) {
269 // Save decorations state.
270 m_pOptions->bMenubar = MenuBar->isVisible();
271 m_pOptions->bToolbar = (fileToolbar->isVisible() || editToolbar->isVisible() || channelsToolbar->isVisible());
272 m_pOptions->bStatusbar = statusBar()->isVisible();
273 // Save the dock windows state.
274 QString sDockables;
275 QTextOStream ostr(&sDockables);
276 ostr << *this;
277 m_pOptions->settings().writeEntry("/Layout/DockWindows", sDockables);
278 // And the main windows state.
279 m_pOptions->saveWidgetGeometry(this);
280 // Stop client and/or server, gracefully.
281 stopServer();
282 }
283 }
284
285 return bQueryClose;
286 }
287
288
289 void qsamplerMainForm::closeEvent ( QCloseEvent *pCloseEvent )
290 {
291 if (queryClose())
292 pCloseEvent->accept();
293 else
294 pCloseEvent->ignore();
295 }
296
297
298 // Drag'n'drop file handler.
299 bool qsamplerMainForm::decodeDragFiles ( const QMimeSource *pEvent, QStringList& files )
300 {
301 bool bDecode = false;
302
303 if (QTextDrag::canDecode(pEvent)) {
304 QString sText;
305 bDecode = QTextDrag::decode(pEvent, sText);
306 if (bDecode) {
307 files = QStringList::split('\n', sText);
308 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++)
309 *iter = (*iter).stripWhiteSpace().replace(QRegExp("^file:"), QString::null);
310 }
311 }
312
313 return bDecode;
314 }
315
316
317 // Window drag-n-drop event handlers.
318 void qsamplerMainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent )
319 {
320 QStringList files;
321
322 pDragEnterEvent->accept(decodeDragFiles(pDragEnterEvent, files));
323 }
324
325
326 void qsamplerMainForm::dropEvent ( QDropEvent* pDropEvent )
327 {
328 QStringList files;
329
330 if (!decodeDragFiles(pDropEvent, files))
331 return;
332
333 for (QStringList::Iterator iter = files.begin(); iter != files.end(); iter++) {
334 const QString& sPath = QUrl(*iter).path();
335 if (qsamplerChannel::isInstrumentFile(sPath)) {
336 // Try to create a new channel from instrument file...
337 qsamplerChannel *pChannel = new qsamplerChannel(this);
338 if (pChannel == NULL)
339 return;
340 // Start setting the instrument filename...
341 pChannel->setInstrument(sPath, 0);
342 // Before we show it up, may be we'll
343 // better ask for some initial values?
344 if (!pChannel->channelSetup(this)) {
345 delete pChannel;
346 return;
347 }
348 // Finally, give it to a new channel strip...
349 if (!createChannelStrip(pChannel)) {
350 delete pChannel;
351 return;
352 }
353 // Make that an overall update.
354 m_iDirtyCount++;
355 m_iChangeCount++;
356 stabilizeForm();
357 } // Otherwise, load an usual session file (LSCP script)...
358 else if (closeSession(true))
359 loadSessionFile(sPath);
360 // Make it look responsive...:)
361 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
362 }
363 }
364
365
366 // Custome event handler.
367 void qsamplerMainForm::customEvent ( QCustomEvent *pCustomEvent )
368 {
369 // For the time being, just pump it to messages.
370 if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) {
371 qsamplerCustomEvent *pEvent = (qsamplerCustomEvent *) pCustomEvent;
372 appendMessagesColor(tr("Notify event: %1 data: %2")
373 .arg(::lscp_event_to_text(pEvent->event()))
374 .arg(pEvent->data()), "#996699");
375 }
376 }
377
378
379 // Context menu event handler.
380 void qsamplerMainForm::contextMenuEvent( QContextMenuEvent *pEvent )
381 {
382 stabilizeForm();
383
384 editMenu->exec(pEvent->globalPos());
385 }
386
387
388 //-------------------------------------------------------------------------
389 // qsamplerMainForm -- Brainless public property accessors.
390
391 // The global options settings property.
392 qsamplerOptions *qsamplerMainForm::options (void)
393 {
394 return m_pOptions;
395 }
396
397 // The LSCP client descriptor property.
398 lscp_client_t *qsamplerMainForm::client (void)
399 {
400 return m_pClient;
401 }
402
403
404 //-------------------------------------------------------------------------
405 // qsamplerMainForm -- Session file stuff.
406
407 // Format the displayable session filename.
408 QString qsamplerMainForm::sessionName ( const QString& sFilename )
409 {
410 bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath);
411 QString sSessionName = sFilename;
412 if (sSessionName.isEmpty())
413 sSessionName = tr("Untitled") + QString::number(m_iUntitled);
414 else if (!bCompletePath)
415 sSessionName = QFileInfo(sSessionName).fileName();
416 return sSessionName;
417 }
418
419
420 // Create a new session file from scratch.
421 bool qsamplerMainForm::newSession (void)
422 {
423 // Check if we can do it.
424 if (!closeSession(true))
425 return false;
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 the whole GUI session.
600 int *piChannelIDs = ::lscp_list_channels(m_pClient);
601 if (piChannelIDs == NULL) {
602 appendMessagesClient("lscp_list_channels");
603 appendMessagesError(tr("Could not get current list of channels.\n\nSorry."));
604 } else {
605 // Try to (re)create each channel.
606 m_pWorkspace->setUpdatesEnabled(false);
607 for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) {
608 createChannelStrip(new qsamplerChannel(this, piChannelIDs[iChannel]));
609 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
610 }
611 m_pWorkspace->setUpdatesEnabled(true);
612 }
613
614 // Save as default session directory.
615 if (m_pOptions)
616 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
617 // We're not dirty anymore.
618 m_iDirtyCount = 0;
619 // Stabilize form...
620 m_sFilename = sFilename;
621 updateRecentFiles(sFilename);
622 appendMessages(tr("Open session: \"%1\".").arg(sessionName(m_sFilename)));
623
624 // Make that an overall update.
625 m_iChangeCount++;
626 stabilizeForm();
627 return true;
628 }
629
630
631 // Save current session to specific file path.
632 bool qsamplerMainForm::saveSessionFile ( const QString& sFilename )
633 {
634 // Open and write into real file.
635 QFile file(sFilename);
636 if (!file.open(IO_WriteOnly | IO_Truncate)) {
637 appendMessagesError(tr("Could not open \"%1\" session file.\n\nSorry.").arg(sFilename));
638 return false;
639 }
640
641 // Write the file.
642 int iErrors = 0;
643 QTextStream ts(&file);
644 ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl;
645 ts << "# " << tr("Version")
646 << ": " QSAMPLER_VERSION << endl;
647 ts << "# " << tr("Build")
648 << ": " __DATE__ " " __TIME__ << endl;
649 ts << "#" << endl;
650 ts << "# " << tr("File")
651 << ": " << QFileInfo(sFilename).fileName() << endl;
652 ts << "# " << tr("Date")
653 << ": " << QDate::currentDate().toString("MMMM dd yyyy")
654 << " " << QTime::currentTime().toString("hh:mm:ss") << endl;
655 ts << "#" << endl;
656 ts << endl;
657 QWidgetList wlist = m_pWorkspace->windowList();
658 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
659 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
660 if (pChannelStrip) {
661 qsamplerChannel *pChannel = pChannelStrip->channel();
662 if (pChannel) {
663 int iChannelID = pChannel->channelID();
664 ts << "# " << pChannelStrip->caption() << endl;
665 ts << "ADD CHANNEL" << endl;
666 ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID << " " << pChannel->audioDriver() << endl;
667 ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl;
668 ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl;
669 ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " ";
670 if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL)
671 ts << "ALL";
672 else
673 ts << pChannel->midiChannel();
674 ts << endl;
675 ts << "LOAD ENGINE " << pChannel->engineName() << " " << iChannelID << endl;
676 ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " << pChannel->instrumentNr() << " " << iChannelID << endl;
677 ts << "SET CHANNEL VOLUME " << iChannelID << " " << pChannel->volume() << endl;
678 ts << endl;
679 }
680 }
681 // Try to keep it snappy :)
682 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
683 }
684
685 // Ok. we've wrote it.
686 file.close();
687
688 // Have we any errors?
689 if (iErrors > 0)
690 appendMessagesError(tr("Some settings could not be saved\nto \"%1\" session file.\n\nSorry.").arg(sFilename));
691
692 // Save as default session directory.
693 if (m_pOptions)
694 m_pOptions->sSessionDir = QFileInfo(sFilename).dirPath(true);
695 // We're not dirty anymore.
696 m_iDirtyCount = 0;
697 // Stabilize form...
698 m_sFilename = sFilename;
699 updateRecentFiles(sFilename);
700 appendMessages(tr("Save session: \"%1\".").arg(sessionName(m_sFilename)));
701 stabilizeForm();
702 return true;
703 }
704
705
706 //-------------------------------------------------------------------------
707 // qsamplerMainForm -- File Action slots.
708
709 // Create a new sampler session.
710 void qsamplerMainForm::fileNew (void)
711 {
712 // Of course we'll start clean new.
713 newSession();
714 }
715
716
717 // Open an existing sampler session.
718 void qsamplerMainForm::fileOpen (void)
719 {
720 // Open it right away.
721 openSession();
722 }
723
724
725 // Open a recent file session.
726 void qsamplerMainForm::fileOpenRecent ( int iIndex )
727 {
728 // Check if we can safely close the current session...
729 if (m_pOptions && closeSession(true)) {
730 QString sFilename = m_pOptions->recentFiles[iIndex];
731 loadSessionFile(sFilename);
732 }
733 }
734
735
736 // Save current sampler session.
737 void qsamplerMainForm::fileSave (void)
738 {
739 // Save it right away.
740 saveSession(false);
741 }
742
743
744 // Save current sampler session with another name.
745 void qsamplerMainForm::fileSaveAs (void)
746 {
747 // Save it right away, maybe with another name.
748 saveSession(true);
749 }
750
751
752 // Reset the sampler instance.
753 void qsamplerMainForm::fileReset (void)
754 {
755 if (m_pClient == NULL)
756 return;
757
758 // Ask user whether he/she want's an internal sampler reset...
759 if (QMessageBox::warning(this, tr("Warning"),
760 tr("Resetting the sampler instance will close\n"
761 "all device and channel configurations.\n\n"
762 "Please note that this operation may cause\n"
763 "temporary MIDI and Audio disruption\n\n"
764 "Do you want to reset the sampler engine now?"),
765 tr("Reset"), tr("Cancel")) > 0)
766 return;
767
768 // Just do the reset, after closing down current session...
769 if (closeSession(true) && ::lscp_reset_sampler(m_pClient) != LSCP_OK) {
770 appendMessagesClient("lscp_reset_sampler");
771 appendMessagesError(tr("Could not reset sampler instance.\n\nSorry."));
772 return;
773 }
774
775 // Log this.
776 appendMessages(tr("Sampler reset."));
777 }
778
779
780 // Restart the client/server instance.
781 void qsamplerMainForm::fileRestart (void)
782 {
783 if (m_pOptions == NULL)
784 return;
785
786 bool bRestart = true;
787
788 // Ask user whether he/she want's a complete restart...
789 // (if we're currently up and running)
790 if (bRestart && m_pClient) {
791 bRestart = (QMessageBox::warning(this, tr("Warning"),
792 tr("New settings will be effective after\n"
793 "restarting the client/server connection.\n\n"
794 "Please note that this operation may cause\n"
795 "temporary MIDI and Audio disruption\n\n"
796 "Do you want to restart the connection now?"),
797 tr("Restart"), tr("Cancel")) == 0);
798 }
799
800 // Are we still for it?
801 if (bRestart && closeSession(true)) {
802 // Stop server, it will force the client too.
803 stopServer();
804 // Reschedule a restart...
805 startSchedule(m_pOptions->iStartDelay);
806 }
807 }
808
809
810 // Exit application program.
811 void qsamplerMainForm::fileExit (void)
812 {
813 // Go for close the whole thing.
814 close();
815 }
816
817
818 //-------------------------------------------------------------------------
819 // qsamplerMainForm -- Edit Action slots.
820
821 // Add a new sampler channel.
822 void qsamplerMainForm::editAddChannel (void)
823 {
824 if (m_pClient == NULL)
825 return;
826
827 // Just create the channel instance...
828 qsamplerChannel *pChannel = new qsamplerChannel(this);
829 if (pChannel == NULL)
830 return;
831
832 // Before we show it up, may be we'll
833 // better ask for some initial values?
834 if (!pChannel->channelSetup(this)) {
835 delete pChannel;
836 return;
837 }
838
839 // And give it to the strip (will own the channel instance, if successful).
840 if (!createChannelStrip(pChannel)) {
841 delete pChannel;
842 return;
843 }
844
845 // Make that an overall update.
846 m_iDirtyCount++;
847 m_iChangeCount++;
848 stabilizeForm();
849 }
850
851
852 // Remove current sampler channel.
853 void qsamplerMainForm::editRemoveChannel (void)
854 {
855 if (m_pClient == NULL)
856 return;
857
858 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
859 if (pChannelStrip == NULL)
860 return;
861
862 qsamplerChannel *pChannel = pChannelStrip->channel();
863 if (pChannel == NULL)
864 return;
865
866 // Prompt user if he/she's sure about this...
867 if (m_pOptions && m_pOptions->bConfirmRemove) {
868 if (QMessageBox::warning(this, tr("Warning"),
869 tr("About to remove channel:\n\n"
870 "%1\n\n"
871 "Are you sure?")
872 .arg(pChannelStrip->caption()),
873 tr("OK"), tr("Cancel")) > 0)
874 return;
875 }
876
877 // Remove the existing sampler channel.
878 if (!pChannel->removeChannel())
879 return;
880
881 // Just delete the channel strip.
882 delete pChannelStrip;
883
884 // Do we auto-arrange?
885 if (m_pOptions && m_pOptions->bAutoArrange)
886 channelsArrange();
887
888 // We'll be dirty, for sure...
889 m_iDirtyCount++;
890 stabilizeForm();
891 }
892
893
894 // Setup current sampler channel.
895 void qsamplerMainForm::editSetupChannel (void)
896 {
897 if (m_pClient == NULL)
898 return;
899
900 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
901 if (pChannelStrip == NULL)
902 return;
903
904 // Just invoque the channel strip procedure.
905 pChannelStrip->channelSetup();
906 }
907
908
909 // Reset current sampler channel.
910 void qsamplerMainForm::editResetChannel (void)
911 {
912 if (m_pClient == NULL)
913 return;
914
915 qsamplerChannelStrip *pChannelStrip = activeChannelStrip();
916 if (pChannelStrip == NULL)
917 return;
918
919 qsamplerChannel *pChannel = pChannelStrip->channel();
920 if (pChannel == NULL)
921 return;
922
923 // Reset the existing sampler channel.
924 pChannel->resetChannel();
925
926 // And force a deferred update.
927 m_iChangeCount++;
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 channelsArrangeAction->setEnabled(bHasChannel);
1191 viewMessagesAction->setOn(m_pMessages && m_pMessages->isVisible());
1192
1193 // Client/Server status...
1194 if (bHasClient) {
1195 m_status[QSAMPLER_STATUS_CLIENT]->setText(tr("Connected"));
1196 m_status[QSAMPLER_STATUS_SERVER]->setText(m_pOptions->sServerHost + ":" + QString::number(m_pOptions->iServerPort));
1197 } else {
1198 m_status[QSAMPLER_STATUS_CLIENT]->clear();
1199 m_status[QSAMPLER_STATUS_SERVER]->clear();
1200 }
1201 // Channel status...
1202 if (bHasChannel)
1203 m_status[QSAMPLER_STATUS_CHANNEL]->setText(pChannelStrip->caption());
1204 else
1205 m_status[QSAMPLER_STATUS_CHANNEL]->clear();
1206 // Session status...
1207 if (m_iDirtyCount > 0)
1208 m_status[QSAMPLER_STATUS_SESSION]->setText(tr("MOD"));
1209 else
1210 m_status[QSAMPLER_STATUS_SESSION]->clear();
1211
1212 // Recent files menu.
1213 m_pRecentFilesMenu->setEnabled(bHasClient && m_pOptions->recentFiles.count() > 0);
1214
1215 // Always make the latest message visible.
1216 if (m_pMessages)
1217 m_pMessages->scrollToBottom();
1218 }
1219
1220
1221 // Channel change receiver slot.
1222 void qsamplerMainForm::channelStripChanged( qsamplerChannelStrip * )
1223 {
1224 // Flag that we're update those channel strips.
1225 m_iChangeCount++;
1226 // Just mark the dirty form.
1227 m_iDirtyCount++;
1228 // and update the form status...
1229 stabilizeForm();
1230 }
1231
1232
1233 // Update the recent files list and menu.
1234 void qsamplerMainForm::updateRecentFiles ( const QString& sFilename )
1235 {
1236 if (m_pOptions == NULL)
1237 return;
1238
1239 // Remove from list if already there (avoid duplicates)
1240 QStringList::Iterator iter = m_pOptions->recentFiles.find(sFilename);
1241 if (iter != m_pOptions->recentFiles.end())
1242 m_pOptions->recentFiles.remove(iter);
1243 // Put it to front...
1244 m_pOptions->recentFiles.push_front(sFilename);
1245
1246 // May update the menu.
1247 updateRecentFilesMenu();
1248 }
1249
1250
1251 // Update the recent files list and menu.
1252 void qsamplerMainForm::updateRecentFilesMenu (void)
1253 {
1254 if (m_pOptions == NULL)
1255 return;
1256
1257 // Time to keep the list under limits.
1258 int iRecentFiles = m_pOptions->recentFiles.count();
1259 while (iRecentFiles > m_pOptions->iMaxRecentFiles) {
1260 m_pOptions->recentFiles.pop_back();
1261 iRecentFiles--;
1262 }
1263
1264 // rebuild the recent files menu...
1265 m_pRecentFilesMenu->clear();
1266 for (int i = 0; i < iRecentFiles; i++) {
1267 const QString& sFilename = m_pOptions->recentFiles[i];
1268 if (QFileInfo(sFilename).exists()) {
1269 m_pRecentFilesMenu->insertItem(QString("&%1 %2")
1270 .arg(i + 1).arg(sessionName(sFilename)),
1271 this, SLOT(fileOpenRecent(int)), 0, i);
1272 }
1273 }
1274 }
1275
1276
1277 // Force update of the channels instrument names mode.
1278 void qsamplerMainForm::updateInstrumentNames (void)
1279 {
1280 // Full channel list update...
1281 QWidgetList wlist = m_pWorkspace->windowList();
1282 if (wlist.isEmpty())
1283 return;
1284
1285 m_pWorkspace->setUpdatesEnabled(false);
1286 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1287 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1288 if (pChannelStrip)
1289 pChannelStrip->updateInstrumentName(true);
1290 }
1291 m_pWorkspace->setUpdatesEnabled(true);
1292 }
1293
1294
1295 // Force update of the channels display font.
1296 void qsamplerMainForm::updateDisplayFont (void)
1297 {
1298 if (m_pOptions == NULL)
1299 return;
1300
1301 // Check if display font is legal.
1302 if (m_pOptions->sDisplayFont.isEmpty())
1303 return;
1304 // Realize it.
1305 QFont font;
1306 if (!font.fromString(m_pOptions->sDisplayFont))
1307 return;
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->setDisplayFont(font);
1319 }
1320 m_pWorkspace->setUpdatesEnabled(true);
1321 }
1322
1323
1324 // Update channel strips background effect.
1325 void qsamplerMainForm::updateDisplayEffect (void)
1326 {
1327 QPixmap pm;
1328 if (m_pOptions->bDisplayEffect)
1329 pm = QPixmap::fromMimeSource("displaybg1.png");
1330
1331 // Full channel list update...
1332 QWidgetList wlist = m_pWorkspace->windowList();
1333 if (wlist.isEmpty())
1334 return;
1335
1336 m_pWorkspace->setUpdatesEnabled(false);
1337 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1338 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1339 if (pChannelStrip)
1340 pChannelStrip->setDisplayBackground(pm);
1341 }
1342 m_pWorkspace->setUpdatesEnabled(true);
1343 }
1344
1345
1346 // Force update of the channels maximum volume setting.
1347 void qsamplerMainForm::updateMaxVolume (void)
1348 {
1349 if (m_pOptions == NULL)
1350 return;
1351
1352 // Full channel list update...
1353 QWidgetList wlist = m_pWorkspace->windowList();
1354 if (wlist.isEmpty())
1355 return;
1356
1357 m_pWorkspace->setUpdatesEnabled(false);
1358 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1359 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1360 if (pChannelStrip)
1361 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1362 }
1363 m_pWorkspace->setUpdatesEnabled(true);
1364 }
1365
1366
1367 //-------------------------------------------------------------------------
1368 // qsamplerMainForm -- Messages window form handlers.
1369
1370 // Messages output methods.
1371 void qsamplerMainForm::appendMessages( const QString& s )
1372 {
1373 if (m_pMessages)
1374 m_pMessages->appendMessages(s);
1375
1376 statusBar()->message(s, 3000);
1377 }
1378
1379 void qsamplerMainForm::appendMessagesColor( const QString& s, const QString& c )
1380 {
1381 if (m_pMessages)
1382 m_pMessages->appendMessagesColor(s, c);
1383
1384 statusBar()->message(s, 3000);
1385 }
1386
1387 void qsamplerMainForm::appendMessagesText( const QString& s )
1388 {
1389 if (m_pMessages)
1390 m_pMessages->appendMessagesText(s);
1391 }
1392
1393 void qsamplerMainForm::appendMessagesError( const QString& s )
1394 {
1395 if (m_pMessages)
1396 m_pMessages->show();
1397
1398 appendMessagesColor(s.simplifyWhiteSpace(), "#ff0000");
1399
1400 QMessageBox::critical(this, tr("Error"), s, tr("Cancel"));
1401 }
1402
1403
1404 // This is a special message format, just for client results.
1405 void qsamplerMainForm::appendMessagesClient( const QString& s )
1406 {
1407 if (m_pClient == NULL)
1408 return;
1409
1410 appendMessagesColor(s + QString(": %1 (errno=%2)")
1411 .arg(::lscp_client_get_result(m_pClient))
1412 .arg(::lscp_client_get_errno(m_pClient)), "#996666");
1413 }
1414
1415
1416 // Force update of the messages font.
1417 void qsamplerMainForm::updateMessagesFont (void)
1418 {
1419 if (m_pOptions == NULL)
1420 return;
1421
1422 if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) {
1423 QFont font;
1424 if (font.fromString(m_pOptions->sMessagesFont))
1425 m_pMessages->setMessagesFont(font);
1426 }
1427 }
1428
1429
1430 // Update messages window line limit.
1431 void qsamplerMainForm::updateMessagesLimit (void)
1432 {
1433 if (m_pOptions == NULL)
1434 return;
1435
1436 if (m_pMessages) {
1437 if (m_pOptions->bMessagesLimit)
1438 m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines);
1439 else
1440 m_pMessages->setMessagesLimit(0);
1441 }
1442 }
1443
1444
1445 // Enablement of the messages capture feature.
1446 void qsamplerMainForm::updateMessagesCapture (void)
1447 {
1448 if (m_pOptions == NULL)
1449 return;
1450
1451 if (m_pMessages)
1452 m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture);
1453 }
1454
1455
1456 //-------------------------------------------------------------------------
1457 // qsamplerMainForm -- MDI channel strip management.
1458
1459 // The channel strip creation executive.
1460 qsamplerChannelStrip *qsamplerMainForm::createChannelStrip ( qsamplerChannel *pChannel )
1461 {
1462 if (m_pClient == NULL || pChannel == NULL)
1463 return NULL;
1464
1465 // Prepare for auto-arrange?
1466 qsamplerChannelStrip *pChannelStrip = NULL;
1467 int y = 0;
1468 if (m_pOptions && m_pOptions->bAutoArrange) {
1469 QWidgetList wlist = m_pWorkspace->windowList();
1470 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1471 pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1472 // y += pChannelStrip->height() + pChannelStrip->parentWidget()->baseSize().height();
1473 y += pChannelStrip->parentWidget()->frameGeometry().height();
1474 }
1475 }
1476
1477 // Add a new channel itema...
1478 WFlags wflags = Qt::WStyle_Customize | Qt::WStyle_Tool | Qt::WStyle_Title | Qt::WStyle_NoBorder;
1479 pChannelStrip = new qsamplerChannelStrip(m_pWorkspace, 0, wflags);
1480 if (pChannelStrip == NULL)
1481 return NULL;
1482
1483 // Actual channel strip setup...
1484 pChannelStrip->setup(pChannel);
1485 QObject::connect(pChannelStrip, SIGNAL(channelChanged(qsamplerChannelStrip *)), this, SLOT(channelStripChanged(qsamplerChannelStrip *)));
1486 // Set some initial aesthetic options...
1487 if (m_pOptions) {
1488 // Background display effect...
1489 pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect);
1490 // We'll need a display font.
1491 QFont font;
1492 if (font.fromString(m_pOptions->sDisplayFont))
1493 pChannelStrip->setDisplayFont(font);
1494 // Maximum allowed volume setting.
1495 pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume);
1496 }
1497
1498 // Now we show up us to the world.
1499 pChannelStrip->show();
1500 // Only then, we'll auto-arrange...
1501 if (m_pOptions && m_pOptions->bAutoArrange) {
1502 int iWidth = m_pWorkspace->width();
1503 // int iHeight = pChannel->height() + pChannel->parentWidget()->baseSize().height();
1504 int iHeight = pChannelStrip->parentWidget()->frameGeometry().height();
1505 pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight);
1506 }
1507
1508 // Return our successful reference...
1509 return pChannelStrip;
1510 }
1511
1512
1513 // Retrieve the active channel strip.
1514 qsamplerChannelStrip *qsamplerMainForm::activeChannelStrip (void)
1515 {
1516 return (qsamplerChannelStrip *) m_pWorkspace->activeWindow();
1517 }
1518
1519
1520 // Retrieve a channel strip by index.
1521 qsamplerChannelStrip *qsamplerMainForm::channelStripAt ( int iChannel )
1522 {
1523 QWidgetList wlist = m_pWorkspace->windowList();
1524 if (wlist.isEmpty())
1525 return 0;
1526
1527 return (qsamplerChannelStrip *) wlist.at(iChannel);
1528 }
1529
1530
1531 // Construct the windows menu.
1532 void qsamplerMainForm::channelsMenuAboutToShow (void)
1533 {
1534 channelsMenu->clear();
1535 channelsArrangeAction->addTo(channelsMenu);
1536 channelsAutoArrangeAction->addTo(channelsMenu);
1537
1538 QWidgetList wlist = m_pWorkspace->windowList();
1539 if (!wlist.isEmpty()) {
1540 channelsMenu->insertSeparator();
1541 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1542 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1543 if (pChannelStrip) {
1544 int iItemID = channelsMenu->insertItem(pChannelStrip->caption(), this, SLOT(channelsMenuActivated(int)));
1545 channelsMenu->setItemParameter(iItemID, iChannel);
1546 channelsMenu->setItemChecked(iItemID, activeChannelStrip() == pChannelStrip);
1547 }
1548 }
1549 }
1550 }
1551
1552
1553 // Windows menu activation slot
1554 void qsamplerMainForm::channelsMenuActivated ( int iChannel )
1555 {
1556 qsamplerChannelStrip *pChannelStrip = channelStripAt(iChannel);
1557 if (pChannelStrip)
1558 pChannelStrip->showNormal();
1559 pChannelStrip->setFocus();
1560 }
1561
1562
1563 //-------------------------------------------------------------------------
1564 // qsamplerMainForm -- Timer stuff.
1565
1566 // Set the pseudo-timer delay schedule.
1567 void qsamplerMainForm::startSchedule ( int iStartDelay )
1568 {
1569 m_iStartDelay = 1 + (iStartDelay * 1000);
1570 m_iTimerDelay = 0;
1571 }
1572
1573 // Suspend the pseudo-timer delay schedule.
1574 void qsamplerMainForm::stopSchedule (void)
1575 {
1576 m_iStartDelay = 0;
1577 m_iTimerDelay = 0;
1578 }
1579
1580 // Timer slot funtion.
1581 void qsamplerMainForm::timerSlot (void)
1582 {
1583 if (m_pOptions == NULL)
1584 return;
1585
1586 // Is it the first shot on server start after a few delay?
1587 if (m_iTimerDelay < m_iStartDelay) {
1588 m_iTimerDelay += QSAMPLER_TIMER_MSECS;
1589 if (m_iTimerDelay >= m_iStartDelay) {
1590 // If we cannot start it now, maybe a lil'mo'later ;)
1591 if (!startClient()) {
1592 m_iStartDelay += m_iTimerDelay;
1593 m_iTimerDelay = 0;
1594 }
1595 }
1596 }
1597
1598 // Refresh each channel usage, on each period...
1599 if (m_pClient && (m_iChangeCount > 0 || m_pOptions->bAutoRefresh)) {
1600 m_iTimerSlot += QSAMPLER_TIMER_MSECS;
1601 if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime && m_pWorkspace->isUpdatesEnabled()) {
1602 m_iTimerSlot = 0;
1603 m_iChangeCount = 0;
1604 QWidgetList wlist = m_pWorkspace->windowList();
1605 for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) {
1606 qsamplerChannelStrip *pChannelStrip = (qsamplerChannelStrip *) wlist.at(iChannel);
1607 if (pChannelStrip && pChannelStrip->isVisible()) {
1608 // If we can't make it clean, try next time.
1609 if (!pChannelStrip->updateChannelUsage())
1610 m_iChangeCount++;
1611 }
1612 }
1613 }
1614 }
1615
1616 // Register the next timer slot.
1617 QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(timerSlot()));
1618 }
1619
1620
1621 //-------------------------------------------------------------------------
1622 // qsamplerMainForm -- Server stuff.
1623
1624 // Start linuxsampler server...
1625 void qsamplerMainForm::startServer (void)
1626 {
1627 if (m_pOptions == NULL)
1628 return;
1629
1630 // Aren't already a client, are we?
1631 if (!m_pOptions->bServerStart || m_pClient)
1632 return;
1633
1634 // Is the server process instance still here?
1635 if (m_pServer) {
1636 switch (QMessageBox::warning(this, tr("Warning"),
1637 tr("Could not start the LinuxSampler server.\n\n"
1638 "Maybe it ss already started."),
1639 tr("Stop"), tr("Kill"), tr("Cancel"))) {
1640 case 0:
1641 m_pServer->tryTerminate();
1642 break;
1643 case 1:
1644 m_pServer->kill();
1645 break;
1646 }
1647 return;
1648 }
1649
1650 // Reset our timer counters...
1651 stopSchedule();
1652
1653 // OK. Let's build the startup process...
1654 m_pServer = new QProcess(this);
1655
1656 // Setup stdout/stderr capture...
1657 //if (m_pOptions->bStdoutCapture) {
1658 m_pServer->setCommunication(QProcess::Stdout | QProcess::Stderr | QProcess::DupStderr);
1659 QObject::connect(m_pServer, SIGNAL(readyReadStdout()), this, SLOT(readServerStdout()));
1660 QObject::connect(m_pServer, SIGNAL(readyReadStderr()), this, SLOT(readServerStdout()));
1661 //}
1662 // The unforgiveable signal communication...
1663 QObject::connect(m_pServer, SIGNAL(processExited()), this, SLOT(processServerExit()));
1664
1665 // Build process arguments...
1666 m_pServer->setArguments(QStringList::split(' ', m_pOptions->sServerCmdLine));
1667
1668 appendMessages(tr("Server is starting..."));
1669 appendMessagesColor(m_pOptions->sServerCmdLine, "#990099");
1670
1671 // Go jack, go...
1672 if (!m_pServer->start()) {
1673 appendMessagesError(tr("Could not start server.\n\nSorry."));
1674 processServerExit();
1675 return;
1676 }
1677
1678 // Show startup results...
1679 appendMessages(tr("Server was started with PID=%1.").arg((long) m_pServer->processIdentifier()));
1680
1681 // Reset (yet again) the timer counters,
1682 // but this time is deferred as the user opted.
1683 startSchedule(m_pOptions->iStartDelay);
1684 stabilizeForm();
1685 }
1686
1687
1688 // Stop linuxsampler server...
1689 void qsamplerMainForm::stopServer (void)
1690 {
1691 // Stop client code.
1692 stopClient();
1693
1694 // And try to stop server.
1695 if (m_pServer) {
1696 appendMessages(tr("Server is stopping..."));
1697 if (m_pServer->isRunning())
1698 m_pServer->tryTerminate();
1699 }
1700
1701 // Give it some time to terminate gracefully and stabilize...
1702 QTime t;
1703 t.start();
1704 while (t.elapsed() < QSAMPLER_TIMER_MSECS)
1705 QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput);
1706
1707 // Do final processing anyway.
1708 processServerExit();
1709 }
1710
1711
1712 // Stdout handler...
1713 void qsamplerMainForm::readServerStdout (void)
1714 {
1715 if (m_pMessages)
1716 m_pMessages->appendStdoutBuffer(m_pServer->readStdout());
1717 }
1718
1719
1720 // Linuxsampler server cleanup.
1721 void qsamplerMainForm::processServerExit (void)
1722 {
1723 // Force client code cleanup.
1724 stopClient();
1725
1726 // Flush anything that maybe pending...
1727 if (m_pMessages)
1728 m_pMessages->flushStdoutBuffer();
1729
1730 if (m_pServer) {
1731 // Force final server shutdown...
1732 appendMessages(tr("Server was stopped with exit status %1.").arg(m_pServer->exitStatus()));
1733 if (!m_pServer->normalExit())
1734 m_pServer->kill();
1735 // Destroy it.
1736 delete m_pServer;
1737 m_pServer = NULL;
1738 }
1739
1740 // Again, make status visible stable.
1741 stabilizeForm();
1742 }
1743
1744
1745 //-------------------------------------------------------------------------
1746 // qsamplerMainForm -- Client stuff.
1747
1748 // The LSCP client callback procedure.
1749 lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData )
1750 {
1751 qsamplerMainForm *pMainForm = (qsamplerMainForm *) pvData;
1752 if (pMainForm == NULL)
1753 return LSCP_FAILED;
1754
1755 // ATTN: DO NOT EVER call any GUI code here,
1756 // as this is run under some other thread context.
1757 // A custom event must be posted here...
1758 QApplication::postEvent(pMainForm, new qsamplerCustomEvent(event, pchData, cchData));
1759
1760 return LSCP_OK;
1761 }
1762
1763
1764 // Start our almighty client...
1765 bool qsamplerMainForm::startClient (void)
1766 {
1767 // Have it a setup?
1768 if (m_pOptions == NULL)
1769 return false;
1770
1771 // Aren't we already started, are we?
1772 if (m_pClient)
1773 return true;
1774
1775 // Log prepare here.
1776 appendMessages(tr("Client connecting..."));
1777
1778 // Create the client handle...
1779 m_pClient = ::lscp_client_create(m_pOptions->sServerHost.latin1(), m_pOptions->iServerPort, qsampler_client_callback, this);
1780 if (m_pClient == NULL) {
1781 // Is this the first try?
1782 // maybe we need to start a local server...
1783 if ((m_pServer && m_pServer->isRunning()) || !m_pOptions->bServerStart)
1784 appendMessagesError(tr("Could not connect to server as client.\n\nSorry."));
1785 else
1786 startServer();
1787 // This is always a failure.
1788 stabilizeForm();
1789 return false;
1790 }
1791 // Just set receive timeout value, blindly.
1792 ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout);
1793 appendMessages(tr("Client receive timeout is set to %1 msec.").arg(::lscp_client_get_timeout(m_pClient)));
1794
1795 // We may stop scheduling around.
1796 stopSchedule();
1797
1798 // We'll accept drops from now on...
1799 setAcceptDrops(true);
1800
1801 // Log success here.
1802 appendMessages(tr("Client connected."));
1803
1804 // Is any session pending to be loaded?
1805 if (!m_pOptions->sSessionFile.isEmpty()) {
1806 // Just load the prabably startup session...
1807 if (loadSessionFile(m_pOptions->sSessionFile)) {
1808 m_pOptions->sSessionFile = QString::null;
1809 return true;
1810 }
1811 }
1812
1813 // Make a new session
1814 return newSession();
1815 }
1816
1817
1818 // Stop client...
1819 void qsamplerMainForm::stopClient (void)
1820 {
1821 if (m_pClient == NULL)
1822 return;
1823
1824 // Log prepare here.
1825 appendMessages(tr("Client disconnecting..."));
1826
1827 // Clear timer counters...
1828 stopSchedule();
1829
1830 // We'll reject drops from now on...
1831 setAcceptDrops(false);
1832
1833 // Force any channel strips around, but
1834 // but avoid removing the corresponding
1835 // channels from the back-end server.
1836 m_iDirtyCount = 0;
1837 closeSession(false);
1838
1839 // Close us as a client...
1840 lscp_client_destroy(m_pClient);
1841 m_pClient = NULL;
1842
1843 // Log final here.
1844 appendMessages(tr("Client disconnected."));
1845
1846 // Make visible status.
1847 stabilizeForm();
1848 }
1849
1850
1851 // end of qsamplerMainForm.ui.h

  ViewVC Help
Powered by ViewVC