/[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 388 - (show annotations) (download) (as text)
Thu Feb 17 17:27:59 2005 UTC (12 years, 10 months ago) by capela
File MIME type: text/x-c++hdr
File size: 57626 byte(s)
* Drag-and-drop of either session files (LSCP scripts) or
  instrument files (GIG) are now supported. Multiple files
  drop is allowed, but it only makes sense for instrument
  files, each one prompting to create a new sampler channel.

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

  ViewVC Help
Powered by ViewVC