/[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 418 - (show annotations) (download) (as text)
Wed Mar 2 16:23:10 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 59666 byte(s)
Minor corrections; Changed widget destruction order on main form close.

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

  ViewVC Help
Powered by ViewVC