/[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 298 - (show annotations) (download) (as text)
Wed Nov 17 13:05:42 2004 UTC (15 years, 2 months ago) by capela
File MIME type: text/x-c++hdr
File size: 54875 byte(s)
Deferred channel info update fixed again.

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

  ViewVC Help
Powered by ViewVC