1 /*!
2     @file           QtWindow.cpp
3 
4     @brief          xxx.
5 
6     @author         L. J. Barman
7 
8     Copyright (c)   2008-2020, L. J. Barman and others, all rights reserved
9 
10     This file is part of the PianoBooster application
11 
12     PianoBooster is free software: you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation, either version 3 of the License, or
15     (at your option) any later version.
16 
17     PianoBooster is distributed in the hope that it will be useful,
18     but WITHOUT ANY WARRANTY; without even the implied warranty of
19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20     GNU General Public License for more details.
21 
22     You should have received a copy of the GNU General Public License
23     along with PianoBooster.  If not, see <http://www.gnu.org/licenses/>.
24 
25 */
26 
27 #include "GlView.h"
28 #include "QtWindow.h"
29 #include "version.h"
30 
31 #ifdef __linux__
32 #ifndef USE_REALTIME_PRIORITY
33 #define USE_REALTIME_PRIORITY 0
34 #endif
35 #endif
36 
37 #if USE_REALTIME_PRIORITY
38 #include <sched.h>
39 
40 /* sets the process to "policy" policy at given priority */
set_realtime_priority(int policy,int prio)41 static int set_realtime_priority(int policy, int prio)
42 {
43     struct sched_param schp;
44     memset(&schp, 0, sizeof(schp));
45 
46     schp.sched_priority = prio;
47     if (sched_setscheduler(0, policy, &schp) != 0) {
48         perror("sched_setscheduler");
49         return -1;
50     }
51     return 0;
52 }
53 #endif
54 
QtWindow()55 QtWindow::QtWindow()
56 {
57     m_settings = new CSettings(this);
58     setWindowIcon(QIcon(":/images/pianobooster.png"));
59     setWindowTitle(tr("Piano Booster"));
60 
61     Cfg::setDefaults();
62 
63     decodeCommandLine();
64 
65     if (Cfg::experimentalSwapInterval != -1)
66     {
67         QGLFormat fmt;
68         fmt.setSwapInterval(Cfg::experimentalSwapInterval);
69         int value = fmt.swapInterval();
70         ppLogInfo("Open GL Swap Interval %d", value);
71         QGLFormat::setDefaultFormat(fmt);
72     }
73 
74     for (int i = 0; i < MAX_RECENT_FILES; ++i)
75          m_recentFileActs[i] = nullptr;
76     m_separatorAct = nullptr;
77 
78 #if USE_REALTIME_PRIORITY
79     int rt_prio = sched_get_priority_max(SCHED_FIFO);
80     set_realtime_priority(SCHED_FIFO, rt_prio);
81 #endif
82 
83     m_glWidget = new CGLView(this, m_settings);
84     m_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
85 
86     m_song = m_glWidget->getSongObject();
87     m_score = m_glWidget->getScoreObject();
88 
89     QHBoxLayout *mainLayout = new QHBoxLayout;
90     QVBoxLayout *columnLayout = new QVBoxLayout;
91 
92     m_sidePanel = new GuiSidePanel(this, m_settings);
93     m_topBar = new GuiTopBar(this, m_settings);
94     m_tutorWindow = new QTextBrowser(this);
95     m_tutorWindow->hide();
96 
97     m_settings->init(m_song, m_sidePanel, m_topBar);
98 
99     mainLayout->addWidget(m_sidePanel);
100     columnLayout->addWidget(m_topBar);
101     columnLayout->addWidget(m_glWidget);
102     columnLayout->addWidget(m_tutorWindow);
103     mainLayout->addLayout(columnLayout);
104 
105     m_song->init2(m_score, m_settings);
106 
107     m_sidePanel->init(m_song, m_song->getTrackList(), m_topBar);
108     m_topBar->init(m_song);
109 
110     QWidget *centralWin = new QWidget();
111     centralWin->setLayout(mainLayout);
112 
113     setCentralWidget(centralWin);
114 
115     m_glWidget->setFocus(Qt::ActiveWindowFocusReason);
116 
117     m_song->setPianoSoundPatches(m_settings->value("Keyboard/RightSound", Cfg::defaultRightPatch()).toInt() - 1,
118                                  m_settings->value("Keyboard/WrongSound", Cfg::defaultWrongPatch()).toInt() - 1, true);
119 
120     m_song->setLatencyFix(m_settings->value("Midi/Latency", 0).toInt());
121 
122     m_song->cfg_timingMarkersFlag = m_settings->value("Score/TimingMarkers", m_song->cfg_timingMarkersFlag ).toBool();
123     m_song->cfg_stopPointMode = static_cast<stopPointMode_t> (m_settings->value("Score/StopPointMode", m_song->cfg_stopPointMode ).toInt());
124     m_song->cfg_rhythmTapping = static_cast<rhythmTapping_t> (m_settings->value("Score/RtyhemTappingMode", m_song->cfg_rhythmTapping ).toInt());
125 
126     m_song->reconnectMidi();
127 
128     readSettings();
129 
130     QTimer::singleShot(100, [&](){
131         QString songName = m_settings->value("CurrentSong").toString();
132         if (!songName.isEmpty())
133             m_settings->openSongFile( songName );
134     });
135 }
136 
init()137 void QtWindow::init()
138 {
139 
140     m_settings->loadSettings();
141 
142     createActions();
143     createMenus();
144     readSettings();
145 
146     refreshTranslate();
147     show();
148 }
149 
~QtWindow()150 QtWindow::~QtWindow()
151 {
152     delete m_settings;
153 }
154 
155 ///////////////////////////////////////////////////////////////////////////////
156 //! @brief               Displays the usage
displayUsage()157 void QtWindow::displayUsage()
158 {
159     fprintf(stdout, "Usage: pianobooster [flags] [midifile]\n");
160     fprintf(stdout, "  -d, --debug             Increase the debug level.\n");
161     fprintf(stdout, "      --Xnote-length      Displays the note length (experimental)\n");
162     fprintf(stdout, "  -h, --help              Displays this help message.\n");
163     fprintf(stdout, "  -v, --version           Displays version number and then exits.\n");
164     fprintf(stdout, "  -l   --log              Write debug info to the \"pb.log\" log file.\n");
165     fprintf(stdout, "       --midi-input-dump  Displays the midi input in hex.\n");
166     fprintf(stdout, "       --lights           Turns on the keyboard lights.\n");
167 }
168 
decodeIntegerParam(QString arg,int defaultParam)169 int QtWindow::decodeIntegerParam(QString arg, int defaultParam)
170 {
171     int n = arg.lastIndexOf('=');
172     if (n == -1 || (n + 1) >= arg.size())
173         return defaultParam;
174     bool ok;
175     int value = arg.mid(n+1).toInt(&ok);
176     if (ok)
177         return value;
178     return defaultParam;
179 }
180 
validateIntegerParam(QString arg)181 bool QtWindow::validateIntegerParam(QString arg)
182 {
183     int n = arg.lastIndexOf('=');
184     if (n == -1 || (n + 1) >= arg.size())
185         return false;
186     bool ok;
187     arg.mid(n+1).toInt(&ok);
188      return ok;
189 }
validateIntegerParamWithMessage(QString arg)190 bool QtWindow::validateIntegerParamWithMessage(QString arg)
191 {
192     bool ok = validateIntegerParam(arg);
193     if (!ok) {
194         fprintf(stderr, "ERROR: Invalid parameter to a command line argument \"%s\".\n", qPrintable(arg));
195         exit(0);
196     }
197      return ok;
198 }
199 
decodeMidiFileArg(QString arg)200 void QtWindow::decodeMidiFileArg(QString arg)
201 {
202 
203     QFileInfo fileInfo(arg);
204 
205     if (!fileInfo.exists() )
206     {
207         QMessageBox::warning(nullptr, tr("PianoBooster Midi File Error"),
208                  tr("Cannot open \"%1\"").arg(QString(fileInfo.absoluteFilePath())));
209         exit(1);
210     }
211         else if ( !(fileInfo.fileName().endsWith(".mid", Qt::CaseInsensitive ) ||
212              fileInfo.fileName().endsWith(".midi", Qt::CaseInsensitive ) ||
213              fileInfo.fileName().endsWith(".kar", Qt::CaseInsensitive )) )
214     {
215         QMessageBox::warning(nullptr, tr("PianoBooster Midi File Error"),
216                  tr("\"%1\" is not a Midi File").arg(QString(fileInfo.fileName())));
217         exit(1);
218     }
219     else
220     {
221         bool vaildMidiFile = true;
222         QFile file(fileInfo.absoluteFilePath());
223         if (!file.open(QIODevice::ReadOnly))
224             vaildMidiFile = false;
225         else
226         {
227             QByteArray bytes = file.read(4);
228             for (int i = 0; i < 4; i++)
229             {
230                 if (bytes[i] !="MThd"[i] )
231                     vaildMidiFile = false;
232             }
233             file.close();
234         }
235         if (vaildMidiFile ==  true)
236             m_settings->setValue("CurrentSong", fileInfo.absoluteFilePath());
237         else
238         {
239             QMessageBox::warning(nullptr, tr("PianoBooster Midi File Error"),
240                  tr("\"%1\" is not a valid Midi file").arg(QString(fileInfo.absoluteFilePath())));
241             exit(1);
242         }
243     }
244 }
245 
decodeCommandLine()246 void QtWindow::decodeCommandLine()
247 {
248     bool hasMidiFile = false;
249     QStringList argList = QCoreApplication::arguments();
250     QString arg;
251     for (int i = 0; i < argList.size(); ++i)
252     {
253         arg = argList[i];
254         if (arg.startsWith("-"))
255         {
256             if (arg.startsWith("-d") || arg.startsWith("--debug"))
257                 Cfg::logLevel++;
258             else if (arg.startsWith("--Xnote-length"))
259                 Cfg::experimentalNoteLength = true;
260             else if (arg.startsWith("--Xtick-rate")) {
261                 if (validateIntegerParamWithMessage(arg)) {
262                     Cfg::tickRate = decodeIntegerParam(arg, 12);
263                 }
264             } else if (arg.startsWith("-l") || arg.startsWith("--log"))
265                 Cfg::useLogFile = true;
266             else if (arg.startsWith("--midi-input-dump"))
267                 Cfg::midiInputDump = true;
268             else if (arg.startsWith("-X1"))
269                 Cfg::experimentalTempo = true;
270             else if (arg.startsWith("-Xswap"))
271                 Cfg::experimentalSwapInterval = decodeIntegerParam(arg, 100);
272 
273             else if (arg.startsWith("--lights"))
274                 Cfg::keyboardLightsChan = 1-1;  // Channel 1 (really a zero)
275 
276             else if (arg.startsWith("-h") || arg.startsWith("-?") || arg.startsWith("--help"))
277             {
278                 displayUsage();
279                 exit(0);
280             }
281             else if (arg.startsWith("-v") || arg.startsWith("--version"))
282             {
283                 fprintf(stdout, "pianobooster Version " PB_VERSION"\n");
284                 exit(0);
285             }
286             else
287             {
288                 fprintf(stderr, "ERROR: Unknown arguments.\n");
289                 displayUsage();
290                 exit(0);
291             }
292         }
293         else {
294             if ( hasMidiFile == false && i > 0)
295             {
296                 hasMidiFile = true;
297                 decodeMidiFileArg(arg);
298             }
299         }
300     }
301 }
302 
addShortcutAction(const QString & key,const char * method)303 void QtWindow::addShortcutAction(const QString & key, const char * method)
304 {
305     QAction* act = new QAction(this);
306     act->setShortcut(m_settings->value(key).toString());
307     connect(act, SIGNAL(triggered()), this, method);
308     addAction(act);
309 }
310 
createActions()311 void QtWindow::createActions()
312 {
313     m_openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this);
314     m_openAct->setShortcut(tr("Ctrl+O"));
315     m_openAct->setToolTip(tr("Open an existing file"));
316     connect(m_openAct, SIGNAL(triggered()), this, SLOT(open()));
317 
318     m_exitAct = new QAction(tr("E&xit"), this);
319     m_exitAct->setShortcut(tr("Ctrl+Q"));
320     m_exitAct->setToolTip(tr("Exit the application"));
321     connect(m_exitAct, SIGNAL(triggered()), this, SLOT(close()));
322 
323     m_aboutAct = new QAction(tr("&About"), this);
324     m_aboutAct->setToolTip(tr("Show the application's About box"));
325     connect(m_aboutAct, SIGNAL(triggered()), this, SLOT(about()));
326 
327     m_shortcutAct = new QAction(tr("&PC Shortcut Keys"), this);
328     m_shortcutAct->setToolTip(tr("The PC Keyboard shortcut keys"));
329     connect(m_shortcutAct, SIGNAL(triggered()), this, SLOT(keyboardShortcuts()));
330 
331     m_setupMidiAct = new QAction(tr("&Midi Setup ..."), this);
332     m_setupMidiAct->setShortcut(tr("Ctrl+S"));
333     m_setupMidiAct->setToolTip(tr("Setup the Midi input and output"));
334     connect(m_setupMidiAct, SIGNAL(triggered()), this, SLOT(showMidiSetup()));
335 
336     m_setupKeyboardAct = new QAction(tr("Piano &Keyboard Setting ..."), this);
337     m_setupKeyboardAct->setShortcut(tr("Ctrl+K"));
338     m_setupKeyboardAct->setToolTip(tr("Change the piano keyboard settings"));
339     connect(m_setupKeyboardAct, SIGNAL(triggered()), this, SLOT(showKeyboardSetup()));
340 
341     m_fullScreenStateAct = new QAction(tr("&Fullscreen"), this);
342     m_fullScreenStateAct->setToolTip(tr("Fullscreen mode"));
343     m_fullScreenStateAct->setShortcut(tr("F11"));
344     m_fullScreenStateAct->setCheckable(true);
345     connect(m_fullScreenStateAct, SIGNAL(triggered()), this, SLOT(onFullScreenStateAct()));
346 
347     m_sidePanelStateAct = new QAction(tr("&Show the Side Panel"), this);
348     m_sidePanelStateAct->setToolTip(tr("Show the Left Side Panel"));
349     m_sidePanelStateAct->setShortcut(tr("F12"));
350     m_sidePanelStateAct->setCheckable(true);
351     m_sidePanelStateAct->setChecked(true);
352     connect(m_sidePanelStateAct, SIGNAL(triggered()), this, SLOT(toggleSidePanel()));
353 
354     m_viewPianoKeyboard = new QAction(tr("Show Piano &Keyboard"), this);
355     m_viewPianoKeyboard->setToolTip(tr("Show Piano Keyboard Widget"));
356     m_viewPianoKeyboard->setCheckable(true);
357     m_viewPianoKeyboard->setChecked(false);
358     if (m_settings->value("View/PianoKeyboard").toString()=="on"){
359         m_viewPianoKeyboard->setChecked(true);
360     }
361     connect(m_viewPianoKeyboard, SIGNAL(triggered()), this, SLOT(onViewPianoKeyboard()));
362 
363     m_setupPreferencesAct = new QAction(tr("&Preferences ..."), this);
364     m_setupPreferencesAct->setToolTip(tr("Settings"));
365     m_setupPreferencesAct->setShortcut(tr("Ctrl+P"));
366     connect(m_setupPreferencesAct, SIGNAL(triggered()), this, SLOT(showPreferencesDialog()));
367 
368     m_songDetailsAct = new QAction(tr("Song &Details ..."), this);
369     m_songDetailsAct->setToolTip(tr("Song Settings"));
370     m_songDetailsAct->setShortcut(tr("Ctrl+D"));
371     connect(m_songDetailsAct, SIGNAL(triggered()), this, SLOT(showSongDetailsDialog()));
372 
373     QAction* act = new QAction(this);
374     act->setShortcut(tr("Shift+F1"));
375     connect(act, SIGNAL(triggered()), this, SLOT(enableFollowTempo()));
376     addAction(act);
377 
378     act = new QAction(this);
379     act->setShortcut(tr("Alt+F1"));
380     connect(act, SIGNAL(triggered()), this, SLOT(disableFollowTempo()));
381     addAction(act);
382 
383     addShortcutAction("ShortCuts/RightHand",        SLOT(on_rightHand()));
384     addShortcutAction("ShortCuts/BothHands",        SLOT(on_bothHands()));
385     addShortcutAction("ShortCuts/LeftHand",         SLOT(on_leftHand()));
386     addShortcutAction("ShortCuts/PlayFromStart",    SLOT(on_playFromStart()));
387     addShortcutAction("ShortCuts/PlayPause",        SLOT(on_playPause()));
388     addShortcutAction("ShortCuts/Faster",           SLOT(on_faster()));
389     addShortcutAction("ShortCuts/Slower",           SLOT(on_slower()));
390     addShortcutAction("ShortCuts/NextSong",         SLOT(on_nextSong()));
391     addShortcutAction("ShortCuts/PreviousSong",     SLOT(on_previousSong()));
392     addShortcutAction("ShortCuts/NextBook",         SLOT(on_nextBook()));
393     addShortcutAction("ShortCuts/PreviousBook",     SLOT(on_previousBook()));
394 
395      for (int i = 0; i < MAX_RECENT_FILES; ++i) {
396          m_recentFileActs[i] = new QAction(this);
397          m_recentFileActs[i]->setVisible(false);
398          connect(m_recentFileActs[i], SIGNAL(triggered()),
399                  this, SLOT(openRecentFile()));
400      }
401 }
402 
createMenus()403 void QtWindow::createMenus()
404 {
405     m_fileMenu = menuBar()->addMenu(tr("&File"));
406     m_fileMenu->setToolTipsVisible(true);
407     m_fileMenu->addAction(m_openAct);
408     m_separatorAct = m_fileMenu->addSeparator();
409     for (int i = 0; i < MAX_RECENT_FILES; ++i)
410        m_fileMenu->addAction(m_recentFileActs[i]);
411     m_fileMenu->addSeparator();
412     m_fileMenu->addAction(m_exitAct);
413     updateRecentFileActions();
414 
415     m_viewMenu = menuBar()->addMenu(tr("&View"));
416     m_viewMenu->setToolTipsVisible(true);
417     m_viewMenu->addAction(m_sidePanelStateAct);
418     m_viewMenu->addAction(m_fullScreenStateAct);
419     m_viewMenu->addAction(m_viewPianoKeyboard);
420 
421     m_songMenu = menuBar()->addMenu(tr("&Song"));
422     m_songMenu->setToolTipsVisible(true);
423     m_songMenu->addAction(m_songDetailsAct);
424 
425     m_setupMenu = menuBar()->addMenu(tr("Set&up"));
426     m_setupMenu->setToolTipsVisible(true);
427     m_setupMenu->addAction(m_setupMidiAct);
428     m_setupMenu->addAction(m_setupKeyboardAct);
429     m_setupMenu->addAction(m_setupPreferencesAct);
430 
431     m_helpMenu = menuBar()->addMenu(tr("&Help"));
432     m_helpMenu->setToolTipsVisible(true);
433 
434     QAction* act;
435     act = new QAction(tr("&Help"), this);
436     act->setToolTip(tr("Piano Booster Help"));
437     connect(act, SIGNAL(triggered()), this, SLOT(help()));
438     m_helpMenu->addAction(act);
439 
440     act = new QAction(tr("&Website"), this);
441     act->setToolTip(tr("Piano Booster Website"));
442     connect(act, SIGNAL(triggered()), this, SLOT(website()));
443     m_helpMenu->addAction(act);
444 
445     m_helpMenu->addAction(m_shortcutAct);
446     m_helpMenu->addAction(m_aboutAct);
447 }
448 
openRecentFile()449 void QtWindow::openRecentFile()
450 {
451     QAction *action = qobject_cast<QAction *>(sender());
452      if (action)
453          m_settings->openSongFile(action->data().toString());
454 }
455 
showMidiSetup()456 void QtWindow::showMidiSetup(){
457 
458     m_topBar->stopMuiscPlaying();
459 
460     m_glWidget->stopTimerEvent();
461     GuiMidiSetupDialog midiSetupDialog(this);
462     midiSetupDialog.init(m_song, m_settings);
463     midiSetupDialog.exec();
464     m_song->flushMidiInput();
465     m_glWidget->startTimerEvent();
466 }
467 
468 // load the recent file list from the config file into the file menu
updateRecentFileActions()469 void QtWindow::updateRecentFileActions()
470 {
471 
472     QStringList files = m_settings->value("RecentFileList").toStringList();
473 
474     int numRecentFiles = qMin(files.size(), (int)MAX_RECENT_FILES);
475 
476     for (int i = 0; i < numRecentFiles; ++i) {
477         QString text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
478         if (m_recentFileActs[i] == nullptr)
479             break;
480         m_recentFileActs[i]->setText(text);
481         m_recentFileActs[i]->setData(files[i]);
482         m_recentFileActs[i]->setVisible(true);
483     }
484 
485     for (int j = numRecentFiles; j < MAX_RECENT_FILES; ++j) {
486         if (m_recentFileActs[j] == nullptr)
487             break;
488         m_recentFileActs[j]->setVisible(false);
489     }
490 
491     if (m_separatorAct)
492         m_separatorAct->setVisible(numRecentFiles > 0);
493 }
494 
strippedName(const QString & fullFileName)495 QString QtWindow::strippedName(const QString &fullFileName)
496 {
497     return QFileInfo(fullFileName).fileName();
498 }
499 
500 // Just used for the RecentFileList
setCurrentFile(const QString & fileName)501 void QtWindow::setCurrentFile(const QString &fileName)
502 {
503 
504     setWindowFilePath(fileName);
505 
506     QStringList files = m_settings->value("RecentFileList").toStringList();
507     files.removeAll(fileName);
508     files.prepend(fileName);
509     while (files.size() > MAX_RECENT_FILES)
510         files.removeLast();
511 
512     m_settings->setValue("RecentFileList", files);
513 
514     updateRecentFileActions();
515 
516 }
517 
website()518 void QtWindow::website()
519 {
520     QDesktopServices::openUrl(QUrl("https://www.pianobooster.org/"));
521 }
522 
help()523 void QtWindow::help()
524 {
525     QMessageBox msgBox(this);
526     msgBox.setWindowTitle (tr("Piano Booster Help"));
527     msgBox.setText(
528     tr("<h3>Getting Started</h3>") +
529     tr("<p>You need a <b>MIDI Piano Keyboard </b> and a <b>MIDI interface</b> for the PC. If you "
530        "don't have a MIDI keyboard you can still try out PianoBooster using the PC keyboard, 'X' is "
531        "middle C.</p>") +
532     tr("<p>To hear the music you will need a <b>General Midi sound synthesizer</b>. "
533        "The \"Microsoft GS Wavetable software synthesizer\" that comes with Windows can be used "
534        "but it introduces an unacceptable delay (latency). In Linux you can use ") +
535        "<a href=\"http://www.fluidsynth.org\">FluidSynth</a> " +
536     tr("or") +
537     " <a href=\"http://timidity.sourceforge.net/\">Timidity</a></p>" +
538     tr("<p>PianoBooster works best with MIDI files that have separate left and right piano parts "
539        "using MIDI channels 3 and 4.") +
540     tr("<h3>Setting Up</h3>") +
541     tr("<p>First use the <i>Setup/Midi Setup</i> menu and in the dialog box select the MIDI input and MIDI "
542        "output interfaces that match your hardware. ") +
543     tr("Next use <i>File/Open</i> to open the MIDI file \".mid\" or a karaoke \".kar\" file. "
544        "Now select whether you want to just <i>listen</i> to the music or "
545        "<i>play along</i> on the piano keyboard by setting the <i>skill</i> level on the side panel. Finally when "
546        "you are ready click the <i>play icon</i> (or press the <i>space bar</i>) to roll the music.") +
547     tr("<h3>Hints on Playing the Piano</h3>"
548        "<p>For hints on how to play the piano see: ") +
549        "<a href=\"https://www.pianobooster.org/music-info.html\" ><b>" + tr("Piano Hints") + "</b></a></p>" +
550     tr("<h3>More Information</h3>"
551        "<p>For more help please visit the PianoBooster ") +
552        "<a href=\"https://www.pianobooster.org\" ><b>" + tr("website") + "</b></a>, " +
553     tr("the PianoBooster") + " <a href=\"https://www.pianobooster.org/faq.html\" ><b> " + tr("FAQ") + "</b></a> " +
554     tr("and the") + " <a href=\"http://piano-booster.2625608.n2.nabble.com/Piano-Booster-Users-f1591936.html\"><b>" +tr("user forum") +"</b></a>."
555     );
556 
557     msgBox.setMinimumWidth(600);
558     msgBox.exec();
559 }
560 
about()561 void QtWindow::about()
562 {
563     QMessageBox msgBox(this);
564     msgBox.setWindowTitle (tr("About Piano Booster"));
565     msgBox.setText(
566             tr("<b>PianoBooster - Version %1</b> <br><br>").arg(PB_VERSION) +
567             tr("<b>Boost</b> your <b>Piano</b> playing skills!<br><br>") +
568             "<a href=\"https://www.pianobooster.org/\" ><b>https://www.pianobooster.org/</b></a><br><br>" +
569             tr("Copyright(c) L. J. Barman, 2008-2020; All rights reserved.<br>") +
570             tr("Copyright(c) Fabien Givors, 2018-2019; All rights reserved.<br>") +
571             "<br>" +
572             tr("This program is made available "
573                 "under the terms of the GNU General Public License version 3 as published by "
574                 "the Free Software Foundation.<br><br>"
575             )
576             #ifdef USE_BUNDLED_RTMIDI
577              +
578             tr("This program also contains RtMIDI: realtime MIDI i/o C++ classes<br>") +
579             tr("Copyright(c) Gary P. Scavone, 2003-2019; All rights reserved.")
580             #endif
581     );
582     msgBox.setMinimumWidth(600);
583     msgBox.exec();
584 }
585 
displayShortCut(QString key,QString description)586 QString QtWindow::displayShortCut(QString key, QString description)
587 {
588     QString space = tr("space");
589 
590     QString str = QString("<tr>"
591                 "<td>%1</td>"
592                 "<td>%2</td>"
593                 "</tr>").arg( description ).arg(tr(m_settings->value(key).toString().toUtf8().data()));
594     return str;
595 
596 }
597 
keyboardShortcuts()598 void QtWindow::keyboardShortcuts()
599 {
600     QMessageBox msgBox(this);
601     msgBox.setWindowTitle (tr("PC Keyboard ShortCuts"));
602     QString msg =
603             tr(
604                 "<h2><center>Keyboard shortcuts</center></h2>"
605                 "<p>The following PC keyboard shortcuts have been defined.</p>"
606                 "<center><table  border='1' cellspacing='0' cellpadding='4' >"
607                 );
608 
609     msg += tr(
610                 "<tr>"
611                 "<th>Action</th>"
612                 "<th>Key</th>"
613                 "</tr>"
614             );
615     msg += displayShortCut("ShortCuts/RightHand", tr("Choose the right hand"));
616     msg += displayShortCut("ShortCuts/BothHands", tr("Choose both hands"));
617     msg += displayShortCut("ShortCuts/LeftHand", tr("Choose the left Hand"));
618     msg += displayShortCut("ShortCuts/PlayFromStart", tr("Play from start toggle"));
619     msg += displayShortCut("ShortCuts/PlayPause", tr("Play Pause Toggle"));
620     msg += displayShortCut("ShortCuts/Faster",  tr("Increase the speed by 5%"));
621     msg += displayShortCut("ShortCuts/Slower", tr("Increase the speed by 5%"));
622     msg += displayShortCut("ShortCuts/NextSong", tr("Change to the Next Song"));
623     msg += displayShortCut("ShortCuts/PreviousSong", tr("Change to the Previous Song"));
624     msg += displayShortCut("ShortCuts/NextBook", tr("Change to the Next Book"));
625     msg += displayShortCut("ShortCuts/PreviousBook", tr("Change to the Previous Book"));
626 
627     msg += tr(
628                 "<tr><td>Fake Piano keys</td><td>X is middle C</td></tr>"
629                 "</table> </center><br>"
630                 );
631     msgBox.setText(msg);
632 
633     msgBox.setMinimumWidth(600);
634     msgBox.exec();
635 }
636 
open()637 void QtWindow::open()
638 {
639     m_glWidget->stopTimerEvent();
640 
641     QFileInfo currentSong = m_settings->getCurrentSongLongFileName();
642 
643     QString dir;
644     if (currentSong.isFile())
645         dir = currentSong.path();
646     else
647         dir = QDir::homePath();
648 
649     QString fileName = QFileDialog::getOpenFileName(this,tr("Open Midi File"),
650                             dir, tr("Midi Files") + " (*.mid *.MID *.midi *.MIDI *.kar *.KAR)");
651     if (!fileName.isEmpty()) {
652 
653         m_settings->openSongFile(fileName);
654         setCurrentFile(fileName);
655     }
656     m_song->flushMidiInput();
657     m_glWidget->startTimerEvent();
658 }
659 
readSettings()660 void QtWindow::readSettings()
661 {
662     QPoint pos = m_settings->value("Window/Pos", QPoint(25, 25)).toPoint();
663     QSize size = m_settings->value("Window/Size", QSize(1200, 800)).toSize();
664     resize(size);
665     move(pos);
666 }
667 
writeSettings()668 void QtWindow::writeSettings()
669 {
670     m_settings->setValue("Window/Pos", pos());
671     m_settings->setValue("Window/Size", size());
672     m_settings->writeSettings();
673 }
674 
closeEvent(QCloseEvent * event)675 void QtWindow::closeEvent(QCloseEvent *event)
676 {
677     if (m_song->playingMusic())
678     {
679         m_song->playMusic(false);
680     }
681 
682     writeSettings();
683 }
684 
keyPressEvent(QKeyEvent * event)685 void QtWindow::keyPressEvent ( QKeyEvent * event )
686 {
687     if (event->text().length() == 0)
688         return;
689 
690     if (event->isAutoRepeat() == true)
691         return;
692 
693     if (event->key() == Qt::Key_F1)
694         return;
695 
696     int c = event->text().toLatin1().at(0);
697     m_song->pcKeyPress( c, true);
698 }
699 
keyReleaseEvent(QKeyEvent * event)700 void QtWindow::keyReleaseEvent ( QKeyEvent * event )
701 {
702     if (event->isAutoRepeat() == true)
703         return;
704 
705     if (event->text().length() == 0)
706         return;
707 
708     int c = event->text().toLatin1().at(0);
709     m_song->pcKeyPress( c, false);
710 }
711 
loadTutorHtml(const QString & name)712 void QtWindow::loadTutorHtml(const QString & name)
713 {
714     if (name.isEmpty())
715     {
716         m_tutorWindow->hide();
717         m_tutorWindow->clear();
718     }
719     else
720     {
721         QFile file(name);
722         if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
723 
724         QTextStream out(&file);
725         out.setCodec("UTF-8");
726 
727         QString htmlStart = "<head><style> body{background-color:#FFFFC0;color: black} p{font-size: 18px;} blockquote{color: #ff0000;}</style></head><body>";
728         QString htmlBody = out.readAll();
729         QString htmlEnd = "</body>";
730         QString htmlText = htmlStart + htmlBody + htmlEnd;
731         m_tutorWindow->setHtml(htmlText.toUtf8().data());
732 
733         // TODO get this working again on small screens
734         //_tutorWindow->setFixedHeight(130);
735         m_tutorWindow->setFixedHeight(180);
736 
737         m_tutorWindow->show();
738 
739         file.close();
740     }
741 
742 }
743 
refreshTranslate()744 void QtWindow::refreshTranslate(){
745 #ifndef NO_LANGS
746     QString appImagePath = qgetenv("APPIMAGE");
747     QString locale = m_settings->selectedLangauge();
748 
749     qApp->removeTranslator(&translator);
750     qApp->removeTranslator(&translatorMusic);
751     qApp->removeTranslator(&qtTranslator);
752 
753     // save original
754     if (listWidgetsRetranslateUi.size()==0){
755         QList<QWidget*> l2 = this->findChildren<QWidget *>();
756         for (auto &w:l2){
757             QMap<QString,QString> m;
758             m["toolTip"]=w->toolTip();
759             m["whatsThis"]=w->whatsThis();
760             m["windowTitle"]=w->windowTitle();
761             m["statusTip"]=w->statusTip();
762             listWidgetsRetranslateUi[w]=m;
763         }
764 
765         QList<QAction*> l = this->findChildren<QAction *>();
766         for (auto &w:l){
767             QMap<QString,QString> m;
768             m["toolTip"]=w->toolTip();
769             m["whatsThis"]=w->whatsThis();
770             m["statusTip"]=w->statusTip();
771             m["text"]=w->text();
772             listActionsRetranslateUi[w]=m;
773         }
774     }
775 
776     QString translationsDir = QApplication::applicationDirPath() + "/translations/";
777 
778     QFile fileTestLocale(translationsDir);
779     if (!fileTestLocale.exists()){
780  #if defined (Q_OS_LINUX) || defined (Q_OS_UNIX)
781         translationsDir=Util::dataDir()+"/translations/";
782  #endif
783  #ifdef Q_OS_DARWIN
784         translationsDir=QApplication::applicationDirPath() + "/../Resources/translations/";
785  #endif
786     }
787     ppLogInfo("Translations loaded from '%s'",  qPrintable(translationsDir));
788 
789     // set translator for app
790     if (!translator.load(QSTR_APPNAME + QString("_") + locale , translationsDir))
791         translator.load(QSTR_APPNAME + QString("_") + locale, QApplication::applicationDirPath());
792     qApp->installTranslator(&translator);
793 
794     // set translator for music
795     if (!translatorMusic.load(QString("music_") + locale , translationsDir))
796        if (!translatorMusic.load(QString("music_") + locale, QApplication::applicationDirPath()  + "/translations/"))
797            translatorMusic.load(QString("music_") + locale, QApplication::applicationDirPath());
798     qApp->installTranslator(&translatorMusic);
799 
800     // set translator for default widget's text (for example: QMessageBox's buttons)
801 #ifdef __WIN32
802     qtTranslator.load("qt_"+locale, translationsDir);
803 #else
804     qtTranslator.load("qt_"+locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
805 #endif
806     qApp->installTranslator(&qtTranslator);
807 
808     // retranslate UI
809     QList<QWidget*> l2 = this->findChildren<QWidget *>();
810     for (auto &w:l2){
811         if (!w->toolTip().isEmpty()) w->setToolTip(tr(listWidgetsRetranslateUi[w]["toolTip"].toStdString().c_str()));
812         if (!w->whatsThis().isEmpty()) w->setWhatsThis(tr(listWidgetsRetranslateUi[w]["whatsThis"].toStdString().c_str()));
813         if (!w->windowTitle().isEmpty()) w->setWindowTitle(tr(listWidgetsRetranslateUi[w]["windowTitle"].toStdString().c_str()));
814         if (!w->statusTip().isEmpty()) w->setStatusTip(tr(listWidgetsRetranslateUi[w]["statusTip"].toStdString().c_str()));
815     }
816 
817     QList<QAction*> l = this->findChildren<QAction *>();
818     for (auto &w:l){
819         if (!w->toolTip().isEmpty()) w->setToolTip(tr(listActionsRetranslateUi[w]["toolTip"].toStdString().c_str()));
820         if (!w->whatsThis().isEmpty()) w->setWhatsThis(tr(listActionsRetranslateUi[w]["whatsThis"].toStdString().c_str()));
821         if (!w->statusTip().isEmpty()) w->setStatusTip(tr(listActionsRetranslateUi[w]["statusTip"].toStdString().c_str()));
822         if (!w->text().isEmpty()) w->setText(tr(listActionsRetranslateUi[w]["text"].toStdString().c_str()));
823     }
824 
825     m_sidePanel->updateTranslate();
826     m_topBar->updateTranslate();
827     m_settings->updateWarningMessages();
828     m_settings->updateTutorPage();
829 
830 #endif
831 }
832