/[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 264 - (show annotations) (download) (as text)
Wed Sep 29 13:12:45 2004 UTC (19 years, 6 months ago) by capela
File MIME type: text/x-c++hdr
File size: 54314 byte(s)
Initial rewrite of sampler channel strips internal control structures.

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

  ViewVC Help
Powered by ViewVC