Updated license to GPL 2+
[jackctlmmc.git] / qt / src / mainWindow.cpp
blob84a1ab4bb1683e560ed7f03a209b19ec9c74d144
1 /***************************************************************************
2 * Copyright (C) 2009 by Alex Montgomery and Nedko Arnaudov *
3 * check@Adaon *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <QMessageBox>
22 #include <QFile>
23 #include <QTextStream>
24 #include <QDir>
25 #include <QWhatsThis>
26 #include <QCloseEvent>
28 #include "mainWindow.h"
29 #include "validator.h"
30 #include "sequencerThread.h"
32 extern "C" {
33 #include "../../common.h"
36 MainWindow::MainWindow() : m_sequencerThread(0), m_settingsMutex(QMutex::Recursive)
38 setupUi(this); // use the UI layout generated by qjackmmc.ui
40 // make sure the fps and jitter edit boxes only take positive whole numbers
41 QValidator* validator = new Validator(this);
42 fpsEdit->setValidator(validator);
43 jitterEdit->setValidator(validator);
45 // make sure the device edit box only take positive hexadecimal values
46 validator = new HexValidator(this);
47 deviceEdit->setValidator(validator);
49 // connect all UI editable elements
50 connect(fpsEdit, SIGNAL(editingFinished()), this, SLOT(onValidateFps()));
51 connect(jitterEdit, SIGNAL(editingFinished()), this, SLOT(onValidateJitter()));
52 connect(deviceEdit, SIGNAL(editingFinished()), this, SLOT(onValidateDeviceID()));
53 connect(rtBox, SIGNAL(clicked(bool)), this, SLOT(onRealtimeChanged(bool)));
54 connect(verboseBox, SIGNAL(clicked(bool)), this, SLOT(onVerboseChanged(bool)));
55 connect(whatsThisButton, SIGNAL(clicked()), this, SLOT(on_actionWhat_triggered()));
57 // set the program's icon
58 const QString iconPath(QString(ICON_DIR) + "/qjackmmc.png");
59 if (QFile::exists(iconPath))
60 setWindowIcon(QPixmap(iconPath));
63 bool MainWindow::init(int argc, char *argv[])
65 bool succeeded = initSound(argc, argv); // setup Jack, ALSA, and Lash so the user can connect MIDI to the program
67 if (succeeded)
69 setDefaultSettings();
71 // load the default configuration file if it exists
72 QFile loadFile(QDir::homePath() + QJACKMMC_CONFIG);
73 if (loadFile.exists())
74 loadConfig(loadFile);
78 // start the MMC listener thread
79 Q_ASSERT(!m_sequencerThread);
80 m_sequencerThread = new SequencerThread(this, &m_settings, &m_settingsMutex);
81 m_sequencerThread->listen(rtBox->isChecked());
83 printMMCMessage("QJackMMC is actively listening for MMC messages. If you want to see information about them as they come in, make sure \"Verbose output\" is checked.");
86 return succeeded;
89 void MainWindow::on_actionQuit_triggered()
91 close();
94 void MainWindow::on_actionAbout_triggered()
96 QString message;
97 message = QString(
98 #include "../../VERSION"
101 QMessageBox* aboutBox = new QMessageBox(this);
102 aboutBox->setText(message);
103 aboutBox->exec();
106 void MainWindow::on_actionWhat_triggered()
108 QWhatsThis::enterWhatsThisMode();
111 void MainWindow::on_loadButton_clicked()
113 QFile loadFile(QDir::homePath() + QJACKMMC_CONFIG);
114 if (loadFile.exists())
115 loadConfig(loadFile);
116 else
118 QMessageBox* fileError = new QMessageBox(this);
119 fileError->setText(QDir::homePath() + QString(QJACKMMC_CONFIG) + " does not exist. You must save a default configuration before you can load one.");
120 fileError->exec();
124 void MainWindow::on_saveButton_clicked()
126 QFile saveFile(QDir::homePath() + QJACKMMC_CONFIG);
127 if (saveFile.open(QIODevice::WriteOnly | QIODevice::Text))
129 QTextStream out(&saveFile);
131 // write out check boxes
132 int boolVal;
133 boolVal = (verboseBox->isChecked() ? 1 : 0);
134 out << boolVal << endl;
135 boolVal = (rtBox->isChecked() ? 1 : 0);
136 out << boolVal << endl;
138 // note: this value is ignored but written for backwards compatibility.
139 // It used to signify "listen on startup" but now QJackMMC is always listening.
140 boolVal = false;
141 out << boolVal << endl;
143 // write out text boxes
144 out << fpsEdit->text() << endl;
145 out << jitterEdit->text() << endl;
146 out << deviceEdit->text() << endl;
148 if (saveFile.error() != QFile::NoError)
150 QMessageBox* jackError = new QMessageBox(this);
151 jackError->setText("An error occurred while writing to " + QDir::homePath() + QString(QJACKMMC_CONFIG));
152 jackError->exec();
155 else
157 QMessageBox* fileError = new QMessageBox(this);
158 fileError->setText(QDir::homePath() + QString(QJACKMMC_CONFIG) + " cannot be opened for writing. Make sure that you have permission to write to that directory and file.");
159 fileError->exec();
164 void MainWindow::onValidateFps()
166 // validate setting, revert to prior value if not valid
167 bool ok = true;
168 int fps = fpsEdit->text().toInt(&ok);
170 // we go to great lengths to make sure that the input boxes only accept positive integers, but check the fields just in case
171 if (!ok || fps < 0)
173 QMessageBox* inputError = new QMessageBox(this);
174 inputError->setText("the frames / sec parameter needs to be a positive integer.");
175 inputError->exec();
177 // revert value
178 QMutexLocker settingsLock(&m_settingsMutex);
179 fpsEdit->setText(QString::number(m_settings.frameRate));
181 else
183 QMutexLocker settingsLock(&m_settingsMutex);
184 m_settings.frameRate = fps;
188 void MainWindow::onValidateJitter()
190 // validate setting, revert to prior value if not valid
191 bool ok = true;
192 int jitterTolerance = jitterEdit->text().toInt(&ok);
193 if (!ok || jitterTolerance < 0)
195 QMessageBox* inputError = new QMessageBox(this);
196 inputError->setText("the jitter tolerance needs to be a positive integer.");
197 inputError->exec();
199 // revert value
200 QMutexLocker settingsLock(&m_settingsMutex);
201 jitterEdit->setText(QString::number(m_settings.jitterTolerance));
203 else
205 QMutexLocker settingsLock(&m_settingsMutex);
206 m_settings.jitterTolerance = jitterTolerance;
210 void MainWindow::onValidateDeviceID()
212 // validate setting, revert to prior value if not valid
213 bool ok = true;
214 int deviceID = deviceEdit->text().toInt(&ok, 16);
215 if (!ok || deviceID > 255 || deviceID < 0)
217 QMessageBox* inputError = new QMessageBox(this);
218 inputError->setText("the deviceID needs to be a hexadecimal number between 0 and ff.");
219 inputError->exec();
221 // revert value
222 QMutexLocker settingsLock(&m_settingsMutex);
223 deviceEdit->setText(QString::number(m_settings.deviceID));
225 else
227 QMutexLocker settingsLock(&m_settingsMutex);
228 m_settings.deviceID = (uint8_t) deviceID;
233 void MainWindow::onVerboseChanged(bool checked)
235 QMutexLocker settingsLock(&m_settingsMutex);
236 m_settings.verbose = checked;
239 void MainWindow::onRealtimeChanged(bool checked)
241 if (m_sequencerThread)
242 m_sequencerThread->die(); // die() calls deleteLater and ensures the thread is cleaned up once the listen loop ends
244 m_sequencerThread = new SequencerThread(this, &m_settings, &m_settingsMutex);
245 m_sequencerThread->listen(checked);
249 void MainWindow::onMessageReceived(QString message)
251 messageArea->append(message);
254 bool MainWindow::initSound(int argc, char *argv[])
256 bool succeeded = true, alsaPortCreated = false, jackPortCreated = false;
257 int ret = init_alsa_sequencer("QJjackMMC");
258 if (ret < 0)
260 QMessageBox* alsaError = new QMessageBox(this);
261 alsaError->setText("Can't create alsa sequencer. You will not be able to connect MIDI devices to this program using ALSA. Jack Midi might still function.");
262 alsaError->exec();
264 else
265 alsaPortCreated = true;
267 if (succeeded && init_jack("QJjackMMC") < 0)
269 QMessageBox* jackError = new QMessageBox(this);
270 jackError->setText("couldn't connect to the JACK server. Would you like to start one with default parameters? (Answering \"No\" will close this program.)");
271 jackError->addButton(QMessageBox::Yes);
272 jackError->addButton(QMessageBox::No);
273 if (jackError->exec() == QMessageBox::No)
274 succeeded = false;
276 #if LASH_SUPPORT
277 init_lash(argc, argv);
278 #else
279 Q_UNUSED(argc);
280 Q_UNUSED(argv);
281 #endif // LASH_SUPPORT
283 #if JACK_MIDI_SUPPORT
284 if (succeeded)
286 if (!init_jack_midi(&m_settings))
288 QMessageBox* activateError = new QMessageBox(this);
289 activateError->setText("couldn't activate JACK midi, you will not be able to connect MIDI devices to this program using JACK midi.");
290 activateError->exec();
292 else
293 jackPortCreated = true;
295 #endif // JACK_MIDI_SUPPORT
297 if (succeeded && activate_jack() != 0)
299 QMessageBox* activateError = new QMessageBox(this);
300 activateError->setText("couldn't activate JACK, Please check your JACK installation and rerun this program.");
301 activateError->exec();
302 succeeded = false;
305 if (jackPortCreated == false && alsaPortCreated == false)
307 QMessageBox* activateError = new QMessageBox(this);
308 activateError->setText("Neither JACK midi nor ALSA midi could be initialized, bailing out.");
309 activateError->exec();
310 succeeded = false;
312 return succeeded;
315 void MainWindow::loadConfig(QFile& loadFile)
317 if (loadFile.open(QIODevice::ReadOnly | QIODevice::Text))
319 QTextStream in(&loadFile);
321 // read in check boxes
322 int boolVal;
323 in >> boolVal;
324 verboseBox->setChecked(boolVal == 1);
325 m_settings.verbose = boolVal == 1;
326 in >> boolVal;
327 rtBox->setChecked(boolVal == 1);
329 // note: this value is ignored but read for backwards compatibility.
330 // It used to signify "listen on startup" but now QJackMMC is always listening.
331 in >> boolVal;
333 // read in edit boxes
334 QString value;
335 in >> value; fpsEdit->setText(value); onValidateFps();
336 in >> value; jitterEdit->setText(value); onValidateJitter();
337 in >> value; deviceEdit->setText(value); onValidateDeviceID();
339 if (loadFile.error() != QFile::NoError)
341 QMessageBox* jackError = new QMessageBox(this);
342 jackError->setText("An error occurred while reading " + QDir::homePath() + QString(QJACKMMC_CONFIG) +
343 ". The file is either nonexistent, corrupt, or from an older version of QJackMMC. Please set the QJackMMC parameters \
344 how you like them and click \"Save as Default settings\".");
345 jackError->exec();
348 else
350 QMessageBox* fileError = new QMessageBox(this);
351 fileError->setText(QDir::homePath() + QString(QJACKMMC_CONFIG) + " cannot be opened for reading. It's either corrupt, or you don't have permission to read it.");
352 fileError->exec();
356 void MainWindow::setDefaultSettings()
358 QMutexLocker settingsLocker(&m_settingsMutex);
359 m_settings.deviceID = 0x7f;
360 m_settings.frameRate = 30;
361 m_settings.jitterTolerance = 50;
362 m_settings.verbose = false;
365 void MainWindow::closeEvent(QCloseEvent* event)
367 // separating the clean-up into the close event (instead of the destructor) prevents an XRun on shutdown
368 if (m_sequencerThread)
370 m_sequencerThread->die(); // die() calls deleteLater and ensures the thread is cleaned up once the listen loop ends
371 m_sequencerThread->wait();
374 cleanup_globals();
375 event->accept();