/[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 395 - (show annotations) (download) (as text)
Sun Feb 20 19:13:33 2005 UTC (19 years, 1 month ago) by capela
File MIME type: text/x-c++hdr
File size: 58877 byte(s)
* Drag-and-drop to an existing channel strip is now also
  featured, allowing the in-place change of the channel
  sampler instrument file.

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

  ViewVC Help
Powered by ViewVC