cleanup and refactor to prepare for "always on" listen loop
[jackctlmmc.git] / qt / src / mainWindow.cpp
blob39f7c5f47eefedcf2d279a893089cf3a3d3a46dc
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>
27 #include "mainWindow.h"
28 #include "validator.h"
29 #include "sequencerThread.h"
31 extern "C" {
32 #include "../../common.h"
35 MainWindow::MainWindow() : sequencerThread(0)
37 setupUi(this); // use the UI layout generated by qjackmmc.ui
39 // make sure the fps and jitter edit boxes only take positive whole numbers
40 QValidator* validator = new Validator(this);
41 fpsEdit->setValidator(validator);
42 jitterEdit->setValidator(validator);
44 // make sure the device edit box only take positive hexadecimal values
45 validator = new HexValidator(this);
46 deviceEdit->setValidator(validator);
48 // set the program's icon
49 const QString iconPath(QString(ICON_DIR) + "/qjackmmc.png");
50 if (QFile::exists(iconPath))
51 setWindowIcon(QPixmap(iconPath));
54 bool MainWindow::init(int argc, char *argv[])
56 bool succeeded = initSound(argc, argv); // setup Jack, ALSA, and Lash so the user can connect MIDI to the program
58 if (succeeded)
60 // load the default configuration file if it exists
61 QFile loadFile(QDir::homePath() + QJACKMMC_CONFIG);
62 if (loadFile.exists())
63 loadConfig(loadFile);
66 if (listenBox->isChecked())
67 on_startButton_clicked();
70 return succeeded;
73 MainWindow::~MainWindow()
75 cleanup_globals();
78 void MainWindow::on_actionQuit_triggered()
80 close();
83 void MainWindow::on_actionAbout_triggered()
85 QString message;
86 message = QString(
87 #include "../../VERSION"
90 QMessageBox* aboutBox = new QMessageBox(this);
91 aboutBox->setText(message);
92 aboutBox->exec();
95 void MainWindow::on_actionWhat_triggered()
97 QWhatsThis::enterWhatsThisMode();
100 void MainWindow::on_startButton_clicked()
102 if (g_isListening)
104 // stop the process and wait for it to die
105 if (sequencerThread)
107 sequencerThread->die();
108 sequencerThread->wait();
109 delete sequencerThread;
110 sequencerThread = 0;
113 startButton->setText("&Start Listening");
115 g_isListening = false;
117 enableRelevantWidgets(g_isListening);
119 else
121 bool ok = true;
122 m_settings.frameRate = fpsEdit->text().toInt(&ok);
124 // we go to great lengths to make sure that the input boxes only accept positive integers, but check the fields just in case
125 if (!ok)
127 QMessageBox* inputError = new QMessageBox(this);
128 inputError->setText("the frames / sec parameter needs to be a positive integer.");
129 inputError->exec();
130 return;
132 m_settings.jitterTolerance = jitterEdit->text().toInt(&ok);
133 if (!ok)
135 QMessageBox* inputError = new QMessageBox(this);
136 inputError->setText("the jitter tolerance needs to be a positive integer.");
137 inputError->exec();
138 return;
140 int deviceID = deviceEdit->text().toInt(&ok, 16);
141 if (!ok || deviceID > 255 || deviceID < 0)
143 QMessageBox* inputError = new QMessageBox(this);
144 inputError->setText("the deviceID needs to be a hexadecimal number between 0 and ff.");
145 inputError->exec();
146 return;
148 else
149 m_settings.deviceID = (uint8_t) deviceID;
151 m_settings.verbose = verboseBox->isChecked();
153 startButton->setText("&Stop Listening");
155 // start the MMC listener thread
156 sequencerThread = new SequencerThread(this, &m_settings, &m_settingsMutex);
160 bool realtime = rtBox->isChecked();
161 sequencerThread->listen(realtime);
163 g_isListening = true;
165 // disable tinkering with MMC parameters while the process is running
166 enableRelevantWidgets(g_isListening);
170 void MainWindow::on_loadButton_clicked()
172 QFile loadFile(QDir::homePath() + QJACKMMC_CONFIG);
173 if (loadFile.exists())
174 loadConfig(loadFile);
175 else
177 QMessageBox* fileError = new QMessageBox(this);
178 fileError->setText(QDir::homePath() + QString(QJACKMMC_CONFIG) + " does not exist. You must save a default configuration before you can load one.");
179 fileError->exec();
183 void MainWindow::on_saveButton_clicked()
185 QFile saveFile(QDir::homePath() + QJACKMMC_CONFIG);
186 if (saveFile.open(QIODevice::WriteOnly | QIODevice::Text))
188 QTextStream out(&saveFile);
190 // write out check boxes
191 int boolVal;
192 boolVal = (verboseBox->isChecked() ? 1 : 0);
193 out << boolVal << endl;
194 boolVal = (rtBox->isChecked() ? 1 : 0);
195 out << boolVal << endl;
196 boolVal = (listenBox->isChecked() ? 1 : 0);
197 out << boolVal << endl;
199 // write out text boxes
200 out << fpsEdit->text() << endl;
201 out << jitterEdit->text() << endl;
202 out << deviceEdit->text() << endl;
204 if (saveFile.error() != QFile::NoError)
206 QMessageBox* jackError = new QMessageBox(this);
207 jackError->setText("An error occurred while writing to " + QDir::homePath() + QString(QJACKMMC_CONFIG));
208 jackError->exec();
211 else
213 QMessageBox* fileError = new QMessageBox(this);
214 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.");
215 fileError->exec();
220 void MainWindow::onMessageReceived(QString message)
222 messageArea->append(message);
225 bool MainWindow::initSound(int argc, char *argv[])
227 bool succeeded = true, alsaPortCreated = false, jackPortCreated = false;
228 int ret = init_alsa_sequencer("QJjackMMC");
229 if (ret < 0)
231 QMessageBox* alsaError = new QMessageBox(this);
232 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.");
233 alsaError->exec();
235 else
236 alsaPortCreated = true;
238 if (succeeded && init_jack("QJjackMMC") < 0)
240 QMessageBox* jackError = new QMessageBox(this);
241 jackError->setText("couldn't connect to the JACK server. Would you like to start one with default parameters? (Answering \"No\" will close this program.)");
242 jackError->addButton(QMessageBox::Yes);
243 jackError->addButton(QMessageBox::No);
244 if (jackError->exec() == QMessageBox::No)
245 succeeded = false;
247 #if LASH_SUPPORT
248 init_lash(argc, argv);
249 #else
250 Q_UNUSED(argc);
251 Q_UNUSED(argv);
252 #endif // LASH_SUPPORT
254 #if JACK_MIDI_SUPPORT
255 if (succeeded)
257 if (!init_jack_midi(&m_settings))
259 QMessageBox* activateError = new QMessageBox(this);
260 activateError->setText("couldn't activate JACK midi, you will not be able to connect MIDI devices to this program using JACK midi.");
261 activateError->exec();
263 else
264 jackPortCreated = true;
266 #endif // JACK_MIDI_SUPPORT
268 if (succeeded && activate_jack() != 0)
270 QMessageBox* activateError = new QMessageBox(this);
271 activateError->setText("couldn't activate JACK, Please check your JACK installation and rerun this program.");
272 activateError->exec();
273 succeeded = false;
276 if (jackPortCreated == false && alsaPortCreated == false)
278 QMessageBox* activateError = new QMessageBox(this);
279 activateError->setText("Neither JACK midi nor ALSA midi could be initialized, bailing out.");
280 activateError->exec();
281 succeeded = false;
283 return succeeded;
286 void MainWindow::loadConfig(QFile& loadFile)
288 if (loadFile.open(QIODevice::ReadOnly | QIODevice::Text))
290 QTextStream in(&loadFile);
292 // read in check boxes
293 int boolVal;
294 in >> boolVal;
295 verboseBox->setChecked(boolVal == 1);
296 in >> boolVal;
297 rtBox->setChecked(boolVal == 1);
298 in >> boolVal;
299 listenBox->setChecked(boolVal == 1);
302 // read in edit boxes
303 QString value;
304 in >> value;
305 if (value.length())
306 fpsEdit->setText(value);
307 in >> value;
308 if (value.length())
309 jitterEdit->setText(value);
310 in >> value;
311 if (value.length())
312 deviceEdit->setText(value);
314 if (loadFile.error() != QFile::NoError)
316 QMessageBox* jackError = new QMessageBox(this);
317 jackError->setText("An error occurred while reading " + QDir::homePath() + QString(QJACKMMC_CONFIG) +
318 ". The file is either nonexistent, corrupt, or from an older version of QJackMMC. Please set the QJackMMC parameters \
319 how you like them and click \"Save as Default settings\".");
320 jackError->exec();
323 else
325 QMessageBox* fileError = new QMessageBox(this);
326 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.");
327 fileError->exec();
331 void MainWindow::enableRelevantWidgets(bool isRunning)
333 // re-enable MMC parameter input boxes
334 fpsEdit->setEnabled(!isRunning);
335 jitterEdit->setEnabled(!isRunning);
336 deviceEdit->setEnabled(!isRunning);
337 rtBox->setEnabled(!isRunning);
338 verboseBox->setEnabled(!isRunning);
339 loadButton->setEnabled(!isRunning);