Merge lp:~mixxxdevelopers/mixxx/features_messageBox into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Sean M. Pappalardo
Status: Merged
Merged at revision: 2414
Proposed branch: lp:~mixxxdevelopers/mixxx/features_messageBox
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 1305 lines (+570/-287)
15 files modified
mixxx/src/SConscript (+1/-1)
mixxx/src/errordialog.cpp (+0/-76)
mixxx/src/errordialog.h (+0/-51)
mixxx/src/errordialoghandler.cpp (+208/-0)
mixxx/src/errordialoghandler.h (+138/-0)
mixxx/src/main.cpp (+28/-88)
mixxx/src/midi/midimapping.cpp (+60/-12)
mixxx/src/midi/midimapping.h (+3/-0)
mixxx/src/midi/midiscriptengine.cpp (+114/-48)
mixxx/src/midi/midiscriptengine.h (+9/-4)
mixxx/src/mixxxcontrol.cpp (+2/-2)
mixxx/src/recording/writeaudiofile.cpp (+1/-1)
mixxx/src/soundsourcemp3.cpp (+1/-1)
mixxx/src/upgrade.cpp (+4/-2)
mixxx/src/widget/wpushbutton.cpp (+1/-1)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/features_messageBox
Reviewer Review Type Date Requested Status
Albert Santoni Approve
Sean M. Pappalardo Needs Resubmitting
Review via email: mp+23707@code.launchpad.net

Description of the change

This branch is now ready to be merged. It successfully provides a more flexible (and two-way) replacement for our flawed use of qWarning to create user-facing dialogs in many places. I have made this replacement in MidiMapping and MidiScriptEngine, of which the latter is a good example of usage and addresses Albert's initial desire to offer the user some actions (ignore or reload) when a MIDI script goes awry. I've tested these replacements extensively and feel that they're ready for code review and wider testing.

I respectfully ask that this branch be considered for merge to trunk in time for the 1.8 release for the following reasons:
1) It provides useful user feedback and actions when there are problems with MIDI scripts, especially now that qWarning has been disabled
2) It doesn't affect the core functionality of any classes except ErrorDialog, and the previous use of that has been accounted for and tests good. (qCritical boxes.)

I ask that reviewers pay special attention to threading intricacies in the new code as I am still very new at multi-threaded programming. I have tried to use signals for cross-thread communication but would not be surprised if I messed something up. (There is also still a thread problem on shutdown with multiple MIDI devices activated that has carried over from trunk.)

To post a comment you must log in.
Revision history for this message
Albert Santoni (gamegod) wrote :

- Change DialogProperties class's name to ErrorDialogProperties.
- The DialogProperties class should have no public variables. Make public get/set methods for all the variables you want to change, and make those variables private.
- You should not have any global variables. Eg. g_pDialogHelper. If you really want to use ErrorDialog the way you are currently, you can get away with it by using the Singleton design pattern: http://www.codeguru.com/forum/showthread.php?t=344782
(The wikipedia article on it has a super overcomplicated C++ example.)
- You must make ErrorDialog thread safe. Use QMutexLocker and a single QMutex.

review: Needs Fixing
2380. By Sean M. Pappalardo

- Renamed class to a more accurate "ErrorDialogHandler"
- Renamed class to "ErrorDialogProperties"
- Protected all ErrorDialogProperties variables and made access functions
- Made ErrorDialogHandler a singleton and removed the previous global variable
- Moved ErrorDialogProperties instantiation into ErrorDialogHandler
- Fixed MidiMapping and MidiScriptEngine to work with the changes, tests good
- Mutex-protected the m_dialogKeys QList in ErrorDialogHandler, the only reentrant shared data item that is used by multiple threads
- Changed qDebugs to qWarnings in a few files where it made sense, since qWarning now just logs and they'll still show up if qDebugs are disabled

Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

Addressed all of Albert's concerns, resubmitting for review.

review: Needs Resubmitting
2381. By Sean M. Pappalardo

Idiot...forgot to add the renamed files.

Revision history for this message
Albert Santoni (gamegod) wrote :

