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