/[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 302 - (show annotations) (download) (as text)
Wed Nov 17 17:17:18 2004 UTC (19 years, 4 months ago) by capela
File MIME type: text/x-c++hdr
File size: 54863 byte(s)
Channel info update on session load (2nd.fix).

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

  ViewVC Help
Powered by ViewVC