You should probably lock around your use of m_pSignalMapper, but other than that, I think this is a huge improvement since the last review! I'm impressed! Your code's starting to look like it was written by a C++ programmer. :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/src/SConscript'
2--- mixxx/src/SConscript 2010-05-04 17:21:17 +0000
3+++ mixxx/src/SConscript 2010-05-07 07:32:30 +0000
4@@ -342,7 +342,7 @@
5 mixxxcontrol.cpp
6 mixxx.cpp
7 mixxxview.cpp
8- errordialog.cpp
9+ errordialoghandler.cpp
10 upgrade.cpp
11
12 soundsource.cpp
13
14=== removed file 'mixxx/src/errordialog.cpp'
15--- mixxx/src/errordialog.cpp 2010-01-05 11:18:14 +0000
16+++ mixxx/src/errordialog.cpp 1970-01-01 00:00:00 +0000
17@@ -1,76 +0,0 @@
18-/***************************************************************************
19- errordialog.cpp - description
20- -------------------
21- begin : Sun Feb 22 2009
22- copyright : (C) 2009 by Sean M. Pappalardo
23- email : pegasus@c64.org
24-***************************************************************************/
25-
26-/***************************************************************************
27-* *
28-* This program is free software; you can redistribute it and/or modify *
29-* it under the terms of the GNU General Public License as published by *
30-* the Free Software Foundation; either version 2 of the License, or *
31-* (at your option) any later version. *
32-* *
33-***************************************************************************/
34-
35-#include "errordialog.h"
36-#include <QMessageBox>
37-#include <QtDebug>
38-#include <QtCore>
39-
40-ErrorDialog::ErrorDialog() {
41- m_errorCondition=false;
42- connect(this, SIGNAL(showErrorDialog(int, QString, QString)), this, SLOT(errorDialog(int, QString, QString)));
43-}
44-
45-ErrorDialog::~ErrorDialog()
46-{
47-}
48-
49-void ErrorDialog::requestErrorDialog(int type, QString message) {
50- switch (type) {
51- case 1: requestErrorDialog(type,"Mixxx - Critical error",message); break;
52- case 0:
53- default:
54- requestErrorDialog(type,"Mixxx - Warning",message);
55- break;
56- }
57-}
58-
59-void ErrorDialog::requestErrorDialog(int type, QString title, QString message) {
60- emit (showErrorDialog(type, title, message));
61- disconnect(this, SIGNAL(showErrorDialog(int, QString, QString)), this, SLOT(errorDialog(int, QString, QString))); // Avoid multiple dialogs until this one is acknowledged
62-}
63-
64-void ErrorDialog::errorDialog(int type, QString title, QString message) {
65- QMessageBox msgBox;
66- msgBox.setText(message);
67- msgBox.setWindowTitle(title);
68- switch (type) {
69- case 1: msgBox.setIcon(QMessageBox::Critical); break;
70- case 0:
71- default:
72- msgBox.setIcon(QMessageBox::Warning);
73- break;
74- }
75- msgBox.exec(); // Block so the user can read it before exiting
76-
77- if (type==1) { // If critical/fatal, gracefully exit application if possible
78- m_errorCondition=true;
79- if (QCoreApplication::instance()) {
80- QCoreApplication::instance()->exit(-1);
81- }
82- else {
83- qDebug() << "QCoreApplication::instance() is NULL! Abruptly quitting...";
84- exit(-1);
85- }
86- }
87-
88- connect(this, SIGNAL(showErrorDialog(int, QString, QString)), this, SLOT(errorDialog(int, QString, QString))); // reconnect the signal
89-}
90-
91-bool ErrorDialog::checkError() {
92- return m_errorCondition;
93-}
94\ No newline at end of file
95
96=== removed file 'mixxx/src/errordialog.h'
97--- mixxx/src/errordialog.h 2010-01-05 11:18:14 +0000
98+++ mixxx/src/errordialog.h 1970-01-01 00:00:00 +0000
99@@ -1,51 +0,0 @@
100-/***************************************************************************
101- errordialog.h - description
102- -------------------
103- begin : Fri Feb 20 2009
104- copyright : (C) 2009 by Sean M. Pappalardo
105- email : pegasus@c64.org
106- ***************************************************************************/
107-
108-/***************************************************************************
109- * *
110- * This program is free software; you can redistribute it and/or modify *
111- * it under the terms of the GNU General Public License as published by *
112- * the Free Software Foundation; either version 2 of the License, or *
113- * (at your option) any later version. *
114- * *
115- ***************************************************************************/
116-
117-#ifndef ERRORDIALOG_H
118-#define ERRORDIALOG_H
119-
120-#include <QObject>
121-
122-/**
123- * Class used to allow all threads to display message boxes on error conditions
124- *
125- *@author Sean M. Pappalardo
126- */
127-
128-class ErrorDialog : public QObject {
129- Q_OBJECT
130-public:
131- ErrorDialog();
132- ~ErrorDialog();
133- /** A qMessageHandler calls either of these to emit a signal to display the requested message box */
134- void requestErrorDialog(int type, QString message);
135- void requestErrorDialog(int type, QString title, QString message);
136- /** Allows a means for main() to skip exec() if there was a critical or fatal error dialog displayed on app initialization */
137- bool checkError();
138-
139-signals:
140- void showErrorDialog(int type, QString title, QString message);
141-
142-private:
143- bool m_errorCondition;
144-
145-private slots:
146- /** Actually displays the box */
147- void errorDialog(int type, QString title, QString message);
148-};
149-
150-#endif
151
152=== added file 'mixxx/src/errordialoghandler.cpp'
153--- mixxx/src/errordialoghandler.cpp 1970-01-01 00:00:00 +0000
154+++ mixxx/src/errordialoghandler.cpp 2010-05-07 07:32:30 +0000
155@@ -0,0 +1,208 @@
156+/***************************************************************************
157+ errordialoghandler.cpp - description
158+ -------------------
159+ begin : Sun Feb 22 2009
160+ copyright : (C) 2009 by Sean M. Pappalardo
161+ email : pegasus@c64.org
162+***************************************************************************/
163+
164+/***************************************************************************
165+* *
166+* This program is free software; you can redistribute it and/or modify *
167+* it under the terms of the GNU General Public License as published by *
168+* the Free Software Foundation; either version 2 of the License, or *
169+* (at your option) any later version. *
170+* *
171+***************************************************************************/
172+
173+#include "errordialoghandler.h"
174+#include <QMessageBox>
175+#include <QtDebug>
176+#include <QtCore>
177+
178+ErrorDialogProperties::ErrorDialogProperties() {
179+ m_type = DLG_NONE;
180+ m_icon = QMessageBox::NoIcon;
181+ m_title = "Mixxx";
182+ m_modal = true;
183+}
184+
185+void ErrorDialogProperties::setTitle(QString title) {
186+ m_title.append(" - ").append(title);
187+}
188+
189+void ErrorDialogProperties::setText(QString text) {
190+ // If no key is set, use this window text since it is likely to be unique
191+ if (m_key.isEmpty()) m_key = text;
192+ m_text = text;
193+}
194+
195+void ErrorDialogProperties::setType(DialogType typeToSet) {
196+ m_type = typeToSet;
197+ switch (m_type) {
198+ case DLG_FATAL: // Fatal uses critical icon
199+ case DLG_CRITICAL: m_icon = QMessageBox::Critical; break;
200+ case DLG_WARNING: m_icon = QMessageBox::Warning; break;
201+ case DLG_INFO: m_icon = QMessageBox::Information; break;
202+ case DLG_QUESTION: m_icon = QMessageBox::Question; break;
203+ case DLG_NONE:
204+ default:
205+ // default is NoIcon
206+ break;
207+ }
208+}
209+
210+void ErrorDialogProperties::addButton(QMessageBox::StandardButton button) {
211+ m_buttons.append(button);
212+}
213+
214+
215+// ----------------------------------------------------
216+// ---------- ErrorDialogHandler begins here ----------
217+
218+ErrorDialogHandler::ErrorDialogHandler() {
219+ m_pSignalMapper = new QSignalMapper(this);
220+ connect(m_pSignalMapper, SIGNAL(mapped(QString)), this, SLOT(boxClosed(QString)));
221+
222+ m_errorCondition=false;
223+ connect(this, SIGNAL(showErrorDialog(ErrorDialogProperties*)),
224+ this, SLOT(errorDialog(ErrorDialogProperties*)));
225+}
226+
227+ErrorDialogHandler::~ErrorDialogHandler()
228+{
229+ delete m_pSignalMapper;
230+ delete s_pInstance;
231+}
232+
233+ErrorDialogProperties* ErrorDialogHandler::newDialogProperties() {
234+ ErrorDialogProperties* props = new ErrorDialogProperties();
235+ return props;
236+}
237+
238+bool ErrorDialogHandler::requestErrorDialog(DialogType type, QString message) {
239+ ErrorDialogProperties* props = newDialogProperties();
240+ props->setType(type);
241+ props->setText(message);
242+ switch (type) {
243+ case DLG_FATAL: props->setTitle("Fatal error"); break;
244+ case DLG_CRITICAL: props->setTitle("Critical error"); break;
245+ case DLG_WARNING: props->setTitle("Warning"); break;
246+ case DLG_INFO: props->setTitle("Information"); break;
247+ case DLG_QUESTION: props->setTitle("Question"); break;
248+ case DLG_NONE:
249+ default:
250+ // Default title & (lack of) icon is fine
251+ break;
252+ }
253+ return requestErrorDialog(props);
254+}
255+
256+bool ErrorDialogHandler::requestErrorDialog(ErrorDialogProperties* props) {
257+
258+ // Make sure the minimum items are set
259+ QString text = props->getText();
260+ Q_ASSERT(!text.isEmpty());
261+
262+ // Skip if a dialog with the same key is already displayed
263+ m_mutex.lock();
264+ bool keyExists = m_dialogKeys.contains(props->getKey());
265+ m_mutex.unlock();
266+ if (keyExists) {
267+ ErrorDialogProperties* dlgPropsTemp = props;
268+ props = NULL;
269+ delete dlgPropsTemp;
270+ return false;
271+ }
272+
273+ emit (showErrorDialog(props));
274+ return true;
275+}
276+
277+void ErrorDialogHandler::errorDialog(ErrorDialogProperties* props) {
278+
279+ // Jest makin' sho' this is only run in the main (GUI) thread
280+ Q_ASSERT(QThread::currentThread()->objectName() == "Main");
281+
282+ QMessageBox* msgBox = new QMessageBox();
283+
284+ msgBox->setIcon(props->m_icon);
285+ msgBox->setWindowTitle(props->m_title);
286+ msgBox->setText(props->m_text);
287+ if (!props->m_infoText.isEmpty()) msgBox->setInformativeText(props->m_infoText);
288+ if (!props->m_details.isEmpty()) msgBox->setDetailedText(props->m_details);
289+
290+ QPushButton* buttonToSet;
291+ bool setDefault;
292+ while(!props->m_buttons.isEmpty()) {
293+ setDefault = false;
294+ if (props->m_buttons.first() == props->m_defaultButton) setDefault = true;
295+
296+ buttonToSet = msgBox->addButton(props->m_buttons.takeFirst());
297+
298+ if (setDefault) msgBox->setDefaultButton(buttonToSet);
299+ }
300+
301+ if (props->m_escapeButton) msgBox->setEscapeButton(msgBox->button(props->m_escapeButton));
302+
303+ msgBox->setModal(props->m_modal);
304+
305+ // This deletes the msgBox automatically, avoiding a memory leak
306+ msgBox->setAttribute(Qt::WA_DeleteOnClose, true);
307+
308+ m_mutex.lock();
309+ m_dialogKeys.append(props->m_key); // To avoid duplicate dialogs on the same error
310+ m_mutex.unlock();
311+
312+ // Signal mapper calls our slot with the key parameter so it knows which to remove from the list
313+ connect(msgBox, SIGNAL(finished(int)), m_pSignalMapper, SLOT(map()));
314+ m_pSignalMapper->setMapping(msgBox, props->m_key);
315+
316+ if (props->m_modal) msgBox->exec(); // Blocks so the user has a chance to read it before application exit
317+ else msgBox->show();
318+
319+ // If fatal, should we just abort here and not try to exit gracefully?
320+
321+ if (props->m_type>=DLG_CRITICAL) { // If critical/fatal, gracefully exit application if possible
322+ m_errorCondition=true;
323+ if (QCoreApplication::instance()) {
324+ QCoreApplication::instance()->exit(-1);
325+ }
326+ else {
327+ qDebug() << "QCoreApplication::instance() is NULL! Abruptly quitting...";
328+ if (props->m_type==DLG_FATAL) abort();
329+ else exit(-1);
330+ }
331+ }
332+
333+ ErrorDialogProperties* dlgPropsTemp = props;
334+ props = NULL;
335+ delete dlgPropsTemp;
336+}
337+
338+void ErrorDialogHandler::boxClosed(QString key)
339+{
340+ QMessageBox* msgBox = (QMessageBox*)m_pSignalMapper->mapping(key);
341+
342+ QMessageBox::StandardButton whichStdButton = msgBox->standardButton(msgBox->clickedButton());
343+
344+ emit stdButtonClicked(key, whichStdButton);
345+
346+ // If the user clicks "Ignore," we leave the key in the list so the same
347+ // error is not displayed again for the duration of the session
348+ if (whichStdButton == QMessageBox::Ignore) {
349+ qWarning() << "Suppressing this" << msgBox->windowTitle() << "error box for the duration of the application.";
350+ return;
351+ }
352+
353+ m_mutex.lock();
354+ if (m_dialogKeys.contains(key)) {
355+ if (!m_dialogKeys.removeOne(key)) qWarning() << "Error dialog key removal from list failed!";
356+ }
357+ else qWarning() << "Error dialog key is missing from key list!";
358+ m_mutex.unlock();
359+}
360+
361+bool ErrorDialogHandler::checkError() {
362+ return m_errorCondition;
363+}
364\ No newline at end of file
365
366=== added file 'mixxx/src/errordialoghandler.h'
367--- mixxx/src/errordialoghandler.h 1970-01-01 00:00:00 +0000
368+++ mixxx/src/errordialoghandler.h 2010-05-07 07:32:30 +0000
369@@ -0,0 +1,138 @@
370+/***************************************************************************
371+ errordialoghandler.h - description
372+ -------------------
373+ begin : Fri Feb 20 2009
374+ copyright : (C) 2009 by Sean M. Pappalardo
375+ email : pegasus@c64.org
376+ ***************************************************************************/
377+
378+/***************************************************************************
379+ * *
380+ * This program is free software; you can redistribute it and/or modify *
381+ * it under the terms of the GNU General Public License as published by *
382+ * the Free Software Foundation; either version 2 of the License, or *
383+ * (at your option) any later version. *
384+ * *
385+ ***************************************************************************/
386+
387+#ifndef ERRORDIALOGHANDLER_H
388+#define ERRORDIALOGHANDLER_H
389+
390+#include <QObject>
391+#include <QMessageBox>
392+#include <QSignalMapper>
393+#include <QMutex>
394+
395+/**
396+ * Class used to allow all threads to display message boxes on error conditions
397+ * with a custom list of standard buttons and to be able to react to them
398+ *
399+ *@author Sean M. Pappalardo
400+ */
401+
402+typedef enum {
403+ DLG_FATAL = 5,
404+ DLG_CRITICAL = 4,
405+ DLG_WARNING = 3,
406+ DLG_INFO = 2,
407+ DLG_QUESTION = 1,
408+ DLG_NONE = 0 // No icon (default)
409+} DialogType;
410+
411+class ErrorDialogProperties {
412+ public:
413+ // Befriending ErrorDialogHandler allows it to have cleaner code
414+ // since the two are closely related anyway
415+ friend class ErrorDialogHandler;
416+
417+ /** Set the window title. ("Mixxx" is always prepended.) */
418+ void setTitle(QString title);
419+ /** Set a key to prevent multiple dialogs until the first is closed */
420+ void setKey(QString key) { m_key = key; }
421+ QString getKey() { return m_key; }
422+ /** Set the primary window text */
423+ void setText(QString text);
424+ QString getText() { return m_text; }
425+ /** Set additional window text */
426+ void setInfoText(QString text) { m_infoText = text; }
427+ /** Set detailed text (causes "Show Details" button to appear.) */
428+ void setDetails(QString text) { m_details = text; }
429+ /** Set whether the box is modal (blocks the GUI) or not */
430+ void setModal(bool modal) { m_modal = modal; }
431+ /** Automatically sets the icon to match the type for convenience. */
432+ void setType(DialogType typeToSet);
433+ /** Set the box's icon to one of the standard ones */
434+ void setIcon(QMessageBox::Icon icon) { m_icon = icon; }
435+
436+ /** Add a standard button to the box */
437+ void addButton(QMessageBox::StandardButton);
438+ /** Set the default button to highlight */
439+ void setDefaultButton(QMessageBox::StandardButton button) { m_defaultButton = button; }
440+ /** Set the button to click if the Escape key is pressed */
441+ void setEscapeButton(QMessageBox::StandardButton button) { m_escapeButton = button; }
442+
443+ protected:
444+ // Protected since only ErrorDialogHandler should instantiate this
445+ ErrorDialogProperties();
446+
447+ QString m_title;
448+ QString m_key;
449+ QString m_text;
450+ QString m_infoText;
451+ QString m_details;
452+ bool m_modal;
453+ DialogType m_type;
454+ QMessageBox::Icon m_icon;
455+ /** List of standard buttons to add to the box, in order
456+ Note that a QMessageBox::Ignore button, if clicked, will suppress
457+ error boxes with the same key for the duration of the application. */
458+ QList<QMessageBox::StandardButton> m_buttons;
459+ /** The default button to highlight, if any. */
460+ QMessageBox::StandardButton m_defaultButton;
461+ /** The button that's clicked if the Escape key is pressed. */
462+ QMessageBox::StandardButton m_escapeButton;
463+};
464+
465+/** Singleton class because we only need one Handler to manage all error dialogs */
466+class ErrorDialogHandler : public QObject {
467+ Q_OBJECT
468+public:
469+ static ErrorDialogHandler* instance() {
470+ if (!s_pInstance)
471+ s_pInstance = new ErrorDialogHandler;
472+ return s_pInstance;
473+ }
474+
475+ ~ErrorDialogHandler();
476+ /** Call this to get a new instance of ErrorDialogProperties to populate with data */
477+ ErrorDialogProperties* newDialogProperties();
478+ /** A qMessageHandler or any thread calls either of these to emit a signal to display the requested message box */
479+ /** They return false if a dialog with the same key (or title if no key) is already displayed */
480+ bool requestErrorDialog(DialogType type, QString message);
481+ bool requestErrorDialog(ErrorDialogProperties* props);
482+ /** Allows a means for main() to skip exec() if there was a critical or fatal error dialog displayed on app initialization */
483+ bool checkError();
484+
485+signals:
486+ void showErrorDialog(ErrorDialogProperties* props);
487+ void stdButtonClicked(QString key, QMessageBox::StandardButton whichStdButton);
488+
489+private:
490+ ErrorDialogHandler(); // Private constructor
491+ ErrorDialogHandler(const ErrorDialogHandler&); // Prevent copy-construction
492+ ErrorDialogHandler& operator=(const ErrorDialogHandler&); // Prevent assignment
493+
494+ static ErrorDialogHandler *s_pInstance;
495+
496+ bool m_errorCondition;
497+ QList<QString> m_dialogKeys;
498+ QSignalMapper* m_pSignalMapper;
499+ QMutex m_mutex;
500+
501+private slots:
502+ /** Actually displays the box */
503+ void errorDialog(ErrorDialogProperties* props);
504+ void boxClosed(QString key);
505+};
506+
507+#endif
508
509=== modified file 'mixxx/src/main.cpp'
510--- mixxx/src/main.cpp 2010-04-12 07:42:45 +0000
511+++ mixxx/src/main.cpp 2010-05-07 07:32:30 +0000
512@@ -31,8 +31,9 @@
513 #include "mixxx.h"
514 #include "qpixmap.h"
515 #include "qsplashscreen.h"
516-#include "errordialog.h"
517+#include "errordialoghandler.h"
518 #include "defs_audiofiles.h"
519+#include "defs_version.h"
520
521 #ifdef __LADSPA__
522 #include <ladspa/ladspaloader.h>
523@@ -70,72 +71,12 @@
524 QApplication * a;
525
526 QStringList plugin_paths; //yes this is global. sometimes global is good.
527-ErrorDialog *dialogHelper; //= new ErrorDialog(); // allows threads to show error dialogs
528+ErrorDialogHandler* ErrorDialogHandler::s_pInstance = 0; // Resolves "undefined reference" linker errors
529
530 void qInitImages_mixxx();
531
532-void MessageOutput( QtMsgType type, const char * msg )
533-{
534- static QMutex mutex;
535- QMutexLocker locker(&mutex);
536-
537- switch ( type ) {
538- case QtDebugMsg:
539-#ifdef __WINDOWS__
540- if (strstr(msg, "doneCurrent")) {
541- break;
542- }
543-#endif
544- fprintf( stderr, "Debug: %s\n", msg );
545- break;
546- case QtWarningMsg:
547- fprintf( stderr, "Warning: %s\n", msg);
548- break;
549- case QtCriticalMsg:
550- fprintf( stderr, "Critical: %s\n", msg );
551- QMessageBox::warning(0, "Mixxx", msg);
552- exit(-1);
553- break;
554- case QtFatalMsg:
555- fprintf( stderr, "Fatal: %s\n", msg );
556- QMessageBox::warning(0, "Mixxx", msg);
557- abort();
558- }
559-}
560-
561 QFile Logfile; // global logfile variable
562
563-
564-void MessageToLogfile( QtMsgType type, const char * msg )
565-{
566- static QMutex mutex;
567- QMutexLocker locker(&mutex);
568-
569- Q3TextStream Log( &Logfile );
570- switch ( type ) {
571- case QtDebugMsg:
572- Log << "Debug: " << msg << "\n";
573- break;
574- case QtWarningMsg:
575- Log << "Warning: " << msg << "\n";
576- //a->lock(); //this doesn't do anything in Qt4
577- QMessageBox::warning(0, "Mixxx", msg);
578- //a->unlock();
579- break;
580- case QtCriticalMsg:
581- fprintf( stderr, "Critical: %s\n", msg );
582- QMessageBox::warning(0, "Mixxx", msg);
583- exit(-1);
584- break;
585- case QtFatalMsg:
586- fprintf( stderr, "Fatal: %s\n", msg );
587- QMessageBox::warning(0, "Mixxx", msg);
588- abort();
589- }
590- Logfile.flush();
591-}
592-
593-
594 /* Debug message handler which outputs to both a logfile and a
595 * and prepends the thread the message came from too.
596 *
597@@ -161,7 +102,9 @@
598 #endif
599 }
600
601- Q3TextStream Log( &Logfile );
602+ Q3TextStream Log( &Logfile );
603+
604+ ErrorDialogHandler* dialogHandler = ErrorDialogHandler::instance();
605
606 switch ( type ) {
607 case QtDebugMsg:
608@@ -176,26 +119,22 @@
609 case QtWarningMsg:
610 fprintf( stderr, "Warning: %s\n", s);
611 Log << "Warning: " << s << "\n";
612- //QMessageBox::warning(0, "Mixxx", input);
613- //dialogHelper->requestErrorDialog(0,input);
614- //I will break your legs if you re-enable the above lines of code.
615- //You shouldn't be using qWarning for reporting user-facing errors.
616- //Implement your own error message box...
617- // - Albert (March 11, 2010)
618+ // Don't use qWarning for reporting user-facing errors.
619+ //dialogHandler->requestErrorDialog(DLG_WARNING,input);
620 break;
621 case QtCriticalMsg:
622 fprintf( stderr, "Critical: %s\n", s );
623 Log << "Critical: " << s << "\n";
624- //QMessageBox::critical(0, "Mixxx", input);
625- dialogHelper->requestErrorDialog(1,input);
626-// exit(-1);
627- break; //NOTREACHED(?)
628+ Logfile.flush(); // Ensure the error is written to the log before exiting
629+ dialogHandler->requestErrorDialog(DLG_CRITICAL,input);
630+// exit(-1); // Done in ErrorDialogHandler
631+ break; //NOTREACHED
632 case QtFatalMsg:
633 fprintf( stderr, "Fatal: %s\n", s );
634 Log << "Fatal: " << s << "\n";
635- //QMessageBox::critical(0, "Mixxx", input);
636- dialogHelper->requestErrorDialog(1,input);
637- abort();
638+ Logfile.flush(); // Ensure the error is written to the log before aborting
639+ dialogHandler->requestErrorDialog(DLG_FATAL,input);
640+// abort(); // Done in ErrorDialogHandler
641 break; //NOTREACHED
642 }
643 Logfile.flush();
644@@ -208,22 +147,21 @@
645
646
647 //it seems like this code should be inline in MessageHandler() but for some reason having it there corrupts the messages sometimes -kousu 2/2009
648-
649+
650
651 #ifdef __WINDOWS__
652 #ifdef DEBUGCONSOLE
653 InitDebugConsole();
654 #endif
655 #endif
656- dialogHelper = new ErrorDialog();
657-
658 qInstallMsgHandler( MessageHandler );
659-
660+
661+ // Other things depend on this name to enforce thread exclusivity,
662+ // so if you change it here, change it also in:
663+ // * ErrorDialogHandler::errorDialog()
664 QThread::currentThread()->setObjectName("Main");
665 a = new QApplication(argc, argv);
666
667-
668-
669 #ifdef __LADSPA__
670 //LADSPALoader ladspaloader;
671 #endif
672@@ -251,7 +189,9 @@
673 for (int i=0; i<argc; ++i)
674 {
675 if (argv[i]==QString("-h") || argv[i]==QString("--h") || argv[i]==QString("--help")) {
676- printf("Mixxx digital DJ software - command line options");
677+ printf("Mixxx digital DJ software v");
678+ printf(VERSION);
679+ printf(" - Command line options");
680 printf("\n(These are case-sensitive.)\n\n\
681 [FILE] Load the specified music file(s) at start-up.\n\
682 Each must be one of the following file types:\n\
683@@ -345,7 +285,7 @@
684
685 int result = -1;
686
687- if (!dialogHelper->checkError()) {
688+ if (!(ErrorDialogHandler::instance()->checkError())) {
689 qDebug() << "Displaying mixxx";
690 mixxx->show();
691
692@@ -355,12 +295,12 @@
693
694 delete mixxx;
695
696- qDebug() << "Mixxx shutdown complete.";
697+ qDebug() << "Mixxx shutdown complete with code" << result;
698
699- // Don't make any more output after this
700- // or mixxx.log will get clobbered!
701+ // Don't make any more output after this
702+ // or mixxx.log will get clobbered!
703 if(Logfile.isOpen())
704- Logfile.close();
705+ Logfile.close();
706
707 //delete plugin_paths;
708 return result;
709
710=== modified file 'mixxx/src/midi/midimapping.cpp'
711--- mixxx/src/midi/midimapping.cpp 2010-02-02 19:33:39 +0000
712+++ mixxx/src/midi/midimapping.cpp 2010-05-07 07:32:30 +0000
713@@ -30,14 +30,15 @@
714 #include "mididevicedummy.h"
715 #include "midiledhandler.h"
716 #include "configobject.h"
717+#include "errordialoghandler.h"
718
719 #define REQUIRED_SCRIPT_FILE "midi-mappings-scripts.js"
720 #define XML_SCHEMA_VERSION "1"
721 #define DEFAULT_DEVICE_PRESET BINDINGS_PATH.append(m_deviceName.right(m_deviceName.size()-m_deviceName.indexOf(" ")-1).replace(" ", "_") + MIDI_MAPPING_EXTENSION)
722
723-static QString toHex(QString numberStr) {
724- return "0x" + QString("0" + QString::number(numberStr.toUShort(), 16).toUpper()).right(2);
725-}
726+// static QString toHex(QString numberStr) {
727+// return "0x" + QString("0" + QString::number(numberStr.toUShort(), 16).toUpper()).right(2);
728+// }
729
730 MidiMapping::MidiMapping(MidiDevice* outputMidiDevice)
731 : QObject(),
732@@ -75,9 +76,12 @@
733
734 if(m_pScriptEngine) return;
735
736- //XXX Deadly hack attack:
737+ //XXX FIXME: Deadly hack attack:
738 if (m_pOutputMidiDevice == NULL) {
739 m_pOutputMidiDevice = new MidiDeviceDummy(this); //Just make some dummy device :(
740+ /* Why can't this be the same as the input MIDI device? Most of the time,
741+ the script engine thinks of it as the input device. Scripting is useful
742+ even if the MIDI device has no outputs - Sean 4/19/10 */
743 }
744 //XXX Memory leak :(
745
746@@ -110,6 +114,9 @@
747 m_pScriptEngine, SLOT(execute(QString)));
748 connect(this, SIGNAL(callMidiScriptFunction(QString, QString)),
749 m_pScriptEngine, SLOT(execute(QString, QString)));
750+
751+ // Allow the MidiScriptEngine to tell us if it needs to reset the controller (on errors)
752+ connect(m_pScriptEngine, SIGNAL(resetController()), this, SLOT(reset()));
753 }
754
755 void MidiMapping::loadScriptCode() {
756@@ -598,13 +605,46 @@
757 if (mixxxControl.getMidiOption()==MIDI_OPT_SCRIPT &&
758 scriptFunctions.indexOf(mixxxControl.getControlObjectValue())==-1) {
759
760- QString statusText = QString(midiMessage.getMidiStatusByte());
761- qWarning() << "Error: Function" << mixxxControl.getControlObjectValue()
762- << "was not found in loaded scripts."
763- << "The MIDI Message with status byte"
764- << statusText << midiMessage.getMidiNo()
765- << "will not be bound. Please check the"
766- << "mapping and script files.";
767+ QString status = QString("%1").arg(midiMessage.getMidiStatusByte(), 0, 16).toUpper();
768+ status = "0x"+status;
769+ QString byte2 = QString("%1").arg(midiMessage.getMidiNo(), 0, 16).toUpper();
770+ byte2 = "0x"+byte2;
771+
772+ // If status is MIDI pitch, the 2nd byte is part of the payload so don't display it
773+ if (midiMessage.getMidiStatusByte() == 0xE0) byte2 = "";
774+
775+ QString errorLog = QString("MIDI script function \"%1\" not found. "
776+ "(Mapped to MIDI message %2 %3)")
777+ .arg(mixxxControl.getControlObjectValue())
778+ .arg(status)
779+ .arg(byte2);
780+
781+ if (m_pOutputMidiDevice != NULL
782+ && m_pOutputMidiDevice->midiDebugging()) {
783+ qCritical() << errorLog;
784+ }
785+ else {
786+ qWarning() << errorLog;
787+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
788+ props->setType(DLG_WARNING);
789+ props->setTitle(tr("MIDI script function not found"));
790+ props->setText(QString(tr("The MIDI script function '%1' was not "
791+ "found in loaded scripts."))
792+ .arg(mixxxControl.getControlObjectValue()));
793+ props->setInfoText(QString(tr("The MIDI message %1 %2 will not be bound."
794+ "\n(Click Show Details for hints.)"))
795+ .arg(status)
796+ .arg(byte2));
797+ QString detailsText = QString(tr("* Check to see that the "
798+ "function name is spelled correctly in the mapping "
799+ "file (.xml) and script file (.js)\n"));
800+ detailsText += QString(tr("* Check to see that the script "
801+ "file name (.js) is spelled correctly in the mapping "
802+ "file (.xml)"));
803+ props->setDetails(detailsText);
804+
805+ ErrorDialogHandler::instance()->requestErrorDialog(props);
806+ }
807 } else {
808 #endif
809 //Add to the input mapping.
810@@ -936,7 +976,7 @@
811 /*MixxxControl* MidiMapping::getInputMixxxControl(MidiMessage command)
812 {
813 if (!m_inputMapping.contains(command)) {
814- qDebug() << "Warning: unbound MIDI command";
815+ qWarning() << "Unbound MIDI command";
816 qDebug() << "Midi Status:" << command.getMidiStatusByte();
817 qDebug() << "Midi No:" << command.getMidiNo();
818 qDebug() << "Midi Channel:" << command.getMidiChannel();
819@@ -1109,6 +1149,14 @@
820 }
821 #endif
822
823+// Reset the MIDI controller
824+void MidiMapping::reset() {
825+#ifdef __MIDISCRIPT__ // Can't ifdef slots in the .h file, so we just do the body.
826+ restartScriptEngine();
827+ applyPreset();
828+#endif
829+}
830+
831 void MidiMapping::slotScriptEngineReady() {
832 #ifdef __MIDISCRIPT__ // Can't ifdef slots in the .h file, so we just do the body.
833
834
835=== modified file 'mixxx/src/midi/midimapping.h'
836--- mixxx/src/midi/midimapping.h 2010-02-02 19:33:39 +0000
837+++ mixxx/src/midi/midimapping.h 2010-05-07 07:32:30 +0000
838@@ -102,6 +102,9 @@
839 void beginMidiLearn(MixxxControl control);
840 void cancelMidiLearn();
841 void slotScriptEngineReady();
842+ /** Restarts the script engine and re-applies the mapping
843+ to effectively reset the controller */
844+ void reset();
845
846 signals:
847 void inputMappingChanged();
848
849=== modified file 'mixxx/src/midi/midiscriptengine.cpp'
850--- mixxx/src/midi/midiscriptengine.cpp 2010-03-24 07:13:38 +0000
851+++ mixxx/src/midi/midiscriptengine.cpp 2010-05-07 07:32:30 +0000
852@@ -20,13 +20,15 @@
853 #include "controlobjectthread.h"
854 #include "mididevice.h"
855 #include "midiscriptengine.h"
856+#include "errordialoghandler.h"
857+
858 // #include <QScriptSyntaxCheckResult>
859
860 #ifdef _MSC_VER
861-#include <float.h> // for _isnan() on VC++
862-#define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan()
863+ #include <float.h> // for _isnan() on VC++
864+ #define isnan(x) _isnan(x) // VC++ uses _isnan() instead of isnan()
865 #else
866-#include <math.h> // for isnan() everywhere else
867+ #include <math.h> // for isnan() everywhere else
868 #endif
869
870
871@@ -34,6 +36,8 @@
872 m_pEngine(NULL),
873 m_pMidiDevice(midiDevice)
874 {
875+ // Handle error dialog buttons
876+ qRegisterMetaType<QMessageBox::StandardButton>("QMessageBox::StandardButton");
877 }
878
879 MidiScriptEngine::~MidiScriptEngine() {
880@@ -56,6 +60,7 @@
881 -------- ------------------------------------------------------ */
882 void MidiScriptEngine::gracefulShutdown(QList<QString> scriptFunctionPrefixes) {
883 qDebug() << "MidiScriptEngine shutting down...";
884+
885 m_scriptEngineLock.lock();
886 // Clear the m_connectedControls hash so we stop responding
887 // to signals.
888@@ -65,7 +70,7 @@
889 disconnect(m_pMidiDevice, SIGNAL(callMidiScriptFunction(QString, char, char,
890 char, MidiStatusByte, QString)),
891 this, SLOT(execute(QString, char, char, char, MidiStatusByte, QString)));
892-
893+
894 // Stop all timers
895 stopAllTimers();
896
897@@ -201,6 +206,9 @@
898 QThread::currentThread()->setObjectName(QString("MidiScriptEngine %1").arg(++id));
899
900 // Prevent the script engine from strangling other parts of Mixxx
901+ // incase of a misbehaving script
902+ // - Should we perhaps not do this when running with --midiDebug so it's more
903+ // obvious if a script is taking too much CPU time? - Sean 4/19/10
904 QThread::currentThread()->setPriority(QThread::LowPriority);
905
906 m_scriptEngineLock.lock();
907@@ -233,7 +241,7 @@
908 bool MidiScriptEngine::execute(QString function) {
909 m_scriptEngineLock.lock();
910 bool ret = safeExecute(function);
911- if (!ret) qDebug() << "MidiScriptEngine: Invalid script function" << function;
912+ if (!ret) qWarning() << "MidiScriptEngine: Invalid script function" << function;
913 m_scriptEngineLock.unlock();
914 return ret;
915 }
916@@ -246,7 +254,7 @@
917 bool MidiScriptEngine::execute(QString function, QString data) {
918 m_scriptEngineLock.lock();
919 bool ret = safeExecute(function, data);
920- if (!ret) qDebug() << "MidiScriptEngine: Invalid script function" << function;
921+ if (!ret) qWarning() << "MidiScriptEngine: Invalid script function" << function;
922 m_scriptEngineLock.unlock();
923 return ret;
924 }
925@@ -262,7 +270,7 @@
926 QString group) {
927 m_scriptEngineLock.lock();
928 bool ret = safeExecute(function, channel, control, value, status, group);
929- if (!ret) qDebug() << "MidiScriptEngine: Invalid script function" << function;
930+ if (!ret) qWarning() << "MidiScriptEngine: Invalid script function" << function;
931 m_scriptEngineLock.unlock();
932 return ret;
933 }
934@@ -326,12 +334,7 @@
935 .arg(scriptCode);
936
937 if (m_midiDebug) qCritical() << "MidiScriptEngine:" << error;
938- else {
939- qDebug() << "MidiScriptEngine:" << error;
940- qWarning() << "There was an error in a MIDI script."
941- "\nA control you just used is not working properly and you may experience erratic behavior."
942- "\nCheck the console or mixxx.log file for details.";
943- }
944+ else scriptErrorDialog(error);
945 return false;
946 }
947
948@@ -433,30 +436,76 @@
949 QString filename = exception.property("fileName").toString();
950
951 QStringList error;
952- error << filename << errorMessage << QString(line);
953- m_scriptErrors.insert(filename, error);
954-
955+ error << (filename.isEmpty() ? "" : filename) << errorMessage << QString(line);
956+ m_scriptErrors.insert((filename.isEmpty() ? "passed code" : filename), error);
957+
958+ QString errorText = QString(tr("Uncaught exception at line %1 in file %2: %3"))
959+ .arg(line)
960+ .arg((filename.isEmpty() ? "" : filename))
961+ .arg(errorMessage);
962+
963+ if (filename.isEmpty())
964+ errorText = QString(tr("Uncaught exception at line %1 in passed code: %2"))
965+ .arg(line)
966+ .arg(errorMessage);
967+
968 if (m_midiDebug)
969- qCritical() << "MidiScriptEngine: uncaught exception:"
970- << errorMessage
971- << "in" << filename << "at line"
972- << line
973+ qCritical() << "MidiScriptEngine:" << errorText
974 << "\nBacktrace:\n"
975 << backtrace;
976- else {
977- qDebug() << "MidiScriptEngine WARNING: uncaught exception:"
978- << errorMessage
979- << "in" << filename << "at line"
980- << line;
981- qWarning() << "There was a problem with a MIDI script."
982- "\nA control you just used is not working properly and you may experience erratic behavior."
983- "\nCheck the console or mixxx.log file for details.";
984- }
985+ else scriptErrorDialog(errorText);
986 return true;
987 }
988 return false;
989 }
990
991+/* -------- ------------------------------------------------------
992+Purpose: Common error dialog creation code for run-time exceptions
993+ Allows users to ignore the error or reload the mappings
994+Input: Detailed error string
995+Output: -
996+-------- ------------------------------------------------------ */
997+void MidiScriptEngine::scriptErrorDialog(QString detailedError) {
998+ qWarning() << "MidiScriptEngine:" << detailedError;
999+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
1000+ props->setType(DLG_WARNING);
1001+ props->setTitle(tr("MIDI script error"));
1002+ props->setText(tr("A MIDI control you just used is not working properly."));
1003+ props->setInfoText(tr("<html>(The MIDI script code needs to be fixed.)"
1004+ "<br>For now, you can:<ul><li>Ignore this error for this session but you may experience erratic behavior</li>"
1005+ "<li>Try to recover by resetting your controller</li></ul></html>"));
1006+ props->setDetails(detailedError);
1007+ props->setKey(detailedError); // To prevent multiple windows for the same error
1008+
1009+ // Allow user to suppress further notifications about this particular error
1010+ props->addButton(QMessageBox::Ignore);
1011+
1012+ props->addButton(QMessageBox::Retry);
1013+ props->addButton(QMessageBox::Close);
1014+ props->setDefaultButton(QMessageBox::Close);
1015+ props->setEscapeButton(QMessageBox::Close);
1016+ props->setModal(false);
1017+
1018+ if (ErrorDialogHandler::instance()->requestErrorDialog(props)) {
1019+ // Enable custom handling of the dialog buttons
1020+ connect(ErrorDialogHandler::instance(), SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)),
1021+ this, SLOT(errorDialogButton(QString, QMessageBox::StandardButton)));
1022+ }
1023+}
1024+
1025+/* -------- ------------------------------------------------------
1026+Purpose: Slot to handle custom button clicks in error dialogs
1027+Input: Key of dialog, StandardButton that was clicked
1028+Output: -
1029+-------- ------------------------------------------------------ */
1030+void MidiScriptEngine::errorDialogButton(QString key, QMessageBox::StandardButton button) {
1031+
1032+ // Something was clicked, so disable this signal now
1033+ disconnect(ErrorDialogHandler::instance(), SIGNAL(stdButtonClicked(QString, QMessageBox::StandardButton)),
1034+ this, SLOT(errorDialogButton(QString, QMessageBox::StandardButton)));
1035+
1036+ if (button == QMessageBox::Retry) emit(resetController());
1037+}
1038
1039 /* -------- ------------------------------------------------------
1040 Purpose: Returns a list of functions available in the QtScript
1041@@ -541,7 +590,7 @@
1042
1043 ControlObjectThread *cot = getControlObjectThread(group, name);
1044 if (cot == NULL) {
1045- qDebug() << "MidiScriptEngine: Unknown control" << group << name;
1046+ qWarning() << "MidiScriptEngine: Unknown control" << group << name;
1047 return 0.0;
1048 }
1049
1050@@ -564,7 +613,7 @@
1051 }
1052
1053 if(isnan(newValue)) {
1054- qDebug() << "Warning: script setting [" << group << "," << name
1055+ qWarning() << "MidiScriptEngine: script setting [" << group << "," << name
1056 << "] to NotANumber, ignoring.";
1057 return;
1058 }
1059@@ -658,7 +707,7 @@
1060
1061 ControlObject* sender = (ControlObject*)this->sender();
1062 if(sender == NULL) {
1063- qDebug() << "MidiScriptEngine::slotValueChanged() Shouldn't happen -- sender == NULL";
1064+ qWarning() << "MidiScriptEngine::slotValueChanged() Shouldn't happen -- sender == NULL";
1065 m_scriptEngineLock.unlock();
1066 return;
1067 }
1068@@ -674,16 +723,15 @@
1069 QScriptValue function_value = m_pEngine->evaluate(function);
1070 QScriptValueList args;
1071 args << QScriptValue(m_pEngine, value);
1072-// function_value.call(QScriptValue(), args);
1073 args << QScriptValue(m_pEngine, key.group); // Added by Math`
1074 args << QScriptValue(m_pEngine, key.item); // Added by Math`
1075 QScriptValue result = function_value.call(QScriptValue(), args);
1076 if (result.isError()) {
1077- qDebug()<< "MidiScriptEngine: Call to " << function << " resulted in an error: " << result.toString();
1078+ qWarning()<< "MidiScriptEngine: Call to " << function << " resulted in an error: " << result.toString();
1079 }
1080
1081 } else {
1082- qDebug() << "MidiScriptEngine::slotValueChanged() Received signal from ControlObject that is not connected to a script function.";
1083+ qWarning() << "MidiScriptEngine::slotValueChanged() Received signal from ControlObject that is not connected to a script function.";
1084 }
1085
1086 m_scriptEngineLock.unlock();
1087@@ -705,11 +753,24 @@
1088 // Read in the script file
1089 QFile input(filename);
1090 if (!input.open(QIODevice::ReadOnly)) {
1091- qCritical() << "MidiScriptEngine: Problem opening the script file: "
1092- << filename
1093- << ", error #"
1094- << input.error();
1095- return false;
1096+ QString errorLog =
1097+ QString("MidiScriptEngine: Problem opening the script file: %1, error # %2, %3")
1098+ .arg(filename)
1099+ .arg(input.error())
1100+ .arg(input.errorString());
1101+
1102+ if (m_midiDebug) qCritical() << errorLog;
1103+ else {
1104+ qWarning() << errorLog;
1105+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
1106+ props->setType(DLG_WARNING);
1107+ props->setTitle("MIDI script file problem");
1108+ props->setText(QString("There was a problem opening the MIDI script file %1.").arg(filename));
1109+ props->setInfoText(input.errorString());
1110+
1111+ ErrorDialogHandler::instance()->requestErrorDialog(props);
1112+ return false;
1113+ }
1114 }
1115 QString scriptCode = "";
1116 scriptCode.append(input.readAll());
1117@@ -738,10 +799,15 @@
1118
1119 if (m_midiDebug) qCritical() << "MidiScriptEngine:" << error;
1120 else {
1121- qDebug() << "MidiScriptEngine:" << error;
1122- qWarning() << "There was an error in the MIDI script file" << filename
1123- << "\nThe functionality provided by this script file will be disabled."
1124- "\nCheck the console or mixxx.log file for details.";
1125+ qWarning() << "MidiScriptEngine:" << error;
1126+ ErrorDialogProperties* props = ErrorDialogHandler::instance()->newDialogProperties();
1127+ props->setType(DLG_WARNING);
1128+ props->setTitle("MIDI script file error");
1129+ props->setText(QString("There was an error in the MIDI script file %1.").arg(filename));
1130+ props->setInfoText("The functionality provided by this script file will be disabled.");
1131+ props->setDetails(error);
1132+
1133+ ErrorDialogHandler::instance()->requestErrorDialog(props);
1134 }
1135 return false;
1136 }
1137@@ -799,7 +865,7 @@
1138 }
1139
1140 if (interval<20) {
1141- qDebug() << "Timer request for" << interval << "ms is too short. Setting to the minimum of 20ms.";
1142+ qWarning() << "Timer request for" << interval << "ms is too short. Setting to the minimum of 20ms.";
1143 interval=20;
1144 }
1145 // This makes use of every QObject's internal timer mechanism. Nice, clean, and simple.
1146@@ -809,7 +875,7 @@
1147 timerTarget.first = scriptCode;
1148 timerTarget.second = oneShot;
1149 m_timers[timerId]=timerTarget;
1150- if (timerId==0) qDebug() << "MIDI Script timer could not be created";
1151+ if (timerId==0) qWarning() << "MIDI Script timer could not be created";
1152 else if (m_midiDebug) {
1153 if (oneShot) qDebug() << "Starting one-shot timer:" << timerId;
1154 else qDebug() << "Starting timer:" << timerId;
1155@@ -830,7 +896,7 @@
1156 if(lock) m_scriptEngineLock.unlock();
1157
1158 if (!m_timers.contains(timerId)) {
1159- qDebug() << "Killing timer" << timerId << ": That timer does not exist!";
1160+ qWarning() << "Killing timer" << timerId << ": That timer does not exist!";
1161 return;
1162 }
1163 if (m_midiDebug) qDebug() << "Killing timer:" << timerId;
1164@@ -868,7 +934,7 @@
1165
1166 m_scriptEngineLock.lock();
1167 if (!m_timers.contains(timerId)) {
1168- qDebug() << "Timer" << timerId << "fired but there's no function mapped to it!";
1169+ qWarning() << "Timer" << timerId << "fired but there's no function mapped to it!";
1170 m_scriptEngineLock.unlock();
1171 return;
1172 }
1173
1174=== modified file 'mixxx/src/midi/midiscriptengine.h'
1175--- mixxx/src/midi/midiscriptengine.h 2010-02-03 14:13:29 +0000
1176+++ mixxx/src/midi/midiscriptengine.h 2010-05-07 07:32:30 +0000
1177@@ -19,6 +19,7 @@
1178
1179 #include <QEvent>
1180 #include <QtScript>
1181+#include <QMessageBox>
1182 #include "configobject.h"
1183 #include "midimessage.h"
1184 class MidiDevice;
1185@@ -68,11 +69,15 @@
1186
1187 signals:
1188 void initialized();
1189+ void resetController();
1190
1191 protected:
1192 void run();
1193 void timerEvent(QTimerEvent *event);
1194
1195+private slots:
1196+ void errorDialogButton(QString key, QMessageBox::StandardButton button);
1197+
1198 private:
1199 // Only call these with the scriptEngineLock
1200 bool safeEvaluate(QString filepath);
1201@@ -82,14 +87,14 @@
1202 bool safeExecute(QString function, char channel,
1203 char control, char value, MidiStatusByte status, QString group);
1204 void initializeScriptEngine();
1205-
1206+
1207+ void scriptErrorDialog(QString detailedError);
1208 void generateScriptFunctions(QString code);
1209 bool checkException();
1210 void stopAllTimers();
1211
1212 ControlObjectThread* getControlObjectThread(QString group, QString name);
1213-
1214-
1215+
1216 MidiDevice* m_pMidiDevice;
1217 bool m_midiDebug;
1218 QHash<ConfigKey, QString> m_connectedControls;
1219@@ -98,7 +103,7 @@
1220 QMap<QString,QStringList> m_scriptErrors;
1221 QMutex m_scriptEngineLock;
1222 QHash<ConfigKey, ControlObjectThread*> m_controlCache;
1223- QHash<int, QPair<QString, bool> > m_timers;
1224+ QHash<int, QPair<QString, bool> > m_timers;
1225 };
1226
1227 #endif
1228
1229=== modified file 'mixxx/src/mixxxcontrol.cpp'
1230--- mixxx/src/mixxxcontrol.cpp 2009-04-26 17:25:30 +0000
1231+++ mixxx/src/mixxxcontrol.cpp 2010-05-07 07:32:30 +0000
1232@@ -61,7 +61,7 @@
1233 m_midiOption = MIDI_OPT_SCRIPT;
1234 else {
1235 m_midiOption = MIDI_OPT_NORMAL;
1236- qDebug() << "Warning: Unknown midioption" << strMidiOption << "in" << __FILE__;
1237+ qWarning() << "Unknown midioption" << strMidiOption << "in" << __FILE__;
1238 }
1239
1240 //Parse threshold stuff, only used for output.
1241@@ -140,7 +140,7 @@
1242 strMidiOption = "script-binding";
1243 else {
1244 strMidiOption = "Unknown";
1245- qDebug() << "Warning: Unknown midioption in" << __FILE__;
1246+ qWarning() << "Unknown midioption in" << __FILE__;
1247 }
1248
1249 QDomElement singleOption = nodeMaker.createElement(strMidiOption);
1250
1251=== modified file 'mixxx/src/recording/writeaudiofile.cpp'
1252--- mixxx/src/recording/writeaudiofile.cpp 2008-04-29 05:21:46 +0000
1253+++ mixxx/src/recording/writeaudiofile.cpp 2010-05-07 07:32:30 +0000
1254@@ -90,7 +90,7 @@
1255 }
1256 }
1257 else
1258- qDebug() << "Warning: Tried to open WriteAudioFile before recording control was set to RECORD_ON";
1259+ qWarning() << "Tried to open WriteAudioFile before recording control was set to RECORD_ON";
1260 }
1261
1262 void WriteAudioFile::write(const CSAMPLE * pIn, int iBufferSize)
1263
1264=== modified file 'mixxx/src/soundsourcemp3.cpp'
1265--- mixxx/src/soundsourcemp3.cpp 2010-02-25 10:14:57 +0000
1266+++ mixxx/src/soundsourcemp3.cpp 2010-05-07 07:32:30 +0000
1267@@ -318,7 +318,7 @@
1268 break;
1269 default: //By the MP3 specs, an MP3 _has_ to have one of the above samplerates...
1270 units = MAD_UNITS_44100_HZ;
1271- qDebug() << "Warning: MP3 with corrupt samplerate (" << SRATE << "), defaulting to 44100";
1272+ qWarning() << "MP3 with corrupt samplerate (" << SRATE << "), defaulting to 44100";
1273
1274 SRATE = 44100; //Prevents division by zero errors.
1275 }
1276
1277=== modified file 'mixxx/src/upgrade.cpp'
1278--- mixxx/src/upgrade.cpp 2010-03-06 21:46:14 +0000
1279+++ mixxx/src/upgrade.cpp 2010-05-07 07:32:30 +0000
1280@@ -190,8 +190,10 @@
1281
1282 if (configVersion == VERSION) qDebug() << "Configuration file is at the current version" << VERSION;
1283 else {
1284- qDebug() << "Warning: Configuration file is at version" << configVersion << "and I don't know how to upgrade it to the current" << VERSION;
1285- qDebug() << " (That means a function to do this needs to be added to upgrade.cpp.)";
1286+ qWarning() << "Configuration file is at version" << configVersion
1287+ << "and I don't know how to upgrade it to the current" << VERSION
1288+ << "\n (That means a function to do this needs to be added to upgrade.cpp.)"
1289+ << "\n-> Leaving the configuration file version as-is.";
1290 }
1291
1292 return config;
1293
1294=== modified file 'mixxx/src/widget/wpushbutton.cpp'
1295--- mixxx/src/widget/wpushbutton.cpp 2009-07-07 06:26:30 +0000
1296+++ mixxx/src/widget/wpushbutton.cpp 2010-05-07 07:32:30 +0000
1297@@ -90,7 +90,7 @@
1298 //If we have 2 states, tell my controlpushbutton object that we're a toggle button.
1299 if (iNumStates == 2) {
1300 if (p == 0)
1301- qDebug() << "Warning: wpushbutton p is null\n";
1302+ qWarning() << "wpushbutton p is null\n";
1303 else
1304 p->setToggleButton(true);
1305 }