1 /*
2     MIDI Virtual Piano Keyboard
3     Copyright (C) 2008-2021, Pedro Lopez-Cabanillas <plcl@users.sf.net>
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 3 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 along
16     with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <QDebug>
20 #include <QDesktopServices>
21 #include <QInputDialog>
22 #include <QFileDialog>
23 #include <QDir>
24 #include <QMessageBox>
25 #include <QApplication>
26 #include <QCloseEvent>
27 #include <QComboBox>
28 #include <QSlider>
29 #include <QSpinBox>
30 #include <QDial>
31 #include <QToolButton>
32 #include <QToolTip>
33 #include <QVBoxLayout>
34 #include <QTextBrowser>
35 #include <QDialogButtonBox>
36 #include <QDialog>
37 #include <QUrl>
38 #include <QString>
39 #include <QTranslator>
40 #include <QMapIterator>
41 #include <QShortcut>
42 #include <QActionGroup>
43 #include <QStandardPaths>
44 
45 #include <drumstick/backendmanager.h>
46 #include <drumstick/rtmidiinput.h>
47 #include <drumstick/rtmidioutput.h>
48 #include <drumstick/pianokeybd.h>
49 #include <drumstick/settingsfactory.h>
50 
51 #include "vpiano.h"
52 #include "instrument.h"
53 #include "mididefs.h"
54 #include "constants.h"
55 #include "riffimportdlg.h"
56 #include "extracontrols.h"
57 #include "about.h"
58 #include "preferences.h"
59 #include "midisetup.h"
60 #include "colordialog.h"
61 #include "vpianosettings.h"
62 
63 #if !defined(SMALL_SCREEN)
64 #include "kmapdialog.h"
65 #include "shortcutdialog.h"
66 #endif
67 
68 #if defined(ENABLE_DBUS)
69 #include "vmpk_adaptor.h"
70 #include <QtDBus/QDBusConnection>
71 #endif
72 
73 using namespace drumstick::rt;
74 using namespace drumstick::widgets;
75 
VPiano(QWidget * parent,Qt::WindowFlags flags)76 VPiano::VPiano( QWidget * parent, Qt::WindowFlags flags )
77     : QMainWindow(parent, flags),
78     m_midiout(nullptr),
79     m_midiin(nullptr),
80     m_backendManager(nullptr),
81     m_initialized(false),
82     m_filter(nullptr)
83 {
84 #if defined(ENABLE_DBUS)
85     new VmpkAdaptor(this);
86     QDBusConnection dbus = QDBusConnection::sessionBus();
87     dbus.registerObject("/", this);
88     dbus.registerService("net.sourceforge.vmpk");
89 #endif
90     m_trq = new QTranslator(this);
91     m_trp = new QTranslator(this);
92     m_trl = new QTranslator(this);
93     QString lang = VPianoSettings::instance()->language();
94     if (!m_trq->load(QSTR_QTPX + lang, VPianoSettings::systemLocales()) && !lang.startsWith("en")) {
95         qWarning() << "Failure loading Qt5 system translations for" << lang
96                    << "from" << VPianoSettings::systemLocales();
97     }
98     if (!m_trp->load(QSTR_VMPKPX + lang, VPianoSettings::localeDirectory()) && !lang.startsWith("en")) {
99         qWarning() << "Failure loading VMPK application translations for" << lang
100                    << "from" << VPianoSettings::localeDirectory();
101     }
102     if (!m_trl->load(QSTR_DRUMSTICKPX + lang, VPianoSettings::drumstickLocales()) && !lang.startsWith("en")) {
103         qWarning() << "Failure loading widgets library translations for" << lang
104                    << "from" << VPianoSettings::drumstickLocales();
105     }
106     QCoreApplication::installTranslator(m_trq);
107     QCoreApplication::installTranslator(m_trp);
108     QCoreApplication::installTranslator(m_trl);
109     VPianoSettings::instance()->retranslatePalettes();
110     ui.setupUi(this);
111     initLanguages();
112 
113     QActionGroup* nameVisibilityGroup = new QActionGroup(this);
114     nameVisibilityGroup->setExclusive(true);
115     nameVisibilityGroup->addAction(ui.actionNever);
116     nameVisibilityGroup->addAction(ui.actionMinimal);
117     nameVisibilityGroup->addAction(ui.actionWhen_Activated);
118     nameVisibilityGroup->addAction(ui.actionAlways);
119     connect(nameVisibilityGroup, &QActionGroup::triggered, this, &VPiano::slotNameVisibility);
120 
121     QActionGroup* blackKeysGroup = new QActionGroup(this);
122     blackKeysGroup->setExclusive(true);
123     blackKeysGroup->addAction(ui.actionFlats);
124     blackKeysGroup->addAction(ui.actionSharps);
125     blackKeysGroup->addAction(ui.actionNothing);
126     connect(blackKeysGroup, &QActionGroup::triggered, this, &VPiano::slotNameVariant);
127 
128     QActionGroup* orientationGroup = new QActionGroup(this);
129     orientationGroup->setExclusive(true);
130     orientationGroup->addAction(ui.actionHorizontal);
131     orientationGroup->addAction(ui.actionVertical);
132     orientationGroup->addAction(ui.actionAutomatic);
133     connect(orientationGroup, &QActionGroup::triggered, this, &VPiano::slotNameOrientation);
134 
135     connect(ui.pianokeybd, &PianoKeybd::signalName, this, &VPiano::slotNoteName);
136     connect(ui.actionAbout, SIGNAL(triggered()), SLOT(slotAbout()));
137     connect(ui.actionAboutQt, SIGNAL(triggered()), SLOT(slotAboutQt()));
138     connect(ui.actionAboutTranslation, SIGNAL(triggered()), SLOT(slotAboutTranslation()));
139     connect(ui.actionConnections, SIGNAL(triggered()), SLOT(slotConnections()));
140     connect(ui.actionPreferences, SIGNAL(triggered()), SLOT(slotPreferences()));
141     connect(ui.actionEditKM, SIGNAL(triggered()), SLOT(slotEditKeyboardMap()));
142     connect(ui.actionContents, SIGNAL(triggered()), SLOT(slotHelpContents()));
143     connect(ui.actionWebSite, SIGNAL(triggered()), SLOT(slotOpenWebSite()));
144     connect(ui.actionImportSoundFont, SIGNAL(triggered()), SLOT(slotImportSF()));
145     connect(ui.actionEditExtraControls, SIGNAL(triggered()), SLOT(slotEditExtraControls()));
146     connect(ui.actionShortcuts, SIGNAL(triggered()), SLOT(slotShortcuts()));
147     connect(ui.actionKeyboardInput, SIGNAL(toggled(bool)), SLOT(slotKeyboardInput(bool)));
148     connect(ui.actionMouseInput, SIGNAL(toggled(bool)), SLOT(slotMouseInput(bool)));
149     connect(ui.actionTouchScreenInput, SIGNAL(toggled(bool)), SLOT(slotTouchScreenInput(bool)));
150     connect(ui.actionColorPalette, SIGNAL(triggered()), SLOT(slotColorPolicy()));
151     connect(ui.actionColorScale, SIGNAL(toggled(bool)), SLOT(slotColorScale(bool)));
152     connect(ui.actionWindowFrame, SIGNAL(toggled(bool)), SLOT(toggleWindowFrame(bool)));
153     connect(ui.actionLoad_Configuration, &QAction::triggered, this, &VPiano::slotLoadConfiguration);
154     connect(ui.actionSave_Configuration, &QAction::triggered, this, &VPiano::slotSaveConfiguration);
155     // Toolbars actions: toggle view
156     connect(ui.toolBarNotes->toggleViewAction(), SIGNAL(toggled(bool)),
157             ui.actionNotes, SLOT(setChecked(bool)));
158     connect(ui.toolBarControllers->toggleViewAction(), SIGNAL(toggled(bool)),
159             ui.actionControllers, SLOT(setChecked(bool)));
160     connect(ui.toolBarBender->toggleViewAction(), SIGNAL(toggled(bool)),
161             ui.actionBender, SLOT(setChecked(bool)));
162     connect(ui.toolBarPrograms->toggleViewAction(), SIGNAL(toggled(bool)),
163             ui.actionPrograms, SLOT(setChecked(bool)));
164     connect(ui.toolBarExtra->toggleViewAction(), SIGNAL(toggled(bool)),
165             ui.actionExtraControls, SLOT(setChecked(bool)));
166 #if defined(SMALL_SCREEN)
167     ui.toolBarControllers->hide();
168     ui.toolBarBender->hide();
169     ui.toolBarExtra->hide();
170     //ui.toolBarNotes->hide();
171     //ui.toolBarPrograms->hide();
172     ui.actionEditKM->setVisible(false);
173     ui.actionShortcuts->setVisible(false);
174     ui.actionStatusBar->setVisible(false);
175     setWindowTitle("VMPK " + PGM_VERSION);
176 #endif
177 #if defined (Q_OS_MACX)
178     //ui.actionEnterFullScreen->setShortcut(QKeySequence::FullScreen);
179     //ui.actionExitFullScreen->setShortcut(QKeySequence::FullScreen);
180 #endif
181     ui.pianokeybd->setPianoHandler(this);
182 #if defined(RAWKBD_SUPPORT)
183     m_filter = new NativeFilter;
184     m_filter->setRawKbdHandler(ui.pianokeybd);
185     qApp->installNativeEventFilter(m_filter);
186 #endif
187     initialization();
188 }
189 
~VPiano()190 VPiano::~VPiano()
191 {
192 #if defined(RAWKBD_SUPPORT)
193     m_filter->setRawKbdEnabled(false);
194     qApp->removeNativeEventFilter(m_filter);
195     delete m_filter;
196 #endif
197     delete m_backendManager;
198 }
199 
initialization()200 void VPiano::initialization()
201 {
202     readSettings();
203     if ((m_initialized = initMidi())) {
204         readMidiControllerSettings();
205         createLanguageMenu();
206         initToolBars();
207         applyPreferences();
208         applyInitialSettings();
209         initExtraControllers();
210         enforceMIDIChannelState();
211         activateWindow();
212     } else {
213         qWarning() << "Unable to initialize all MIDI drivers. Terminating.";
214     }
215 }
216 
initMidi()217 bool VPiano::initMidi()
218 {
219     m_backendManager = new BackendManager();
220     m_backendManager->refresh(VPianoSettings::instance()->settingsMap());
221 
222     m_midiin = m_backendManager->findInput(VPianoSettings::instance()->lastInputBackend());
223     if (m_midiin == nullptr) {
224         qWarning() << "MIDI IN driver not available";
225     }
226 
227     m_midiout = m_backendManager->findOutput(VPianoSettings::instance()->lastOutputBackend());
228     if (m_midiout == nullptr) {
229         qWarning() << "MIDI OUT driver not available";
230     }
231 
232     SettingsFactory settings;
233 
234     if (m_midiin != nullptr) {
235         connectMidiInSignals();
236         m_midiin->initialize(settings.getQSettings());
237         MIDIConnection conn;
238         auto connections = m_midiin->connections(VPianoSettings::instance()->advanced());
239         auto lastConn = VPianoSettings::instance()->lastInputConnection();
240         auto itr = std::find_if(connections.constBegin(), connections.constEnd(), [lastConn](const MIDIConnection& c){return c.first == lastConn;});
241         if (itr == connections.constEnd()) {
242             if (!connections.isEmpty()) {
243                 conn = connections.first();
244             }
245         } else {
246             conn = (*itr);
247         }
248         m_midiin->open(conn);
249         auto metaObj = m_midiin->metaObject();
250         if ((metaObj->indexOfProperty("status") != -1) &&
251             (metaObj->indexOfProperty("diagnostics") != -1)) {
252             auto status = m_midiin->property("status");
253             if (status.isValid() && !status.toBool()) {
254                 auto diagnostics = m_midiin->property("diagnostics");
255                 if (diagnostics.isValid()) {
256                     auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed();
257                     qWarning() << "MIDI Input" << text;
258                 }
259             }
260         }
261     }
262 
263     if (m_midiout != nullptr) {
264         m_midiout->initialize(settings.getQSettings());
265         MIDIConnection conn;
266         auto connections = m_midiout->connections(VPianoSettings::instance()->advanced());
267         auto lastConn = VPianoSettings::instance()->lastOutputConnection();
268         auto itr = std::find_if(connections.constBegin(), connections.constEnd(), [lastConn](const MIDIConnection& c){return c.first == lastConn;});
269         if (itr == connections.constEnd()) {
270             if (!connections.isEmpty()) {
271                 conn = connections.first();
272             }
273         } else {
274             conn = (*itr);
275         }
276         m_midiout->open(conn);
277         auto metaObj = m_midiout->metaObject();
278         if ((metaObj->indexOfProperty("status") != -1) &&
279             (metaObj->indexOfProperty("diagnostics") != -1)) {
280             auto status = m_midiout->property("status");
281             if (status.isValid() && !status.toBool()) {
282                 auto diagnostics = m_midiout->property("diagnostics");
283                 if (diagnostics.isValid()) {
284                     auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed();
285                     qWarning() << "MIDI Output" << text;
286                 }
287             }
288         }
289         if (m_midiin != nullptr) {
290             m_midiin->setMIDIThruDevice(m_midiout);
291             m_midiin->enableMIDIThru(VPianoSettings::instance()->midiThru());
292         }
293     }
294 
295     return (m_midiout != nullptr);
296 }
297 
initToolBars()298 void VPiano::initToolBars()
299 {
300     // Notes tool bar
301     QWidget *w = ui.toolBarNotes->widgetForAction(ui.actionPanic);
302     w->setMaximumWidth(120);
303     m_lblChannel = new QLabel(this);
304     ui.toolBarNotes->addWidget(m_lblChannel);
305     m_lblChannel->setMargin(TOOLBARLABELMARGIN);
306     m_sboxChannel = new QSpinBox(this);
307     m_sboxChannel->setMinimum(1);
308     m_sboxChannel->setMaximum(MIDICHANNELS);
309     m_sboxChannel->setValue(VPianoSettings::instance()->channel() + 1);
310     m_sboxChannel->setFocusPolicy(Qt::NoFocus);
311     ui.toolBarNotes->addWidget(m_sboxChannel);
312     m_lblBaseOctave = new QLabel(this);
313     ui.toolBarNotes->addWidget(m_lblBaseOctave);
314     m_lblBaseOctave->setMargin(TOOLBARLABELMARGIN);
315     m_sboxOctave = new QSpinBox(this);
316     m_sboxOctave->setMinimum(0);
317     m_sboxOctave->setMaximum(9);
318     m_sboxOctave->setValue(VPianoSettings::instance()->baseOctave());
319     m_sboxOctave->setFocusPolicy(Qt::NoFocus);
320     ui.toolBarNotes->addWidget(m_sboxOctave);
321     m_lblTranspose = new QLabel(this);
322     ui.toolBarNotes->addWidget(m_lblTranspose);
323     m_lblTranspose->setMargin(TOOLBARLABELMARGIN);
324     m_sboxTranspose = new QSpinBox(this);
325     m_sboxTranspose->setMinimum(-11);
326     m_sboxTranspose->setMaximum(11);
327     m_sboxTranspose->setValue(VPianoSettings::instance()->transpose());
328     m_sboxTranspose->setFocusPolicy(Qt::NoFocus);
329     ui.toolBarNotes->addWidget(m_sboxTranspose);
330     m_lblVelocity = new QLabel(this);
331     ui.toolBarNotes->addWidget(m_lblVelocity);
332     m_lblVelocity->setMargin(TOOLBARLABELMARGIN);
333     m_Velocity = new QDial(this);
334     m_Velocity->setFixedSize(32, 32);
335     m_Velocity->setMinimum(0);
336     m_Velocity->setMaximum(127);
337     m_Velocity->setValue(VPianoSettings::instance()->velocity());
338     m_Velocity->setToolTip(QString::number(VPianoSettings::instance()->velocity()));
339     m_Velocity->setFocusPolicy(Qt::NoFocus);
340     ui.toolBarNotes->addWidget(m_Velocity);
341     connect( m_sboxChannel, SIGNAL(valueChanged(int)),
342              SLOT(slotChannelValueChanged(int)));
343     connect( m_sboxOctave, SIGNAL(valueChanged(int)),
344              SLOT(slotBaseOctaveValueChanged(int)) );
345     connect( m_sboxTranspose, SIGNAL(valueChanged(int)),
346              SLOT(slotTransposeValueChanged(int)) );
347     connect( m_Velocity, SIGNAL(valueChanged(int)),
348              SLOT(slotVelocityValueChanged(int)) );
349     connect( ui.actionChannelUp, SIGNAL(triggered()),
350              m_sboxChannel, SLOT(stepUp()) );
351     connect( ui.actionChannelDown, SIGNAL(triggered()),
352              m_sboxChannel, SLOT(stepDown()) );
353     connect( ui.actionOctaveUp, SIGNAL(triggered()),
354              m_sboxOctave, SLOT(stepUp()) );
355     connect( ui.actionOctaveDown, SIGNAL(triggered()),
356              m_sboxOctave, SLOT(stepDown()) );
357     connect( ui.actionTransposeUp, SIGNAL(triggered()),
358              m_sboxTranspose, SLOT(stepUp()) );
359     connect( ui.actionTransposeDown, SIGNAL(triggered()),
360              m_sboxTranspose, SLOT(stepDown()) );
361     connect( ui.actionVelocityUp, SIGNAL(triggered()),
362              SLOT(slotVelocityUp()) );
363     connect( ui.actionVelocityDown, SIGNAL(triggered()),
364              SLOT(slotVelocityDown()) );
365     // Controllers tool bar
366     m_lblControl = new QLabel(this);
367     ui.toolBarControllers->addWidget(m_lblControl);
368     m_lblControl ->setMargin(TOOLBARLABELMARGIN);
369     m_comboControl = new QComboBox(this);
370     m_comboControl->setObjectName("cboControl");
371     //m_comboControl->setStyleSheet("combobox-popup: 0;");
372     m_comboControl->setSizeAdjustPolicy(QComboBox::AdjustToContents);
373     m_comboControl->setFocusPolicy(Qt::NoFocus);
374     ui.toolBarControllers->addWidget(m_comboControl);
375     m_lblValue = new QLabel(this);
376     ui.toolBarControllers->addWidget(m_lblValue);
377     m_lblValue->setMargin(TOOLBARLABELMARGIN);
378     m_Control= new QDial(this);
379     m_Control->setFixedSize(32, 32);
380     m_Control->setMinimum(0);
381     m_Control->setMaximum(127);
382     m_Control->setValue(0);
383     m_Control->setToolTip("0");
384     m_Control->setFocusPolicy(Qt::NoFocus);
385     ui.toolBarControllers->addWidget(m_Control);
386     connect( m_comboControl, SIGNAL(currentIndexChanged(int)),
387              SLOT(slotComboControlCurrentIndexChanged(int)) );
388     connect( m_Control, SIGNAL(sliderMoved(int)),
389              SLOT(slotControlSliderMoved(int)) );
390     // Pitch bender tool bar
391     m_lblBender = new QLabel(this);
392     ui.toolBarBender->addWidget(m_lblBender);
393     m_lblBender->setMargin(TOOLBARLABELMARGIN);
394     m_bender = new QSlider(this);
395     m_bender->setOrientation(Qt::Horizontal);
396     m_bender->setMaximumWidth(200);
397     m_bender->setMinimum(BENDER_MIN);
398     m_bender->setMaximum(BENDER_MAX);
399     m_bender->setValue(0);
400     m_bender->setToolTip("0");
401     m_bender->setFocusPolicy(Qt::NoFocus);
402     ui.toolBarBender->addWidget(m_bender);
403     connect( m_bender, SIGNAL(sliderMoved(int)),
404              SLOT(slotBenderSliderMoved(int)) );
405     connect( m_bender, SIGNAL(sliderReleased()),
406              SLOT(slotBenderSliderReleased()) );
407     // Programs tool bar
408     m_lblBank = new QLabel(this);
409     ui.toolBarPrograms->addWidget(m_lblBank);
410     m_lblBank->setMargin(TOOLBARLABELMARGIN);
411     m_comboBank = new QComboBox(this);
412     m_comboBank->setObjectName("cboBank");
413     m_comboBank->setSizeAdjustPolicy(QComboBox::AdjustToContents);
414     //m_comboBank->setStyleSheet("combobox-popup: 0;");
415     m_comboBank->setFocusPolicy(Qt::NoFocus);
416     ui.toolBarPrograms->addWidget(m_comboBank);
417     m_lblProgram = new QLabel(this);
418     ui.toolBarPrograms->addWidget(m_lblProgram);
419     m_lblProgram->setMargin(TOOLBARLABELMARGIN);
420     m_comboProg = new QComboBox(this);
421     m_comboProg->setObjectName("cboProg");
422     m_comboProg->setStyleSheet("combobox-popup: 0;");
423     m_comboProg->setSizeAdjustPolicy(QComboBox::AdjustToContents);
424     m_comboProg->setFocusPolicy(Qt::NoFocus);
425     ui.toolBarPrograms->addWidget(m_comboProg);
426     connect( m_comboBank, SIGNAL(activated(int)),
427              SLOT(slotComboBankActivated(int)) );
428     connect( m_comboProg, SIGNAL(activated(int)),
429              SLOT(slotComboProgActivated(int)) );
430     // Toolbars actions: buttons
431     connect( ui.actionPanic, SIGNAL(triggered()),
432              SLOT(slotPanic()));
433     connect( ui.actionResetAll, SIGNAL(triggered()),
434              SLOT(slotResetAllControllers()));
435     connect( ui.actionReset, SIGNAL(triggered()),
436              SLOT(slotResetBender()));
437     connect( ui.actionEditExtra, SIGNAL(triggered()),
438              SLOT(slotEditExtraControls()));
439     // Tools actions
440     connect( ui.actionNextBank, SIGNAL(triggered()),
441              SLOT(slotBankNext()) );
442     connect( ui.actionPreviousBank, SIGNAL(triggered()),
443              SLOT(slotBankPrev()) );
444     connect( ui.actionNextProgram, SIGNAL(triggered()),
445              SLOT(slotProgramNext()) );
446     connect( ui.actionPreviousProgram, SIGNAL(triggered()),
447              SLOT(slotProgramPrev()) );
448     connect( ui.actionNextController, SIGNAL(triggered()),
449              SLOT(slotControllerNext()) );
450     connect( ui.actionPreviousController, SIGNAL(triggered()),
451              SLOT(slotControllerPrev()) );
452     connect( ui.actionControllerDown, SIGNAL(triggered()),
453              SLOT(slotControllerDown()) );
454     connect( ui.actionControllerUp, SIGNAL(triggered()),
455              SLOT(slotControllerUp()) );
456     /* connect( ui.actionEditPrograms, SIGNAL(triggered()),
457              SLOT(slotEditPrograms())); */
458     retranslateToolbars();
459 }
460 
461 //void VPiano::slotDebugDestroyed(QObject *obj)
462 //{
463 //    qDebug() << Q_FUNC_INFO << obj->metaObject()->className();
464 //}
465 
clearExtraControllers()466 void VPiano::clearExtraControllers()
467 {
468     QList<QAction*> allActs = ui.toolBarExtra->actions();
469     foreach(QAction* a, allActs) {
470         if (a != ui.actionEditExtra) {
471             ui.toolBarExtra->removeAction(a);
472             delete a;
473         }
474     }
475     ui.toolBarExtra->clear();
476     ui.toolBarExtra->addAction(ui.actionEditExtra);
477     ui.toolBarExtra->addSeparator();
478 }
479 
readSysexDataFile(const QString & fileName)480 QByteArray VPiano::readSysexDataFile(const QString& fileName)
481 {
482     QFile file(fileName);
483     file.open(QIODevice::ReadOnly);
484     QByteArray res = file.readAll();
485     file.close();
486     return res;
487 }
488 
initExtraControllers()489 void VPiano::initExtraControllers()
490 {
491     QWidget *w = nullptr;
492     QCheckBox *chkbox = nullptr;
493     QDial *knob = nullptr;
494     QSpinBox *spin = nullptr;
495     QSlider *slider = nullptr;
496     QToolButton *button = nullptr;
497     foreach(const QString& s, m_extraControls) {
498         QString lbl;
499         int control = 0;
500         int type = 0;
501         int minValue = 0;
502         int maxValue = 127;
503         int defValue = 0;
504         int value = 0;
505         int size = 100;
506         int channel = VPianoSettings::instance()->channel();
507         QString fileName;
508         QString keySequence;
509         ExtraControl::decodeString( s, lbl, control, type,
510                                     minValue, maxValue, defValue,
511                                     size, fileName, keySequence );
512         if (m_ctlState[channel].contains(control))
513             value = m_ctlState[channel][control];
514         else
515             value = defValue;
516         switch(type) {
517         case ExtraControl::ControlType::SwitchControl:
518             chkbox = new QCheckBox(this);
519             chkbox->setStyleSheet(QSTR_CHKBOXSTYLE);
520             chkbox->setProperty(MIDICTLONVALUE, maxValue);
521             chkbox->setProperty(MIDICTLOFFVALUE, minValue);
522             chkbox->setChecked(bool(value));
523             connect(chkbox, SIGNAL(clicked(bool)), SLOT(slotControlClicked(bool)));
524             if (!keySequence.isEmpty()) {
525                 QShortcut *s = new QShortcut(QKeySequence(keySequence), chkbox, SLOT(click()));
526                 s->setAutoRepeat(false);
527             }
528             w = chkbox;
529             break;
530         case ExtraControl::ControlType::KnobControl:
531             knob = new QDial(this);
532             knob->setFixedSize(32, 32);
533             knob->setMinimum(minValue);
534             knob->setMaximum(maxValue);
535             knob->setValue(value);
536             knob->setToolTip(QString::number(value));
537             connect(knob, SIGNAL(sliderMoved(int)), SLOT(slotExtraController(int)));
538             w = knob;
539             break;
540         case ExtraControl::ControlType::SpinBoxControl:
541             spin = new QSpinBox(this);
542             spin->setMinimum(minValue);
543             spin->setMaximum(maxValue);
544             spin->setValue(value);
545             connect(spin, SIGNAL(valueChanged(int)), SLOT(slotExtraController(int)));
546             w = spin;
547             break;
548         case ExtraControl::ControlType::SliderControl:
549             slider = new QSlider(this);
550             slider->setOrientation(Qt::Horizontal);
551             slider->setFixedWidth(size);
552             slider->setMinimum(minValue);
553             slider->setMaximum(maxValue);
554             slider->setToolTip(QString::number(value));
555             slider->setValue(value);
556             connect(slider, SIGNAL(sliderMoved(int)), SLOT(slotExtraController(int)));
557             w = slider;
558             break;
559         case ExtraControl::ControlType::ButtonCtlControl:
560             button = new QToolButton(this);
561             button->setText(lbl);
562             button->setProperty(MIDICTLONVALUE, maxValue);
563             button->setProperty(MIDICTLOFFVALUE, minValue);
564             connect(button, SIGNAL(clicked(bool)), SLOT(slotControlClicked(bool)));
565             if (!keySequence.isEmpty()) {
566                 QShortcut *s = new QShortcut(QKeySequence(keySequence), button, SLOT(animateClick()));
567                 s->setAutoRepeat(false);
568             }
569             w = button;
570             break;
571         case ExtraControl::ControlType::ButtonSyxControl:
572             control = 255;
573             button = new QToolButton(this);
574             button->setText(lbl);
575             button->setProperty(SYSEXFILENAME, fileName);
576             button->setProperty(SYSEXFILEDATA, readSysexDataFile(fileName));
577             connect(button, SIGNAL(clicked(bool)), SLOT(slotControlClicked(bool)));
578             if (!keySequence.isEmpty()) {
579                 QShortcut *s = new QShortcut(QKeySequence(keySequence), button, SLOT(animateClick()));
580                 s->setAutoRepeat(false);
581             }
582             w = button;
583             break;
584         default:
585             w = nullptr;
586         }
587         if (w != nullptr) {
588             if (!lbl.isEmpty() && type < 4) {
589                 QLabel *qlbl = new QLabel(lbl, this);
590                 qlbl->setMargin(TOOLBARLABELMARGIN);
591                 ui.toolBarExtra->addWidget(qlbl);
592                 //connect(qlbl, SIGNAL(destroyed(QObject*)), SLOT(slotDebugDestroyed(QObject*)));
593             }
594             w->setProperty(MIDICTLNUMBER, control);
595             w->setFocusPolicy(Qt::NoFocus);
596             ui.toolBarExtra->addWidget(w);
597             //connect(w, SIGNAL(destroyed(QObject*)), SLOT(slotDebugDestroyed(QObject*)));
598         }
599     }
600 }
601 
readSettings()602 void VPiano::readSettings()
603 {
604     VPianoSettings::instance()->retranslatePalettes();
605 
606     ui.actionStatusBar->setChecked(VPianoSettings::instance()->showStatusBar());
607     ui.statusBar->setVisible(VPianoSettings::instance()->showStatusBar());
608     ui.pianokeybd->setNumKeys(VPianoSettings::instance()->numKeys(), VPianoSettings::instance()->startingKey());
609     ui.pianokeybd->setVelocityTint(VPianoSettings::instance()->velocityColor());
610     ui.pianokeybd->setVelocity(VPianoSettings::instance()->velocity());
611     ui.pianokeybd->setTranspose(VPianoSettings::instance()->transpose());
612     ui.pianokeybd->setBaseOctave(VPianoSettings::instance()->baseOctave());
613     ui.pianokeybd->setKeyboardEnabled(VPianoSettings::instance()->enableKeyboard());
614     ui.pianokeybd->setMouseEnabled(VPianoSettings::instance()->enableMouse());
615     ui.pianokeybd->setTouchEnabled(VPianoSettings::instance()->enableTouch());
616     ui.pianokeybd->setChannel(VPianoSettings::instance()->channel());
617     ui.actionColorScale->setChecked(VPianoSettings::instance()->colorScale());
618     ui.pianokeybd->setFont(VPianoSettings::instance()->namesFont());
619 
620     switch(VPianoSettings::instance()->namesVisibility()) {
621     case ShowNever:
622         ui.actionNever->setChecked(true);
623         break;
624     case ShowMinimum:
625         ui.actionMinimal->setChecked(true);
626         break;
627     case ShowActivated:
628         ui.actionWhen_Activated->setChecked(true);
629         break;
630     case ShowAlways:
631         ui.actionAlways->setChecked(true);
632         break;
633     }
634     ui.pianokeybd->setShowLabels(VPianoSettings::instance()->namesVisibility());
635 
636     switch(VPianoSettings::instance()->alterations()) {
637     case ShowSharps:
638         ui.actionSharps->setChecked(true);
639         break;
640     case ShowFlats:
641         ui.actionFlats->setChecked(true);
642         break;
643     case ShowNothing:
644         ui.actionNothing->setChecked(true);
645         break;
646     }
647     ui.pianokeybd->setLabelAlterations(VPianoSettings::instance()->alterations());
648 
649     switch(VPianoSettings::instance()->namesOrientation()) {
650     case HorizontalOrientation:
651         ui.actionHorizontal->setChecked(true);
652         break;
653     case VerticalOrientation:
654         ui.actionVertical->setChecked(true);
655         break;
656     case AutomaticOrientation:
657         ui.actionAutomatic->setChecked(true);
658         break;
659     }
660     ui.pianokeybd->setLabelOrientation(VPianoSettings::instance()->namesOrientation());
661     ui.pianokeybd->setLabelOctave(VPianoSettings::instance()->namesOctave());
662 
663     bool savedShortcuts = VPianoSettings::instance()->savedShortcuts();
664     QList<QAction *> actions = findChildren<QAction *> ();
665     foreach(QAction* pAction, actions)
666     {
667         if (pAction->objectName().isEmpty())
668             continue;
669         const QString& sKey = pAction->objectName();
670         QList<QKeySequence> sShortcuts = pAction->shortcuts();
671         m_defaultShortcuts.insert(sKey, sShortcuts);
672         if (savedShortcuts)
673         {
674             const QString& sValue = VPianoSettings::instance()->getShortcut(sKey);
675             if (sValue.isEmpty())
676             {
677                 if(sShortcuts.count() == 0)
678                 {
679                     continue;
680                 }
681                 else
682                 {
683                     pAction->setShortcuts(QList<QKeySequence>());
684                 }
685             }
686             else
687             {
688                 pAction->setShortcut(QKeySequence(sValue));
689             }
690         }
691     }
692 
693     QString mapFile = VPianoSettings::instance()->getMapFile();
694     QString rawMapFile = VPianoSettings::instance()->getRawMapFile();
695     if (!mapFile.isEmpty() && mapFile != QSTR_DEFAULT) {
696         ui.pianokeybd->setKeyboardMap(VPianoSettings::instance()->getKeyboardMap());
697     }
698     if (!rawMapFile.isEmpty() && rawMapFile != QSTR_DEFAULT) {
699         ui.pianokeybd->setRawKeyboardMap(VPianoSettings::instance()->getRawKeyboardMap());
700     }
701 }
702 
readMidiControllerSettings()703 void VPiano::readMidiControllerSettings()
704 {
705     SettingsFactory settings;
706     for (int chan=0; chan<MIDICHANNELS; ++chan) {
707         QString group = QSTR_INSTRUMENT + QString::number(chan);
708         settings->beginGroup(group);
709         m_lastBank[chan] = settings->value(QSTR_BANK, -1).toInt();
710         m_lastProg[chan] = settings->value(QSTR_PROGRAM, 0).toInt();
711         m_lastCtl[chan] = settings->value(QSTR_CONTROLLER, 1).toInt();
712         settings->endGroup();
713 
714         group = QSTR_CONTROLLERS + QString::number(chan);
715         settings->beginGroup(group);
716         foreach(const QString& key, settings->allKeys()) {
717             int ctl = key.toInt();
718             int val = settings->value(key, 0).toInt();
719             m_ctlSettings[chan][ctl] = val;
720         }
721         settings->endGroup();
722     }
723 
724     settings->beginGroup(QSTR_EXTRACONTROLLERS);
725     m_extraControls.clear();
726     QStringList keys = settings->allKeys();
727     keys.sort();
728     foreach(const QString& key, keys) {
729         m_extraControls << settings->value(key, QString()).toString();
730     }
731     settings->endGroup();
732     if (m_extraControls.isEmpty()) {
733         m_extraControls = QStringList{"Sustain,64,0,0,64,0,", "Sostenuto,66,0,0,64,0,", "Soft,67,0,0,64,0,"};
734     }
735 }
736 
writeSettings()737 void VPiano::writeSettings()
738 {
739     VPianoSettings::instance()->setShowStatusBar(ui.actionStatusBar->isChecked());
740     VPianoSettings::instance()->setGeometry(saveGeometry());
741     VPianoSettings::instance()->setState(saveState());
742     VPianoSettings::instance()->SaveSettings();
743 
744     SettingsFactory settings;
745 
746     for (int chan=0; chan<MIDICHANNELS; ++chan) {
747         QString group = QSTR_CONTROLLERS + QString::number(chan);
748         settings->beginGroup(group);
749         settings->remove("");
750         QMap<int,int>::const_iterator it, end;
751         it = m_ctlState[chan].constBegin();
752         end = m_ctlState[chan].constEnd();
753         for (; it != end; ++it)
754             settings->setValue(QString::number(it.key()), it.value());
755         settings->endGroup();
756 
757         group = QSTR_INSTRUMENT + QString::number(chan);
758         settings->beginGroup(group);
759         settings->setValue(QSTR_BANK, m_lastBank[chan]);
760         settings->setValue(QSTR_PROGRAM, m_lastProg[chan]);
761         settings->setValue(QSTR_CONTROLLER, m_lastCtl[chan]);
762         settings->endGroup();
763     }
764 
765     settings->beginGroup(QSTR_EXTRACONTROLLERS);
766     settings->remove("");
767     int i = 0;
768     foreach(const QString& ctl, m_extraControls)  {
769         QString key = QString("%1").arg(i++, 2, 10, QChar('0'));
770         settings->setValue(key, ctl);
771     }
772     settings->endGroup();
773 
774     settings->beginGroup(QSTR_SHORTCUTS);
775     settings->remove("");
776     QList<QAction *> actions = findChildren<QAction *> ();
777     foreach(QAction *pAction, actions)
778     {
779         if (pAction->objectName().isEmpty())
780             continue;
781         const QString& sKey = '/' + pAction->objectName();
782         const QString& sValue = pAction->shortcut().toString();
783         QList<QKeySequence> defShortcuts = m_defaultShortcuts.value(sKey);
784         if (sValue.isEmpty() && defShortcuts.count() == 0)
785         {
786             if (settings->contains(sKey))
787                 settings->remove(sKey);
788         }
789         else
790         {
791             settings->setValue(sKey, sValue);
792         }
793     }
794     settings->endGroup();
795     settings->sync();
796 }
797 
closeEvent(QCloseEvent * event)798 void VPiano::closeEvent( QCloseEvent *event )
799 {
800     //qDebug() << Q_FUNC_INFO;
801     if (m_initialized) {
802         writeSettings();
803     }
804     event->accept();
805 }
806 
getDegree(const int note) const807 int VPiano::getDegree(const int note) const
808 {
809     return note % 12;
810 }
811 
getType(const int note) const812 int VPiano::getType(const int note) const
813 {
814     int g = getDegree(note);
815     if (g == 1 || g == 3 || g == 6 || g == 8 || g == 10)
816         return 1;
817     return 0;
818 }
819 
getHighlightColorFromPolicy(const int chan,const int note,const int vel)820 QColor VPiano::getHighlightColorFromPolicy(const int chan, const int note, const int vel)
821 {
822     Q_UNUSED(vel)
823     PianoPalette palette = VPianoSettings::instance()->getPalette(VPianoSettings::instance()->highlightPaletteId());
824     switch (palette.paletteId()) {
825     case PAL_SINGLE:
826         return palette.getColor(0);
827     case PAL_DOUBLE:
828         return palette.getColor(getType(note));
829     case PAL_CHANNELS:
830         return palette.getColor(chan);
831     case PAL_HISCALE:
832         return palette.getColor(getDegree(note));
833     default:
834         break;
835     }
836     return QColor();
837 }
838 
slotNoteOn(const int chan,const int note,const int vel)839 void VPiano::slotNoteOn(const int chan, const int note, const int vel)
840 {
841     if (VPianoSettings::instance()->channel() == chan || VPianoSettings::instance()->omniMode()) {
842         if (vel == 0) {
843             slotNoteOff(chan, note, vel);
844         } else {
845             QColor c = getHighlightColorFromPolicy(chan, note, vel);
846             int v = (VPianoSettings::instance()->velocityColor() ? vel : MIDIVELOCITY );
847             ui.pianokeybd->showNoteOn(note, c, v);
848 #ifdef ENABLE_DBUS
849             emit event_noteon(note);
850 #endif
851         }
852     }
853 }
854 
slotNoteOff(const int chan,const int note,const int vel)855 void VPiano::slotNoteOff(const int chan, const int note, const int vel)
856 {
857     Q_UNUSED(vel)
858     if (VPianoSettings::instance()->channel() == chan || VPianoSettings::instance()->omniMode()) {
859         ui.pianokeybd->showNoteOff(note);
860 #ifdef ENABLE_DBUS
861         emit event_noteoff(note);
862 #endif
863     }
864 }
865 
slotKeyPressure(const int chan,const int note,const int value)866 void VPiano::slotKeyPressure(const int chan, const int note, const int value)
867 {
868 #ifdef ENABLE_DBUS
869     int channel = VPianoSettings::instance()->channel();
870     if (channel == chan) {
871         emit event_polykeypress(note, value);
872     }
873 #else
874     Q_UNUSED(chan)
875     Q_UNUSED(note)
876     Q_UNUSED(value)
877 #endif
878 }
879 
slotController(const int chan,const int control,const int value)880 void VPiano::slotController(const int chan, const int control, const int value)
881 {
882     int channel = VPianoSettings::instance()->channel();
883     if (channel == chan) {
884         switch (control) {
885         case CTL_ALL_SOUND_OFF:
886         case CTL_ALL_NOTES_OFF:
887             ui.pianokeybd->allKeysOff();
888             break;
889         case CTL_RESET_ALL_CTL:
890             initializeAllControllers();
891             break;
892         default:
893             updateController(control, value);
894             updateExtraController(control, value);
895         }
896 #ifdef ENABLE_DBUS
897         emit event_controlchange(control, value);
898 #endif
899     }
900 }
901 
slotProgram(const int chan,const int program)902 void VPiano::slotProgram(const int chan, const int program)
903 {
904     int channel = VPianoSettings::instance()->channel();
905     if (channel == chan) {
906         updateProgramChange(program);
907 #ifdef ENABLE_DBUS
908         emit event_programchange(program);
909 #endif
910     }
911 }
912 
slotChannelPressure(const int chan,const int value)913 void VPiano::slotChannelPressure(const int chan, const int value)
914 {
915     int channel = VPianoSettings::instance()->channel();
916     if (channel == chan) {
917 #ifdef ENABLE_DBUS
918         emit event_chankeypress(value);
919 #else
920         Q_UNUSED(value)
921 #endif
922     }
923 }
924 
slotPitchBend(const int chan,const int value)925 void VPiano::slotPitchBend(const int chan, const int value)
926 {
927     int channel = VPianoSettings::instance()->channel();
928     if (channel == chan) {
929         m_bender->setValue(value);
930         m_bender->setToolTip(QString::number(value));
931 #ifdef ENABLE_DBUS
932         emit event_pitchwheel(value);
933 #endif
934     }
935 }
936 
showEvent(QShowEvent * event)937 void VPiano::showEvent ( QShowEvent *event )
938 {
939     static bool firstTime{true};
940     //qDebug() << Q_FUNC_INFO << firstTime;
941     if (firstTime) {
942         if (!restoreGeometry(VPianoSettings::instance()->geometry())) {
943             qWarning() << "restoreGeometry() failed!";
944         }
945         if (!restoreState(VPianoSettings::instance()->state())) {
946             qWarning() << "restoreState() failed!";
947         }
948         firstTime = false;
949     }
950     QMainWindow::showEvent(event);
951 #if !defined(SMALL_SCREEN)
952     if (m_initialized) {
953         ui.pianokeybd->setFocus();
954         grabKb();
955     }
956 #endif
957 }
958 
hideEvent(QHideEvent * event)959 void VPiano::hideEvent( QHideEvent *event )
960 {
961     //qDebug() << "hideEvent:" << event->type();
962 //#if !defined(SMALL_SCREEN)
963     //releaseKb();
964 //#endif
965     QMainWindow::hideEvent(event);
966 }
967 
sendNoteOn(const int midiNote,const int vel)968 void VPiano::sendNoteOn(const int midiNote, const int vel)
969 {
970     if ((midiNote & MASK_SAFETY) == midiNote) {
971         int channel = VPianoSettings::instance()->channel();
972         m_midiout->sendNoteOn( channel, midiNote, vel );
973     }
974 }
975 
noteOn(const int midiNote,const int vel)976 void VPiano::noteOn(const int midiNote, const int vel)
977 {
978     sendNoteOn(midiNote, vel);
979 #ifdef ENABLE_DBUS
980     emit event_noteon(midiNote);
981 #endif
982 }
983 
sendNoteOff(const int midiNote,const int vel)984 void VPiano::sendNoteOff(const int midiNote, const int vel)
985 {
986     if ((midiNote & MASK_SAFETY) == midiNote) {
987         int channel = VPianoSettings::instance()->channel();
988         m_midiout->sendNoteOff( channel, midiNote, vel );
989     }
990 }
991 
noteOff(const int midiNote,const int vel)992 void VPiano::noteOff(const int midiNote, const int vel)
993 {
994     sendNoteOff(midiNote, vel);
995 #ifdef ENABLE_DBUS
996     emit event_noteoff(midiNote);
997 #endif
998 }
999 
sendController(const int controller,const int value)1000 void VPiano::sendController(const int controller, const int value)
1001 {
1002     int channel = VPianoSettings::instance()->channel();
1003     m_midiout->sendController( channel, controller, value );
1004 }
1005 
resetAllControllers()1006 void VPiano::resetAllControllers()
1007 {
1008     sendController(CTL_RESET_ALL_CTL, 0);
1009     initializeAllControllers();
1010 }
1011 
initializeAllControllers()1012 void VPiano::initializeAllControllers()
1013 {
1014     int channel = VPianoSettings::instance()->channel();
1015     int index = m_comboControl->currentIndex();
1016     int ctl = m_comboControl->itemData(index).toInt();
1017     int val = m_ctlState[channel][ctl];
1018     initControllers(channel);
1019     m_comboControl->setCurrentIndex(index);
1020     m_Control->setValue(val);
1021     m_Control->setToolTip(QString::number(val));
1022     // extra controllers
1023     QList<QWidget *> allWidgets = ui.toolBarExtra->findChildren<QWidget *>();
1024     foreach(QWidget *w, allWidgets) {
1025         QVariant c = w->property(MIDICTLNUMBER);
1026         if (c.isValid()) {
1027             ctl = c.toInt();
1028             if (m_ctlState[channel].contains(ctl)) {
1029                 val = m_ctlState[channel][ctl];
1030                 QVariant p = w->property("value");
1031                 if (p.isValid()) {
1032                     w->setProperty("value", val);
1033                     w->setToolTip(QString::number(val));
1034                     continue;
1035                 }
1036                 p = w->property("checked");
1037                 if (p.isValid()) {
1038                     QVariant on = w->property(MIDICTLONVALUE);
1039                     w->setProperty("checked", (val >= on.toInt()));
1040                 }
1041             }
1042         }
1043     }
1044 }
1045 
allNotesOff()1046 void VPiano::allNotesOff()
1047 {
1048     sendController(CTL_ALL_NOTES_OFF, 0);
1049     ui.pianokeybd->allKeysOff();
1050 }
1051 
sendProgramChange(const int program)1052 void VPiano::sendProgramChange(const int program)
1053 {
1054     int channel = VPianoSettings::instance()->channel();
1055     m_midiout->sendProgram( channel, program );
1056 }
1057 
sendBankChange(const int bank)1058 void VPiano::sendBankChange(const int bank)
1059 {
1060     int channel = VPianoSettings::instance()->channel();
1061     int method = (m_ins != nullptr) ? m_ins->bankSelMethod() : 0;
1062     int lsb, msb;
1063     switch (method) {
1064     case 0:
1065         lsb = CALC_LSB(bank);
1066         msb = CALC_MSB(bank);
1067         sendController(CTL_MSB, msb);
1068         sendController(CTL_LSB, lsb);
1069         break;
1070     case 1:
1071         sendController(CTL_MSB, bank);
1072         break;
1073     case 2:
1074         sendController(CTL_LSB, bank);
1075         break;
1076     default: /* if method is 3 or above, do nothing */
1077         break;
1078     }
1079     m_lastBank[channel] = bank;
1080 }
1081 
sendPolyKeyPress(const int note,const int value)1082 void VPiano::sendPolyKeyPress(const int note, const int value)
1083 {
1084     int channel = VPianoSettings::instance()->channel();
1085     m_midiout->sendKeyPressure( channel, note, value );
1086 }
1087 
sendChanKeyPress(const int value)1088 void VPiano::sendChanKeyPress(const int value)
1089 {
1090     int channel = VPianoSettings::instance()->channel();
1091     m_midiout->sendChannelPressure( channel, value );
1092 }
1093 
sendBender(const int value)1094 void VPiano::sendBender(const int value)
1095 {
1096     int channel = VPianoSettings::instance()->channel();
1097     m_midiout->sendPitchBend( channel, value );
1098 }
1099 
slotPanic()1100 void VPiano::slotPanic()
1101 {
1102     allNotesOff();
1103 }
1104 
slotResetAllControllers()1105 void VPiano::slotResetAllControllers()
1106 {
1107     resetAllControllers();
1108 }
1109 
slotResetBender()1110 void VPiano::slotResetBender()
1111 {
1112     m_bender->setValue(0);
1113     sendBender(0);
1114 }
1115 
sendSysex(const QByteArray & data)1116 void VPiano::sendSysex(const QByteArray& data)
1117 {
1118     m_midiout->sendSysex( data );
1119 }
1120 
slotControlClicked(const bool boolValue)1121 void VPiano::slotControlClicked(const bool boolValue)
1122 {
1123     QObject *s = sender();
1124     QVariant p = s->property(MIDICTLNUMBER);
1125     if (p.isValid()) {
1126         int controller = p.toInt();
1127         if (controller < 128) {
1128             QVariant on = s->property(MIDICTLONVALUE);
1129             QVariant off = s->property(MIDICTLOFFVALUE);
1130             int value = boolValue ? on.toInt() : off.toInt();
1131             sendController( controller, value );
1132             updateController( controller, value );
1133         } else {
1134             QVariant data = s->property(SYSEXFILEDATA);
1135             sendSysex(data.toByteArray());
1136         }
1137     }
1138 }
1139 
slotVelocityValueChanged(int value)1140 void VPiano::slotVelocityValueChanged(int value)
1141 {
1142     VPianoSettings::instance()->setVelocity(value);
1143     setWidgetTip(m_Velocity, value);
1144     ui.pianokeybd->setVelocity(value);
1145 }
1146 
slotExtraController(const int value)1147 void VPiano::slotExtraController(const int value)
1148 {
1149     QWidget *w = static_cast<QWidget *>(sender());
1150     QVariant p = w->property(MIDICTLNUMBER);
1151     if (p.isValid()) {
1152         int controller = p.toInt();
1153         sendController( controller, value );
1154         updateController( controller, value );
1155         setWidgetTip(w, value);
1156     }
1157 }
1158 
slotControlSliderMoved(const int value)1159 void VPiano::slotControlSliderMoved(const int value)
1160 {
1161     int index = m_comboControl->currentIndex();
1162     int controller = m_comboControl->itemData(index).toInt();
1163     int channel = VPianoSettings::instance()->channel();
1164     sendController( controller, value );
1165     updateExtraController( controller, value );
1166     m_ctlState[channel][controller] = value;
1167     setWidgetTip(m_Control, value);
1168 }
1169 
slotBenderSliderMoved(const int pos)1170 void VPiano::slotBenderSliderMoved(const int pos)
1171 {
1172     sendBender(pos);
1173     setWidgetTip(m_bender, pos);
1174 }
1175 
slotBenderSliderReleased()1176 void VPiano::slotBenderSliderReleased()
1177 {
1178     m_bender->setValue(0);
1179     sendBender(0);
1180     setWidgetTip(m_bender, 0);
1181 }
1182 
slotAbout()1183 void VPiano::slotAbout()
1184 {
1185     releaseKb();
1186     QPointer<About> dlgAbout = new About(this);
1187     dlgAbout->exec();
1188     grabKb();
1189     delete dlgAbout;
1190 }
1191 
slotAboutQt()1192 void VPiano::slotAboutQt()
1193 {
1194     releaseKb();
1195     QApplication::aboutQt();
1196     grabKb();
1197 }
1198 
connectMidiInSignals()1199 void VPiano::connectMidiInSignals()
1200 {
1201     if (m_midiin != nullptr) {
1202         connect(m_midiin, &MIDIInput::midiNoteOn, this, &VPiano::slotNoteOn, Qt::QueuedConnection);
1203         connect(m_midiin, &MIDIInput::midiNoteOff, this, &VPiano::slotNoteOff, Qt::QueuedConnection);
1204         connect(m_midiin, &MIDIInput::midiKeyPressure, this, &VPiano::slotKeyPressure, Qt::QueuedConnection);
1205         connect(m_midiin, &MIDIInput::midiChannelPressure, this, &VPiano::slotChannelPressure, Qt::QueuedConnection);
1206         connect(m_midiin, &MIDIInput::midiController, this, &VPiano::slotController, Qt::QueuedConnection);
1207         connect(m_midiin, &MIDIInput::midiProgram, this, &VPiano::slotProgram, Qt::QueuedConnection);
1208         connect(m_midiin, &MIDIInput::midiPitchBend, this, &VPiano::slotPitchBend, Qt::QueuedConnection);
1209     }
1210 }
1211 
slotConnections()1212 void VPiano::slotConnections()
1213 {
1214     QPointer<MidiSetup> dlgMidiSetup = new MidiSetup(this);
1215     dlgMidiSetup->setInputs(m_backendManager->availableInputs());
1216     dlgMidiSetup->setOutputs(m_backendManager->availableOutputs());
1217     dlgMidiSetup->setInput(m_midiin);
1218     dlgMidiSetup->setOutput(m_midiout);
1219     releaseKb();
1220     if (dlgMidiSetup->exec() == QDialog::Accepted) {
1221         if (m_midiin != nullptr) {
1222             m_midiin->disconnect();
1223         }
1224         if (m_midiout != nullptr) {
1225             m_midiout->disconnect();
1226         }
1227         m_midiin = dlgMidiSetup->getInput();
1228         m_midiout = dlgMidiSetup->getOutput();
1229         connectMidiInSignals();
1230         enforceMIDIChannelState();
1231     }
1232     grabKb();
1233     delete dlgMidiSetup;
1234 }
1235 
initControllers(int channel)1236 void VPiano::initControllers(int channel)
1237 {
1238     if (m_ins != nullptr) {
1239         InstrumentData controls = m_ins->control();
1240         InstrumentData::ConstIterator it, end;
1241         it = controls.constBegin();
1242         end = controls.constEnd();
1243         for( ; it != end; ++it ) {
1244             int ctl = it.key();
1245             switch (ctl) {
1246             case CTL_VOLUME:
1247                 m_ctlState[channel][CTL_VOLUME] = MIDIVOLUME;
1248                 break;
1249             case CTL_PAN:
1250                 m_ctlState[channel][CTL_PAN] = MIDIPAN;
1251                 break;
1252             case CTL_EXPRESSION:
1253                 m_ctlState[channel][CTL_EXPRESSION] = MIDIMAXVALUE;
1254                 break;
1255             case CTL_REVERB_SEND:
1256                 m_ctlState[channel][CTL_REVERB_SEND] = MIDIMAXVALUE;
1257                 break;
1258             default:
1259                 m_ctlState[channel][ctl] = 0;
1260             }
1261         }
1262     }
1263 }
1264 
populateControllers()1265 void VPiano::populateControllers()
1266 {
1267     m_comboControl->blockSignals(true);
1268     m_comboControl->clear();
1269     if (m_ins != nullptr) {
1270         InstrumentData controls = m_ins->control();
1271         InstrumentData::ConstIterator it, end = controls.constEnd();
1272         for( it = controls.constBegin(); it != end; ++it )
1273             m_comboControl->addItem(it.value(), it.key());
1274     }
1275     m_comboControl->blockSignals(false);
1276 }
1277 
applyPreferences()1278 void VPiano::applyPreferences()
1279 {
1280     static QPalette defaultPalette = qApp->palette();
1281     static QPalette darkPalette(QColor(0x30,0x30,0x30));
1282 
1283     ui.pianokeybd->allKeysOff();
1284     ui.pianokeybd->setFont(VPianoSettings::instance()->namesFont());
1285     ui.pianokeybd->setLabelOctave(VPianoSettings::instance()->namesOctave());
1286     if ( ui.pianokeybd->numKeys() != VPianoSettings::instance()->numKeys() ||
1287          ui.pianokeybd->startKey() != VPianoSettings::instance()->startingKey() )
1288     {
1289         ui.pianokeybd->setNumKeys(VPianoSettings::instance()->numKeys(), VPianoSettings::instance()->startingKey());
1290     }
1291 #if defined(RAWKBD_SUPPORT)
1292     m_filter->setRawKbdEnabled(VPianoSettings::instance()->rawKeyboard());
1293 #endif
1294     ui.pianokeybd->setRawKeyboardMode(VPianoSettings::instance()->rawKeyboard());
1295     ui.pianokeybd->setVelocityTint(VPianoSettings::instance()->velocityColor());
1296     ui.pianokeybd->setVelocity(VPianoSettings::instance()->velocity());
1297     bool enableKeyboard = VPianoSettings::instance()->enableKeyboard();
1298     bool enableMouse = VPianoSettings::instance()->enableMouse();
1299     bool enableTouch = VPianoSettings::instance()->enableTouch();
1300     ui.pianokeybd->setKeyboardEnabled(enableKeyboard);
1301     ui.pianokeybd->setMouseEnabled(enableMouse);
1302     ui.pianokeybd->setTouchEnabled(enableTouch);
1303     ui.actionKeyboardInput->setChecked(enableKeyboard);
1304     ui.actionMouseInput->setChecked(enableMouse);
1305     ui.actionTouchScreenInput->setChecked(enableTouch);
1306 
1307 #if defined(Q_OS_WINDOWS)
1308     m_snapper.SetEnabled(VPianoSettings::instance()->getWinSnap());
1309 #endif
1310     qApp->setPalette( VPianoSettings::instance()->getDarkMode() ? darkPalette : defaultPalette );
1311     qApp->setStyle( VPianoSettings::instance()->getStyle() );
1312 
1313     VMPKKeyboardMap* map = VPianoSettings::instance()->getKeyboardMap();
1314     if (!map->getFileName().isEmpty() && map->getFileName() != QSTR_DEFAULT )
1315         ui.pianokeybd->setKeyboardMap(map);
1316     else
1317         ui.pianokeybd->resetKeyboardMap();
1318 
1319     map = VPianoSettings::instance()->getRawKeyboardMap();
1320     if (!map->getFileName().isEmpty() && map->getFileName() != QSTR_DEFAULT )
1321         ui.pianokeybd->setRawKeyboardMap(map);
1322     else
1323         ui.pianokeybd->resetRawKeyboardMap();
1324 
1325     PianoPalette highlight = VPianoSettings::instance()->getPalette(VPianoSettings::instance()->highlightPaletteId());
1326     ui.pianokeybd->setHighlightPalette(highlight);
1327     PianoPalette background = VPianoSettings::instance()->getPalette(VPianoSettings::instance()->colorScale() ? PAL_SCALE : PAL_KEYS);
1328     ui.pianokeybd->setBackgroundPalette(background);
1329     ui.pianokeybd->setForegroundPalette(VPianoSettings::instance()->getPalette(PAL_FONT));
1330     ui.pianokeybd->setShowColorScale(VPianoSettings::instance()->colorScale());
1331 
1332     populateInstruments();
1333     populateControllers();
1334 
1335     QPoint wpos = pos();
1336     Qt::WindowFlags flags = windowFlags();
1337     if (VPianoSettings::instance()->alwaysOnTop())
1338         flags |= Qt::WindowStaysOnTopHint;
1339     else
1340         flags &= ~Qt::WindowStaysOnTopHint;
1341     setWindowFlags( flags );
1342     move(wpos);
1343 }
1344 
populateInstruments()1345 void VPiano::populateInstruments()
1346 {
1347     m_ins = nullptr;
1348     m_comboBank->clear();
1349     m_comboProg->clear();
1350     int channel = VPianoSettings::instance()->channel();
1351     //qDebug() << Q_FUNC_INFO << VPianoSettings::instance()->insFileName();
1352     if (!VPianoSettings::instance()->insFileName().isEmpty() &&
1353          VPianoSettings::instance()->insFileName() != QSTR_DEFAULT) {
1354         if (channel == VPianoSettings::instance()->drumsChannel())
1355             m_ins = VPianoSettings::instance()->getDrumsInstrument();
1356         else
1357             m_ins = VPianoSettings::instance()->getInstrument();
1358         if (m_ins != nullptr) {
1359             //qDebug() << "Instrument Name:" << m_ins->instrumentName();
1360             //qDebug() << "Bank Selection method: " << m_ins->bankSelMethod();
1361             InstrumentPatches patches = m_ins->patches();
1362             InstrumentPatches::ConstIterator j;
1363             for( j = patches.constBegin(); j != patches.constEnd(); ++j ) {
1364                 //if (j.key() < 0) continue;
1365                 InstrumentData patch = j.value();
1366                 m_comboBank->addItem(patch.name(), j.key());
1367                 //qDebug() << "---- Bank[" << j.key() << "]=" << patch.name();
1368             }
1369             updateBankChange(m_lastBank[channel]);
1370         }
1371     }
1372 }
1373 
applyInitialSettings()1374 void VPiano::applyInitialSettings()
1375 {
1376     int idx, ctl, channel;
1377     for ( int ch=0; ch<MIDICHANNELS; ++ch) {
1378         initControllers(ch);
1379         QMap<int,int>::Iterator i, j, end;
1380         i = m_ctlSettings[ch].begin();
1381         end = m_ctlSettings[ch].end();
1382         for (; i != end; ++i) {
1383             j = m_ctlState[ch].find(i.key());
1384             if (j != m_ctlState[ch].end())
1385                 m_ctlState[ch][i.key()] = i.value();
1386         }
1387     }
1388     channel = VPianoSettings::instance()->channel();
1389     ctl = m_lastCtl[channel];
1390     idx = m_comboControl->findData(ctl);
1391     if (idx != -1)
1392         m_comboControl->setCurrentIndex(idx);
1393     updateBankChange(m_lastBank[channel]);
1394     idx = m_comboProg->findData(m_lastProg[channel]);
1395     m_comboProg->setCurrentIndex(idx);
1396 }
1397 
slotPreferences()1398 void VPiano::slotPreferences()
1399 {
1400     QPointer<Preferences> dlgPreferences = new Preferences(this);
1401     dlgPreferences->setNoteNames(ui.pianokeybd->standardNoteNames());
1402     releaseKb();
1403     if (dlgPreferences->exec() == QDialog::Accepted) {
1404         applyPreferences();
1405     }
1406     delete dlgPreferences;
1407     grabKb();
1408 }
1409 
1410 
slotEditKeyboardMap()1411 void VPiano::slotEditKeyboardMap()
1412 {
1413 #if !defined(SMALL_SCREEN)
1414     VMPKKeyboardMap* map = nullptr;
1415     if (VPianoSettings::instance()->rawKeyboard()) {
1416         map = VPianoSettings::instance()->getRawKeyboardMap();
1417         map->copyFrom(ui.pianokeybd->getRawKeyboardMap(), true);
1418     } else {
1419         map = VPianoSettings::instance()->getKeyboardMap();
1420         map->copyFrom(ui.pianokeybd->getKeyboardMap(), false);
1421     }
1422     QPointer<KMapDialog> dlgKeyMap = new KMapDialog(this);
1423     dlgKeyMap->displayMap(map);
1424     releaseKb();
1425     if (dlgKeyMap->exec() == QDialog::Accepted) {
1426         dlgKeyMap->getMap(map);
1427         if (VPianoSettings::instance()->rawKeyboard()) {
1428             ui.pianokeybd->setRawKeyboardMap(map);
1429         } else {
1430             ui.pianokeybd->setKeyboardMap(map);
1431         }
1432     }
1433     grabKb();
1434     delete dlgKeyMap;
1435 #endif
1436 }
1437 
populatePrograms(int bank)1438 void VPiano::populatePrograms(int bank)
1439 {
1440     if (bank < 0)
1441         return;
1442     m_comboProg->clear();
1443     if (m_ins != nullptr) {
1444         InstrumentData patch = m_ins->patch(bank);
1445         InstrumentData::ConstIterator k;
1446         for( k = patch.constBegin(); k != patch.constEnd(); ++k )
1447             m_comboProg->addItem(k.value(), k.key());
1448             //qDebug() << "patch[" << k.key() << "]=" << k.value();
1449     }
1450 }
1451 
slotComboBankActivated(const int index)1452 void VPiano::slotComboBankActivated(const int index)
1453 {
1454     int idx = index;
1455     if (idx < 0)
1456         m_comboBank->setCurrentIndex(idx = 0);
1457     int bank = m_comboBank->itemData(idx).toInt();
1458     populatePrograms(bank);
1459     slotComboProgActivated();
1460 }
1461 
slotComboProgActivated(const int index)1462 void VPiano::slotComboProgActivated(const int index)
1463 {
1464     int idx = index;
1465     int channel = VPianoSettings::instance()->channel();
1466     if (idx < 0)
1467         m_comboProg->setCurrentIndex(idx = 0);
1468     int bankIdx = m_comboBank->currentIndex();
1469     int bank = m_comboBank->itemData(bankIdx).toInt();
1470     if (bank >= 0) {
1471         sendBankChange(bank);
1472         m_lastBank[channel] = bank;
1473     }
1474     int pgm = m_comboProg->itemData(idx).toInt();
1475     if (pgm >= 0) {
1476         sendProgramChange(pgm);
1477         m_lastProg[channel] = pgm;
1478     }
1479     updateNoteNames(channel == VPianoSettings::instance()->drumsChannel());
1480 }
1481 
slotBaseOctaveValueChanged(const int octave)1482 void VPiano::slotBaseOctaveValueChanged(const int octave)
1483 {
1484     if (octave != VPianoSettings::instance()->baseOctave()) {
1485         allNotesOff();
1486         ui.pianokeybd->setBaseOctave(octave);
1487         VPianoSettings::instance()->setBaseOctave(octave);
1488     }
1489 }
1490 
slotTransposeValueChanged(const int transpose)1491 void VPiano::slotTransposeValueChanged(const int transpose)
1492 {
1493     if (transpose != VPianoSettings::instance()->transpose()) {
1494         ui.pianokeybd->setTranspose(transpose);
1495         VPianoSettings::instance()->setTranspose(transpose);
1496     }
1497 }
1498 
updateNoteNames(bool drums)1499 void VPiano::updateNoteNames(bool drums)
1500 {
1501     if (drums && (m_ins != nullptr)) {
1502         int channel = VPianoSettings::instance()->drumsChannel();
1503         int b = m_lastBank[channel];
1504         int p = m_lastProg[channel];
1505         const InstrumentData& notes = m_ins->notes(b, p);
1506         QStringList noteNames;
1507         for(int n=0; n<128; ++n) {
1508             if (notes.contains(n))
1509                 noteNames << notes[n];
1510             else
1511                 noteNames << QString();
1512         }
1513         ui.pianokeybd->useCustomNoteNames(noteNames);
1514     } else
1515         ui.pianokeybd->useStandardNoteNames();
1516 }
1517 
slotChannelValueChanged(const int channel)1518 void VPiano::slotChannelValueChanged(const int channel)
1519 {
1520     int idx;
1521     int c = channel - 1;
1522     int baseChannel = VPianoSettings::instance()->channel();
1523     if (c != baseChannel) {
1524         int drms = VPianoSettings::instance()->drumsChannel();
1525         bool updDrums = ((c == drms) || (baseChannel == drms));
1526         baseChannel = c;
1527         allNotesOff();
1528         VPianoSettings::instance()->setChannel(c);
1529         ui.pianokeybd->setChannel(c);
1530         if (updDrums) {
1531             populateInstruments();
1532             populateControllers();
1533             updateNoteNames(c == drms);
1534         }
1535         idx = m_comboControl->findData(m_lastCtl[baseChannel]);
1536         if (idx != -1) {
1537             int ctl = m_lastCtl[baseChannel];
1538             m_comboControl->setCurrentIndex(idx);
1539             updateController(ctl, m_ctlState[baseChannel][ctl]);
1540             updateExtraController(ctl, m_ctlState[baseChannel][ctl]);
1541         }
1542         updateBankChange(m_lastBank[baseChannel]);
1543         updateProgramChange(m_lastProg[baseChannel]);
1544         enforceMIDIChannelState();
1545     }
1546 }
1547 
updateController(int ctl,int val)1548 void VPiano::updateController(int ctl, int val)
1549 {
1550     int index = m_comboControl->currentIndex();
1551     int controller = m_comboControl->itemData(index).toInt();
1552     int channel = VPianoSettings::instance()->channel();
1553     if (controller == ctl) {
1554         m_Control->setValue(val);
1555         m_Control->setToolTip(QString::number(val));
1556     }
1557     m_ctlState[channel][ctl] = val;
1558     if ((ctl == CTL_MSB || ctl == CTL_LSB ) && m_ins != nullptr) {
1559         if (m_ins->bankSelMethod() == 0)
1560             m_lastBank[channel] = m_ctlState[channel][CTL_MSB] << 7 |
1561                                     m_ctlState[channel][CTL_LSB];
1562         else
1563             m_lastBank[channel] = val;
1564 
1565         updateBankChange(m_lastBank[channel]);
1566     }
1567 }
1568 
updateExtraController(int ctl,int val)1569 void VPiano::updateExtraController(int ctl, int val)
1570 {
1571     QList<QWidget *> allWidgets = ui.toolBarExtra->findChildren<QWidget *>();
1572     foreach(QWidget *w, allWidgets) {
1573         QVariant p = w->property(MIDICTLNUMBER);
1574         if (p.isValid() && p.toInt() == ctl) {
1575             QVariant v = w->property("value");
1576             if (v.isValid() && v.toInt() != val) {
1577                 w->setProperty("value", val);
1578                 w->setToolTip(QString::number(val));
1579                 continue;
1580             }
1581             v = w->property("checked");
1582             if (v.isValid()) {
1583                 QVariant on = w->property(MIDICTLONVALUE);
1584                 bool checked = (val >= on.toInt());
1585                 w->setProperty("checked", checked);
1586             }
1587         }
1588     }
1589 }
1590 
updateBankChange(int bank)1591 void VPiano::updateBankChange(int bank)
1592 {
1593     int idx;
1594     int channel = VPianoSettings::instance()->channel();
1595     if (bank < 0) {
1596         m_comboBank->setCurrentIndex(idx = 0);
1597         bank = m_comboBank->itemData(idx).toInt();
1598         if (bank < 0)
1599             bank = 0;
1600     } else {
1601         idx = m_comboBank->findData(bank);
1602         if (idx != -1) {
1603             m_comboBank->setCurrentIndex(idx);
1604             m_lastBank[channel] = bank;
1605         }
1606     }
1607     populatePrograms(bank);
1608     updateProgramChange();
1609 }
1610 
updateProgramChange(int program)1611 void VPiano::updateProgramChange(int program)
1612 {
1613     int idx;
1614     int channel = VPianoSettings::instance()->channel();
1615     if (program < 0) {
1616         m_comboProg->setCurrentIndex(idx = 0);
1617         program = m_comboProg->itemData(idx).toInt();
1618     } else {
1619         idx = m_comboProg->findData(program);
1620         if (idx != -1) {
1621             m_comboProg->setCurrentIndex(idx);
1622             m_lastProg[channel] = program;
1623         }
1624     }
1625     updateNoteNames(channel == VPianoSettings::instance()->drumsChannel());
1626 }
1627 
slotComboControlCurrentIndexChanged(const int index)1628 void VPiano::slotComboControlCurrentIndexChanged(const int index)
1629 {
1630     int channel = VPianoSettings::instance()->channel();
1631     int ctl = m_comboControl->itemData(index).toInt();
1632     int val = m_ctlState[channel][ctl];
1633     m_Control->setValue(val);
1634     m_Control->setToolTip(QString::number(val));
1635     m_lastCtl[channel] = ctl;
1636 }
1637 
grabKb()1638 void VPiano::grabKb()
1639 {
1640 #if defined(RAWKBD_SUPPORT)
1641     m_filter->setRawKbdEnabled(VPianoSettings::instance()->rawKeyboard());
1642 #endif
1643 }
1644 
releaseKb()1645 void VPiano::releaseKb()
1646 {
1647 #if defined(RAWKBD_SUPPORT)
1648     m_filter->setRawKbdEnabled(false);
1649 #endif
1650 }
1651 
1652 #if defined(SMALL_SCREEN)
1653 class HelpDialog : public QDialog
1654 {
1655 public:
HelpDialog(const QUrl & document,QWidget * parent=nullptr)1656     HelpDialog(const QUrl &document, QWidget *parent = nullptr) : QDialog(parent)
1657     {
1658         setWindowState(Qt::WindowMaximized | Qt::WindowActive);
1659         QVBoxLayout *layout = new QVBoxLayout(this);
1660         QTextBrowser *browser = new QTextBrowser(this);
1661         layout->addWidget(browser);
1662         QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
1663         buttonBox->setOrientation(Qt::Horizontal);
1664         buttonBox->setStandardButtons(QDialogButtonBox::Ok);
1665         layout->addWidget(buttonBox);
1666         browser->setSource(document);
1667         connect(buttonBox, SIGNAL(accepted()), SLOT(close()));
1668     }
1669 };
1670 #endif
1671 
slotHelpContents()1672 void VPiano::slotHelpContents()
1673 {
1674     QStringList hlps;
1675     QLocale loc(VPianoSettings::instance()->language());
1676     QStringList lc = loc.name().split("_");
1677     hlps += QSTR_HELPL.arg(loc.name());
1678     if (lc.count() > 1)
1679         hlps += QSTR_HELPL.arg(lc[0]);
1680     hlps += QSTR_HELP;
1681     QDir hlpDir(VPianoSettings::dataDirectory());
1682     foreach(const QString& hlp_name, hlps) {
1683         if (hlpDir.exists(hlp_name)) {
1684             QUrl url = QUrl::fromLocalFile(hlpDir.absoluteFilePath(hlp_name));
1685 #if defined(SMALL_SCREEN)
1686             HelpDialog hlpDlg(url, this);
1687             hlpDlg.exec();
1688 #else
1689             QDesktopServices::openUrl(url);
1690 #endif
1691             return;
1692         }
1693     }
1694     QMessageBox::critical(this, tr("Error"), tr("No help file found"));
1695 }
1696 
slotOpenWebSite()1697 void VPiano::slotOpenWebSite()
1698 {
1699     QUrl url(QSTR_VMPKURL);
1700     QDesktopServices::openUrl(url);
1701 }
1702 
slotImportSF()1703 void VPiano::slotImportSF()
1704 {
1705     QPointer<RiffImportDlg> dlgRiffImport = new RiffImportDlg(this);
1706     releaseKb();
1707     if ((dlgRiffImport->exec() == QDialog::Accepted) &&
1708         !dlgRiffImport->getOutput().isEmpty()) {
1709         dlgRiffImport->save();
1710         VPianoSettings::instance()->setInstruments(dlgRiffImport->getOutput(), dlgRiffImport->getName());
1711         applyPreferences();
1712     }
1713     grabKb();
1714     delete dlgRiffImport;
1715 }
1716 
slotEditExtraControls()1717 void VPiano::slotEditExtraControls()
1718 {
1719     QPointer<DialogExtraControls> dlgExtra = new DialogExtraControls(this);
1720     dlgExtra->setControls(m_extraControls);
1721     releaseKb();
1722     if (dlgExtra->exec() == QDialog::Accepted) {
1723         m_extraControls = dlgExtra->getControls();
1724         clearExtraControllers();
1725         initExtraControllers();
1726     }
1727     grabKb();
1728     delete dlgExtra;
1729 }
1730 
setWidgetTip(QWidget * w,int val)1731 void VPiano::setWidgetTip(QWidget* w, int val)
1732 {
1733     QString tip = QString::number(val);
1734     w->setToolTip(tip);
1735     QToolTip::showText(w->parentWidget()->mapToGlobal(w->pos()), tip);
1736 }
1737 
1738 //void VPiano::slotEditPrograms()
1739 //{ }
1740 
1741 #if defined(ENABLE_DBUS)
quit()1742 void VPiano::quit()
1743 {
1744     close();
1745 }
1746 
panic()1747 void VPiano::panic()
1748 {
1749     allNotesOff();
1750 }
1751 
reset_controllers()1752 void VPiano::reset_controllers()
1753 {
1754     resetAllControllers();
1755 }
1756 
channel(int value)1757 void VPiano::channel(int value)
1758 {
1759     if (value >= 0 && value < MIDICHANNELS)
1760         m_sboxChannel->setValue(value + 1);
1761 }
1762 
octave(int value)1763 void VPiano::octave(int value)
1764 {
1765     m_sboxOctave->setValue(value);
1766 }
1767 
transpose(int value)1768 void VPiano::transpose(int value)
1769 {
1770     m_sboxTranspose->setValue(value);
1771 }
1772 
velocity(int value)1773 void VPiano::velocity(int value)
1774 {
1775     m_Velocity->setValue(value);
1776 }
1777 
connect_in(const QString & value)1778 void VPiano::connect_in(const QString &value)
1779 {
1780     if( m_midiin != nullptr) {
1781         m_midiin->close();
1782         if (!value.isEmpty()) {
1783             for(const MIDIConnection& conn : m_midiin->connections()) {
1784                 if (conn.first == value) {
1785                     m_midiin->open(conn);
1786                     break;
1787                 }
1788             }
1789         }
1790     }
1791 }
1792 
connect_out(const QString & value)1793 void VPiano::connect_out(const QString &value)
1794 {
1795     if( m_midiout != nullptr) {
1796         m_midiout->close();
1797         if (!value.isEmpty()) {
1798             for(const MIDIConnection& conn : m_midiout->connections()) {
1799                 if (conn.first == value) {
1800                     m_midiout->open(conn);
1801                     if (m_midiin != nullptr) {
1802                         m_midiin->setMIDIThruDevice(m_midiout);
1803                         m_midiin->enableMIDIThru(VPianoSettings::instance()->midiThru());
1804                     }
1805                     break;
1806                 }
1807             }
1808         }
1809     }
1810 }
1811 
connect_thru(bool value)1812 void VPiano::connect_thru(bool value)
1813 {
1814     if( m_midiin != nullptr && m_midiout != nullptr) {
1815         m_midiin->enableMIDIThru(value);
1816         m_midiin->setMIDIThruDevice(m_midiout);
1817     }
1818 }
1819 
noteoff(int note)1820 void VPiano::noteoff(int note)
1821 {
1822     sendNoteOff(note, 0);
1823 }
1824 
noteon(int note)1825 void VPiano::noteon(int note)
1826 {
1827     sendNoteOn(note, VPianoSettings::instance()->velocity());
1828 }
1829 
polykeypress(int note,int value)1830 void VPiano::polykeypress(int note, int value)
1831 {
1832     sendPolyKeyPress(note, value);
1833 }
1834 
controlchange(int control,int value)1835 void VPiano::controlchange(int control, int value)
1836 {
1837     sendController(control, value);
1838 }
1839 
programchange(int value)1840 void VPiano::programchange(int value)
1841 {
1842     sendProgramChange(value);
1843 }
1844 
programnamechange(const QString & value)1845 void VPiano::programnamechange(const QString &value)
1846 {
1847     int idx = m_comboProg->findText(value, Qt::MatchFixedString);
1848     if (idx != -1) {
1849         int prg = m_comboProg->itemData(idx).toInt();
1850         programchange(prg);
1851     }
1852 }
1853 
chankeypress(int value)1854 void VPiano::chankeypress(int value)
1855 {
1856     sendChanKeyPress(value);
1857 }
1858 
pitchwheel(int value)1859 void VPiano::pitchwheel(int value)
1860 {
1861     sendBender(value);
1862 }
1863 
1864 #endif /* ENABLE_DBUS */
1865 
slotShortcuts()1866 void VPiano::slotShortcuts()
1867 {
1868 #if !defined(SMALL_SCREEN)
1869     QPointer<ShortcutDialog> shcutDlg = new ShortcutDialog(findChildren<QAction*>(), this);
1870     releaseKb();
1871     shcutDlg->setDefaultShortcuts(m_defaultShortcuts);
1872     shcutDlg->exec();
1873     grabKb();
1874     delete shcutDlg;
1875 #endif
1876 }
1877 
slotBankNext()1878 void VPiano::slotBankNext()
1879 {
1880     int index = m_comboBank->currentIndex();
1881     if (index < m_comboBank->count()-1) {
1882         m_comboBank->setCurrentIndex(++index);
1883         slotComboBankActivated(index);
1884     }
1885 }
1886 
slotBankPrev()1887 void VPiano::slotBankPrev()
1888 {
1889     int index = m_comboBank->currentIndex();
1890     if (index > 0) {
1891         m_comboBank->setCurrentIndex(--index);
1892         slotComboBankActivated(index);
1893     }
1894 }
1895 
slotProgramNext()1896 void VPiano::slotProgramNext()
1897 {
1898     int index = m_comboProg->currentIndex();
1899     if (index < m_comboProg->count()-1) {
1900         m_comboProg->setCurrentIndex(++index);
1901         slotComboProgActivated(index);
1902     }
1903 }
1904 
slotProgramPrev()1905 void VPiano::slotProgramPrev()
1906 {
1907     int index = m_comboProg->currentIndex();
1908     if (index > 0) {
1909         m_comboProg->setCurrentIndex(--index);
1910         slotComboProgActivated(index);
1911     }
1912 }
1913 
slotControllerNext()1914 void VPiano::slotControllerNext()
1915 {
1916     int index = m_comboControl->currentIndex();
1917     if (index < m_comboControl->count()-1)
1918         m_comboControl->setCurrentIndex(++index);
1919 }
1920 
slotControllerPrev()1921 void VPiano::slotControllerPrev()
1922 {
1923     int index = m_comboControl->currentIndex();
1924     if (index > 0)
1925         m_comboControl->setCurrentIndex(--index);
1926 }
1927 
slotVelocityUp()1928 void VPiano::slotVelocityUp()
1929 {
1930     m_Velocity->triggerAction(QDial::SliderPageStepAdd);
1931 }
1932 
slotVelocityDown()1933 void VPiano::slotVelocityDown()
1934 {
1935     m_Velocity->triggerAction(QDial::SliderPageStepSub);
1936 }
1937 
slotControllerUp()1938 void VPiano::slotControllerUp()
1939 {
1940     m_Control->triggerAction(QDial::SliderPageStepAdd);
1941     slotControlSliderMoved(m_Control->value());
1942 }
1943 
slotControllerDown()1944 void VPiano::slotControllerDown()
1945 {
1946     m_Control->triggerAction(QDial::SliderPageStepSub);
1947     slotControlSliderMoved(m_Control->value());
1948 }
1949 
slotSwitchLanguage(QAction * action)1950 void VPiano::slotSwitchLanguage(QAction *action)
1951 {
1952     QString lang = action->data().toString();
1953     bool result = QMessageBox::question (this, tr("Language Changed"),
1954             tr("The language for this application is going to change to %1. "
1955                "Do you want to continue?").arg(m_supportedLangs[lang]),
1956             QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
1957     if ( result )
1958     {
1959         VPianoSettings::instance()->setLanguage(lang);
1960         retranslateUi();
1961     } else {
1962         m_currentLang->setChecked(true);
1963     }
1964 }
1965 
createLanguageMenu()1966 void VPiano::createLanguageMenu()
1967 {
1968     QString currentLang = VPianoSettings::instance()->language();
1969     QActionGroup *languageGroup = new QActionGroup(this);
1970     languageGroup->setExclusive(true);
1971     connect(languageGroup, &QActionGroup::triggered, this, &VPiano::slotSwitchLanguage, Qt::QueuedConnection);
1972     QDir dir(VPianoSettings::localeDirectory());
1973     QStringList fileNames = dir.entryList(QStringList(QSTR_VMPKPX + "*.qm"));
1974     QStringList locales;
1975     locales << "en";
1976     foreach (const QString& fileName, fileNames) {
1977         QString locale = fileName;
1978         locale.remove(0, locale.indexOf('_') + 1);
1979         locale.truncate(locale.lastIndexOf('.'));
1980         locales << locale;
1981     }
1982     locales.sort();
1983     foreach (const QString& loc, locales) {
1984         QAction *action = new QAction(m_supportedLangs.value(loc), this);
1985         action->setCheckable(true);
1986         action->setData(loc);
1987         ui.menuLanguage->addAction(action);
1988         languageGroup->addAction(action);
1989         if (currentLang.startsWith(loc)) {
1990             action->setChecked(true);
1991             m_currentLang = action;
1992         }
1993     }
1994 }
1995 
slotAboutTranslation()1996 void VPiano::slotAboutTranslation()
1997 {
1998     QString common = tr("<p>VMPK is developed and translated thanks to the "
1999         "volunteer work of many people from around the world. If you want to "
2000         "join the team or have any question, please visit the forums at "
2001         "<a href='http://sourceforge.net/projects/vmpk/forums'>SourceForge</a>"
2002         "</p>");
2003     QString currentLang = VPianoSettings::instance()->language();
2004     bool supported(false);
2005     if (!currentLang.startsWith("en")) {
2006         QMapIterator<QString,QString> it(m_supportedLangs);
2007         while (it.hasNext())  {
2008             it.next();
2009             if (currentLang.startsWith(it.key())) {
2010                 supported = true;
2011                 break;
2012             }
2013         }
2014     }
2015     if (supported)
2016         QMessageBox::information(this, tr("Translation"),
2017             tr("<p>Translation by TRANSLATOR_NAME_AND_EMAIL</p>%1").arg(common));
2018     else
2019         QMessageBox::information(this, tr("Translation Information"), common);
2020 }
2021 
retranslateUi()2022 void VPiano::retranslateUi()
2023 {
2024     m_trq->load(QSTR_QTPX + VPianoSettings::instance()->language(), VPianoSettings::systemLocales());
2025     m_trp->load(QSTR_VMPKPX + VPianoSettings::instance()->language(), VPianoSettings::localeDirectory());
2026     m_trl->load(QSTR_DRUMSTICKPX + VPianoSettings::instance()->language(), VPianoSettings::drumstickLocales());
2027     VPianoSettings::instance()->retranslatePalettes();
2028     ui.retranslateUi(this);
2029     ui.pianokeybd->retranslate();
2030     initLanguages();
2031     ui.menuLanguage->clear();
2032     createLanguageMenu();
2033     retranslateToolbars();
2034 }
2035 
initLanguages()2036 void VPiano::initLanguages()
2037 {
2038     m_supportedLangs.clear();
2039     m_supportedLangs.insert(QLatin1String("cs"), tr("Czech"));
2040     m_supportedLangs.insert(QLatin1String("de"), tr("German"));
2041     m_supportedLangs.insert(QLatin1String("en"), tr("English"));
2042     m_supportedLangs.insert(QLatin1String("es"), tr("Spanish"));
2043     m_supportedLangs.insert(QLatin1String("fr"), tr("French"));
2044     m_supportedLangs.insert(QLatin1String("gl"), tr("Galician"));
2045     m_supportedLangs.insert(QLatin1String("nl"), tr("Dutch"));
2046     m_supportedLangs.insert(QLatin1String("ru"), tr("Russian"));
2047     m_supportedLangs.insert(QLatin1String("sr"), tr("Serbian"));
2048     m_supportedLangs.insert(QLatin1String("sv"), tr("Swedish"));
2049     m_supportedLangs.insert(QLatin1String("zh_CN"), tr("Chinese"));
2050 }
2051 
retranslateToolbars()2052 void VPiano::retranslateToolbars()
2053 {
2054     m_lblChannel->setText(
2055 #if defined(SMALL_SCREEN)
2056         tr("Chan:")
2057 #else
2058         tr("Channel:")
2059 #endif
2060     );
2061     m_lblBaseOctave->setText(
2062 #if defined(SMALL_SCREEN)
2063         tr("Oct:")
2064 #else
2065         tr("Base Octave:")
2066 #endif
2067     );
2068     m_lblTranspose->setText(
2069 #if defined(SMALL_SCREEN)
2070         tr("Trans:")
2071 #else
2072         tr("Transpose:")
2073 #endif
2074     );
2075     m_lblVelocity->setText(
2076 #if defined(SMALL_SCREEN)
2077         tr("Vel:")
2078 #else
2079         tr("Velocity:")
2080 #endif
2081     );
2082     m_lblBank->setText(tr("Bank:"));
2083     m_lblBender->setText(tr("Bender:"));
2084     m_lblControl->setText(tr("Control:"));
2085     m_lblProgram->setText(tr("Program:"));
2086     m_lblValue->setText(tr("Value:"));
2087 }
2088 
createPopupMenu()2089 QMenu * VPiano::createPopupMenu ()
2090 {
2091 #if defined(SMALL_SCREEN)
2092     return 0;
2093 #else
2094     return QMainWindow::createPopupMenu();
2095 #endif
2096 }
2097 
enforceMIDIChannelState()2098 void VPiano::enforceMIDIChannelState()
2099 {
2100     if (VPianoSettings::instance()->enforceChannelState()) {
2101         int channel = VPianoSettings::instance()->channel();
2102         //qDebug() << Q_FUNC_INFO << "channel=" << m_channel << endl;
2103         QMap<int,int>::Iterator i, end;
2104         i = m_ctlSettings[channel].begin();
2105         end = m_ctlSettings[channel].end();
2106         for (; i != end; ++i) {
2107             //qDebug() << "ctl=" << i.key() << "val=" << i.value();
2108             sendController(i.key(), i.value());
2109         }
2110         //qDebug() << "bank=" << m_lastBank[m_channel];
2111         sendBankChange(m_lastBank[channel]);
2112         //qDebug() << "prog=" << m_lastProg[m_channel];
2113         sendProgramChange(m_lastProg[channel]);
2114     }
2115 }
2116 
slotKeyboardInput(bool value)2117 void VPiano::slotKeyboardInput(bool value)
2118 {
2119     VPianoSettings::instance()->setEnableKeyboard(value);
2120     ui.pianokeybd->setKeyboardEnabled(value);
2121 }
2122 
slotMouseInput(bool value)2123 void VPiano::slotMouseInput(bool value)
2124 {
2125     VPianoSettings::instance()->setEnableMouse(value);
2126     ui.pianokeybd->setMouseEnabled(value);
2127 }
2128 
slotTouchScreenInput(bool value)2129 void VPiano::slotTouchScreenInput(bool value)
2130 {
2131     VPianoSettings::instance()->setEnableTouch(value);
2132     ui.pianokeybd->setTouchEnabled(value);
2133 }
2134 
slotColorPolicy()2135 void VPiano::slotColorPolicy()
2136 {
2137     QPointer<ColorDialog> dlgColorPolicy = new ColorDialog(false, this);
2138     dlgColorPolicy->loadPalette(VPianoSettings::instance()->highlightPaletteId());
2139     if (dlgColorPolicy->exec() == QDialog::Accepted) {
2140         int pal = dlgColorPolicy->selectedPalette();
2141         PianoPalette editedPalette = VPianoSettings::instance()->getPalette(pal);
2142         if (editedPalette.isHighLight()) {
2143             VPianoSettings::instance()->setHighlightPaletteId(pal);
2144             ui.pianokeybd->setHighlightPalette(editedPalette);
2145         } else if (editedPalette.isBackground()) {
2146             if ((VPianoSettings::instance()->colorScale() && (editedPalette.paletteId() == PAL_SCALE)) ||
2147                 (!VPianoSettings::instance()->colorScale() && (editedPalette.paletteId() == PAL_KEYS))) {
2148                 ui.pianokeybd->setBackgroundPalette(editedPalette);
2149             }
2150         } else if (editedPalette.isForeground()) {
2151             ui.pianokeybd->setForegroundPalette(editedPalette);
2152         }
2153     }
2154     delete dlgColorPolicy;
2155 }
2156 
slotColorScale(bool value)2157 void VPiano::slotColorScale(bool value)
2158 {
2159     if (value != VPianoSettings::instance()->colorScale()) {
2160         VPianoSettings::instance()->setColorScale(value);
2161         ui.pianokeybd->setBackgroundPalette(VPianoSettings::instance()->getPalette(value ? PAL_SCALE : PAL_KEYS));
2162         ui.pianokeybd->setShowColorScale(value);
2163     }
2164 }
2165 
toggleWindowFrame(const bool state)2166 void VPiano::toggleWindowFrame(const bool state)
2167 {
2168     Qt::WindowFlags flags = windowFlags();
2169 
2170     if (state)
2171         flags &= ~Qt::FramelessWindowHint;
2172     else
2173         flags |= Qt::FramelessWindowHint;
2174 
2175     setWindowFlags(flags);
2176     show();
2177 }
2178 
2179 
slotNameOrientation(QAction * action)2180 void VPiano::slotNameOrientation(QAction* action)
2181 {
2182     if(action == ui.actionHorizontal) {
2183         VPianoSettings::instance()->setNamesOrientation(HorizontalOrientation);
2184     } else if(action == ui.actionVertical) {
2185         VPianoSettings::instance()->setNamesOrientation(VerticalOrientation);
2186     } else if(action == ui.actionAutomatic) {
2187         VPianoSettings::instance()->setNamesOrientation(AutomaticOrientation);
2188     }
2189     ui.pianokeybd->setLabelOrientation(VPianoSettings::instance()->namesOrientation());
2190 }
2191 
slotNameVisibility(QAction * action)2192 void VPiano::slotNameVisibility(QAction* action)
2193 {
2194     if(action == ui.actionNever) {
2195         VPianoSettings::instance()->setNamesVisibility(ShowNever);
2196     } else if(action == ui.actionMinimal) {
2197         VPianoSettings::instance()->setNamesVisibility(ShowMinimum);
2198     } else if(action == ui.actionWhen_Activated) {
2199         VPianoSettings::instance()->setNamesVisibility(ShowActivated);
2200     } else if(action == ui.actionAlways) {
2201         VPianoSettings::instance()->setNamesVisibility(ShowAlways);
2202     }
2203     ui.pianokeybd->setShowLabels(VPianoSettings::instance()->namesVisibility());
2204 }
2205 
slotNameVariant(QAction * action)2206 void VPiano::slotNameVariant(QAction* action)
2207 {
2208     if(action == ui.actionSharps) {
2209         VPianoSettings::instance()->setNamesAlterations(ShowSharps);
2210     } else if(action == ui.actionFlats) {
2211         VPianoSettings::instance()->setNamesAlterations(ShowFlats);
2212     } else if(action == ui.actionNothing) {
2213         VPianoSettings::instance()->setNamesAlterations(ShowNothing);
2214     }
2215     ui.pianokeybd->setLabelAlterations(VPianoSettings::instance()->alterations());
2216 }
2217 
slotNoteName(const QString & name)2218 void VPiano::slotNoteName(const QString& name)
2219 {
2220     if (name.isEmpty()) {
2221         ui.statusBar->clearMessage();
2222     } else {
2223         ui.statusBar->showMessage(name);
2224     }
2225 }
2226 
slotLoadConfiguration()2227 void VPiano::slotLoadConfiguration()
2228 {
2229     releaseKb();
2230     auto configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
2231     QString fileName = QFileDialog::getOpenFileName(this, tr("Open Configuration File"),
2232         configDir, tr("Configuration files (*.conf *.ini)"));
2233     if (!fileName.isEmpty()) {
2234         VPianoSettings::instance()->ReadFromFile(fileName);
2235         qApp->exit(RESTART_VMPK);
2236     }
2237     grabKb();
2238 }
2239 
slotSaveConfiguration()2240 void VPiano::slotSaveConfiguration()
2241 {
2242     releaseKb();
2243     auto configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
2244     QFileDialog dlg(this);
2245     dlg.setNameFilter(tr("Configuration files (*.conf *.ini)"));
2246     dlg.setDirectory(configDir);
2247     dlg.setWindowTitle(tr("Save Configuration File"));
2248     dlg.setDefaultSuffix("conf");
2249     dlg.setFileMode(QFileDialog::AnyFile);
2250     dlg.setAcceptMode(QFileDialog::AcceptSave);
2251     if (dlg.exec() == QDialog::Accepted) {
2252         auto selected = dlg.selectedFiles();
2253         if (!selected.isEmpty()) {
2254             VPianoSettings::instance()->SaveToFile(selected.first());
2255         }
2256     }
2257     grabKb();
2258 }
2259 
2260 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
nativeEvent(const QByteArray & eventType,void * message,long * result)2261 bool VPiano::nativeEvent(const QByteArray &eventType, void *message, long *result)
2262 #else
2263 bool VPiano::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
2264 #endif
2265 {
2266 #if defined(Q_OS_WINDOWS)
2267     if (VPianoSettings::instance()->getWinSnap() && m_snapper.HandleMessage(message)) {
2268         result = 0;
2269         return true;
2270     }
2271 #endif
2272     return QWidget::nativeEvent(eventType, message, result);
2273 }
2274 
changeEvent(QEvent * event)2275 void VPiano::changeEvent(QEvent *event)
2276 {
2277 #if defined(Q_OS_WINDOWS)
2278     if (event->type() == QEvent::PaletteChange) {
2279         foreach(QToolBar *tb, findChildren<QToolBar*>()) {
2280             foreach(QComboBox *cb, tb->findChildren<QComboBox*>()) {
2281                 cb->setPalette(qApp->palette());
2282                 foreach(QWidget *w, cb->findChildren<QWidget*>()) {
2283                     w->setPalette(qApp->palette());
2284                 }
2285                 //qDebug() << cb;
2286             }
2287         }
2288     }
2289 #endif
2290     QMainWindow::changeEvent(event);
2291 }
2292