1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2011-2017 - Daniel De Matteis
3 * Copyright (C) 2016-2019 - Brad Parker
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <QApplication>
18 #include <QAbstractEventDispatcher>
19 #include <QtWidgets>
20 #include <QtWidgets/QFileDialog>
21 #include <QtWidgets/QMessageBox>
22 #include <QtCore/QString>
23 #include <QDesktopWidget>
24 #include <QtGlobal>
25 #include <QCloseEvent>
26 #include <QResizeEvent>
27 #include <QStyle>
28 #include <QString>
29 #include <QTimer>
30 #include <QLabel>
31 #include <QFileDialog>
32 #include <QFileSystemModel>
33 #include <QListWidgetItem>
34 #include <QTableWidgetItem>
35 #include <QHash>
36 #include <QPushButton>
37 #include <QToolButton>
38 #include <QMenu>
39 #include <QDockWidget>
40 #include <QList>
41 #include <QInputDialog>
42 #include <QMimeData>
43 #include <QProgressDialog>
44 #include <QDragEnterEvent>
45 #include <QDropEvent>
46 #include <QtConcurrentRun>
47 #include <QtNetwork>
48
49 #include "ui_qt.h"
50 #include "qt/gridview.h"
51 #include "qt/ui_qt_load_core_window.h"
52 #include "qt/coreinfodialog.h"
53 #include "qt/playlistentrydialog.h"
54 #if defined(HAVE_MENU)
55 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
56 #include "qt/shaderparamsdialog.h"
57 #endif
58 #endif
59 #include "qt/coreoptionsdialog.h"
60 #include "qt/viewoptionsdialog.h"
61
62 #ifndef CXX_BUILD
63 extern "C" {
64 #endif
65
66 #include <file/file_path.h>
67 #include <file/archive_file.h>
68 #include <retro_timers.h>
69 #include <string/stdstring.h>
70 #include <retro_miscellaneous.h>
71
72 #ifdef Q_OS_UNIX
73 #include <locale.h>
74 #endif
75
76 #ifdef HAVE_OPENSSL
77 #include <openssl/ssl.h>
78 #include <openssl/opensslv.h>
79 #endif
80
81 #ifdef HAVE_CONFIG_H
82 #include "../../config.h"
83 #endif
84
85 #ifdef HAVE_MENU
86 #include "../../menu/menu_driver.h"
87 #endif
88
89 #include "../../core_info.h"
90 #include "../../command.h"
91 #include "../ui_companion_driver.h"
92 #include "../../configuration.h"
93 #include "../../frontend/frontend.h"
94 #include "../../frontend/frontend_driver.h"
95 #include "../../file_path_special.h"
96 #include "../../paths.h"
97 #include "../../retroarch.h"
98 #include "../../verbosity.h"
99 #include "../../version.h"
100 #include "../../msg_hash.h"
101 #include "../../tasks/task_content.h"
102 #include "../../tasks/tasks_internal.h"
103 #include "../../AUTHORS.h"
104 #ifdef HAVE_GIT_VERSION
105 #include "../../version_git.h"
106 #endif
107
108 #ifndef CXX_BUILD
109 }
110 #endif
111
112 #define INITIAL_WIDTH 1280
113 #define INITIAL_HEIGHT 720
114
115 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
116 #define GROUPED_DRAGGING QMainWindow::GroupedDragging
117 #else
118 #define GROUPED_DRAGGING static_cast<QMainWindow::DockOption>(0)
119 #endif
120
121 #define TIMER_MSEC 1000 /* periodic timer for gathering statistics */
122 #define STATUS_MSG_THROTTLE_MSEC 250
123
124 #define GENERIC_FOLDER_ICON "/xmb/dot-art/png/folder.png"
125 #define HIRAGANA_START 0x3041U
126 #define HIRAGANA_END 0x3096U
127 #define KATAKANA_START 0x30A1U
128 #define KATAKANA_END 0x30F6U
129 #define HIRA_KATA_OFFSET (KATAKANA_START - HIRAGANA_START)
130 #define DOCS_URL "http://docs.libretro.com/"
131
132 enum core_selection
133 {
134 CORE_SELECTION_CURRENT = 0,
135 CORE_SELECTION_PLAYLIST_SAVED,
136 CORE_SELECTION_PLAYLIST_DEFAULT,
137 CORE_SELECTION_ASK,
138 CORE_SELECTION_LOAD_CORE
139 };
140
141 static AppHandler *app_handler;
142 static ui_application_qt_t ui_application;
143
144 /* %1 is a placeholder for palette(highlight) or the equivalent chosen by the user */
145 static const QString qt_theme_default_stylesheet = QStringLiteral(R"(
146 QPushButton[flat="true"] {
147 min-height:20px;
148 min-width:80px;
149 padding:1px 3px 1px 3px;
150 background-color: transparent;
151 border: 1px solid #ddd;
152 }
153 ThumbnailWidget#thumbnailWidget, ThumbnailLabel#thumbnailGridLabel, QLabel#thumbnailQLabel {
154 background-color:#d4d4d4;
155 }
156 QLabel#dropIndicator {
157 font-size: 9pt;
158 color: darkgrey;
159 border: 2px dashed lightgrey;
160 border-radius: 5px;
161 margin: 20px;
162 }
163 ThumbnailWidget#thumbnailWidgetSelected {
164 background-color:#d4d4d4;
165 border:3px solid %1;
166 }
167 QFrame#playlistWidget, QFrame#browserWidget, QFrame#logWidget {
168 padding: 8px;
169 }
170 QListWidget {
171 icon-size: 32px;
172 }
173 /* color of the icons on the settings dialog */
174 /* QLabel#iconColor {
175 color: black;
176 } */
177 )");
178
179 static const QString qt_theme_dark_stylesheet = QStringLiteral(R"(
180 QWidget {
181 color:white;
182 background-color:rgb(53,53,53);
183 selection-background-color:%1;
184 }
185 QWidget:disabled {
186 color:rgb(127,127,127);
187 }
188 QFrame#playlistWidget, QFrame#browserWidget, QStackedWidget#centralWidget, QFrame#logWidget {
189 padding: 8px;
190 background-color:rgb(66,66,66);
191 border-top:1px solid rgba(175,175,175,50%);
192 border-left:1px solid rgba(125,125,125,50%);
193 border-right:1px solid rgba(125,125,125,50%);
194 border-bottom:1px solid rgba(25,25,25,75%);
195 }
196 QListWidget {
197 icon-size: 32px;
198 }
199 QLabel#dropIndicator {
200 font-size: 9pt;
201 color: #575757;
202 border: 2px dashed #575757;
203 border-radius: 5px;
204 margin: 20px;
205 }
206 QTextEdit, LogTextEdit {
207 background-color:rgb(25,25,25);
208 }
209 QSpinBox, QDoubleSpinBox, QCheckBox, QRadioButton {
210 background-color:rgb(25,25,25);
211 }
212 QCheckBox:checked, QCheckBox:unchecked, QRadioButton:checked, QRadioButton:unchecked {
213 background-color:transparent;
214 }
215 /* Groupboxes for the settings window, can be restricted later with ViewOptionsDialog QGroupBox */
216 QGroupBox {
217 background-color:rgba(80,80,80,50%);
218 margin-top:27px;
219 border:1px solid rgba(25,25,25,127);
220 border-top-left-radius:0px;
221 border-top-right-radius:4px;
222 }
223 QGroupBox::title {
224 min-height:28px;
225 subcontrol-origin:margin;
226 subcontrol-position:left top;
227 padding:4px 6px 5px 6px;
228 margin-left:0px;
229 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgb(65,65,65),stop: 0.4 rgb(70,70,70),stop:1 rgb(90,90,90));
230 border:1px solid rgba(25,25,25,127);
231 border-bottom:1px solid rgb(65,65,65);
232 border-top-left-radius:4px;
233 border-top-right-radius:4px;
234 }
235 QGroupBox::indicator:checked {
236 background-color:%1;
237 border:4px solid rgb(45,45,45);
238 }
239 QGroupBox::indicator:unchecked {
240 background-color:rgba(25,25,25,50%);
241 }
242 QGroupBox::indicator {
243 width:16px;
244 height:16px;
245 }
246 QWidget#shaderParamsWidget {
247 background-color:rgb(25,25,25);
248 }
249 QDialog#shaderParamsDialog QGroupBox {
250 background-color:rgb(53,53,53);
251 border-top-left-radius:0px;
252 }
253 QDialog#shaderParamsDialog QGroupBox::title {
254 margin-left:0px;
255 min-height:28px;
256 padding:4px 10px;
257 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgb(53,53,53),stop:1 rgba(125,125,125,127));
258 border:1px solid rgba(25,25,25,75);
259 border-top:1px solid rgba(175,175,175,50%);
260 border-bottom:none transparent;
261 }
262 QToolTip {
263 color:white;
264 background-color:rgb(53,53,53);
265 border:1px solid rgb(80,80,80);
266 border-radius:4px;
267 }
268 QMenuBar {
269 background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
270 border-bottom:2px solid rgba(25,25,25,75);
271 }
272 QMenuBar::item {
273 spacing:2px;
274 padding:3px 4px;
275 background-color:transparent;
276 }
277 QMenuBar::item:selected {
278 background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(106,106,106,255),stop:1 rgba(106,106,106,75));
279 border:1px solid %1;
280 }
281 QMenuBar::item:pressed {
282 background-color:%1;
283 border-left:1px solid rgba(25,25,25,127);
284 border-right:1px solid rgba(25,25,25,127);
285 }
286 QMenu {
287 background-color:rgb(45,45,45);
288 border:1px solid palette(shadow);
289 }
290 QMenu::item {
291 padding:3px 25px 3px 25px;
292 border:1px solid transparent;
293 }
294 QMenu::item:disabled {
295 color:rgb(127,127,127);
296 }
297 QMenu::item:selected {
298 border-color:rgba(200,200,200,127);
299 background-color:%1;
300 }
301 QMenu::icon:checked {
302 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
303 border:1px solid %1;
304 border-radius:2px;
305 }
306 QMenu::separator {
307 height:1px;
308 background-color:rgb(100,100,100);
309 margin-left:5px;
310 margin-right:5px;
311 }
312 QMenu::indicator {
313 width:18px;
314 height:18px;
315 }
316 QToolBar::top {
317 background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
318 border-bottom:3px solid qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
319 }
320 QToolBar::bottom {
321 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
322 border-top:3px solid qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
323 }
324 QToolBar::left {
325 background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
326 border-right:3px solid qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
327 }
328 QToolBar::right {
329 background-color:qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
330 border-left:3px solid qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
331 }
332 QMainWindow {
333 background-color:rgb(53,53,53);
334 }
335 QMainWindow::separator {
336 width:6px;
337 height:5px;
338 padding:2px;
339 background-color:rgba(25,25,25,50%);
340 }
341 QLineEdit {
342 color:white;
343 background-color:rgb(25,25,25);
344 }
345 QLineEdit::focus {
346 border:1px solid %1;
347 border-radius:3px;
348 color:white;
349 background-color:rgb(25,25,25);
350 }
351 QSplitter::handle:horizontal {
352 width:10px;
353 }
354 QSplitter::handle:vertical {
355 height:10px;
356 }
357 QMainWindow::separator:hover, QSplitter::handle:hover {
358 }
359 QDockWidget {
360 font-family:"Segoe UI";
361 font-size:9pt;
362 }
363 QDockWidget::title {
364 padding:3px 4px;
365 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,175),stop:1 rgba(53,53,53,75));
366 border:1px solid rgba(25,25,25,75);
367 border-top:1px solid rgba(175,175,175,50%);
368 border-bottom:1px solid rgba(25,25,25,127);
369 }
370 QDockWidget::close-button, QDockWidget::float-button {
371 subcontrol-position:top right;
372 subcontrol-origin:margin;
373 position:absolute;
374 top:3px;
375 bottom:0px;
376 width:20px;
377 height:20px;
378 }
379 QDockWidget::close-button:hover, QDockWidget::float-button:hover {
380 border:1px solid %1;
381 border-radius:4px;
382 }
383 QDockWidget::close-button {
384 right:3px;
385 }
386 QDockWidget::float-button {
387 right:25px;
388 }
389 QTabWidget::pane {
390 background-color:rgba(66,66,66,50%);
391 }
392 QTabWidget::tab-bar {
393 }
394 QTabBar {
395 background-color:transparent;
396 qproperty-drawBase:0;
397 border-bottom:1px solid rgba(25,25,25,50%);
398 }
399 QTabBar::tab {
400 padding:4px 6px;
401 background-color:rgba(25,25,25,127);
402 border:1px solid rgba(25,25,25,75);
403 }
404 QTabBar::tab:selected {
405 background-color:rgb(66,66,66);
406 border-bottom-color:rgba(66,66,66,75%);
407 }
408 QTabBar::tab:!selected {
409 color:rgb(175,175,175);
410 }
411 QComboBox {
412 min-height:20px;
413 padding:1px 6px 1px 6px;
414 }
415 QComboBox::focus {
416 background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(100,100,100,25));
417 border:1px solid %1;
418 border-radius:4px;
419 }
420 QComboBox::hover {
421 background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(127,127,127,50));
422 border:1px solid %1;
423 border-radius:4px;
424 }
425 QComboBox::drop-down {
426 background-color:transparent;
427 width:0px;
428 }
429 QComboBox::selected:on, QComboBox::selected:off {
430 background-color:%1;
431 }
432 QTabBar::tab:hover {
433 color:white;
434 background-color:%1;
435 }
436 QComboBox::separator {
437 background-color:rgb(100,100,100);
438 height:1px;
439 margin-left:4px;
440 margin-right:4px;
441 }
442 QCheckBox::indicator {
443 width:18px;
444 height:18px;
445 }
446 QPushButton {
447 min-height:20px;
448 min-width:80px;
449 padding:1px 3px 1px 3px;
450 outline:none;
451 }
452 QPushButton::disabled, QToolButton::disabled {
453 color:grey;
454 background-color:rgb(25,25,25);
455 }
456 QPushButton::focus, QToolButton::focus {
457 background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,50), stop: 1 rgba(100,100,100,25));
458 border:1px solid %1;
459 border-radius:4px;
460 }
461 QPushButton::hover, QToolButton::hover {
462 background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,75), stop: 1 rgba(100,100,100,50));
463 border:1px solid %1;
464 border-radius:4px;
465 }
466 QPushButton::pressed, QToolButton::pressed {
467 background-color:transparent;
468 border:1px solid %1;
469 border-radius:4px;
470 }
471 QPushButton[flat="true"] {
472 background-color:transparent;
473 }
474 QPushButton[flat="true"]::menu-indicator {
475 position:relative;
476 bottom:4px;
477 right:4px;
478 }
479 QRadioButton::indicator {
480 width:18px;
481 height:18px;
482 }
483 QListWidget::item:selected, QTreeView::item:selected, QTableView::item:selected {
484 color:white;
485 background-color:%1;
486 }
487 QTreeView {
488 background-color:rgb(25,25,25);
489 selection-background-color:%1;
490 }
491 QTreeView::branch:selected {
492 background-color:%1;
493 }
494 QTreeView::item:selected:disabled, QTableView::item:selected:disabled {
495 background-color:rgb(80,80,80);
496 }
497 QTreeView::branch:open, QTreeView::branch:closed {
498 background-color:solid;
499 }
500 QTableView, QListWidget {
501 background-color:rgb(25,25,25);
502 }
503 QTreeView QHeaderView::section, QTableView QHeaderView::section {
504 /*height:24px;*/
505 background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
506 border-style:none;
507 border-bottom:1px solid rgb(65,65,65);
508 padding-left:5px;
509 padding-right:5px;
510 }
511 QTableView {
512 background-color:rgb(25,25,25);
513 alternate-background-color:rgb(40,40,40);
514 }
515 QScrollBar:vertical, QScrollBar:horizontal {
516 background-color:rgb(35,35,35);
517 }
518 QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
519 background-color:rgb(65,65,65);
520 border-right:1px solid rgba(175,175,175,50%);
521 border-top:1px solid rgba(175,175,175,50%);
522 border-bottom:1px solid rgba(25,25,25,75);
523 border-radius:2px;
524 }
525 QScrollBar::handle:horizontal:hover, QScrollBar::handle:vertical:hover {
526 border:1px solid %1;
527 background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgba(255,255,255,75), stop: 1 rgba(127,127,127,75));
528 }
529 QScrollBar:vertical {
530 border-top-right-radius:2px;
531 border-bottom-right-radius:2px;
532 width:16px;
533 margin:0px;
534 }
535 QScrollBar::handle:vertical {
536 min-height:20px;
537 margin:2px 4px 2px 4px;
538 }
539 QScrollBar::add-line:vertical {
540 background:none;
541 height:0px;
542 subcontrol-position:right;
543 subcontrol-origin:margin;
544 }
545 QScrollBar::sub-line:vertical {
546 background:none;
547 height:0px;
548 subcontrol-position:left;
549 subcontrol-origin:margin;
550 }
551 QScrollBar:horizontal {
552 height:16px;
553 margin:0px;
554 }
555 QScrollBar::handle:horizontal {
556 min-width:20px;
557 margin:4px 2px 4px 2px;
558 }
559 QScrollBar::add-line:horizontal {
560 background:none;
561 width:0px;
562 subcontrol-position:bottom;
563 subcontrol-origin:margin;
564 }
565 QScrollBar::sub-line:horizontal {
566 background:none;
567 width:0px;
568 subcontrol-position:top;
569 subcontrol-origin:margin;
570 }
571 QSlider {
572 background:transparent;
573 }
574 QSlider::sub-page {
575 background:%1;
576 }
577 QSlider::groove:vertical {
578 width:3px;
579 background:rgb(25,25,25);
580 }
581 QSlider::handle:vertical {
582 background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(175,175,175), stop: 1 rgb(75,75,75));
583 border:1px solid rgb(35,35,35);
584 border-radius:2px;
585 height:16px;
586 margin:0 -4px;
587 }
588 QSlider::handle:vertical:hover {
589 background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(200,200,200), stop: 1 rgba(100,100,100));
590 border:1px solid %1;
591 border-radius:2px;
592 height:16px;
593 margin:0 -4px;
594 }
595 QSlider::groove:horizontal {
596 height:3px;
597 background:rgb(25,25,25);
598 }
599 QSlider::handle:horizontal {
600 background:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(175,175,175), stop: 1 rgb(75,75,75));
601 border:1px solid rgb(35,35,35);
602 border-radius:2px;
603 width:16px;
604 margin:-4px 0;
605 }
606 QSlider::handle:horizontal:hover {
607 background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 rgb(200,200,200), stop: 1 rgba(100,100,100));
608 border:1px solid %1;
609 border-radius:2px;
610 width:16px;
611 margin:-4px 0;
612 }
613 QStatusBar {
614 color:white;
615 background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));
616 }
617 QStatusBar QLabel {
618 background-color:transparent;
619 }
620 QLabel {
621 background-color:transparent;
622 }
623 QSizeGrip {
624 background-color:solid;
625 }
626 GridView::item {
627 background-color:rgb(40,40,40);
628 }
629 GridView::item:selected {
630 border:3px solid %1;
631 }
632 GridView {
633 background-color:rgb(25,25,25);
634 selection-color: white;
635 qproperty-layout: "fixed";
636 }
637 GridItem {
638 qproperty-thumbnailvalign: "center";
639 }
640 QLabel#itemsCountLabel {
641 padding-left: 5px;
642 }
643 )");
644
645 /* ARGB 16x16 */
646 static const unsigned retroarch_qt_icon_data[] = {
647 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
648 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
649 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
650 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
651 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
652 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
653 0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
654 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
655 0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000,
656 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
657 0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
658 0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000,
659 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
660 0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,
661 0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000,
662 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
663 };
664
665 static unsigned char invader_png[] = {
666 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
667 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x44,
668 0x08, 0x06, 0x00, 0x00, 0x00, 0xac, 0xf5, 0x3a, 0x40, 0x00, 0x00, 0x00,
669 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
670 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67,
671 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00, 0x00,
672 0x0f, 0x4a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x5d, 0x79, 0x50,
673 0x95, 0xd7, 0x15, 0xbf, 0x6f, 0xe3, 0xf1, 0x58, 0xa2, 0x28, 0x8a, 0xec,
674 0x82, 0x58, 0x8c, 0xc6, 0x25, 0xa8, 0xc4, 0xa5, 0x21, 0x1a, 0x97, 0xd6,
675 0x50, 0x26, 0xd5, 0x6a, 0x3b, 0x6d, 0x4d, 0xeb, 0x42, 0x27, 0x13, 0xd3,
676 0x24, 0xda, 0x31, 0xa6, 0x63, 0xa7, 0xda, 0x3f, 0xda, 0xce, 0x44, 0x27,
677 0xc6, 0x24, 0xd3, 0x4c, 0x26, 0xea, 0x38, 0x4d, 0xc6, 0x4e, 0x4d, 0x62,
678 0x27, 0x13, 0x71, 0xa3, 0x46, 0x31, 0x46, 0x4c, 0x80, 0x04, 0x0b, 0xc4,
679 0x22, 0x8a, 0x82, 0x86, 0x25, 0x8a, 0x02, 0x0a, 0xc8, 0xe3, 0x6d, 0x3d,
680 0x3f, 0x38, 0xcf, 0x12, 0xf3, 0xbe, 0xe5, 0xc1, 0x7b, 0xbc, 0x4f, 0x7c,
681 0x67, 0xe6, 0x37, 0xe3, 0x72, 0xdf, 0xf7, 0xdd, 0xef, 0xfc, 0xee, 0x3d,
682 0xf7, 0x9c, 0x73, 0x37, 0x9d, 0xcb, 0xe5, 0x12, 0x1a, 0x12, 0x3d, 0x61,
683 0x24, 0x21, 0x96, 0x60, 0x50, 0x28, 0xdb, 0x45, 0xa8, 0x23, 0x5c, 0x0f,
684 0x40, 0x3d, 0x43, 0x08, 0xc9, 0x84, 0x07, 0x08, 0x3a, 0x85, 0xb2, 0x6d,
685 0x84, 0xcb, 0x84, 0x0e, 0xad, 0x28, 0xd9, 0x28, 0xb4, 0x25, 0xe1, 0x84,
686 0x25, 0x84, 0xe5, 0x84, 0x30, 0x15, 0xca, 0x2c, 0x20, 0xec, 0x26, 0x54,
687 0x0f, 0x30, 0xe1, 0x8b, 0x08, 0x2b, 0x98, 0x78, 0xbd, 0x4c, 0x59, 0xf4,
688 0xa8, 0x72, 0xc2, 0x36, 0x42, 0x19, 0xc1, 0x19, 0x24, 0xfd, 0xbb, 0x82,
689 0xde, 0xd0, 0xcc, 0xbd, 0x3d, 0xa5, 0xa9, 0xa9, 0x49, 0x77, 0xfb, 0xf6,
690 0x6d, 0x8f, 0x05, 0x87, 0x0e, 0x1d, 0xea, 0x8a, 0x8c, 0x8c, 0x4c, 0xe5,
691 0x86, 0xf2, 0x0a, 0xe1, 0xeb, 0x01, 0xd2, 0xd7, 0x42, 0xc2, 0xf3, 0x84,
692 0x99, 0xf5, 0xf5, 0xf5, 0xa1, 0x0e, 0x87, 0xc3, 0x63, 0xc1, 0x51, 0xa3,
693 0x46, 0x09, 0x93, 0xc9, 0xd4, 0x4e, 0x7f, 0x2c, 0x21, 0xdc, 0xd0, 0x0a,
694 0xe1, 0x3d, 0x4d, 0x91, 0xcc, 0xbb, 0xc6, 0x30, 0x9c, 0xb0, 0x8d, 0x70,
695 0x35, 0x3f, 0x3f, 0xdf, 0x49, 0x8a, 0x73, 0x71, 0x8f, 0xf9, 0x16, 0xe2,
696 0xe3, 0xe3, 0x5d, 0xe7, 0xcf, 0x9f, 0x77, 0x50, 0xb9, 0xcb, 0x84, 0xdf,
697 0x13, 0x22, 0xfc, 0x5c, 0x2f, 0x1d, 0x61, 0x21, 0xe1, 0xa8, 0xd3, 0xe9,
698 0x6c, 0xdf, 0xb8, 0x71, 0xa3, 0xc7, 0x7a, 0x01, 0x4b, 0x97, 0x2e, 0x75,
699 0x51, 0x19, 0x2b, 0x95, 0x3d, 0x41, 0x98, 0xc9, 0xbf, 0xd5, 0x8c, 0x8e,
700 0xb5, 0x48, 0x3a, 0x30, 0x9e, 0xf0, 0x4f, 0x42, 0xeb, 0x8e, 0x1d, 0x3b,
701 0x9c, 0x52, 0xca, 0x9d, 0x32, 0x65, 0x8a, 0xab, 0xb3, 0xb3, 0xd3, 0x4e,
702 0xe5, 0xbe, 0x22, 0x2c, 0x27, 0x18, 0xfd, 0x58, 0xa7, 0x34, 0xae, 0x53,
703 0xfb, 0x9b, 0x6f, 0xbe, 0x29, 0x59, 0xa7, 0xd1, 0xa3, 0x47, 0xbb, 0x9a,
704 0x9b, 0x9b, 0x51, 0xa7, 0xb3, 0x84, 0x95, 0x04, 0x83, 0xd6, 0xf4, 0xab,
705 0x55, 0xd2, 0xa1, 0xa8, 0x2c, 0xc2, 0x01, 0xc2, 0xed, 0x67, 0x9f, 0x7d,
706 0x56, 0xb2, 0x57, 0xcd, 0x9f, 0x3f, 0xdf, 0xd5, 0xd6, 0xd6, 0xd6, 0xc5,
707 0xbd, 0xea, 0x07, 0x7e, 0x22, 0x3e, 0x81, 0xad, 0x4f, 0xd3, 0xc1, 0x83,
708 0x07, 0x9d, 0x61, 0x61, 0x61, 0x1e, 0xeb, 0x12, 0x17, 0x17, 0xe7, 0xaa,
709 0xae, 0xae, 0x76, 0x52, 0xb9, 0x1a, 0xc2, 0x1f, 0xd9, 0x6a, 0x89, 0x20,
710 0xe9, 0xea, 0xa1, 0x27, 0x2c, 0x25, 0x94, 0x50, 0x6f, 0xee, 0x4a, 0x4f,
711 0x4f, 0x97, 0x24, 0x3e, 0x37, 0x37, 0x97, 0x8a, 0xb9, 0xda, 0x08, 0x1f,
712 0x11, 0xbe, 0xef, 0xe3, 0x7a, 0x0c, 0x23, 0xfc, 0x01, 0xc3, 0x48, 0x71,
713 0x71, 0xb1, 0xe4, 0x70, 0x03, 0x1c, 0x38, 0x70, 0x00, 0x84, 0xdf, 0x20,
714 0xfc, 0x8d, 0x10, 0xa7, 0x55, 0xdd, 0x6a, 0x99, 0x74, 0x20, 0x92, 0xf0,
715 0x5b, 0x42, 0x55, 0x49, 0x49, 0x89, 0x23, 0x22, 0x22, 0xc2, 0xa3, 0xb2,
716 0x75, 0x3a, 0x9d, 0x6b, 0xf7, 0xee, 0xdd, 0x50, 0xf8, 0x4d, 0xc2, 0x5e,
717 0xc2, 0x18, 0x1f, 0xbd, 0xdf, 0x42, 0x78, 0x1e, 0xa6, 0xba, 0xb1, 0xb1,
718 0xd1, 0x96, 0x9a, 0x9a, 0x2a, 0x49, 0x38, 0xac, 0x11, 0xbf, 0xff, 0x1f,
719 0x84, 0x87, 0xb5, 0xac, 0x57, 0xad, 0x93, 0x0e, 0x3c, 0xc0, 0x8e, 0xda,
720 0xd5, 0xa2, 0xa2, 0x22, 0xd7, 0x90, 0x21, 0x43, 0x3c, 0x2a, 0x1d, 0x3d,
721 0xf0, 0xdd, 0x77, 0xdf, 0x85, 0xe2, 0x9b, 0x09, 0xaf, 0x13, 0x52, 0xfb,
722 0xf9, 0xde, 0x10, 0xc2, 0x2f, 0x08, 0xe7, 0x5a, 0x5b, 0x5b, 0xed, 0x99,
723 0x99, 0x99, 0x92, 0x84, 0x2f, 0x5b, 0xb6, 0xcc, 0xd5, 0xd5, 0xd5, 0x65,
724 0xe3, 0xe1, 0x28, 0x53, 0x6b, 0x8e, 0xdb, 0xbd, 0x48, 0xba, 0x60, 0x02,
725 0x3f, 0x21, 0x38, 0x76, 0xee, 0xdc, 0x29, 0xa9, 0x7c, 0xf4, 0x78, 0x78,
726 0xfc, 0x54, 0xae, 0x9e, 0xb0, 0x95, 0x10, 0xdb, 0xc7, 0xf7, 0xc1, 0x2f,
727 0xc8, 0x26, 0x1c, 0xb3, 0x5a, 0xad, 0x5d, 0xb3, 0x67, 0xcf, 0x96, 0x7c,
728 0xe7, 0xb4, 0x69, 0xd3, 0x5c, 0x36, 0x9b, 0xcd, 0xdd, 0xd8, 0x9e, 0xd1,
729 0x3a, 0xe1, 0x80, 0x4e, 0x45, 0x46, 0x0e, 0xb1, 0x69, 0x34, 0x61, 0x98,
730 0x42, 0x96, 0x0c, 0x31, 0x69, 0x23, 0xe1, 0x36, 0x2b, 0x44, 0x4a, 0x90,
731 0xc5, 0x1a, 0x45, 0x30, 0x7b, 0x11, 0x59, 0xc6, 0x11, 0xfe, 0x4c, 0xc8,
732 0xc0, 0x58, 0x4f, 0x3d, 0x4b, 0xec, 0xdb, 0xb7, 0x4f, 0x32, 0x3e, 0xfe,
733 0xec, 0xb3, 0xcf, 0x9c, 0xc9, 0xc9, 0xc9, 0xc8, 0x82, 0xed, 0x21, 0xec,
734 0xf7, 0x32, 0x1b, 0x86, 0x6f, 0x9c, 0xcc, 0xc9, 0x97, 0x4c, 0xf2, 0x17,
735 0xc2, 0x76, 0xed, 0xda, 0xe5, 0xb1, 0x20, 0x59, 0x1d, 0xf1, 0xf9, 0xe7,
736 0x9f, 0x0b, 0xf2, 0x37, 0xf0, 0xd7, 0x5b, 0x9c, 0x84, 0xd9, 0xcf, 0xd9,
737 0x42, 0x35, 0x82, 0x28, 0xa0, 0x95, 0xf0, 0x0d, 0xc1, 0xa6, 0xc0, 0x41,
738 0x0c, 0x21, 0x4a, 0x21, 0x03, 0xe8, 0xe6, 0x40, 0xf6, 0x7b, 0xd5, 0x90,
739 0x8e, 0x04, 0xc8, 0x6a, 0xc2, 0x0c, 0x82, 0x49, 0xa6, 0x5c, 0x0d, 0xe1,
740 0x2d, 0x42, 0x11, 0xc1, 0x2e, 0x51, 0x26, 0x94, 0xf0, 0x24, 0xe1, 0xe7,
741 0xdc, 0x88, 0xd4, 0x88, 0x8e, 0x1b, 0xca, 0x18, 0x4e, 0xc4, 0x88, 0x96,
742 0x96, 0x16, 0x41, 0xe1, 0x9a, 0xa8, 0xad, 0xad, 0xf5, 0xf8, 0x83, 0x47,
743 0x1e, 0x79, 0x44, 0x7c, 0xfa, 0xe9, 0xa7, 0x4e, 0xa3, 0xd1, 0x88, 0xa4,
744 0xc8, 0x25, 0x42, 0xa7, 0x97, 0x09, 0x18, 0xa4, 0x81, 0xe3, 0xc8, 0x4f,
745 0x30, 0xad, 0x5a, 0xb5, 0x4a, 0x52, 0xc9, 0x68, 0x0c, 0xf4, 0xff, 0xee,
746 0xbf, 0x3a, 0x58, 0xe1, 0x57, 0x14, 0x08, 0xbc, 0x9b, 0xf4, 0x33, 0x84,
747 0xb7, 0x09, 0x67, 0x65, 0xca, 0xa1, 0x3e, 0xcf, 0x10, 0xb2, 0x14, 0x32,
748 0x80, 0x48, 0x50, 0xbd, 0x43, 0x38, 0x26, 0xd7, 0xf0, 0x8c, 0x2a, 0x5a,
749 0x3d, 0xc8, 0x5e, 0x56, 0x51, 0x51, 0x91, 0x52, 0x53, 0x53, 0xe3, 0x51,
750 0x01, 0xd4, 0xab, 0xc4, 0xc4, 0x89, 0x13, 0x13, 0xe8, 0x8f, 0x87, 0x99,
751 0x74, 0x29, 0x49, 0x02, 0xe1, 0xed, 0xed, 0xed, 0x0b, 0x8f, 0x1f, 0x3f,
752 0x1e, 0xe2, 0x05, 0x11, 0xba, 0xbb, 0x3f, 0x76, 0xed, 0xda, 0xb5, 0xe2,
753 0xc5, 0x17, 0x5f, 0x14, 0x76, 0xfb, 0x77, 0xdb, 0x17, 0x7a, 0xdf, 0xe2,
754 0xc5, 0x8b, 0xf5, 0x4f, 0x3f, 0xfd, 0xb4, 0xdb, 0x42, 0xb9, 0xbc, 0x7c,
755 0x97, 0xee, 0xda, 0xb5, 0x6b, 0xba, 0x17, 0x5e, 0x78, 0x41, 0xb2, 0x50,
756 0x46, 0x46, 0x86, 0x18, 0x31, 0x62, 0x84, 0xc8, 0xcb, 0xcb, 0xeb, 0xad,
757 0xab, 0x38, 0x26, 0x48, 0xd5, 0xfb, 0x62, 0x62, 0x62, 0xc4, 0xf4, 0xe9,
758 0xd3, 0x53, 0x39, 0x8d, 0x0c, 0x58, 0x25, 0x8a, 0x5a, 0x08, 0x53, 0x6e,
759 0xdd, 0xba, 0x35, 0xeb, 0xc4, 0x89, 0x13, 0x92, 0xa4, 0x2f, 0x58, 0xb0,
760 0xa0, 0xdd, 0x6c, 0x36, 0x83, 0xec, 0xaf, 0xb8, 0xf1, 0xf5, 0x29, 0x23,
761 0x07, 0xef, 0xf9, 0x2f, 0x84, 0x96, 0xac, 0xac, 0x2c, 0xc9, 0x71, 0x6d,
762 0xcb, 0x96, 0x2d, 0x18, 0x47, 0xff, 0x43, 0x98, 0xab, 0xf0, 0xbc, 0x1f,
763 0x11, 0x2a, 0xc9, 0x34, 0x4b, 0x26, 0x37, 0xee, 0x27, 0xa4, 0xa4, 0xa4,
764 0xc0, 0x17, 0xe8, 0x20, 0xec, 0x24, 0x8c, 0x90, 0xd1, 0x5b, 0x14, 0xe1,
765 0xed, 0x9b, 0x37, 0x6f, 0x5a, 0xf5, 0x7a, 0xbd, 0xe4, 0xf3, 0xc8, 0xba,
766 0x21, 0x43, 0x59, 0xc2, 0x59, 0x40, 0x49, 0x1e, 0xf4, 0x0a, 0x8d, 0x11,
767 0xbd, 0x64, 0x1c, 0xb5, 0x30, 0x0b, 0x8d, 0x93, 0x92, 0x85, 0x66, 0xcc,
768 0x98, 0xe1, 0xe2, 0x96, 0x55, 0x2b, 0xf3, 0x2c, 0x98, 0xf6, 0x71, 0x84,
769 0xe1, 0x07, 0x0f, 0x1e, 0xd4, 0x89, 0xa0, 0x88, 0x4b, 0x97, 0x2e, 0x61,
770 0x88, 0x82, 0xc5, 0x4b, 0x63, 0x3f, 0x47, 0x4a, 0xe0, 0x2f, 0x9c, 0x8f,
771 0x8c, 0x8c, 0xb4, 0xce, 0x9a, 0x35, 0x4b, 0xb2, 0x10, 0x71, 0xa4, 0xe7,
772 0xe7, 0x8c, 0x55, 0x9a, 0xca, 0x94, 0x93, 0xd1, 0xa8, 0x10, 0xb5, 0x20,
773 0x23, 0x85, 0x24, 0x1e, 0x0b, 0x50, 0x45, 0x30, 0x86, 0xe2, 0x3f, 0xff,
774 0xcb, 0x0e, 0x89, 0x94, 0xc0, 0x09, 0x99, 0xe4, 0x70, 0x38, 0x22, 0x3e,
775 0xfc, 0xf0, 0xc3, 0x20, 0xe3, 0x2c, 0x05, 0x05, 0x05, 0x06, 0x9e, 0xad,
776 0xfb, 0x9e, 0x4c, 0x31, 0x8c, 0x61, 0x15, 0x98, 0x8c, 0x82, 0x13, 0x2b,
777 0x25, 0x64, 0xfa, 0xbb, 0xe7, 0xa2, 0x08, 0x13, 0xb9, 0x93, 0xf5, 0x89,
778 0x74, 0xb4, 0x98, 0x91, 0xdc, 0x82, 0x3c, 0xca, 0xdc, 0xb9, 0x73, 0x45,
779 0x48, 0x48, 0x08, 0xbc, 0xc6, 0x73, 0x0a, 0x5e, 0x23, 0xbc, 0xcf, 0xd4,
780 0xb3, 0x67, 0xcf, 0x9a, 0xae, 0x5f, 0xbf, 0x1e, 0x64, 0xfb, 0xff, 0xbd,
781 0xd3, 0x4d, 0x54, 0xba, 0x42, 0x74, 0x04, 0x87, 0xb4, 0xf1, 0x89, 0x27,
782 0x9e, 0x70, 0xca, 0x3d, 0x8b, 0xcc, 0xb7, 0x99, 0x9d, 0xde, 0xc8, 0xbe,
783 0x90, 0x6e, 0xe1, 0x8a, 0x44, 0xc2, 0x31, 0x92, 0x92, 0xec, 0xec, 0x6c,
784 0xc1, 0xd3, 0xa1, 0xe7, 0x15, 0x1c, 0x18, 0x98, 0xb0, 0x78, 0xaa, 0x98,
785 0x21, 0x48, 0xf5, 0xb7, 0x9d, 0x4e, 0x5e, 0x3b, 0x80, 0x9e, 0x1e, 0x21,
786 0x53, 0xb4, 0x09, 0xc3, 0x67, 0x5a, 0x5a, 0x9a, 0x23, 0x31, 0x31, 0xd1,
787 0x63, 0x01, 0x72, 0x3e, 0x45, 0x59, 0x59, 0x99, 0xdb, 0x72, 0x8c, 0xea,
788 0x0b, 0xe9, 0x68, 0x29, 0x48, 0x67, 0x9a, 0xe5, 0x48, 0xe7, 0x96, 0x57,
789 0x2f, 0xeb, 0x2d, 0xf6, 0xb4, 0x60, 0x7c, 0xd4, 0x50, 0x39, 0xdf, 0xe0,
790 0x7e, 0x14, 0x22, 0x49, 0x90, 0x83, 0x86, 0x28, 0x2a, 0x45, 0x21, 0x8c,
791 0xbd, 0xc9, 0x1d, 0xcb, 0x8a, 0x90, 0x54, 0x4a, 0xd8, 0x5f, 0x8a, 0xe1,
792 0xa1, 0xd9, 0x6b, 0xd2, 0xd1, 0x52, 0x92, 0x8b, 0x8b, 0x8b, 0x0d, 0x88,
793 0x8b, 0x3d, 0xc9, 0x98, 0x31, 0x63, 0x44, 0x42, 0x42, 0x82, 0x9d, 0x4d,
794 0xcf, 0x0d, 0x99, 0x67, 0x45, 0x30, 0xe9, 0x61, 0x72, 0x0d, 0xe8, 0x7e,
795 0x14, 0x2c, 0xc2, 0xc8, 0xcf, 0xcf, 0xd7, 0x73, 0xb8, 0x97, 0x28, 0x53,
796 0xb4, 0x8b, 0x87, 0xd0, 0x56, 0x72, 0x9c, 0xe5, 0x48, 0x77, 0x27, 0xc0,
797 0xc6, 0x49, 0x85, 0xe4, 0x46, 0x05, 0x27, 0x2e, 0xe6, 0xc0, 0x81, 0x03,
798 0x3a, 0x19, 0xaf, 0xdd, 0xbd, 0xda, 0xa5, 0x4a, 0xf4, 0x2c, 0x5f, 0x92,
799 0x92, 0xe1, 0xbc, 0x12, 0xc6, 0x48, 0x63, 0xba, 0xac, 0x12, 0x28, 0xd6,
800 0x14, 0x7b, 0xf7, 0xee, 0x15, 0x3c, 0x54, 0x38, 0x65, 0x86, 0x0c, 0x28,
801 0x4a, 0xdf, 0xdc, 0xdc, 0xdc, 0xdd, 0xf8, 0x06, 0x4a, 0x96, 0x2c, 0x59,
802 0x22, 0x76, 0xee, 0xdc, 0xa9, 0xba, 0x7e, 0xc7, 0x8e, 0x1d, 0x13, 0x4b,
803 0x97, 0x2e, 0x95, 0x7d, 0x26, 0xe9, 0x18, 0x65, 0xa2, 0xd8, 0x87, 0xfa,
804 0x44, 0xa6, 0x28, 0x7a, 0xfa, 0x37, 0xa4, 0xf7, 0x58, 0xa9, 0x0e, 0x5b,
805 0x58, 0x58, 0x28, 0x28, 0xda, 0x32, 0x93, 0x83, 0x3d, 0x96, 0xad, 0x75,
806 0xb3, 0x5a, 0xd2, 0x8d, 0x3c, 0x9e, 0x3f, 0xc0, 0x2d, 0x47, 0x8e, 0xf4,
807 0x16, 0x26, 0xdd, 0x2e, 0x53, 0x59, 0xb4, 0xe0, 0xb8, 0xc3, 0x87, 0x0f,
808 0xeb, 0x95, 0x32, 0x80, 0x26, 0x93, 0x49, 0x44, 0x45, 0x45, 0x41, 0x99,
809 0xf0, 0xf6, 0xca, 0x25, 0x2c, 0x88, 0x89, 0xc7, 0xad, 0x74, 0xcc, 0x84,
810 0x0d, 0x64, 0xcf, 0x24, 0xa7, 0xd5, 0xab, 0xfa, 0x91, 0xf2, 0x15, 0xeb,
811 0x77, 0xe4, 0xc8, 0x11, 0xe1, 0x74, 0x3a, 0xc3, 0x29, 0x06, 0x1f, 0xc7,
812 0xbe, 0xd4, 0x6d, 0x89, 0xa2, 0x18, 0x46, 0x6b, 0x32, 0x32, 0x32, 0x1e,
813 0x32, 0x9b, 0xcd, 0x21, 0x56, 0xeb, 0x77, 0x73, 0x39, 0xf4, 0x1c, 0x51,
814 0x54, 0x54, 0x64, 0x9c, 0x37, 0x6f, 0xde, 0x18, 0xb6, 0xd6, 0xaa, 0x49,
815 0x47, 0xab, 0x9b, 0xd0, 0xd0, 0xd0, 0x10, 0xfa, 0xc5, 0x17, 0x5f, 0x48,
816 0x56, 0xf6, 0xb1, 0xc7, 0x1e, 0x73, 0x70, 0x6c, 0x5e, 0xa5, 0x10, 0x16,
817 0x4e, 0x20, 0x44, 0xbf, 0xff, 0xfe, 0xfb, 0x6a, 0x75, 0x8b, 0x68, 0xe0,
818 0x03, 0xc2, 0x6b, 0x12, 0x4a, 0x85, 0x8f, 0x90, 0x41, 0xd8, 0x4c, 0x98,
819 0xa6, 0x22, 0x0a, 0xf1, 0xb5, 0xb8, 0xeb, 0xf7, 0xba, 0xf0, 0xbc, 0x1a,
820 0x17, 0xf5, 0x7b, 0x98, 0xf0, 0x27, 0xc2, 0x74, 0xa1, 0xb0, 0x62, 0x96,
821 0xf4, 0x2c, 0xca, 0xcb, 0xcb, 0x43, 0x26, 0x4f, 0x9e, 0xfc, 0x20, 0x8f,
822 0xc7, 0x35, 0x32, 0xce, 0x5c, 0xb9, 0xc5, 0x62, 0x79, 0x3c, 0x33, 0x33,
823 0x33, 0xe4, 0xe4, 0xc9, 0x93, 0x52, 0x61, 0xa0, 0x9e, 0x48, 0x47, 0xf6,
824 0x13, 0xcf, 0xab, 0xbc, 0xdb, 0x1a, 0xe9, 0x65, 0xc6, 0xf3, 0x14, 0x78,
825 0xda, 0x52, 0x3d, 0x33, 0x22, 0x22, 0x42, 0x4c, 0x98, 0x30, 0xc1, 0xc6,
826 0xa4, 0x5f, 0x95, 0xf9, 0x26, 0xe4, 0xcb, 0xd3, 0x6d, 0x36, 0x9b, 0xe5,
827 0xe8, 0xd1, 0xa3, 0x6a, 0x95, 0xda, 0xc1, 0xa9, 0x44, 0xa4, 0x26, 0xaf,
828 0x79, 0x00, 0x72, 0xdc, 0x58, 0x5d, 0xfa, 0xb5, 0x08, 0xcc, 0x82, 0x43,
829 0x9f, 0xd7, 0x8f, 0x74, 0xad, 0x63, 0x8b, 0x38, 0x5a, 0xe1, 0xbd, 0x18,
830 0xd7, 0x5b, 0xe4, 0x9c, 0x39, 0x76, 0x96, 0xa3, 0xd8, 0x5a, 0x87, 0xaa,
831 0x71, 0xe4, 0xf4, 0x3c, 0xb6, 0xc4, 0x91, 0xd3, 0x25, 0xd9, 0x83, 0xf0,
832 0x52, 0x32, 0x47, 0x3a, 0x6e, 0xd5, 0x36, 0x6e, 0xcd, 0x77, 0x03, 0xbf,
833 0x9f, 0x84, 0xde, 0x78, 0xe6, 0xcc, 0x99, 0x90, 0x8e, 0x0e, 0xaf, 0x96,
834 0x7e, 0x3b, 0x14, 0xfe, 0xdf, 0x25, 0x02, 0xbb, 0xc2, 0xd4, 0xa7, 0xf5,
835 0x23, 0x5d, 0xeb, 0xb8, 0xb3, 0xcd, 0xe6, 0x10, 0x4e, 0x27, 0x01, 0xd8,
836 0x74, 0xa7, 0x9c, 0x33, 0x57, 0x52, 0x52, 0x02, 0x07, 0x31, 0x9c, 0x7b,
837 0xfa, 0x50, 0x35, 0xe6, 0x3d, 0x94, 0x5b, 0x48, 0x94, 0x5c, 0x78, 0xc5,
838 0x2d, 0xcd, 0xc8, 0x66, 0xf6, 0x77, 0x1c, 0xb2, 0xdd, 0x6d, 0x16, 0x86,
839 0x20, 0x7f, 0x03, 0xe2, 0xb9, 0x25, 0x07, 0x45, 0x42, 0x4e, 0x9f, 0x3e,
840 0xed, 0xee, 0x9d, 0xcb, 0x98, 0x83, 0x3a, 0x09, 0x7d, 0xce, 0xc3, 0x10,
841 0x20, 0x47, 0x3a, 0xa2, 0xad, 0xca, 0xca, 0x4a, 0x13, 0x59, 0xe2, 0x54,
842 0x1e, 0x2e, 0x1a, 0x94, 0x48, 0xc7, 0x83, 0x1f, 0x24, 0x73, 0x1c, 0x2e,
843 0x37, 0x9e, 0xf3, 0x4b, 0x0d, 0x6c, 0x15, 0x9e, 0x11, 0x9e, 0xa7, 0xf2,
844 0x0c, 0xec, 0x41, 0x86, 0x04, 0x43, 0x35, 0x79, 0xa9, 0xaa, 0xaa, 0x12,
845 0x14, 0x89, 0x98, 0xc8, 0x49, 0x84, 0xff, 0x93, 0xa4, 0xa4, 0xcf, 0xf8,
846 0xf8, 0xf8, 0xee, 0x79, 0xfc, 0x73, 0xe7, 0xce, 0x49, 0x99, 0x78, 0x03,
847 0x91, 0x1e, 0xcf, 0xd9, 0xb9, 0x33, 0x4a, 0xa4, 0xc3, 0xc4, 0xa4, 0x56,
848 0x54, 0x54, 0x98, 0xda, 0xda, 0xa4, 0xa3, 0xb0, 0xe3, 0xc7, 0x8f, 0x0b,
849 0x32, 0xd9, 0xee, 0x67, 0x44, 0x29, 0x7d, 0xd4, 0xc7, 0x1f, 0x7f, 0x1c,
850 0x64, 0x56, 0x46, 0xe0, 0x75, 0x6f, 0xd8, 0xb0, 0x41, 0x24, 0x25, 0x25,
851 0xa9, 0xd2, 0x67, 0xb7, 0xb3, 0x14, 0x1e, 0x2e, 0x3b, 0xae, 0xaf, 0x5e,
852 0xbd, 0xba, 0x77, 0x7a, 0xd7, 0x21, 0x47, 0x3a, 0x5a, 0x86, 0x62, 0xba,
853 0xf4, 0xd5, 0x57, 0x5f, 0xf5, 0xb7, 0x1e, 0xb4, 0x3e, 0x1c, 0xf8, 0xbc,
854 0x7e, 0x1c, 0xff, 0xfb, 0x44, 0x3c, 0xa4, 0x77, 0x5b, 0xa5, 0x1c, 0x39,
855 0x4c, 0xf3, 0x3d, 0x84, 0x74, 0x20, 0xcf, 0xd8, 0x04, 0x4a, 0x50, 0x8f,
856 0x78, 0xf6, 0xfc, 0x8d, 0x1e, 0x60, 0xe2, 0xb1, 0x6a, 0x58, 0x00, 0xc2,
857 0xb5, 0xde, 0xf5, 0x8b, 0x90, 0xa9, 0xdf, 0x48, 0xae, 0x5f, 0x40, 0x1a,
858 0x2f, 0x59, 0x6a, 0x51, 0x57, 0x57, 0x67, 0xe4, 0xe1, 0x77, 0xa4, 0x9c,
859 0x79, 0x0f, 0xe7, 0x42, 0xa1, 0x01, 0xce, 0x91, 0x43, 0x99, 0x39, 0xec,
860 0xc8, 0x78, 0x72, 0x68, 0xcc, 0xec, 0x40, 0x4e, 0x0c, 0x90, 0x52, 0xbd,
861 0xa9, 0x5f, 0x20, 0x1a, 0x65, 0xf7, 0x62, 0x89, 0x43, 0x87, 0x0e, 0xe9,
862 0x73, 0x73, 0x73, 0x63, 0xd9, 0x47, 0x38, 0x2f, 0x45, 0xfa, 0x08, 0xc4,
863 0x89, 0x17, 0x2f, 0x5e, 0x34, 0x5e, 0xbe, 0x7c, 0x39, 0x90, 0xa4, 0xa3,
864 0xa7, 0x8c, 0xe7, 0xb8, 0xb5, 0x4b, 0x22, 0xac, 0xb4, 0xf4, 0x0a, 0x6d,
865 0xb4, 0x5c, 0xbf, 0x80, 0x09, 0xb2, 0xa9, 0x44, 0xba, 0x3b, 0x5e, 0x3f,
866 0xee, 0x0e, 0x21, 0xef, 0x26, 0x1d, 0x2d, 0x22, 0x96, 0x0a, 0xeb, 0x03,
867 0xb5, 0x6f, 0x1d, 0xb3, 0x4e, 0x2b, 0x57, 0xae, 0x54, 0xed, 0x20, 0x7a,
868 0x4a, 0x45, 0xfa, 0x53, 0x30, 0x56, 0x7a, 0x53, 0xbf, 0xfa, 0xfa, 0xfa,
869 0x80, 0x91, 0x9e, 0x9f, 0x9f, 0x8f, 0x35, 0x84, 0x16, 0xa3, 0xd1, 0x38,
870 0x96, 0x1b, 0x60, 0xdb, 0x1d, 0x33, 0xd0, 0x6b, 0x1b, 0xd1, 0x1a, 0xac,
871 0xdf, 0x5e, 0xb4, 0x68, 0xd1, 0x7d, 0xbf, 0x7e, 0x6d, 0xb0, 0x00, 0x3b,
872 0x83, 0x88, 0xd3, 0x3c, 0x42, 0x8a, 0xa7, 0x35, 0x72, 0xdd, 0xe3, 0x39,
873 0xf5, 0x1c, 0x0b, 0x66, 0x86, 0x82, 0x32, 0x38, 0xa4, 0x57, 0x7a, 0x37,
874 0xd9, 0x93, 0xf7, 0x8e, 0xf1, 0x7c, 0xec, 0x97, 0x5f, 0x7e, 0x69, 0x1a,
875 0x68, 0x93, 0x19, 0x14, 0xbf, 0x93, 0x0e, 0x6e, 0x1f, 0x64, 0x5f, 0xe4,
876 0x5b, 0xa4, 0xa3, 0x25, 0x24, 0x06, 0xd3, 0xa5, 0x83, 0x4b, 0x38, 0x5e,
877 0x47, 0x16, 0x6f, 0x1c, 0x47, 0x1d, 0x77, 0x48, 0x0f, 0xe1, 0x7f, 0x1c,
878 0xc1, 0x89, 0xff, 0xa0, 0x0c, 0x12, 0xb9, 0x70, 0xe1, 0x82, 0x68, 0x6a,
879 0x6a, 0x32, 0x73, 0x92, 0x26, 0xba, 0x37, 0xe9, 0x58, 0x5e, 0x83, 0xd9,
880 0xb0, 0x21, 0x01, 0x4e, 0xca, 0x04, 0xc5, 0x0f, 0xf1, 0x3a, 0x71, 0xea,
881 0x5e, 0x83, 0x97, 0xd6, 0x3b, 0x64, 0x83, 0xcd, 0x4f, 0x6b, 0x68, 0x68,
882 0x08, 0xc1, 0xa6, 0x3c, 0xc0, 0x17, 0x52, 0x5d, 0x5d, 0xed, 0x71, 0xdb,
883 0x51, 0x50, 0x94, 0x25, 0x21, 0x21, 0x41, 0x36, 0xb7, 0xee, 0x8d, 0xd4,
884 0xd6, 0xd6, 0xc2, 0x7a, 0x0f, 0xe7, 0xde, 0x9e, 0x6f, 0xec, 0xd5, 0xd3,
885 0x43, 0x63, 0x63, 0x63, 0xad, 0x95, 0x95, 0x95, 0xb6, 0x7e, 0x3c, 0xdf,
886 0xc0, 0xce, 0x82, 0xae, 0xbd, 0xbd, 0x5d, 0x0c, 0x1b, 0x36, 0x2c, 0xc8,
887 0x5e, 0x1f, 0x65, 0xfd, 0xfa, 0xf5, 0xa2, 0xd7, 0x5e, 0x3a, 0x3b, 0xa3,
888 0x3f, 0xc9, 0x13, 0x3b, 0x27, 0x8c, 0xc2, 0xdd, 0xa4, 0x63, 0x85, 0x07,
889 0xd6, 0x32, 0x95, 0x89, 0xbe, 0xa7, 0x0d, 0xdd, 0x2b, 0x3a, 0x33, 0x61,
890 0x39, 0x28, 0x0a, 0xd0, 0x49, 0xed, 0x8a, 0x09, 0x8a, 0x2a, 0xaf, 0xdb,
891 0x4d, 0x3a, 0x56, 0x9e, 0x94, 0x8a, 0x9e, 0x15, 0x33, 0xfd, 0x51, 0x28,
892 0x96, 0x75, 0x15, 0x20, 0x9f, 0xe5, 0x26, 0x1d, 0x93, 0xec, 0xbb, 0x84,
893 0xfc, 0x56, 0x64, 0x35, 0x02, 0x2f, 0x71, 0x0b, 0x61, 0x31, 0x55, 0xda,
894 0x1c, 0xa4, 0xae, 0xdf, 0x5e, 0x37, 0x04, 0x29, 0xbd, 0x97, 0x09, 0xa7,
895 0x44, 0xff, 0x56, 0x0a, 0x61, 0x6a, 0x15, 0x5b, 0xb6, 0xed, 0x6e, 0xd2,
896 0xf1, 0xb0, 0x76, 0x1f, 0xd4, 0x15, 0x01, 0x3e, 0x26, 0x20, 0x1c, 0xc1,
897 0x4d, 0x0d, 0xfd, 0x13, 0xde, 0xdc, 0xe8, 0x4a, 0x4e, 0x4e, 0xc6, 0xca,
898 0x58, 0xac, 0x68, 0xbd, 0xe1, 0xab, 0x67, 0xfb, 0x7a, 0x06, 0x08, 0xb9,
899 0x68, 0xe4, 0xef, 0x4d, 0xc1, 0x95, 0x32, 0xfd, 0x17, 0xde, 0xad, 0x32,
900 0x84, 0x3d, 0x6f, 0xbd, 0x56, 0x49, 0xc7, 0x9a, 0xac, 0x31, 0xa5, 0xa5,
901 0xa5, 0xc6, 0xba, 0xba, 0xba, 0x20, 0x6b, 0xfd, 0x14, 0x5e, 0x32, 0x1e,
902 0xc5, 0xe1, 0x74, 0xa4, 0x16, 0x49, 0x87, 0xe7, 0x8e, 0x29, 0x3c, 0xd9,
903 0x5d, 0x31, 0x41, 0x51, 0x2f, 0x58, 0xd7, 0xde, 0xd1, 0xd1, 0x61, 0x66,
904 0xbd, 0x0e, 0xd3, 0x22, 0xe9, 0x48, 0xf1, 0x21, 0xab, 0x27, 0xbb, 0x2b,
905 0x26, 0x28, 0x5e, 0xc4, 0x58, 0x76, 0xbb, 0x28, 0x2e, 0x2e, 0x86, 0xdf,
906 0xd5, 0x9d, 0x22, 0xd7, 0x22, 0xe9, 0x08, 0xfe, 0xd3, 0x5b, 0x5b, 0x5b,
907 0xcd, 0x41, 0x27, 0xce, 0xa7, 0xa1, 0x9b, 0xfb, 0x0c, 0xfc, 0xb1, 0xbe,
908 0x7a, 0xa6, 0x2f, 0x8f, 0xfe, 0xc6, 0x9a, 0xb1, 0xa4, 0xc6, 0xc6, 0x46,
909 0xa3, 0xd2, 0x86, 0x3d, 0x35, 0x82, 0x6c, 0x1e, 0xc5, 0xfa, 0x8a, 0xe5,
910 0x90, 0x00, 0x9a, 0x37, 0x6f, 0xde, 0x80, 0x91, 0xd0, 0xd4, 0xd4, 0xd4,
911 0xbd, 0x12, 0x58, 0x49, 0xb0, 0xe7, 0x2d, 0x27, 0x27, 0x07, 0x1b, 0x42,
912 0xfa, 0xf5, 0xbe, 0xb0, 0xb0, 0x30, 0x77, 0x28, 0x0c, 0x13, 0x6f, 0x16,
913 0xd2, 0x87, 0x11, 0x05, 0x84, 0x74, 0x2c, 0xd0, 0xb7, 0xa7, 0xa7, 0xa7,
914 0xb7, 0xbd, 0xf7, 0xde, 0x7b, 0xde, 0x8c, 0xe9, 0x06, 0xfe, 0x98, 0x3b,
915 0xab, 0x6f, 0xb1, 0xb7, 0x0b, 0x47, 0x86, 0x29, 0x09, 0x36, 0x3b, 0xee,
916 0xdf, 0xbf, 0x5f, 0xcc, 0x9a, 0x35, 0xcb, 0xce, 0xca, 0xf0, 0xe7, 0x8e,
917 0x17, 0x7c, 0x93, 0x09, 0x27, 0x49, 0x2e, 0x5c, 0xb8, 0x50, 0xa7, 0xb4,
918 0x45, 0x0b, 0x89, 0x29, 0xec, 0x0d, 0x40, 0x66, 0xad, 0x97, 0x38, 0x39,
919 0xc1, 0xe2, 0x6d, 0x76, 0xcd, 0xca, 0xfa, 0xf5, 0x09, 0xe9, 0x3a, 0x1f,
920 0x2d, 0x8b, 0x82, 0x42, 0x90, 0x8d, 0x5b, 0xc0, 0x21, 0x9b, 0x37, 0xcd,
921 0x1b, 0xde, 0xe9, 0xe3, 0xa2, 0x67, 0xcd, 0x99, 0xbe, 0xb3, 0xb3, 0x53,
922 0xcc, 0x9f, 0x3f, 0x5f, 0x9c, 0x3a, 0x75, 0x4a, 0xf1, 0x87, 0xdb, 0xb7,
923 0x6f, 0x47, 0xd6, 0x0a, 0x4b, 0x80, 0x60, 0x12, 0x10, 0x23, 0x76, 0xf8,
924 0x99, 0x74, 0xf4, 0xb6, 0x39, 0x57, 0xae, 0x5c, 0x19, 0xf1, 0xe8, 0xa3,
925 0x8f, 0xea, 0xa5, 0xce, 0xb1, 0x73, 0x8b, 0xd9, 0x6c, 0xee, 0x5e, 0xb2,
926 0x94, 0x95, 0x95, 0xd5, 0x3b, 0xd1, 0xf2, 0x6f, 0xd1, 0xb3, 0xff, 0xcf,
927 0x1b, 0xc5, 0x77, 0xf2, 0xf7, 0x9d, 0x12, 0xfd, 0xcb, 0xca, 0xf5, 0x88,
928 0x8f, 0x0f, 0xc1, 0xd7, 0xf3, 0xb1, 0xdd, 0xde, 0x20, 0x94, 0xf0, 0x32,
929 0xc1, 0xea, 0x70, 0x38, 0xba, 0xcf, 0x59, 0x15, 0x2a, 0x96, 0x01, 0x6d,
930 0xde, 0xbc, 0x19, 0xc7, 0x71, 0xdd, 0x22, 0xec, 0x23, 0xcc, 0xe0, 0xa3,
931 0x3d, 0x0d, 0x7e, 0xc6, 0x28, 0x02, 0x5e, 0x7c, 0x91, 0x88, 0x77, 0xe0,
932 0x6c, 0x77, 0xa5, 0x7a, 0xd2, 0xf0, 0xe3, 0xa2, 0x71, 0x19, 0x75, 0xc5,
933 0xb2, 0xa5, 0x63, 0x7c, 0xe4, 0x69, 0x5f, 0xde, 0xad, 0x1b, 0x4c, 0x67,
934 0xc3, 0x4e, 0x25, 0x1c, 0x22, 0xd8, 0xd6, 0xac, 0x59, 0xa3, 0x8a, 0x70,
935 0x34, 0x0c, 0x12, 0xf7, 0x19, 0xef, 0x3f, 0xe4, 0xc6, 0x36, 0x50, 0xf5,
936 0x1d, 0x49, 0x78, 0x05, 0x67, 0xbf, 0xd3, 0xd8, 0xae, 0xea, 0x3c, 0x3c,
937 0x9c, 0x5e, 0x5d, 0x55, 0x55, 0x85, 0xb3, 0xf6, 0xaa, 0x09, 0xeb, 0xf8,
938 0x90, 0xe3, 0xfb, 0xf6, 0x40, 0xe0, 0xc9, 0x84, 0xbf, 0x13, 0xae, 0x6f,
939 0xdd, 0xba, 0x55, 0x15, 0xe1, 0xe8, 0x5d, 0x57, 0xaf, 0x5e, 0xb5, 0xf1,
940 0x6d, 0x0e, 0xab, 0x70, 0x26, 0x4e, 0x00, 0xea, 0x3d, 0x91, 0xf0, 0x0e,
941 0x16, 0x91, 0xbe, 0xf4, 0xd2, 0x4b, 0xaa, 0xea, 0x3d, 0x69, 0xd2, 0x24,
942 0x17, 0x45, 0x36, 0x68, 0xa8, 0x15, 0x5c, 0x6f, 0xd3, 0xfd, 0x48, 0x7a,
943 0x34, 0x1f, 0x86, 0x7f, 0x1d, 0xe7, 0xb8, 0xca, 0x9d, 0x84, 0x28, 0x7a,
944 0x9d, 0xf2, 0x5c, 0x50, 0x50, 0xe0, 0xe4, 0x7b, 0x5b, 0x36, 0x12, 0x86,
945 0x06, 0xa8, 0xee, 0x3a, 0x3e, 0x95, 0xf1, 0x90, 0xcd, 0x66, 0xb3, 0xce,
946 0x99, 0x33, 0x47, 0x15, 0xf1, 0xcb, 0x97, 0x2f, 0x77, 0x5b, 0xa8, 0x4f,
947 0xf8, 0xe6, 0x8a, 0xfb, 0x8a, 0x74, 0x98, 0xb7, 0xb5, 0x84, 0xda, 0x0b,
948 0x17, 0x2e, 0x38, 0x93, 0x92, 0x92, 0x14, 0x15, 0x66, 0x30, 0x18, 0x5c,
949 0x38, 0xf6, 0x9b, 0x8f, 0xd8, 0xde, 0x4e, 0x48, 0x0c, 0xb0, 0x95, 0x82,
950 0x0f, 0xf1, 0x13, 0x58, 0x9c, 0x96, 0x96, 0x16, 0x3b, 0x7a, 0xb2, 0x1a,
951 0xe2, 0x37, 0x6c, 0xd8, 0x80, 0x4b, 0x7d, 0xda, 0xe9, 0x77, 0x1f, 0x10,
952 0xa6, 0x05, 0xe2, 0xa8, 0xf0, 0x40, 0x28, 0x0b, 0x8e, 0xdb, 0x6f, 0x60,
953 0xe6, 0xc8, 0x4c, 0xdb, 0x13, 0x12, 0x12, 0x54, 0x29, 0xeb, 0xb9, 0xe7,
954 0x9e, 0x03, 0xe1, 0xb7, 0x79, 0xfc, 0x9f, 0xa0, 0x91, 0xb3, 0xd3, 0xc3,
955 0xd9, 0xb1, 0xab, 0x2b, 0x2b, 0x2b, 0xc3, 0xa9, 0xd3, 0xaa, 0xbe, 0xe5,
956 0x8d, 0x37, 0xde, 0x70, 0x5f, 0xfb, 0xf1, 0x16, 0x5f, 0x08, 0x34, 0xa8,
957 0x49, 0x47, 0xef, 0xf8, 0x31, 0xe1, 0x94, 0x95, 0x64, 0xee, 0xdc, 0xb9,
958 0xaa, 0xc7, 0x43, 0xbe, 0x9c, 0xa7, 0x98, 0x7b, 0x97, 0x41, 0x63, 0x17,
959 0x10, 0xbc, 0x0d, 0xc7, 0x6e, 0xdb, 0xb6, 0x6d, 0xaa, 0xbe, 0xc7, 0x62,
960 0xb1, 0xb8, 0x0a, 0x0b, 0x0b, 0xe1, 0xcd, 0x37, 0xf0, 0x85, 0x40, 0x89,
961 0x83, 0x95, 0x74, 0x5c, 0x8f, 0xf1, 0x53, 0xc2, 0x69, 0xdc, 0x59, 0xf6,
962 0xd4, 0x53, 0x4f, 0xa9, 0x52, 0x10, 0xee, 0x5f, 0xbb, 0x74, 0xe9, 0x12,
963 0x14, 0x74, 0x86, 0xf0, 0x2b, 0xb6, 0x14, 0x5a, 0xbb, 0x2d, 0x01, 0xbd,
964 0x75, 0x07, 0x42, 0xc8, 0x4d, 0x9b, 0x36, 0xa9, 0xfa, 0xae, 0xe8, 0xe8,
965 0x68, 0x57, 0x69, 0x69, 0x29, 0x7a, 0x7c, 0x23, 0x61, 0xcb, 0x40, 0x12,
966 0x3f, 0x90, 0x8a, 0x99, 0x43, 0xc8, 0x87, 0x89, 0x5e, 0xbf, 0x7e, 0xbd,
967 0x2a, 0xc5, 0xe0, 0x2a, 0xac, 0xf2, 0xf2, 0x72, 0x27, 0xf7, 0x08, 0xdc,
968 0xe3, 0x12, 0xa6, 0xe1, 0x6b, 0x32, 0x66, 0x72, 0x08, 0xd9, 0x85, 0xcb,
969 0xf8, 0xd4, 0x7c, 0x1f, 0x6e, 0x91, 0xa4, 0x06, 0x6d, 0xe7, 0x2b, 0xbd,
970 0x36, 0xf1, 0x51, 0xeb, 0x83, 0x86, 0xf4, 0x87, 0x08, 0xff, 0xc2, 0x6d,
971 0x46, 0x34, 0x9e, 0xa9, 0xde, 0x87, 0xc5, 0x37, 0x30, 0xb5, 0x70, 0x2f,
972 0x4a, 0xd0, 0xf8, 0xdd, 0x28, 0x26, 0x0e, 0xc5, 0xbe, 0x42, 0x48, 0x39,
973 0x7e, 0xfc, 0x78, 0x55, 0xdf, 0x38, 0x75, 0xea, 0x54, 0x17, 0x1c, 0x41,
974 0x5c, 0x10, 0x44, 0xf8, 0x25, 0x5b, 0xc4, 0x7b, 0x9e, 0xf4, 0x18, 0x4e,
975 0x66, 0xb4, 0x1c, 0x39, 0x72, 0x44, 0xb5, 0xb3, 0xb3, 0x6e, 0xdd, 0x3a,
976 0xf7, 0x01, 0xf8, 0x79, 0x4a, 0x87, 0xd6, 0x6b, 0x08, 0x43, 0x39, 0x94,
977 0xbc, 0x5c, 0x5f, 0x5f, 0xef, 0x80, 0x09, 0x57, 0xf3, 0xad, 0x33, 0x67,
978 0xce, 0xc4, 0x4d, 0x92, 0x5d, 0x9c, 0xb1, 0xcb, 0xf6, 0xf3, 0x4d, 0x92,
979 0x7e, 0x27, 0xfd, 0x4e, 0xda, 0xf2, 0xe4, 0xc9, 0x93, 0x92, 0x37, 0x17,
980 0xde, 0x8d, 0x9c, 0x9c, 0x1c, 0x97, 0xdd, 0x6e, 0x87, 0x12, 0x0a, 0x09,
981 0x4f, 0x06, 0x32, 0x91, 0xd1, 0x07, 0x24, 0x72, 0x48, 0x79, 0x2d, 0x2f,
982 0x2f, 0x4f, 0x55, 0xfe, 0x01, 0x58, 0xb1, 0x62, 0x05, 0x1a, 0x79, 0x3b,
983 0x0f, 0x81, 0x73, 0xfc, 0x19, 0xca, 0xf9, 0xf3, 0x56, 0x65, 0xac, 0xac,
984 0xfd, 0x19, 0xe1, 0xd7, 0x98, 0x84, 0xa9, 0xae, 0xae, 0xd6, 0x91, 0x93,
985 0xa3, 0xea, 0x87, 0xb9, 0xb9, 0xb9, 0x0e, 0x8a, 0xcb, 0x31, 0x29, 0x81,
986 0x4b, 0x68, 0x0e, 0x09, 0xf5, 0x17, 0xe1, 0x68, 0x41, 0x70, 0xb4, 0xda,
987 0x0e, 0xc2, 0xa8, 0xec, 0xec, 0xec, 0x45, 0x7b, 0xf6, 0xec, 0x89, 0xe4,
988 0xcd, 0x06, 0x8a, 0x42, 0xd6, 0xc1, 0x12, 0x17, 0x17, 0x37, 0x53, 0xf4,
989 0x5c, 0x94, 0x54, 0x23, 0xa4, 0x4f, 0x8e, 0xd4, 0xc4, 0x2c, 0x9b, 0x27,
990 0xc1, 0x4a, 0x8f, 0xbf, 0x62, 0x56, 0x4a, 0x78, 0xb7, 0xb4, 0x1a, 0x15,
991 0xc2, 0x89, 0x8b, 0xb8, 0x83, 0x0b, 0xb7, 0x18, 0x5d, 0xbd, 0x07, 0xd7,
992 0x3e, 0x60, 0x9a, 0x18, 0xf7, 0x6e, 0xac, 0x11, 0x3d, 0x87, 0x01, 0x86,
993 0x78, 0xf9, 0xfd, 0x58, 0x92, 0x8e, 0x2b, 0xc9, 0x3e, 0x12, 0xf2, 0x67,
994 0xee, 0x6a, 0x8e, 0x74, 0x2c, 0x9f, 0xc2, 0xfd, 0x66, 0xd1, 0xde, 0x4e,
995 0xfc, 0x89, 0x9e, 0xa3, 0x36, 0x71, 0x0c, 0xe7, 0xcd, 0x7b, 0x78, 0xd1,
996 0x0b, 0xa6, 0x97, 0x47, 0x8b, 0x9e, 0xad, 0x44, 0xde, 0xee, 0x01, 0x70,
997 0xdf, 0xb8, 0xe4, 0x97, 0x63, 0x50, 0xfd, 0x49, 0x7a, 0x50, 0x34, 0x2a,
998 0xff, 0x13, 0x60, 0x00, 0x43, 0x56, 0xa7, 0xa6, 0x51, 0x00, 0x63, 0x17,
999 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
1000 };
1001 static unsigned int invader_png_len = 4008;
1002
1003 static ui_window_qt_t ui_window = {0};
1004
getInvader(void)1005 static const QPixmap getInvader(void)
1006 {
1007 QPixmap pix;
1008 pix.loadFromData(invader_png, invader_png_len, "PNG");
1009
1010 return pix;
1011 }
1012
1013 #ifdef HAVE_LIBRETRODB
scan_finished_handler(retro_task_t * task,void * task_data,void * user_data,const char * err)1014 static void scan_finished_handler(retro_task_t *task,
1015 void *task_data, void *user_data, const char *err)
1016 {
1017 bool dontAsk = false;
1018 bool answer = false;
1019 #ifdef HAVE_MENU
1020 menu_ctx_environment_t menu_environ;
1021 menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST;
1022 menu_environ.data = NULL;
1023
1024 menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ);
1025 #endif
1026 if (!ui_window.qtWindow->settings()->value(
1027 "scan_finish_confirm", true).toBool())
1028 return;
1029
1030 answer = ui_window.qtWindow->showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SCAN_FINISHED), MainWindow::MSGBOX_TYPE_QUESTION_OKCANCEL, Qt::ApplicationModal, true, &dontAsk);
1031
1032 if (answer && dontAsk)
1033 ui_window.qtWindow->settings()->setValue("scan_finish_confirm", false);
1034 }
1035 #endif
1036
1037 /* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */
expScale(double inputValue,double midValue,double maxValue)1038 static double expScale(double inputValue, double midValue, double maxValue)
1039 {
1040 double M = maxValue / midValue;
1041 double C = log(pow(M - 1, 2));
1042 double B = maxValue / (exp(C) - 1);
1043 double A = -1 * B;
1044 double returnValue = A + B * exp(C * inputValue);
1045 return returnValue;
1046 }
1047
TreeView(QWidget * parent)1048 TreeView::TreeView(QWidget *parent) :
1049 QTreeView(parent)
1050 {
1051 }
1052
columnCountChanged(int oldCount,int newCount)1053 void TreeView::columnCountChanged(int oldCount, int newCount)
1054 {
1055 QTreeView::columnCountChanged(oldCount, newCount);
1056 }
1057
selectionChanged(const QItemSelection & selected,const QItemSelection & deselected)1058 void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
1059 {
1060 QModelIndexList list = selected.indexes();
1061
1062 QTreeView::selectionChanged(selected, deselected);
1063
1064 emit itemsSelected(list);
1065 }
1066
TableView(QWidget * parent)1067 TableView::TableView(QWidget *parent) :
1068 QTableView(parent)
1069 {
1070 }
1071
isEditorOpen()1072 bool TableView::isEditorOpen()
1073 {
1074 return (state() == QAbstractItemView::EditingState);
1075 }
1076
ListWidget(QWidget * parent)1077 ListWidget::ListWidget(QWidget *parent) :
1078 QListWidget(parent)
1079 {
1080 }
1081
isEditorOpen()1082 bool ListWidget::isEditorOpen()
1083 {
1084 return (state() == QAbstractItemView::EditingState);
1085 }
1086
keyPressEvent(QKeyEvent * event)1087 void ListWidget::keyPressEvent(QKeyEvent *event)
1088 {
1089 if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
1090 emit enterPressed();
1091 else if (event->key() == Qt::Key_Delete)
1092 emit deletePressed();
1093
1094 QListWidget::keyPressEvent(event);
1095 }
1096
CoreInfoLabel(QString text,QWidget * parent)1097 CoreInfoLabel::CoreInfoLabel(QString text, QWidget *parent) :
1098 QLabel(text, parent)
1099 {
1100 setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
1101 }
1102
CoreInfoWidget(CoreInfoLabel * label,QWidget * parent)1103 CoreInfoWidget::CoreInfoWidget(CoreInfoLabel *label, QWidget *parent) :
1104 QWidget(parent)
1105 ,m_label(label)
1106 ,m_scrollArea(new QScrollArea(this))
1107 {
1108 m_scrollArea->setWidgetResizable(true);
1109 m_scrollArea->setWidget(m_label);
1110 }
1111
sizeHint() const1112 QSize CoreInfoWidget::sizeHint() const
1113 {
1114 return QSize(256, 256);
1115 }
1116
resizeEvent(QResizeEvent * event)1117 void CoreInfoWidget::resizeEvent(QResizeEvent *event)
1118 {
1119 QWidget::resizeEvent(event);
1120 m_scrollArea->resize(event->size());
1121 }
1122
LogTextEdit(QWidget * parent)1123 LogTextEdit::LogTextEdit(QWidget *parent) :
1124 QPlainTextEdit(parent)
1125 {
1126
1127 }
1128
appendMessage(const QString & text)1129 void LogTextEdit::appendMessage(const QString& text)
1130 {
1131 if (text.isEmpty())
1132 return;
1133
1134 appendPlainText(text);
1135 verticalScrollBar()->setValue(verticalScrollBar()->maximum());
1136 }
1137
1138 /* only accept indexes from current path. https://www.qtcentre.org/threads/50700-QFileSystemModel-and-QSortFilterProxyModel-don-t-work-well-together */
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const1139 bool FileSystemProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
1140 {
1141 QFileSystemModel *sm = qobject_cast<QFileSystemModel*>(sourceModel());
1142 QModelIndex rootIndex = sm->index(sm->rootPath());
1143
1144 if (sourceParent == rootIndex)
1145 return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
1146 return true;
1147 }
1148
sort(int column,Qt::SortOrder order)1149 void FileSystemProxyModel::sort(int column, Qt::SortOrder order)
1150 {
1151 /* sort the source (QFileSystemModel to keep directories before files) */
1152 sourceModel()->sort(column, order);
1153 }
1154
MainWindow(QWidget * parent)1155 MainWindow::MainWindow(QWidget *parent) :
1156 QMainWindow(parent)
1157 ,m_loadCoreWindow(new LoadCoreWindow(this))
1158 ,m_timer(new QTimer(this))
1159 ,m_currentCore()
1160 ,m_currentCoreVersion()
1161 ,m_statusLabel(new QLabel(this))
1162 ,m_dirTree(new TreeView(this))
1163 ,m_dirModel(new QFileSystemModel(m_dirTree))
1164 ,m_fileModel(new QFileSystemModel(this))
1165 ,m_listWidget(new ListWidget(this))
1166 ,m_centralWidget(new QStackedWidget(this))
1167 ,m_tableView(new TableView(this))
1168 ,m_fileTableView(new QTableView(this))
1169 ,m_playlistViews(new FileDropWidget(this))
1170 ,m_searchWidget(new QWidget(this))
1171 ,m_searchLineEdit(new QLineEdit(this))
1172 ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), this))
1173 ,m_playlistFiles()
1174 ,m_launchWithComboBox(new QComboBox(this))
1175 ,m_startCorePushButton(new QToolButton(this))
1176 ,m_coreInfoPushButton(new QToolButton(this))
1177 ,m_runPushButton(new QToolButton(this))
1178 ,m_stopPushButton(new QToolButton(this))
1179 ,m_browserAndPlaylistTabWidget(new QTabWidget(this))
1180 ,m_pendingRun(false)
1181 ,m_thumbnailPixmap(NULL)
1182 ,m_thumbnailPixmap2(NULL)
1183 ,m_thumbnailPixmap3(NULL)
1184 ,m_settings(NULL)
1185 ,m_viewOptionsDialog(NULL)
1186 ,m_coreInfoDialog(new CoreInfoDialog(this, NULL))
1187 ,m_defaultStyle(NULL)
1188 ,m_defaultPalette()
1189 ,m_currentTheme(THEME_SYSTEM_DEFAULT)
1190 ,m_coreInfoDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO), this))
1191 ,m_coreInfoLabel(new CoreInfoLabel(QString(), this))
1192 ,m_coreInfoWidget(new CoreInfoWidget(m_coreInfoLabel, this))
1193 ,m_logDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG), this))
1194 ,m_logWidget(new QFrame(this))
1195 ,m_logTextEdit(new LogTextEdit(m_logWidget))
1196 ,m_historyPlaylistsItem(NULL)
1197 ,m_folderIcon()
1198 ,m_customThemeString()
1199 ,m_gridView(new GridView(this))
1200 ,m_playlistViewsAndFooter(new QWidget(this))
1201 ,m_zoomSlider(NULL)
1202 ,m_lastZoomSliderValue(0)
1203 ,m_viewType(VIEW_TYPE_LIST)
1204 ,m_thumbnailType(THUMBNAIL_TYPE_BOXART)
1205 ,m_gridProgressBar(NULL)
1206 ,m_gridProgressWidget(NULL)
1207 ,m_currentGridHash()
1208 ,m_currentGridWidget(NULL)
1209 ,m_allPlaylistsListMaxCount(0)
1210 ,m_allPlaylistsGridMaxCount(0)
1211 ,m_playlistEntryDialog(NULL)
1212 ,m_statusMessageElapsedTimer()
1213 #if defined(HAVE_MENU)
1214 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
1215 ,m_shaderParamsDialog(new ShaderParamsDialog())
1216 #endif
1217 #endif
1218 ,m_coreOptionsDialog(new CoreOptionsDialog())
1219 ,m_networkManager(new QNetworkAccessManager(this))
1220 ,m_updateProgressDialog(new QProgressDialog())
1221 ,m_updateFile()
1222 ,m_updateReply()
1223 ,m_thumbnailDownloadProgressDialog(new QProgressDialog())
1224 ,m_thumbnailDownloadFile()
1225 ,m_thumbnailDownloadReply()
1226 ,m_pendingThumbnailDownloadTypes()
1227 ,m_thumbnailPackDownloadProgressDialog(new QProgressDialog())
1228 ,m_thumbnailPackDownloadFile()
1229 ,m_thumbnailPackDownloadReply()
1230 ,m_playlistThumbnailDownloadProgressDialog(new QProgressDialog())
1231 ,m_playlistThumbnailDownloadFile()
1232 ,m_playlistThumbnailDownloadReply()
1233 ,m_pendingPlaylistThumbnails()
1234 ,m_downloadedThumbnails(0)
1235 ,m_failedThumbnails(0)
1236 ,m_playlistThumbnailDownloadWasCanceled(false)
1237 ,m_pendingDirScrollPath()
1238 ,m_thumbnailTimer(new QTimer(this))
1239 ,m_gridItem(this)
1240 ,m_currentBrowser(BROWSER_TYPE_PLAYLISTS)
1241 ,m_searchRegExp()
1242 ,m_zoomWidget(new QWidget(this))
1243 ,m_itemsCountLiteral(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ITEMS_COUNT))
1244 ,m_itemsCountLabel(new QLabel(this))
1245 {
1246 settings_t *settings = config_get_ptr();
1247 const char *path_dir_playlist = settings->paths.directory_playlist;
1248 const char *path_dir_assets = settings->paths.directory_assets;
1249 const char *path_dir_menu_content = settings->paths.directory_menu_content;
1250 QDir playlistDir(path_dir_playlist);
1251 QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath();
1252 QToolButton *searchResetButton = NULL;
1253 QHBoxLayout *zoomLayout = new QHBoxLayout();
1254 QLabel *zoomLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ZOOM), m_zoomWidget);
1255 QPushButton *thumbnailTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE), m_zoomWidget);
1256 QMenu *thumbnailTypeMenu = new QMenu(thumbnailTypePushButton);
1257 QAction *thumbnailTypeBoxartAction = NULL;
1258 QAction *thumbnailTypeScreenshotAction = NULL;
1259 QAction *thumbnailTypeTitleAction = NULL;
1260 QPushButton *viewTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW), m_zoomWidget);
1261 QMenu *viewTypeMenu = new QMenu(viewTypePushButton);
1262 QAction *viewTypeIconsAction = NULL;
1263 QAction *viewTypeListAction = NULL;
1264 QHBoxLayout *gridProgressLayout = new QHBoxLayout();
1265 QLabel *gridProgressLabel = NULL;
1266 QHBoxLayout *gridFooterLayout = NULL;
1267
1268 qRegisterMetaType<QPointer<ThumbnailWidget> >("ThumbnailWidget");
1269 qRegisterMetaType<retro_task_callback_t>("retro_task_callback_t");
1270
1271 /* Cancel all progress dialogs immediately since
1272 * they show as soon as they're constructed. */
1273 m_updateProgressDialog->cancel();
1274 m_thumbnailDownloadProgressDialog->cancel();
1275 m_thumbnailPackDownloadProgressDialog->cancel();
1276 m_playlistThumbnailDownloadProgressDialog->cancel();
1277
1278 m_gridProgressWidget = new QWidget();
1279 gridProgressLabel = new QLabel(
1280 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PROGRESS),
1281 m_gridProgressWidget);
1282
1283 thumbnailTypePushButton->setObjectName("thumbnailTypePushButton");
1284 thumbnailTypePushButton->setFlat(true);
1285
1286 thumbnailTypeBoxartAction = thumbnailTypeMenu->addAction(
1287 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART));
1288 thumbnailTypeScreenshotAction = thumbnailTypeMenu->addAction(
1289 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT));
1290 thumbnailTypeTitleAction = thumbnailTypeMenu->addAction(
1291 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN));
1292
1293 thumbnailTypePushButton->setMenu(thumbnailTypeMenu);
1294
1295 viewTypePushButton->setObjectName("viewTypePushButton");
1296 viewTypePushButton->setFlat(true);
1297
1298 viewTypeIconsAction = viewTypeMenu->addAction(
1299 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS));
1300 viewTypeListAction = viewTypeMenu->addAction(
1301 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST));
1302
1303 viewTypePushButton->setMenu(viewTypeMenu);
1304
1305 gridProgressLabel->setObjectName("gridProgressLabel");
1306
1307 m_gridProgressBar = new QProgressBar(
1308 m_gridProgressWidget);
1309
1310 m_gridProgressBar->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
1311
1312 zoomLabel->setObjectName("zoomLabel");
1313
1314 m_zoomSlider = new QSlider(
1315 Qt::Horizontal, m_zoomWidget);
1316
1317 m_zoomSlider->setMinimum(0);
1318 m_zoomSlider->setMaximum(100);
1319 m_zoomSlider->setValue(50);
1320 m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
1321
1322 m_lastZoomSliderValue = m_zoomSlider->value();
1323
1324 m_playlistViewsAndFooter->setLayout(new QVBoxLayout());
1325
1326 m_gridView->setSelectionMode(QAbstractItemView::SingleSelection);
1327 m_gridView->setEditTriggers(QAbstractItemView::NoEditTriggers);
1328
1329 m_playlistViews->addWidget(m_gridView);
1330 m_playlistViews->addWidget(m_tableView);
1331 m_centralWidget->setObjectName("centralWidget");
1332
1333 m_playlistViewsAndFooter->layout()->addWidget(m_playlistViews);
1334 m_playlistViewsAndFooter->layout()->setAlignment(Qt::AlignCenter);
1335 m_playlistViewsAndFooter->layout()->setContentsMargins(0, 0, 0, 0);
1336
1337 m_gridProgressWidget->setLayout(gridProgressLayout);
1338 gridProgressLayout->setContentsMargins(0, 0, 0, 0);
1339 gridProgressLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
1340 gridProgressLayout->addWidget(gridProgressLabel);
1341 gridProgressLayout->addWidget(m_gridProgressBar);
1342
1343 m_playlistViewsAndFooter->layout()->addWidget(m_gridProgressWidget);
1344
1345 m_zoomWidget->setLayout(zoomLayout);
1346 zoomLayout->setContentsMargins(0, 0, 0, 0);
1347 zoomLayout->addWidget(zoomLabel);
1348 zoomLayout->addWidget(m_zoomSlider);
1349
1350 m_itemsCountLabel->setObjectName("itemsCountLabel");
1351
1352 gridFooterLayout = new QHBoxLayout();
1353 gridFooterLayout->addWidget(m_itemsCountLabel);
1354 gridFooterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
1355 gridFooterLayout->addWidget(m_gridProgressWidget);
1356 gridFooterLayout->addWidget(m_zoomWidget);
1357 gridFooterLayout->addWidget(thumbnailTypePushButton);
1358 gridFooterLayout->addWidget(viewTypePushButton);
1359
1360 static_cast<QVBoxLayout*>(m_playlistViewsAndFooter->layout())->addLayout(gridFooterLayout);
1361
1362 m_gridProgressWidget->hide();
1363
1364 m_playlistModel = new PlaylistModel(this);
1365 m_proxyModel = new QSortFilterProxyModel(this);
1366 m_proxyModel->setSourceModel(m_playlistModel);
1367 m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
1368
1369 m_proxyFileModel = new FileSystemProxyModel();
1370 m_proxyFileModel->setSourceModel(m_fileModel);
1371 m_proxyFileModel->setSortCaseSensitivity(Qt::CaseInsensitive);
1372
1373 m_tableView->setAlternatingRowColors(true);
1374 m_tableView->setModel(m_proxyModel);
1375 m_tableView->setSortingEnabled(true);
1376 m_tableView->verticalHeader()->setVisible(false);
1377 m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
1378 m_tableView->setSelectionMode(QAbstractItemView::SingleSelection);
1379 m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed);
1380 m_tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1381 m_tableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
1382 m_tableView->horizontalHeader()->setStretchLastSection(true);
1383 m_tableView->setWordWrap(false);
1384
1385 m_fileTableView->setModel(m_fileModel);
1386 m_fileTableView->sortByColumn(0, Qt::AscendingOrder);
1387 m_fileTableView->setSortingEnabled(true);
1388 m_fileTableView->setAlternatingRowColors(true);
1389 m_fileTableView->verticalHeader()->setVisible(false);
1390 m_fileTableView->setSelectionMode(QAbstractItemView::SingleSelection);
1391 m_fileTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
1392 m_fileTableView->horizontalHeader()->setStretchLastSection(true);
1393 m_fileTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
1394 m_fileTableView->setWordWrap(false);
1395
1396 m_gridView->setItemDelegate(new ThumbnailDelegate(m_gridItem, this));
1397 m_gridView->setModel(m_proxyModel);
1398
1399 m_gridView->setSelectionModel(m_tableView->selectionModel());
1400
1401 m_logWidget->setObjectName("logWidget");
1402
1403 m_folderIcon = QIcon(QString(path_dir_assets) + GENERIC_FOLDER_ICON);
1404 m_imageFormats = QVector<QByteArray>::fromList(QImageReader::supportedImageFormats());
1405 m_defaultStyle = QApplication::style();
1406 m_defaultPalette = QApplication::palette();
1407
1408 /* ViewOptionsDialog needs m_settings set before it's constructed */
1409 m_settings = new QSettings(configDir + "/retroarch_qt.cfg", QSettings::IniFormat, this);
1410 m_viewOptionsDialog = new ViewOptionsDialog(this, 0);
1411 m_playlistEntryDialog = new PlaylistEntryDialog(this, 0);
1412
1413 /* default NULL parameter for parent wasn't added until 5.7 */
1414 m_startCorePushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_START_CORE), m_startCorePushButton));
1415 m_startCorePushButton->setFixedSize(m_startCorePushButton->sizeHint());
1416
1417 m_runPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN), m_runPushButton));
1418 m_runPushButton->setFixedSize(m_runPushButton->sizeHint());
1419
1420 m_stopPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_STOP), m_stopPushButton));
1421 m_stopPushButton->setFixedSize(m_stopPushButton->sizeHint());
1422
1423 m_coreInfoPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFO), m_coreInfoPushButton));
1424 m_coreInfoPushButton->setFixedSize(m_coreInfoPushButton->sizeHint());
1425
1426 searchResetButton = new QToolButton(m_searchWidget);
1427 searchResetButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_SEARCH_CLEAR), searchResetButton));
1428 searchResetButton->setFixedSize(searchResetButton->sizeHint());
1429
1430 connect(searchResetButton, SIGNAL(clicked()), this, SLOT(onSearchResetClicked()));
1431
1432 m_dirModel->setFilter(QDir::NoDotAndDotDot |
1433 QDir::AllDirs |
1434 QDir::Drives |
1435 (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast<QDir::Filter>(0)));
1436
1437 m_fileModel->setFilter(QDir::NoDot |
1438 QDir::AllEntries |
1439 (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast<QDir::Filter>(0)));
1440
1441 #if defined(Q_OS_WIN)
1442 m_dirModel->setRootPath("");
1443 m_fileModel->setRootPath("");
1444 #else
1445 m_dirModel->setRootPath("/");
1446 m_fileModel->setRootPath("/");
1447 #endif
1448
1449 m_dirTree->setModel(m_dirModel);
1450 m_dirTree->setSelectionMode(QAbstractItemView::SingleSelection);
1451 m_dirTree->header()->setVisible(false);
1452
1453 m_fileTableView->setModel(m_proxyFileModel);
1454
1455 if (m_dirModel->columnCount() > 3)
1456 {
1457 /* size */
1458 m_dirTree->hideColumn(1);
1459 /* type */
1460 m_dirTree->hideColumn(2);
1461 /* date modified */
1462 m_dirTree->hideColumn(3);
1463 }
1464
1465 reloadPlaylists();
1466
1467 m_searchWidget->setLayout(new QHBoxLayout());
1468 m_searchWidget->layout()->addWidget(m_searchLineEdit);
1469 m_searchWidget->layout()->addWidget(searchResetButton);
1470
1471 m_searchDock->setObjectName("searchDock");
1472 m_searchDock->setProperty("default_area", Qt::LeftDockWidgetArea);
1473 m_searchDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH));
1474 m_searchDock->setWidget(m_searchWidget);
1475 m_searchDock->setFixedHeight(m_searchDock->minimumSizeHint().height());
1476
1477 addDockWidget(static_cast<Qt::DockWidgetArea>(m_searchDock->property("default_area").toInt()), m_searchDock);
1478
1479 m_coreInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
1480 m_coreInfoLabel->setTextFormat(Qt::RichText);
1481 m_coreInfoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
1482 m_coreInfoLabel->setOpenExternalLinks(true);
1483
1484 m_coreInfoDock->setObjectName("coreInfoDock");
1485 m_coreInfoDock->setProperty("default_area", Qt::RightDockWidgetArea);
1486 m_coreInfoDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO));
1487 m_coreInfoDock->setWidget(m_coreInfoWidget);
1488
1489 addDockWidget(static_cast<Qt::DockWidgetArea>(m_coreInfoDock->property("default_area").toInt()), m_coreInfoDock);
1490
1491 m_logWidget->setLayout(new QVBoxLayout());
1492 m_logWidget->layout()->addWidget(m_logTextEdit);
1493 m_logWidget->layout()->setContentsMargins(0, 0, 0, 0);
1494
1495 m_logDock->setObjectName("logDock");
1496 m_logDock->setProperty("default_area", Qt::BottomDockWidgetArea);
1497 m_logDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG));
1498 m_logDock->setWidget(m_logWidget);
1499
1500 addDockWidget(static_cast<Qt::DockWidgetArea>(m_logDock->property("default_area").toInt()), m_logDock);
1501
1502 /* Hide the log by default. If user has saved their dock positions with the log visible,
1503 * then this hide() call will be reversed later by restoreState().
1504 * FIXME: If user unchecks "save dock positions", the log will not be unhidden even if
1505 * it was previously saved in the config.
1506 */
1507 m_logDock->hide();
1508
1509 m_dirTree->setContextMenuPolicy(Qt::CustomContextMenu);
1510 m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
1511
1512 connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(onSearchEnterPressed()));
1513 connect(m_searchLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onSearchLineEditEdited(const QString&)));
1514 connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
1515 connect(m_loadCoreWindow, SIGNAL(coreLoaded()), this, SLOT(onCoreLoaded()));
1516 connect(m_loadCoreWindow, SIGNAL(windowClosed()), this, SLOT(onCoreLoadWindowClosed()));
1517 connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(onCurrentListItemChanged(QListWidgetItem*, QListWidgetItem*)));
1518 connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked()));
1519 connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo()));
1520 connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked()));
1521 connect(m_stopPushButton, SIGNAL(clicked()), this, SLOT(onStopClicked()));
1522 connect(m_dirTree, SIGNAL(itemsSelected(QModelIndexList)), this, SLOT(onTreeViewItemsSelected(QModelIndexList)));
1523 connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&)));
1524 connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&)));
1525 connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int)));
1526 connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int)));
1527 connect(thumbnailTypeBoxartAction, SIGNAL(triggered()), this, SLOT(onBoxartThumbnailClicked()));
1528 connect(thumbnailTypeScreenshotAction, SIGNAL(triggered()), this, SLOT(onScreenshotThumbnailClicked()));
1529 connect(thumbnailTypeTitleAction, SIGNAL(triggered()), this, SLOT(onTitleThumbnailClicked()));
1530 connect(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked()));
1531 connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked()));
1532 connect(m_dirModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileSystemDirLoaded(const QString&)));
1533 connect(m_fileModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileBrowserTableDirLoaded(const QString&)));
1534
1535 m_dirTree->setCurrentIndex(m_dirModel->index(path_dir_menu_content));
1536 m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop);
1537 m_dirTree->expand(m_dirTree->currentIndex());
1538
1539 /* must use queued connection */
1540 connect(this, SIGNAL(scrollToDownloads(QString)), this, SLOT(onDownloadScroll(QString)), Qt::QueuedConnection);
1541 connect(this, SIGNAL(scrollToDownloadsAgain(QString)), this, SLOT(onDownloadScrollAgain(QString)), Qt::QueuedConnection);
1542
1543 connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), m_playlistThumbnailDownloadProgressDialog, SLOT(cancel()));
1544 connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onPlaylistThumbnailDownloadCanceled()));
1545
1546 connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailDownloadProgressDialog, SLOT(cancel()));
1547 connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailDownloadCanceled()));
1548
1549 connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailPackDownloadProgressDialog, SLOT(cancel()));
1550 connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailPackDownloadCanceled()));
1551
1552 connect(this, SIGNAL(itemChanged()), this, SLOT(onItemChanged()));
1553 connect(this, SIGNAL(gotThumbnailDownload(QString,QString)), this, SLOT(onDownloadThumbnail(QString,QString)));
1554
1555 m_thumbnailTimer->setSingleShot(true);
1556 connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems()));
1557 connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems()));
1558
1559 /* TODO: Handle scroll and resize differently. */
1560 connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer()));
1561
1562 connect(m_tableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentItemChanged(const QModelIndex&)));
1563 connect(m_fileTableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentFileChanged(const QModelIndex&)));
1564
1565 connect(m_gridView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&)));
1566 connect(m_tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&)));
1567 connect(m_fileTableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onFileDoubleClicked(const QModelIndex&)));
1568
1569 connect(m_playlistModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), this, SLOT(onCurrentTableItemDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
1570
1571 /* make sure these use an auto connection so it will be queued if called from a different thread (some facilities in RA log messages from other threads) */
1572 connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection);
1573 connect(this, SIGNAL(gotStatusMessage(QString,unsigned,unsigned,bool)), this, SLOT(onGotStatusMessage(QString,unsigned,unsigned,bool)), Qt::AutoConnection);
1574 connect(this, SIGNAL(gotReloadPlaylists()), this, SLOT(onGotReloadPlaylists()), Qt::AutoConnection);
1575 #if defined(HAVE_MENU)
1576 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
1577 connect(this, SIGNAL(gotReloadShaderParams()), this, SLOT(onGotReloadShaderParams()), Qt::AutoConnection);
1578 #endif
1579 #endif
1580 connect(this, SIGNAL(gotReloadCoreOptions()), this, SLOT(onGotReloadCoreOptions()), Qt::AutoConnection);
1581
1582 /* these are always queued */
1583 connect(this, SIGNAL(showErrorMessageDeferred(QString)), this, SLOT(onShowErrorMessage(QString)), Qt::QueuedConnection);
1584 connect(this, SIGNAL(showInfoMessageDeferred(QString)), this, SLOT(onShowInfoMessage(QString)), Qt::QueuedConnection);
1585 connect(this, SIGNAL(extractArchiveDeferred(QString,QString,QString,retro_task_callback_t)), this, SLOT(onExtractArchive(QString,QString,QString,retro_task_callback_t)), Qt::QueuedConnection);
1586
1587 m_timer->start(TIMER_MSEC);
1588
1589 statusBar()->addPermanentWidget(m_statusLabel);
1590
1591 setCurrentCoreLabel();
1592 setCoreActions();
1593
1594 /* both of these are necessary to get the folder to scroll to the top of the view */
1595 qApp->processEvents();
1596 QTimer::singleShot(0, this, SLOT(onBrowserStartClicked()));
1597
1598 m_searchLineEdit->setFocus();
1599 m_loadCoreWindow->setWindowModality(Qt::ApplicationModal);
1600
1601 m_statusMessageElapsedTimer.start();
1602
1603 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
1604 resizeDocks(QList<QDockWidget*>() << m_searchDock, QList<int>() << 1, Qt::Vertical);
1605 #endif
1606
1607 #ifdef HAVE_OPENSSL
1608 {
1609 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
1610 const SSL_METHOD *method = TLS_method();
1611 SSL_CTX *ctx = SSL_CTX_new(method);
1612
1613 if (ctx)
1614 SSL_CTX_free(ctx);
1615 #else
1616 const SSL_METHOD *method = TLSv1_method();
1617 RARCH_LOG("[Qt]: TLS supports %d ciphers.\n", method->num_ciphers());
1618 #endif
1619 RARCH_LOG("[Qt]: Using %s\n", OPENSSL_VERSION_TEXT);
1620 }
1621 #endif
1622 }
1623
~MainWindow()1624 MainWindow::~MainWindow()
1625 {
1626 if (m_thumbnailPixmap)
1627 delete m_thumbnailPixmap;
1628 if (m_thumbnailPixmap2)
1629 delete m_thumbnailPixmap2;
1630 if (m_thumbnailPixmap3)
1631 delete m_thumbnailPixmap3;
1632 if (m_proxyFileModel)
1633 delete m_proxyFileModel;
1634 }
1635
startTimer()1636 void MainWindow::startTimer()
1637 {
1638 if (m_thumbnailTimer->isActive())
1639 {
1640 m_thumbnailTimer->stop();
1641 m_thumbnailTimer->start(50);
1642 }
1643 else
1644 m_thumbnailTimer->start(50);
1645 }
1646
updateVisibleItems()1647 void MainWindow::updateVisibleItems()
1648 {
1649 if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS && m_viewType == VIEW_TYPE_ICONS)
1650 {
1651 unsigned i;
1652 QVector<QModelIndex> indexes = m_gridView->visibleIndexes();
1653 size_t size = indexes.size();
1654
1655 for (i = 0; i < size; i++)
1656 m_playlistModel->loadThumbnail(m_proxyModel->mapToSource(indexes.at(i)));
1657 }
1658 }
1659
setThumbnailCacheLimit(int count)1660 void MainWindow::setThumbnailCacheLimit(int count)
1661 {
1662 if (count < 1)
1663 count = 0;
1664
1665 m_playlistModel->setThumbnailCacheLimit(count);
1666 }
1667
onFileSystemDirLoaded(const QString & path)1668 void MainWindow::onFileSystemDirLoaded(const QString &path)
1669 {
1670 if (path.isEmpty() || m_pendingDirScrollPath.isEmpty())
1671 return;
1672
1673 if (QDir(path) == QDir(m_pendingDirScrollPath))
1674 {
1675 m_pendingDirScrollPath = QString();
1676
1677 emit scrollToDownloads(path);
1678 }
1679 }
1680
1681 /* workaround for columns being resized */
onFileBrowserTableDirLoaded(const QString & path)1682 void MainWindow::onFileBrowserTableDirLoaded(const QString &path)
1683 {
1684 if (path.isEmpty())
1685 return;
1686
1687 m_fileTableView->horizontalHeader()->restoreState(m_fileTableHeaderState);
1688 }
1689
getPlaylists()1690 QVector<QPair<QString, QString> > MainWindow::getPlaylists()
1691 {
1692 unsigned i;
1693 QVector<QPair<QString, QString> > playlists;
1694 size_t size = m_listWidget->count();
1695
1696 for (i = 0; i < size; i++)
1697 {
1698 QString label, path;
1699 QPair<QString, QString> pair;
1700 QListWidgetItem *item = m_listWidget->item(i);
1701
1702 if (!item)
1703 continue;
1704
1705 label = item->text();
1706 path = item->data(Qt::UserRole).toString();
1707
1708 pair.first = label;
1709 pair.second = path;
1710
1711 playlists.append(pair);
1712 }
1713
1714 return playlists;
1715 }
1716
onItemChanged()1717 void MainWindow::onItemChanged()
1718 {
1719 QModelIndex index = getCurrentContentIndex();
1720 m_playlistModel->reloadThumbnail(index);
1721 onCurrentItemChanged(index);
1722 }
1723
getSpecialPlaylistPath(SpecialPlaylist playlist)1724 QString MainWindow::getSpecialPlaylistPath(SpecialPlaylist playlist)
1725 {
1726 switch (playlist)
1727 {
1728 case SPECIAL_PLAYLIST_HISTORY:
1729 if (m_historyPlaylistsItem)
1730 return m_historyPlaylistsItem->data(Qt::UserRole).toString();
1731 break;
1732 default:
1733 break;
1734 }
1735
1736 return QString();
1737 }
1738
lerp(double x,double y,double a,double b,double d)1739 double MainWindow::lerp(double x, double y, double a, double b, double d)
1740 {
1741 return a + (b - a) * ((double)(d - x) / (double)(y - x));
1742 }
1743
onIconViewClicked()1744 void MainWindow::onIconViewClicked()
1745 {
1746 setCurrentViewType(VIEW_TYPE_ICONS);
1747 }
1748
onListViewClicked()1749 void MainWindow::onListViewClicked()
1750 {
1751 setCurrentViewType(VIEW_TYPE_LIST);
1752 }
1753
onBoxartThumbnailClicked()1754 void MainWindow::onBoxartThumbnailClicked()
1755 {
1756 setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART);
1757 }
1758
onScreenshotThumbnailClicked()1759 void MainWindow::onScreenshotThumbnailClicked()
1760 {
1761 setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT);
1762 }
1763
onTitleThumbnailClicked()1764 void MainWindow::onTitleThumbnailClicked()
1765 {
1766 setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN);
1767 }
1768
setIconViewZoom(int zoomValue)1769 void MainWindow::setIconViewZoom(int zoomValue)
1770 {
1771 m_zoomSlider->setValue(zoomValue);
1772 }
1773
onZoomValueChanged(int zoomValue)1774 void MainWindow::onZoomValueChanged(int zoomValue)
1775 {
1776 int newSize = 0;
1777
1778 if (zoomValue < 50)
1779 newSize = expScale(
1780 lerp(0, 49, 25, 49, zoomValue) / 50.0, 102, 256);
1781 else
1782 newSize = expScale(zoomValue / 100.0, 256, 1024);
1783
1784 m_gridView->setGridSize(newSize);
1785
1786 m_lastZoomSliderValue = zoomValue;
1787 }
1788
showWelcomeScreen()1789 void MainWindow::showWelcomeScreen()
1790 {
1791 bool dontAsk = false;
1792 bool answer = false;
1793 const QString welcomeText = QStringLiteral(""
1794 "Welcome to the RetroArch Desktop Menu!<br>\n"
1795 "<br>\n"
1796 "Many settings and actions are currently only available in the familiar Big Picture menu, "
1797 "but this Desktop Menu should be functional for launching content and managing playlists.<br>\n"
1798 "<br>\n"
1799 "Some useful hotkeys for interacting with the Big Picture menu include:\n"
1800 "<ul><li>F1 - Bring up the Big Picture menu</li>\n"
1801 "<li>F - Switch between fullscreen and windowed modes</li>\n"
1802 "<li>F5 - Bring the Desktop Menu back if closed</li>\n"
1803 "<li>Esc - Exit RetroArch</li></ul>\n"
1804 "\n"
1805 "For more hotkeys and their assignments, see:<br>\n"
1806 "Settings -> Input -> Input Hotkey Binds<br>\n"
1807 "<br>\n"
1808 "Documentation for RetroArch, libretro and cores:<br>\n"
1809 "<a href=\"https://docs.libretro.com/\">https://docs.libretro.com/</a>");
1810
1811 if (!m_settings->value("show_welcome_screen", true).toBool())
1812 return;
1813
1814 answer = showMessageBox(welcomeText, MainWindow::MSGBOX_TYPE_QUESTION_OKCANCEL, Qt::ApplicationModal, true, &dontAsk);
1815
1816 if (answer && dontAsk)
1817 m_settings->setValue("show_welcome_screen", false);
1818 }
1819
customThemeString() const1820 const QString& MainWindow::customThemeString() const
1821 {
1822 return m_customThemeString;
1823 }
1824
setCustomThemeFile(QString filePath)1825 bool MainWindow::setCustomThemeFile(QString filePath)
1826 {
1827 if (filePath.isEmpty())
1828 {
1829 QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_PATH_IS_BLANK));
1830 return false;
1831 }
1832
1833 QFile file(filePath);
1834
1835 if (file.exists())
1836 {
1837 bool opened = file.open(QIODevice::ReadOnly);
1838
1839 if (!opened)
1840 {
1841 QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED));
1842 return false;
1843 }
1844
1845 {
1846 QByteArray fileArray = file.readAll();
1847 QString fileStr = QString::fromUtf8(fileArray);
1848
1849 file.close();
1850
1851 if (fileStr.isEmpty())
1852 {
1853 QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY));
1854 return false;
1855 }
1856
1857 setCustomThemeString(fileStr);
1858 }
1859 }
1860 else
1861 {
1862 QMessageBox::critical(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CUSTOM_THEME), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST));
1863 return false;
1864 }
1865
1866 return true;
1867 }
1868
setCustomThemeString(QString qss)1869 void MainWindow::setCustomThemeString(QString qss)
1870 {
1871 m_customThemeString = qss;
1872 }
1873
showMessageBox(QString msg,MessageBoxType msgType,Qt::WindowModality modality,bool showDontAsk,bool * dontAsk)1874 bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality, bool showDontAsk, bool *dontAsk)
1875 {
1876 QCheckBox *checkBox = NULL;
1877
1878 QPointer<QMessageBox> msgBoxPtr = new QMessageBox(this);
1879 QMessageBox *msgBox = msgBoxPtr.data();
1880
1881 msgBox->setWindowModality(modality);
1882 msgBox->setTextFormat(Qt::RichText);
1883 msgBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
1884
1885 if (showDontAsk)
1886 {
1887 checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox);
1888 /* QMessageBox::setCheckBox() is available since 5.2 */
1889 msgBox->setCheckBox(checkBox);
1890 }
1891
1892 switch (msgType)
1893 {
1894 case MSGBOX_TYPE_INFO:
1895 msgBox->setIcon(QMessageBox::Information);
1896 msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFORMATION));
1897 break;
1898 case MSGBOX_TYPE_WARNING:
1899 msgBox->setIcon(QMessageBox::Warning);
1900 msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_WARNING));
1901 break;
1902 case MSGBOX_TYPE_ERROR:
1903 msgBox->setIcon(QMessageBox::Critical);
1904 msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ERROR));
1905 break;
1906 case MSGBOX_TYPE_QUESTION_YESNO:
1907 msgBox->setIcon(QMessageBox::Question);
1908 msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION));
1909 msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1910 break;
1911 case MSGBOX_TYPE_QUESTION_OKCANCEL:
1912 msgBox->setIcon(QMessageBox::Question);
1913 msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION));
1914 msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
1915 break;
1916 default:
1917 break;
1918 }
1919
1920 msgBox->setText(msg);
1921 msgBox->exec();
1922
1923 if (!msgBoxPtr)
1924 return true;
1925
1926 if (
1927 msgBox->result() != QMessageBox::Ok
1928 && msgBox->result() != QMessageBox::Yes)
1929 return false;
1930
1931 if (checkBox)
1932 if (dontAsk)
1933 *dontAsk = checkBox->isChecked();
1934
1935 return true;
1936 }
1937
onFileBrowserTreeContextMenuRequested(const QPoint &)1938 void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&)
1939 {
1940 #ifdef HAVE_LIBRETRODB
1941 QDir dir;
1942 QByteArray dirArray;
1943 QPointer<QAction> action;
1944 QList<QAction*> actions;
1945 QScopedPointer<QAction> scanAction;
1946 QString currentDirString = QDir::toNativeSeparators(
1947 m_dirModel->filePath(m_dirTree->currentIndex()));
1948 settings_t *settings = config_get_ptr();
1949 const char *fullpath = NULL;
1950 const char *path_dir_playlist = settings->paths.directory_playlist;
1951 const char *path_content_db = settings->paths.path_content_database;
1952
1953 if (currentDirString.isEmpty())
1954 return;
1955
1956 dir = currentDirString;
1957
1958 if (!dir.exists())
1959 return;
1960
1961 /* default NULL parameter for parent wasn't added until 5.7 */
1962 scanAction.reset(new QAction(
1963 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY), 0));
1964
1965 actions.append(scanAction.data());
1966
1967 action = QMenu::exec(actions, QCursor::pos(), NULL, m_dirTree);
1968
1969 if (!action)
1970 return;
1971
1972 dirArray = currentDirString.toUtf8();
1973 fullpath = dirArray.constData();
1974
1975 task_push_dbscan(
1976 path_dir_playlist,
1977 path_content_db,
1978 fullpath, true,
1979 m_settings->value("show_hidden_files", true).toBool(),
1980 scan_finished_handler);
1981 #endif
1982 }
1983
showStatusMessage(QString msg,unsigned priority,unsigned duration,bool flush)1984 void MainWindow::showStatusMessage(QString msg,
1985 unsigned priority, unsigned duration, bool flush)
1986 {
1987 emit gotStatusMessage(msg, priority, duration, flush);
1988 }
1989
onGotStatusMessage(QString msg,unsigned priority,unsigned duration,bool flush)1990 void MainWindow::onGotStatusMessage(
1991 QString msg, unsigned priority, unsigned duration, bool flush)
1992 {
1993 int msecDuration = 0;
1994 QScreen *screen = qApp->primaryScreen();
1995 QStatusBar *status = statusBar();
1996
1997 Q_UNUSED(priority)
1998
1999 if (msg.isEmpty())
2000 return;
2001
2002 if (!status)
2003 return;
2004
2005 if (screen)
2006 msecDuration = (duration / screen->refreshRate()) * 1000;
2007
2008 if (msecDuration <= 0)
2009 msecDuration = 1000;
2010
2011 if (status->currentMessage().isEmpty() || flush)
2012 {
2013 if (m_statusMessageElapsedTimer.elapsed() >= STATUS_MSG_THROTTLE_MSEC)
2014 {
2015 qint64 msgDuration = qMax(msecDuration, STATUS_MSG_THROTTLE_MSEC);
2016 m_statusMessageElapsedTimer.restart();
2017 status->showMessage(msg, msgDuration);
2018 }
2019 }
2020 }
2021
deferReloadShaderParams()2022 void MainWindow::deferReloadShaderParams()
2023 {
2024 #if defined(HAVE_MENU)
2025 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
2026 emit gotReloadShaderParams();
2027 #endif
2028 #endif
2029 }
2030
onShaderParamsClicked()2031 void MainWindow::onShaderParamsClicked()
2032 {
2033 #if defined(HAVE_MENU)
2034 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
2035 if (!m_shaderParamsDialog)
2036 return;
2037
2038 m_shaderParamsDialog->show();
2039
2040 onGotReloadShaderParams();
2041 #endif
2042 #endif
2043 }
2044
onGotReloadShaderParams()2045 void MainWindow::onGotReloadShaderParams()
2046 {
2047 #if defined(HAVE_MENU)
2048 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
2049 if (m_shaderParamsDialog && m_shaderParamsDialog->isVisible())
2050 m_shaderParamsDialog->reload();
2051 #endif
2052 #endif
2053 }
2054
onCoreOptionsClicked()2055 void MainWindow::onCoreOptionsClicked()
2056 {
2057 if (!m_coreOptionsDialog)
2058 return;
2059
2060 m_coreOptionsDialog->show();
2061
2062 onGotReloadCoreOptions();
2063 }
2064
onGotReloadCoreOptions()2065 void MainWindow::onGotReloadCoreOptions()
2066 {
2067 if (m_coreOptionsDialog && m_coreOptionsDialog->isVisible())
2068 m_coreOptionsDialog->reload();
2069 }
2070
appendLogMessage(const QString & msg)2071 void MainWindow::appendLogMessage(const QString &msg)
2072 {
2073 emit gotLogMessage(msg);
2074 }
2075
onGotLogMessage(const QString & msg)2076 void MainWindow::onGotLogMessage(const QString &msg)
2077 {
2078 QString newMsg = msg;
2079
2080 if (newMsg.at(newMsg.size() - 1) == '\n')
2081 newMsg.chop(1);
2082
2083 m_logTextEdit->appendMessage(newMsg);
2084 }
2085
onLaunchWithComboBoxIndexChanged(int)2086 void MainWindow::onLaunchWithComboBoxIndexChanged(int)
2087 {
2088 QString coreInfoText;
2089 QVector<QHash<QString, QString> >
2090 infoList = getCoreInfo();
2091 QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value<QVariantMap>();
2092 core_selection coreSelection = static_cast<core_selection>(coreMap.value("core_selection").toInt());
2093 int i = 0;
2094
2095 if (infoList.count() == 0)
2096 return;
2097
2098 for (i = 0; i < infoList.count(); i++)
2099 {
2100 const QHash<QString, QString> &hash = infoList.at(i);
2101 const QString &key =
2102 hash.value("html_key", hash.value("key"));
2103 const QString &value =
2104 hash.value("html_value", hash.value("value"));
2105
2106 if (!key.isEmpty())
2107 coreInfoText += key;
2108
2109 if (!value.isEmpty())
2110 {
2111 if (!key.isEmpty())
2112 coreInfoText += " ";
2113
2114 coreInfoText += value;
2115 }
2116
2117 if (i < infoList.count() - 1)
2118 coreInfoText += "<br>\n";
2119 }
2120
2121 m_coreInfoLabel->setText(coreInfoText);
2122
2123 if (coreSelection == CORE_SELECTION_LOAD_CORE)
2124 onLoadCoreClicked();
2125 else
2126 m_loadCoreWindow->setProperty("last_launch_with_index",
2127 m_launchWithComboBox->currentIndex());
2128 }
2129
getThemeFromString(QString themeString)2130 MainWindow::Theme MainWindow::getThemeFromString(QString themeString)
2131 {
2132 if (themeString == "default")
2133 return THEME_SYSTEM_DEFAULT;
2134 else if (themeString == "dark")
2135 return THEME_DARK;
2136 else if (themeString == "custom")
2137 return THEME_CUSTOM;
2138
2139 return THEME_SYSTEM_DEFAULT;
2140 }
2141
getThemeString(Theme theme)2142 QString MainWindow::getThemeString(Theme theme)
2143 {
2144 switch (theme)
2145 {
2146 case THEME_SYSTEM_DEFAULT:
2147 return "default";
2148 case THEME_DARK:
2149 return "dark";
2150 case THEME_CUSTOM:
2151 return "custom";
2152 default:
2153 break;
2154 }
2155
2156 return "default";
2157 }
2158
theme()2159 MainWindow::Theme MainWindow::theme()
2160 {
2161 return m_currentTheme;
2162 }
2163
setTheme(Theme theme)2164 void MainWindow::setTheme(Theme theme)
2165 {
2166 m_currentTheme = theme;
2167
2168 setDefaultCustomProperties();
2169
2170 switch(theme)
2171 {
2172 case THEME_SYSTEM_DEFAULT:
2173 qApp->setStyleSheet(qt_theme_default_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString()));
2174 break;
2175 case THEME_DARK:
2176 qApp->setStyleSheet(qt_theme_dark_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString()));
2177 break;
2178 case THEME_CUSTOM:
2179 qApp->setStyleSheet(m_customThemeString);
2180 break;
2181 default:
2182 break;
2183 }
2184 #ifdef HAVE_MENU
2185 m_viewOptionsDialog->repaintIcons();
2186 #endif
2187 }
2188
setDefaultCustomProperties()2189 void MainWindow::setDefaultCustomProperties()
2190 {
2191 m_gridView->setLayout(QString(DEFAULT_GRID_LAYOUT));
2192 m_gridView->setSpacing(DEFAULT_GRID_SPACING);
2193 m_gridItem.setThumbnailVerticalAlign(QString(DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT));
2194 m_gridItem.setPadding(DEFAULT_GRID_ITEM_MARGIN);
2195 }
2196
changeThumbnailType(ThumbnailType type)2197 void MainWindow::changeThumbnailType(ThumbnailType type)
2198 {
2199 m_playlistModel->setThumbnailType(type);
2200 updateVisibleItems();
2201 m_gridView->viewport()->update();
2202 }
2203
changeThumbnail(const QImage & image,QString type)2204 QString MainWindow::changeThumbnail(const QImage &image, QString type)
2205 {
2206 QHash<QString, QString> hash = getCurrentContentHash();
2207 QString dirString = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]) + "/" + type;
2208 QString thumbPath = dirString + "/" + m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]);
2209 QByteArray dirArray = QDir::toNativeSeparators(dirString).toUtf8();
2210 const char *dirData = dirArray.constData();
2211 QByteArray thumbArray = QDir::toNativeSeparators(thumbPath).toUtf8();
2212 const char *thumbData = thumbArray.constData();
2213 int quality = -1;
2214 QDir dir(dirString);
2215 QImage scaledImage(image);
2216
2217 if (!dir.exists())
2218 {
2219 if (dir.mkpath("."))
2220 RARCH_LOG("[Qt]: Created directory: %s\n", dirData);
2221 else
2222 {
2223 RARCH_ERR("[Qt]: Could not create directory: %s\n", dirData);
2224 return QString();
2225 }
2226 }
2227
2228 if (m_settings->contains("thumbnail_max_size"))
2229 {
2230 int size = m_settings->value("thumbnail_max_size", 0).toInt();
2231
2232 if (size != 0 && (image.height() > size || image.width() > size))
2233 scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
2234 }
2235
2236 if (m_settings->contains("thumbnail_quality"))
2237 quality = m_settings->value("thumbnail_quality", -1).toInt();
2238
2239 if (scaledImage.save(thumbPath, "png", quality))
2240 {
2241 RARCH_LOG("[Qt]: Saved image: %s\n", thumbData);
2242 m_playlistModel->reloadThumbnailPath(thumbPath);
2243 updateVisibleItems();
2244
2245 return thumbPath;
2246 }
2247
2248 RARCH_ERR("[Qt]: Could not save image: %s\n", thumbData);
2249 return QString();
2250 }
2251
onThumbnailDropped(const QImage & image,ThumbnailType thumbnailType)2252 void MainWindow::onThumbnailDropped(const QImage &image,
2253 ThumbnailType thumbnailType)
2254 {
2255 switch (thumbnailType)
2256 {
2257 case THUMBNAIL_TYPE_BOXART:
2258 {
2259 QString path = changeThumbnail(image, THUMBNAIL_BOXART);
2260
2261 if (path.isNull())
2262 return;
2263
2264 if (m_thumbnailPixmap)
2265 delete m_thumbnailPixmap;
2266
2267 m_thumbnailPixmap = new QPixmap(path);
2268
2269 onResizeThumbnailOne(*m_thumbnailPixmap, true);
2270 break;
2271 }
2272
2273 case THUMBNAIL_TYPE_TITLE_SCREEN:
2274 {
2275 QString path = changeThumbnail(image, THUMBNAIL_TITLE);
2276
2277 if (path.isNull())
2278 return;
2279
2280 if (m_thumbnailPixmap2)
2281 delete m_thumbnailPixmap2;
2282
2283 m_thumbnailPixmap2 = new QPixmap(path);
2284
2285 onResizeThumbnailTwo(*m_thumbnailPixmap2, true);
2286 break;
2287 }
2288
2289 case THUMBNAIL_TYPE_SCREENSHOT:
2290 {
2291 QString path = changeThumbnail(image, THUMBNAIL_SCREENSHOT);
2292
2293 if (path.isNull())
2294 return;
2295
2296 if (m_thumbnailPixmap3)
2297 delete m_thumbnailPixmap3;
2298
2299 m_thumbnailPixmap3 = new QPixmap(path);
2300
2301 onResizeThumbnailThree(*m_thumbnailPixmap3, true);
2302 break;
2303 }
2304 }
2305 }
2306
getCoreInfo()2307 QVector<QHash<QString, QString> > MainWindow::getCoreInfo()
2308 {
2309 unsigned i;
2310 QVector<QHash<QString, QString> > infoList;
2311 QHash<QString, QString> currentCore = getSelectedCore();
2312 core_info_t *core_info = NULL;
2313 QByteArray currentCorePathArray = currentCore["core_path"].toUtf8();
2314 const char *current_core_path_data = currentCorePathArray.constData();
2315
2316 /* Search for current core */
2317 core_info_find(current_core_path_data, &core_info);
2318
2319 if ( currentCore["core_path"].isEmpty()
2320 || !core_info
2321 || !core_info->has_info)
2322 {
2323 QHash<QString, QString> hash;
2324
2325 hash["key"] = msg_hash_to_str(
2326 MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE);
2327 hash["value"] = "";
2328
2329 infoList.append(hash);
2330
2331 return infoList;
2332 }
2333
2334 if (core_info->core_name)
2335 {
2336 QHash<QString, QString> hash;
2337
2338 hash["key"] = QString(
2339 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME)) + ":";
2340 hash["value"] = core_info->core_name;
2341
2342 infoList.append(hash);
2343 }
2344
2345 if (core_info->display_name)
2346 {
2347 QHash<QString, QString> hash;
2348
2349 hash["key"] = QString(
2350 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL)) + ":";
2351 hash["value"] = core_info->display_name;
2352
2353 infoList.append(hash);
2354 }
2355
2356 if (core_info->systemname)
2357 {
2358 QHash<QString, QString> hash;
2359
2360 hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME)) + ":";
2361 hash["value"] = core_info->systemname;
2362
2363 infoList.append(hash);
2364 }
2365
2366 if (core_info->system_manufacturer)
2367 {
2368 QHash<QString, QString> hash;
2369
2370 hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER)) + ":";
2371 hash["value"] = core_info->system_manufacturer;
2372
2373 infoList.append(hash);
2374 }
2375
2376 if (core_info->categories_list)
2377 {
2378 QHash<QString, QString> hash;
2379 QString categories;
2380
2381 for (i = 0; i < core_info->categories_list->size; i++)
2382 {
2383 categories += core_info->categories_list->elems[i].data;
2384
2385 if (i < core_info->categories_list->size - 1)
2386 categories += ", ";
2387 }
2388
2389 hash["key"] = QString(
2390 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES)) + ":";
2391 hash["value"] = categories;
2392
2393 infoList.append(hash);
2394 }
2395
2396 if (core_info->authors_list)
2397 {
2398 QHash<QString, QString> hash;
2399 QString authors;
2400
2401 for (i = 0; i < core_info->authors_list->size; i++)
2402 {
2403 authors += core_info->authors_list->elems[i].data;
2404
2405 if (i < core_info->authors_list->size - 1)
2406 authors += ", ";
2407 }
2408
2409 hash["key"] = QString(
2410 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS)) + ":";
2411 hash["value"] = authors;
2412
2413 infoList.append(hash);
2414 }
2415
2416 if (core_info->permissions_list)
2417 {
2418 QHash<QString, QString> hash;
2419 QString permissions;
2420
2421 for (i = 0; i < core_info->permissions_list->size; i++)
2422 {
2423 permissions += core_info->permissions_list->elems[i].data;
2424
2425 if (i < core_info->permissions_list->size - 1)
2426 permissions += ", ";
2427 }
2428
2429 hash["key"] = QString(
2430 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS)) + ":";
2431 hash["value"] = permissions;
2432
2433 infoList.append(hash);
2434 }
2435
2436 if (core_info->licenses_list)
2437 {
2438 QHash<QString, QString> hash;
2439 QString licenses;
2440
2441 for (i = 0; i < core_info->licenses_list->size; i++)
2442 {
2443 licenses += core_info->licenses_list->elems[i].data;
2444
2445 if (i < core_info->licenses_list->size - 1)
2446 licenses += ", ";
2447 }
2448
2449 hash["key"] = QString(
2450 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES)) + ":";
2451 hash["value"] = licenses;
2452
2453 infoList.append(hash);
2454 }
2455
2456 if (core_info->supported_extensions_list)
2457 {
2458 QHash<QString, QString> hash;
2459 QString supported_extensions;
2460
2461 for (i = 0; i < core_info->supported_extensions_list->size; i++)
2462 {
2463 supported_extensions += core_info->supported_extensions_list->elems[i].data;
2464
2465 if (i < core_info->supported_extensions_list->size - 1)
2466 supported_extensions += ", ";
2467 }
2468
2469 hash["key"] = QString(
2470 msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS)) + ":";
2471 hash["value"] = supported_extensions;
2472
2473 infoList.append(hash);
2474 }
2475
2476 if (core_info->firmware_count > 0)
2477 {
2478 core_info_ctx_firmware_t firmware_info;
2479 bool update_missing_firmware = false;
2480 bool set_missing_firmware = false;
2481 settings_t *settings = config_get_ptr();
2482
2483 firmware_info.path = core_info->path;
2484 firmware_info.directory.system = settings->paths.directory_system;
2485
2486 rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL);
2487
2488 update_missing_firmware = core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware);
2489
2490 if (set_missing_firmware)
2491 rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL);
2492
2493 if (update_missing_firmware)
2494 {
2495 QHash<QString, QString> hash;
2496
2497 hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE)) + ":";
2498 hash["value"] = "";
2499
2500 infoList.append(hash);
2501
2502 /* FIXME: This looks hacky and probably
2503 * needs to be improved for good translation support. */
2504
2505 for (i = 0; i < core_info->firmware_count; i++)
2506 {
2507 if (core_info->firmware[i].desc)
2508 {
2509 QString valueText;
2510 QHash<QString, QString> hash;
2511 QString labelText = "(!) ";
2512 bool missing = false;
2513
2514 if (core_info->firmware[i].missing)
2515 {
2516 missing = true;
2517 labelText += msg_hash_to_str(
2518 MENU_ENUM_LABEL_VALUE_MISSING);
2519 }
2520 else
2521 labelText += msg_hash_to_str(
2522 MENU_ENUM_LABEL_VALUE_PRESENT);
2523
2524 labelText += ", ";
2525
2526 if (core_info->firmware[i].optional)
2527 labelText += msg_hash_to_str(
2528 MENU_ENUM_LABEL_VALUE_OPTIONAL);
2529 else
2530 labelText += msg_hash_to_str(
2531 MENU_ENUM_LABEL_VALUE_REQUIRED);
2532
2533 labelText += ":";
2534
2535 if (core_info->firmware[i].desc)
2536 valueText = core_info->firmware[i].desc;
2537 else
2538 valueText = msg_hash_to_str(
2539 MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME);
2540
2541 hash["key"] = labelText;
2542 hash["value"] = valueText;
2543
2544 if (missing)
2545 {
2546 QString style = "font-weight: bold; color: #ff0000";
2547 hash["label_style"] = style;
2548 hash["value_style"] = style;
2549 hash["html_key"] = "<b><font color=\"#ff0000\">" + hash["key"] + "</font></b>";
2550 hash["html_value"] = "<b><font color=\"#ff0000\">" + hash["value"] + "</font></b>";
2551 }
2552 else
2553 {
2554 QString style = "font-weight: bold; color: rgb(0, 175, 0)";
2555 hash["label_style"] = style;
2556 hash["value_style"] = style;
2557 hash["html_key"] = "<b><font color=\"#00af00\">" + hash["key"] + "</font></b>";
2558 hash["html_value"] = "<b><font color=\"#00af00\">" + hash["value"] + "</font></b>";
2559 }
2560
2561 infoList.append(hash);
2562 }
2563 }
2564 }
2565 }
2566
2567 if (core_info->notes)
2568 {
2569 for (i = 0; i < core_info->note_list->size; i++)
2570 {
2571 QHash<QString, QString> hash;
2572
2573 hash["key"] = "";
2574 hash["value"] = core_info->note_list->elems[i].data;
2575
2576 infoList.append(hash);
2577 }
2578 }
2579
2580 return infoList;
2581 }
2582
onSearchResetClicked()2583 void MainWindow::onSearchResetClicked()
2584 {
2585 m_searchLineEdit->clear();
2586 onSearchEnterPressed();
2587 }
2588
coreInfoPushButton()2589 QToolButton* MainWindow::coreInfoPushButton()
2590 {
2591 return m_coreInfoPushButton;
2592 }
2593
onTreeViewItemsSelected(QModelIndexList selectedIndexes)2594 void MainWindow::onTreeViewItemsSelected(QModelIndexList selectedIndexes)
2595 {
2596 QString dir;
2597
2598 if (selectedIndexes.isEmpty())
2599 return;
2600
2601 dir = m_dirModel->filePath(selectedIndexes.first());
2602
2603 selectBrowserDir(dir);
2604 }
2605
onFileDoubleClicked(const QModelIndex & proxyIndex)2606 void MainWindow::onFileDoubleClicked(const QModelIndex &proxyIndex)
2607 {
2608 const QModelIndex index = m_proxyFileModel->mapToSource(proxyIndex);
2609
2610 if (m_fileModel->isDir(index))
2611 m_dirTree->setCurrentIndex(m_dirModel->index(m_fileModel->filePath(index)));
2612 else
2613 loadContent(getFileContentHash(index));
2614 }
2615
selectBrowserDir(QString path)2616 void MainWindow::selectBrowserDir(QString path)
2617 {
2618 if (!path.isEmpty())
2619 {
2620 QModelIndex sourceIndex = m_fileModel->setRootPath(path);
2621 QModelIndex proxyIndex = m_proxyFileModel->mapFromSource(sourceIndex);
2622 m_fileTableHeaderState = m_fileTableView->horizontalHeader()->saveState();
2623
2624 if (proxyIndex.isValid())
2625 m_fileTableView->setRootIndex(proxyIndex);
2626 else
2627 {
2628 /* the directory is filtered out. Remove the filter for a moment.
2629 * FIXME: Find a way to not have to do this
2630 * (not filtering dirs is one). */
2631 m_proxyFileModel->setFilterRegExp(QRegExp());
2632 m_fileTableView->setRootIndex(m_proxyFileModel->mapFromSource(sourceIndex));
2633 m_proxyFileModel->setFilterRegExp(m_searchRegExp);
2634 }
2635 }
2636 setCoreActions();
2637 }
2638
browserAndPlaylistTabWidget()2639 QTabWidget* MainWindow::browserAndPlaylistTabWidget()
2640 {
2641 return m_browserAndPlaylistTabWidget;
2642 }
2643
onDropWidgetEnterPressed()2644 void MainWindow::onDropWidgetEnterPressed()
2645 {
2646 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
2647 /* entry is being renamed, ignore this enter press */
2648 if (m_tableView->isPersistentEditorOpen(m_tableView->currentIndex()))
2649 #else
2650 /* we can only check if any editor at all is open */
2651 if (m_tableView->isEditorOpen())
2652 #endif
2653 return;
2654 onRunClicked();
2655 }
2656
getCurrentContentIndex()2657 QModelIndex MainWindow::getCurrentContentIndex()
2658 {
2659 if (m_viewType == VIEW_TYPE_LIST)
2660 return m_tableView->currentIndex();
2661 else if (m_viewType == VIEW_TYPE_ICONS)
2662 return m_gridView->currentIndex();
2663 return QModelIndex();
2664 }
2665
getCurrentContentHash()2666 QHash<QString, QString> MainWindow::getCurrentContentHash()
2667 {
2668 return getCurrentContentIndex().data(PlaylistModel::HASH).value<QHash<QString, QString> >();
2669 }
2670
getFileContentHash(const QModelIndex & index)2671 QHash<QString, QString> MainWindow::getFileContentHash(const QModelIndex &index)
2672 {
2673 QHash<QString, QString> hash;
2674 QFileInfo fileInfo = m_fileModel->fileInfo(index);
2675
2676 hash["path"] = QDir::toNativeSeparators(m_fileModel->filePath(index));
2677 hash["label"] = hash["path"];
2678 hash["label_noext"] = fileInfo.completeBaseName();
2679 hash["db_name"] = fileInfo.dir().dirName();
2680
2681 return hash;
2682 }
2683
onContentItemDoubleClicked(const QModelIndex & index)2684 void MainWindow::onContentItemDoubleClicked(const QModelIndex &index)
2685 {
2686 Q_UNUSED(index);
2687 onRunClicked();
2688 }
2689
onStartCoreClicked()2690 void MainWindow::onStartCoreClicked()
2691 {
2692 content_ctx_info_t content_info;
2693
2694 content_info.argc = 0;
2695 content_info.argv = NULL;
2696 content_info.args = NULL;
2697 content_info.environ_get = NULL;
2698
2699 path_clear(RARCH_PATH_BASENAME);
2700
2701 if (!task_push_start_current_core(&content_info))
2702 QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT));
2703 }
2704
getSelectedCore()2705 QHash<QString, QString> MainWindow::getSelectedCore()
2706 {
2707 QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value<QVariantMap>();
2708 core_selection coreSelection = static_cast<core_selection>(coreMap.value("core_selection").toInt());
2709 QHash<QString, QString> coreHash;
2710 QHash<QString, QString> contentHash;
2711 ViewType viewType = getCurrentViewType();
2712
2713 if (viewType == VIEW_TYPE_LIST)
2714 contentHash = m_tableView->currentIndex().data(PlaylistModel::HASH).value<QHash<QString, QString> >();
2715 else if (viewType == VIEW_TYPE_ICONS)
2716 contentHash = m_gridView->currentIndex().data(PlaylistModel::HASH).value<QHash<QString, QString> >();
2717 else
2718 return coreHash;
2719
2720 switch(coreSelection)
2721 {
2722 case CORE_SELECTION_CURRENT:
2723 coreHash["core_path"] = path_get(RARCH_PATH_CORE);
2724 break;
2725 case CORE_SELECTION_PLAYLIST_SAVED:
2726 if (contentHash.isEmpty() || contentHash["core_path"].isEmpty())
2727 break;
2728
2729 coreHash["core_path"] = contentHash["core_path"];
2730
2731 break;
2732 case CORE_SELECTION_PLAYLIST_DEFAULT:
2733 {
2734 QString plName;
2735 QString defaultCorePath;
2736
2737 if (contentHash.isEmpty())
2738 break;
2739
2740 plName = contentHash["pl_name"].isEmpty() ?
2741 contentHash["db_name"] : contentHash["pl_name"];
2742
2743 if (plName.isEmpty())
2744 break;
2745
2746 defaultCorePath = getPlaylistDefaultCore(plName);
2747
2748 if (!defaultCorePath.isEmpty())
2749 coreHash["core_path"] = defaultCorePath;
2750
2751 break;
2752 }
2753 default:
2754 break;
2755 }
2756
2757 return coreHash;
2758 }
2759
2760 /* the hash typically has the following keys:
2761 path - absolute path to the content file
2762 core_path - absolute path to the core, or "DETECT" to ask the user
2763 db_name - the display name of the rdb database this content is from
2764 label - the display name of the content, usually comes from the database
2765 crc32 - an upper-case, 8 byte string representation of the hex CRC32 checksum (e.g. ABCDEF12) followed by "|crc"
2766 core_name - the display name of the core, or "DETECT" if unknown
2767 label_noext - the display name of the content that is guaranteed not to contain a file extension
2768 */
loadContent(const QHash<QString,QString> & contentHash)2769 void MainWindow::loadContent(const QHash<QString, QString> &contentHash)
2770 {
2771 content_ctx_info_t content_info;
2772 QByteArray corePathArray;
2773 QByteArray contentPathArray;
2774 QByteArray contentLabelArray;
2775 QByteArray contentDbNameArray;
2776 QByteArray contentCrc32Array;
2777 char contentDbNameFull[PATH_MAX_LENGTH];
2778 const char *corePath = NULL;
2779 const char *contentPath = NULL;
2780 const char *contentLabel = NULL;
2781 const char *contentDbName = NULL;
2782 const char *contentCrc32 = NULL;
2783 QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value<QVariantMap>();
2784 core_selection coreSelection = static_cast<core_selection>(coreMap.value("core_selection").toInt());
2785 core_info_t *coreInfo = NULL;
2786
2787 contentDbNameFull[0] = '\0';
2788
2789 if (m_pendingRun)
2790 coreSelection = CORE_SELECTION_CURRENT;
2791
2792 if (coreSelection == CORE_SELECTION_ASK)
2793 {
2794 QStringList extensionFilters;
2795
2796 if (contentHash.contains("path"))
2797 {
2798 QString extensionStr;
2799 int lastIndex = contentHash["path"].lastIndexOf('.');
2800 QByteArray pathArray = contentHash["path"].toUtf8();
2801 const char *pathData = pathArray.constData();
2802
2803 if (lastIndex >= 0)
2804 {
2805 extensionStr = contentHash["path"].mid(lastIndex + 1);
2806
2807 if (!extensionStr.isEmpty())
2808 extensionFilters.append(extensionStr.toLower());
2809 }
2810
2811 if (path_is_compressed_file(pathData))
2812 {
2813 unsigned i = 0;
2814 struct string_list *list = file_archive_get_file_list(pathData, NULL);
2815
2816 if (list)
2817 {
2818 if (list->size > 0)
2819 {
2820 for (i = 0; i < list->size; i++)
2821 {
2822 const char *filePath = list->elems[i].data;
2823 const char *extension = path_get_extension(filePath);
2824
2825 if (!extensionFilters.contains(extension, Qt::CaseInsensitive))
2826 extensionFilters.append(extension);
2827 }
2828 }
2829
2830 string_list_free(list);
2831 }
2832 }
2833 }
2834
2835 m_pendingRun = true;
2836 onLoadCoreClicked(extensionFilters);
2837
2838 return;
2839 }
2840
2841 switch (coreSelection)
2842 {
2843 case CORE_SELECTION_CURRENT:
2844 corePathArray = path_get(RARCH_PATH_CORE);
2845 contentPathArray = contentHash["path"].toUtf8();
2846 contentLabelArray = contentHash["label_noext"].toUtf8();
2847 break;
2848 case CORE_SELECTION_PLAYLIST_SAVED:
2849 corePathArray = contentHash["core_path"].toUtf8();
2850 contentPathArray = contentHash["path"].toUtf8();
2851 contentLabelArray = contentHash["label_noext"].toUtf8();
2852 break;
2853 case CORE_SELECTION_PLAYLIST_DEFAULT:
2854 {
2855 QString plName = contentHash["pl_name"].isEmpty() ?
2856 contentHash["db_name"] : contentHash["pl_name"];
2857
2858 QString defaultCorePath = getPlaylistDefaultCore(plName);
2859
2860 if (!defaultCorePath.isEmpty())
2861 {
2862 corePathArray = defaultCorePath.toUtf8();
2863 contentPathArray = contentHash["path"].toUtf8();
2864 contentLabelArray = contentHash["label_noext"].toUtf8();
2865 }
2866
2867 break;
2868 }
2869 default:
2870 return;
2871 }
2872
2873 contentDbNameArray = contentHash["db_name"].toUtf8();
2874 contentCrc32Array = contentHash["crc32"].toUtf8();
2875
2876 corePath = corePathArray.constData();
2877 contentPath = contentPathArray.constData();
2878 contentLabel = contentLabelArray.constData();
2879 contentDbName = contentDbNameArray.constData();
2880 contentCrc32 = contentCrc32Array.constData();
2881
2882 /* Search for specified core - ensures path
2883 * is 'sanitised' */
2884 if (core_info_find(corePath, &coreInfo) &&
2885 !string_is_empty(coreInfo->path))
2886 corePath = coreInfo->path;
2887
2888 /* Add lpl extension to db_name, if required */
2889 if (!string_is_empty(contentDbName))
2890 {
2891 const char *extension = NULL;
2892
2893 strlcpy(contentDbNameFull, contentDbName, sizeof(contentDbNameFull));
2894 extension = path_get_extension(contentDbNameFull);
2895
2896 if ( string_is_empty(extension)
2897 || !string_is_equal_noncase(
2898 extension, FILE_PATH_LPL_EXTENSION_NO_DOT))
2899 strlcat(
2900 contentDbNameFull, FILE_PATH_LPL_EXTENSION,
2901 sizeof(contentDbNameFull));
2902 }
2903
2904 content_info.argc = 0;
2905 content_info.argv = NULL;
2906 content_info.args = NULL;
2907 content_info.environ_get = NULL;
2908
2909 #ifdef HAVE_MENU
2910 menu_navigation_set_selection(0);
2911 #endif
2912
2913 command_event(CMD_EVENT_UNLOAD_CORE, NULL);
2914
2915 if (!task_push_load_content_with_new_core_from_companion_ui(
2916 corePath, contentPath, contentLabel, contentDbNameFull, contentCrc32,
2917 &content_info, NULL, NULL))
2918 {
2919 QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR),
2920 msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT));
2921 return;
2922 }
2923
2924 #ifdef HAVE_MENU
2925 menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL);
2926 #endif
2927 }
2928
onRunClicked()2929 void MainWindow::onRunClicked()
2930 {
2931 QHash<QString, QString> contentHash;
2932
2933 switch (m_currentBrowser)
2934 {
2935 case BROWSER_TYPE_FILES:
2936 contentHash = getFileContentHash(
2937 m_proxyFileModel->mapToSource(m_fileTableView->currentIndex()));
2938 break;
2939 case BROWSER_TYPE_PLAYLISTS:
2940 contentHash = getCurrentContentHash();
2941 break;
2942 }
2943
2944 if (contentHash.isEmpty())
2945 return;
2946
2947 loadContent(contentHash);
2948 }
2949
isContentLessCore()2950 bool MainWindow::isContentLessCore()
2951 {
2952 rarch_system_info_t *system = runloop_get_system_info();
2953
2954 return system->load_no_content;
2955 }
2956
isCoreLoaded()2957 bool MainWindow::isCoreLoaded()
2958 {
2959 if ( m_currentCore.isEmpty() ||
2960 m_currentCore == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE))
2961 return false;
2962
2963 return true;
2964 }
2965
playlistEntryDialog()2966 PlaylistEntryDialog* MainWindow::playlistEntryDialog()
2967 {
2968 return m_playlistEntryDialog;
2969 }
2970
viewOptionsDialog()2971 ViewOptionsDialog* MainWindow::viewOptionsDialog()
2972 {
2973 return m_viewOptionsDialog;
2974 }
2975
setCoreActions()2976 void MainWindow::setCoreActions()
2977 {
2978 QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem();
2979 ViewType viewType = getCurrentViewType();
2980 QHash<QString, QString> hash = getCurrentContentHash();
2981 QString currentPlaylistFileName = QString();
2982
2983 m_launchWithComboBox->clear();
2984
2985 if (isContentLessCore())
2986 m_startCorePushButton->show();
2987 else
2988 m_startCorePushButton->hide();
2989
2990 if ( isCoreLoaded()
2991 && m_settings->value("suggest_loaded_core_first", false).toBool())
2992 {
2993 QVariantMap comboBoxMap;
2994 comboBoxMap["core_name"] = m_currentCore;
2995 comboBoxMap["core_path"] = path_get(RARCH_PATH_CORE);
2996 comboBoxMap["core_selection"] = CORE_SELECTION_CURRENT;
2997 m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap));
2998 }
2999
3000 if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS)
3001 {
3002 if (!hash.isEmpty())
3003 {
3004 QString coreName = hash["core_name"];
3005
3006 if (coreName.isEmpty())
3007 coreName = "<n/a>";
3008 else
3009 {
3010 const char *detect_str = "DETECT";
3011
3012 if (coreName != detect_str)
3013 {
3014 if (m_launchWithComboBox->findText(coreName) == -1)
3015 {
3016 int i = 0;
3017 bool found_existing = false;
3018
3019 for (i = 0; i < m_launchWithComboBox->count(); i++)
3020 {
3021 QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap();
3022
3023 if (map.value("core_path").toString() == hash["core_path"] || map.value("core_name").toString() == coreName)
3024 {
3025 found_existing = true;
3026 break;
3027 }
3028 }
3029
3030 if (!found_existing)
3031 {
3032 QVariantMap comboBoxMap;
3033 comboBoxMap["core_name"] = coreName;
3034 comboBoxMap["core_path"] = hash["core_path"];
3035 comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_SAVED;
3036 m_launchWithComboBox->addItem(coreName, QVariant::fromValue(comboBoxMap));
3037 }
3038 }
3039 }
3040 }
3041 }
3042 }
3043
3044 switch(m_currentBrowser)
3045 {
3046 case BROWSER_TYPE_PLAYLISTS:
3047 currentPlaylistFileName = hash["pl_name"].isEmpty() ?
3048 hash["db_name"] : hash["pl_name"];
3049 break;
3050 case BROWSER_TYPE_FILES:
3051 currentPlaylistFileName = m_fileModel->rootDirectory().dirName();
3052 break;
3053 }
3054
3055 if (!currentPlaylistFileName.isEmpty())
3056 {
3057 QString defaultCorePath = getPlaylistDefaultCore(currentPlaylistFileName);
3058
3059 if (!defaultCorePath.isEmpty())
3060 {
3061 QString currentPlaylistItemDataString;
3062 bool allPlaylists = false;
3063 int row = 0;
3064 QByteArray defaultCorePathArray = defaultCorePath.toUtf8();
3065 const char *default_core_path_data = defaultCorePathArray.constData();
3066
3067 if (currentPlaylistItem)
3068 {
3069 currentPlaylistItemDataString = currentPlaylistItem->data(Qt::UserRole).toString();
3070 allPlaylists = (
3071 currentPlaylistItemDataString == ALL_PLAYLISTS_TOKEN);
3072 }
3073
3074 for (row = 0; row < m_listWidget->count(); row++)
3075 {
3076 core_info_t *coreInfo = NULL;
3077
3078 if (allPlaylists)
3079 {
3080 QFileInfo info;
3081 QListWidgetItem *listItem = m_listWidget->item(row);
3082 QString listItemString = listItem->data(Qt::UserRole).toString();
3083
3084 info.setFile(listItemString);
3085
3086 if (listItemString == ALL_PLAYLISTS_TOKEN)
3087 continue;
3088 }
3089
3090 /* Search for default core */
3091 if (core_info_find(default_core_path_data, &coreInfo))
3092 {
3093 if (m_launchWithComboBox->findText(coreInfo->core_name) == -1)
3094 {
3095 int i = 0;
3096 bool found_existing = false;
3097
3098 for (i = 0; i < m_launchWithComboBox->count(); i++)
3099 {
3100 QVariantMap map =
3101 m_launchWithComboBox->itemData(
3102 i, Qt::UserRole).toMap();
3103 QByteArray CorePathArray =
3104 map.value("core_path").toString().toUtf8();
3105 const char *core_path_data = CorePathArray.constData();
3106
3107 if (string_starts_with(path_basename(core_path_data),
3108 coreInfo->core_file_id.str) ||
3109 map.value("core_name").toString() == coreInfo->core_name ||
3110 map.value("core_name").toString() == coreInfo->display_name)
3111 {
3112 found_existing = true;
3113 break;
3114 }
3115 }
3116
3117 if (!found_existing)
3118 {
3119 QVariantMap comboBoxMap;
3120 comboBoxMap["core_name"] = coreInfo->core_name;
3121 comboBoxMap["core_path"] = coreInfo->path;
3122 comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT;
3123 m_launchWithComboBox->addItem(coreInfo->core_name, QVariant::fromValue(comboBoxMap));
3124 }
3125 }
3126 }
3127
3128 if (!allPlaylists)
3129 break;
3130 }
3131 }
3132 }
3133
3134 {
3135 QVariantMap comboBoxMap;
3136 comboBoxMap["core_selection"] = CORE_SELECTION_ASK;
3137 m_launchWithComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK), QVariant::fromValue(comboBoxMap));
3138 m_launchWithComboBox->insertSeparator(m_launchWithComboBox->count());
3139 comboBoxMap["core_selection"] = CORE_SELECTION_LOAD_CORE;
3140 m_launchWithComboBox->addItem(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)) + "...", QVariant::fromValue(comboBoxMap));
3141 }
3142 }
3143
onTabWidgetIndexChanged(int index)3144 void MainWindow::onTabWidgetIndexChanged(int index)
3145 {
3146 if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER))
3147 {
3148 m_currentBrowser = BROWSER_TYPE_FILES;
3149
3150 m_centralWidget->setCurrentWidget(m_fileTableView);
3151
3152 onCurrentFileChanged(m_fileTableView->currentIndex());
3153 }
3154 else if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS))
3155 {
3156 m_currentBrowser = BROWSER_TYPE_PLAYLISTS;
3157
3158 m_centralWidget->setCurrentWidget(m_playlistViewsAndFooter);
3159
3160 onCurrentItemChanged(m_tableView->currentIndex());
3161 }
3162
3163 applySearch();
3164
3165 setCoreActions();
3166 }
3167
runPushButton()3168 QToolButton* MainWindow::runPushButton()
3169 {
3170 return m_runPushButton;
3171 }
3172
stopPushButton()3173 QToolButton* MainWindow::stopPushButton()
3174 {
3175 return m_stopPushButton;
3176 }
3177
startCorePushButton()3178 QToolButton* MainWindow::startCorePushButton()
3179 {
3180 return m_startCorePushButton;
3181 }
3182
launchWithComboBox()3183 QComboBox* MainWindow::launchWithComboBox()
3184 {
3185 return m_launchWithComboBox;
3186 }
3187
onSearchLineEditEdited(const QString & text)3188 void MainWindow::onSearchLineEditEdited(const QString &text)
3189 {
3190 int i = 0;
3191 QVector<unsigned> textUnicode = text.toUcs4();
3192 QVector<unsigned> textHiraToKata;
3193 QVector<unsigned> textKataToHira;
3194 bool foundHira = false;
3195 bool foundKata = false;
3196
3197 for (i = 0; i < textUnicode.size(); i++)
3198 {
3199 unsigned code = textUnicode.at(i);
3200
3201 if (code >= HIRAGANA_START && code <= HIRAGANA_END)
3202 {
3203 foundHira = true;
3204 textHiraToKata += code + HIRA_KATA_OFFSET;
3205 }
3206 else if (code >= KATAKANA_START && code <= KATAKANA_END)
3207 {
3208 foundKata = true;
3209 textKataToHira += code - HIRA_KATA_OFFSET;
3210 }
3211 else
3212 {
3213 textHiraToKata += code;
3214 textKataToHira += code;
3215 }
3216 }
3217
3218 if (!foundHira && !foundKata)
3219 m_searchRegExp = QRegExp(text, Qt::CaseInsensitive);
3220 else if (foundHira && !foundKata)
3221 m_searchRegExp = QRegExp(text + "|"
3222 + QString::fromUcs4(textHiraToKata.constData(),
3223 textHiraToKata.size()), Qt::CaseInsensitive);
3224 else if (!foundHira && foundKata)
3225 m_searchRegExp = QRegExp(text + "|"
3226 + QString::fromUcs4(textKataToHira.constData(),
3227 textKataToHira.size()), Qt::CaseInsensitive);
3228 else
3229 m_searchRegExp = QRegExp(text + "|"
3230 + QString::fromUcs4(textHiraToKata.constData(),
3231 textHiraToKata.size()) + "|" +
3232 QString::fromUcs4(textKataToHira.constData(),
3233 textKataToHira.size()), Qt::CaseInsensitive);
3234
3235 applySearch();
3236 }
3237
applySearch()3238 void MainWindow::applySearch()
3239 {
3240 switch (m_currentBrowser)
3241 {
3242 case BROWSER_TYPE_PLAYLISTS:
3243 if (m_proxyModel->filterRegExp() != m_searchRegExp)
3244 {
3245 m_proxyModel->setFilterRegExp(m_searchRegExp);
3246 updateItemsCount();
3247 }
3248 break;
3249 case BROWSER_TYPE_FILES:
3250 if (m_proxyFileModel->filterRegExp() != m_searchRegExp)
3251 m_proxyFileModel->setFilterRegExp(m_searchRegExp);
3252 break;
3253 }
3254 }
3255
onViewClosedDocksAboutToShow()3256 void MainWindow::onViewClosedDocksAboutToShow()
3257 {
3258 QList<QDockWidget*> dockWidgets;
3259 int i = 0;
3260 QMenu *menu = qobject_cast<QMenu*>(sender());
3261 bool found = false;
3262
3263 if (!menu)
3264 return;
3265
3266 dockWidgets = findChildren<QDockWidget*>();
3267
3268 menu->clear();
3269
3270 if (dockWidgets.isEmpty())
3271 {
3272 menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE));
3273 return;
3274 }
3275
3276 for (i = 0; i < dockWidgets.count(); i++)
3277 {
3278 const QDockWidget *dock = dockWidgets.at(i);
3279
3280 if (!dock->isVisible())
3281 {
3282 QAction *action = menu->addAction(
3283 dock->property("menu_text").toString(),
3284 this, SLOT(onShowHiddenDockWidgetAction()));
3285 action->setProperty("dock_name", dock->objectName());
3286 found = true;
3287 }
3288 }
3289
3290 if (!found)
3291 menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE));
3292 }
3293
onShowHiddenDockWidgetAction()3294 void MainWindow::onShowHiddenDockWidgetAction()
3295 {
3296 QAction *action = qobject_cast<QAction*>(sender());
3297 QDockWidget *dock = NULL;
3298
3299 if (!action)
3300 return;
3301
3302 dock = findChild<QDockWidget*>(action->property("dock_name").toString());
3303
3304 if (!dock)
3305 return;
3306
3307 if (!dock->isVisible())
3308 {
3309 addDockWidget(static_cast<Qt::DockWidgetArea>(
3310 dock->property("default_area").toInt()), dock);
3311 dock->setVisible(true);
3312 dock->setFloating(false);
3313 }
3314 }
3315
searchWidget()3316 QWidget* MainWindow::searchWidget()
3317 {
3318 return m_searchWidget;
3319 }
3320
searchLineEdit()3321 QLineEdit* MainWindow::searchLineEdit()
3322 {
3323 return m_searchLineEdit;
3324 }
3325
onSearchEnterPressed()3326 void MainWindow::onSearchEnterPressed()
3327 {
3328 onSearchLineEditEdited(m_searchLineEdit->text());
3329 }
3330
onCurrentTableItemDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles)3331 void MainWindow::onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
3332 {
3333 QHash<QString, QString> hash;
3334
3335 if (!roles.contains(Qt::EditRole))
3336 return;
3337
3338 if (topLeft != bottomRight)
3339 return;
3340
3341 hash = topLeft.data(PlaylistModel::HASH).value<QHash<QString, QString>>();
3342
3343 updateCurrentPlaylistEntry(hash);
3344
3345 onCurrentItemChanged(topLeft);
3346 }
3347
onCurrentListItemDataChanged(QListWidgetItem * item)3348 void MainWindow::onCurrentListItemDataChanged(QListWidgetItem *item)
3349 {
3350 renamePlaylistItem(item, item->text());
3351 }
3352
renamePlaylistItem(QListWidgetItem * item,QString newName)3353 void MainWindow::renamePlaylistItem(QListWidgetItem *item, QString newName)
3354 {
3355 QString oldPath;
3356 QString newPath;
3357 QString extension;
3358 QString oldName;
3359 QFile file;
3360 QFileInfo info;
3361 QFileInfo playlistInfo;
3362 QString playlistPath;
3363 bool specialPlaylist = false;
3364 settings_t *settings = config_get_ptr();
3365 const char *path_dir_playlist = settings->paths.directory_playlist;
3366 QDir playlistDir(path_dir_playlist);
3367
3368 if (!item)
3369 return;
3370
3371 playlistPath = item->data(Qt::UserRole).toString();
3372 playlistInfo = playlistPath;
3373 oldName = playlistInfo.completeBaseName();
3374
3375 /* Don't just compare strings in case there are
3376 * case differences on Windows that should be ignored. */
3377 if (QDir(playlistInfo.absoluteDir()) != QDir(playlistDir))
3378 {
3379 /* special playlists like history etc. can't have an association */
3380 specialPlaylist = true;
3381 }
3382
3383 if (specialPlaylist)
3384 {
3385 /* special playlists shouldn't be editable already,
3386 * but just in case, set the old name back and
3387 * early return if they rename it */
3388 item->setText(oldName);
3389 return;
3390 }
3391
3392 /* block this signal because setData() would trigger
3393 * an infinite loop here */
3394 disconnect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*)));
3395
3396 oldPath = item->data(Qt::UserRole).toString();
3397
3398 file.setFileName(oldPath);
3399 info = file;
3400
3401 extension = info.suffix();
3402
3403 newPath = info.absolutePath();
3404
3405 /* absolutePath() will always use / even on Windows */
3406 if (newPath.at(newPath.count() - 1) != '/')
3407 /* add trailing slash if the path doesn't have one */
3408 newPath += '/';
3409
3410 newPath += newName + "." + extension;
3411
3412 item->setData(Qt::UserRole, newPath);
3413
3414 if (!file.rename(newPath))
3415 {
3416 RARCH_ERR("[Qt]: Could not rename playlist.\n");
3417 item->setText(oldName);
3418 }
3419
3420 connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*)));
3421 }
3422
onCurrentItemChanged(const QModelIndex & index)3423 void MainWindow::onCurrentItemChanged(const QModelIndex &index)
3424 {
3425 onCurrentItemChanged(index.data(PlaylistModel::HASH).value<QHash<QString, QString>>());
3426 }
3427
onCurrentFileChanged(const QModelIndex & index)3428 void MainWindow::onCurrentFileChanged(const QModelIndex &index)
3429 {
3430 onCurrentItemChanged(getFileContentHash(m_proxyFileModel->mapToSource(index)));
3431 }
3432
onCurrentItemChanged(const QHash<QString,QString> & hash)3433 void MainWindow::onCurrentItemChanged(const QHash<QString, QString> &hash)
3434 {
3435 QString path = hash["path"];
3436 bool acceptDrop = false;
3437
3438 if (m_thumbnailPixmap)
3439 delete m_thumbnailPixmap;
3440 if (m_thumbnailPixmap2)
3441 delete m_thumbnailPixmap2;
3442 if (m_thumbnailPixmap3)
3443 delete m_thumbnailPixmap3;
3444
3445 if (m_playlistModel->isSupportedImage(path))
3446 {
3447 /* use thumbnail widgets to show regular image files */
3448 m_thumbnailPixmap = new QPixmap(path);
3449 m_thumbnailPixmap2 = new QPixmap(*m_thumbnailPixmap);
3450 m_thumbnailPixmap3 = new QPixmap(*m_thumbnailPixmap);
3451 }
3452 else
3453 {
3454 QString thumbnailsDir = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]);
3455 QString thumbnailName = m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]);
3456
3457 m_thumbnailPixmap = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_BOXART + "/" + thumbnailName);
3458 m_thumbnailPixmap2 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_TITLE + "/" + thumbnailName);
3459 m_thumbnailPixmap3 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_SCREENSHOT + "/" + thumbnailName);
3460
3461 if ( m_currentBrowser == BROWSER_TYPE_PLAYLISTS
3462 && !currentPlaylistIsSpecial())
3463 acceptDrop = true;
3464 }
3465
3466 onResizeThumbnailOne(*m_thumbnailPixmap, acceptDrop);
3467 onResizeThumbnailTwo(*m_thumbnailPixmap2, acceptDrop);
3468 onResizeThumbnailThree(*m_thumbnailPixmap3, acceptDrop);
3469
3470 setCoreActions();
3471 }
3472
setThumbnail(QString widgetName,QPixmap & pixmap,bool acceptDrop)3473 void MainWindow::setThumbnail(QString widgetName,
3474 QPixmap &pixmap, bool acceptDrop)
3475 {
3476 ThumbnailWidget *thumbnail = findChild<ThumbnailWidget*>(widgetName);
3477
3478 if (thumbnail)
3479 thumbnail->setPixmap(pixmap, acceptDrop);
3480 }
3481
onResizeThumbnailOne(QPixmap & pixmap,bool acceptDrop)3482 void MainWindow::onResizeThumbnailOne(QPixmap &pixmap, bool acceptDrop)
3483 {
3484 setThumbnail("thumbnail", pixmap, acceptDrop);
3485 }
3486
onResizeThumbnailTwo(QPixmap & pixmap,bool acceptDrop)3487 void MainWindow::onResizeThumbnailTwo(QPixmap &pixmap, bool acceptDrop)
3488 {
3489 setThumbnail("thumbnail2", pixmap, acceptDrop);
3490 }
3491
onResizeThumbnailThree(QPixmap & pixmap,bool acceptDrop)3492 void MainWindow::onResizeThumbnailThree(QPixmap &pixmap, bool acceptDrop)
3493 {
3494 setThumbnail("thumbnail3", pixmap, acceptDrop);
3495 }
3496
setCurrentViewType(ViewType viewType)3497 void MainWindow::setCurrentViewType(ViewType viewType)
3498 {
3499 m_viewType = viewType;
3500
3501 switch (viewType)
3502 {
3503 case VIEW_TYPE_ICONS:
3504 m_playlistViews->setCurrentWidget(m_gridView);
3505 m_zoomWidget->show();
3506 break;
3507 case VIEW_TYPE_LIST:
3508 default:
3509 m_playlistViews->setCurrentWidget(m_tableView);
3510 m_zoomWidget->hide();
3511 break;
3512 }
3513 }
3514
setCurrentThumbnailType(ThumbnailType thumbnailType)3515 void MainWindow::setCurrentThumbnailType(ThumbnailType thumbnailType)
3516 {
3517 m_thumbnailType = thumbnailType;
3518
3519 m_playlistModel->setThumbnailType(thumbnailType);
3520 updateVisibleItems();
3521 m_gridView->viewport()->update();
3522 }
3523
getCurrentViewType()3524 MainWindow::ViewType MainWindow::getCurrentViewType()
3525 {
3526 return m_viewType;
3527 }
3528
getCurrentThumbnailType()3529 ThumbnailType MainWindow::getCurrentThumbnailType()
3530 {
3531 return m_thumbnailType;
3532 }
3533
onCurrentListItemChanged(QListWidgetItem * current,QListWidgetItem * previous)3534 void MainWindow::onCurrentListItemChanged(
3535 QListWidgetItem *current, QListWidgetItem *previous)
3536 {
3537 Q_UNUSED(current)
3538 Q_UNUSED(previous)
3539
3540 initContentTableWidget();
3541
3542 setCoreActions();
3543 }
3544
contentTableView()3545 TableView* MainWindow::contentTableView()
3546 {
3547 return m_tableView;
3548 }
3549
fileTableView()3550 QTableView* MainWindow::fileTableView()
3551 {
3552 return m_fileTableView;
3553 }
3554
centralWidget()3555 QStackedWidget* MainWindow::centralWidget()
3556 {
3557 return m_centralWidget;
3558 }
3559
playlistViews()3560 FileDropWidget* MainWindow::playlistViews()
3561 {
3562 return m_playlistViews;
3563 }
3564
playlistViewsAndFooter()3565 QWidget* MainWindow::playlistViewsAndFooter()
3566 {
3567 return m_playlistViewsAndFooter;
3568 }
3569
contentGridView()3570 GridView* MainWindow::contentGridView()
3571 {
3572 return m_gridView;
3573 }
3574
onBrowserDownloadsClicked()3575 void MainWindow::onBrowserDownloadsClicked()
3576 {
3577 settings_t *settings = config_get_ptr();
3578 QDir dir(settings->paths.directory_core_assets);
3579 QString path = dir.absolutePath();
3580 QModelIndex index;
3581
3582 m_pendingDirScrollPath = path;
3583
3584 index = m_dirModel->index(path);
3585
3586 m_dirTree->setCurrentIndex(index);
3587
3588 onDownloadScroll(path);
3589 }
3590
onDownloadScroll(QString path)3591 void MainWindow::onDownloadScroll(QString path)
3592 {
3593 QModelIndex index = m_dirModel->index(path);
3594 m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop);
3595 m_dirTree->expand(index);
3596
3597 /* FIXME: Find a way to make this unnecessary */
3598 emit scrollToDownloadsAgain(path);
3599 }
3600
onDownloadScrollAgain(QString path)3601 void MainWindow::onDownloadScrollAgain(QString path)
3602 {
3603 QModelIndex index = m_dirModel->index(path);
3604 m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop);
3605 m_dirTree->expand(index);
3606 }
3607
onBrowserUpClicked()3608 void MainWindow::onBrowserUpClicked()
3609 {
3610 QDir dir(m_dirModel->filePath(m_dirTree->currentIndex()));
3611
3612 dir.cdUp();
3613
3614 m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath()));
3615 m_dirTree->scrollTo(m_dirTree->currentIndex(),
3616 QAbstractItemView::EnsureVisible);
3617 }
3618
onBrowserStartClicked()3619 void MainWindow::onBrowserStartClicked()
3620 {
3621 settings_t *settings = config_get_ptr();
3622
3623 m_dirTree->setCurrentIndex(
3624 m_dirModel->index(settings->paths.directory_menu_content));
3625 m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop);
3626 }
3627
playlistListWidget()3628 ListWidget* MainWindow::playlistListWidget()
3629 {
3630 return m_listWidget;
3631 }
3632
dirTreeView()3633 TreeView* MainWindow::dirTreeView()
3634 {
3635 return m_dirTree;
3636 }
3637
onTimeout()3638 void MainWindow::onTimeout()
3639 {
3640 bool contentless = false;
3641 bool is_inited = false;
3642
3643 content_get_status(&contentless, &is_inited);
3644
3645 if (is_inited)
3646 {
3647 if (m_runPushButton->isVisible())
3648 m_runPushButton->hide();
3649 if (!m_stopPushButton->isVisible())
3650 m_stopPushButton->show();
3651 }
3652 else
3653 {
3654 if (!m_runPushButton->isVisible())
3655 m_runPushButton->show();
3656 if (m_stopPushButton->isVisible())
3657 m_stopPushButton->hide();
3658 }
3659
3660 setCurrentCoreLabel();
3661 }
3662
onStopClicked()3663 void MainWindow::onStopClicked()
3664 {
3665 #ifdef HAVE_MENU
3666 menu_navigation_set_selection(0);
3667 #endif
3668 command_event(CMD_EVENT_UNLOAD_CORE, NULL);
3669 setCurrentCoreLabel();
3670 activateWindow();
3671 raise();
3672 }
3673
setCurrentCoreLabel()3674 void MainWindow::setCurrentCoreLabel()
3675 {
3676 bool update = false;
3677 struct retro_system_info *system = runloop_get_libretro_system_info();
3678 QString libraryName = system->library_name;
3679 const char *no_core_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE);
3680
3681 if ( (m_statusLabel->text().isEmpty())
3682 || (m_currentCore != no_core_str && libraryName.isEmpty())
3683 )
3684 {
3685 m_currentCore = no_core_str;
3686 m_currentCoreVersion = "";
3687 update = true;
3688 }
3689 else
3690 {
3691 if (m_currentCore != libraryName && !libraryName.isEmpty())
3692 {
3693 m_currentCore = system->library_name;
3694 m_currentCoreVersion = (string_is_empty(system->library_version) ? "" : system->library_version);
3695 update = true;
3696 }
3697 }
3698
3699 if (update)
3700 {
3701 QAction *unloadCoreAction = findChild<QAction*>("unloadCoreAction");
3702 QString text = QString(PACKAGE_VERSION) + " - " + m_currentCore + " " + m_currentCoreVersion;
3703 m_statusLabel->setText(text);
3704 m_loadCoreWindow->setStatusLabel(text);
3705 setCoreActions();
3706
3707 if (unloadCoreAction)
3708 {
3709 if (libraryName.isEmpty())
3710 unloadCoreAction->setEnabled(false);
3711 else
3712 unloadCoreAction->setEnabled(true);
3713 }
3714 }
3715 }
3716
onCoreLoadWindowClosed()3717 void MainWindow::onCoreLoadWindowClosed()
3718 {
3719 QVariant lastLaunchWithVariant = m_loadCoreWindow->property("last_launch_with_index");
3720 int lastLaunchWithIndex = lastLaunchWithVariant.toInt();
3721
3722 m_pendingRun = false;
3723
3724 if (lastLaunchWithVariant.isValid() && lastLaunchWithIndex >= 0)
3725 {
3726 m_launchWithComboBox->setCurrentIndex(lastLaunchWithIndex);
3727 m_loadCoreWindow->setProperty("last_launch_with_index", -1);
3728 }
3729 }
3730
onCoreLoaded()3731 void MainWindow::onCoreLoaded()
3732 {
3733 QAction *unloadAction = findChild<QAction*>("unloadCoreAction");
3734
3735 activateWindow();
3736 raise();
3737 setCurrentCoreLabel();
3738 setCoreActions();
3739
3740 if (unloadAction)
3741 unloadAction->setEnabled(true);
3742
3743 m_loadCoreWindow->hide();
3744
3745 if (m_pendingRun)
3746 {
3747 onRunClicked();
3748 m_pendingRun = false;
3749 }
3750 }
3751
onUnloadCoreMenuAction()3752 void MainWindow::onUnloadCoreMenuAction()
3753 {
3754 QAction *action = qobject_cast<QAction*>(sender());
3755
3756 #ifdef HAVE_MENU
3757 menu_navigation_set_selection(0);
3758 #endif
3759
3760 /* TODO */
3761 if (!command_event(CMD_EVENT_UNLOAD_CORE, NULL))
3762 return;
3763
3764 setCurrentCoreLabel();
3765 setCoreActions();
3766
3767 if (!action)
3768 return;
3769
3770 action->setEnabled(false);
3771 activateWindow();
3772 raise();
3773 }
3774
onLoadCoreClicked(const QStringList & extensionFilters)3775 void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters)
3776 {
3777 m_loadCoreWindow->show();
3778 m_loadCoreWindow->resize(width() / 2, height());
3779 m_loadCoreWindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, m_loadCoreWindow->size(), geometry()));
3780 m_loadCoreWindow->initCoreList(extensionFilters);
3781 }
3782
initContentTableWidget()3783 void MainWindow::initContentTableWidget()
3784 {
3785 QString path;
3786 QListWidgetItem *item = m_listWidget->currentItem();
3787
3788 if (!item)
3789 return;
3790
3791 m_currentGridHash.clear();
3792
3793 if (m_currentGridWidget)
3794 {
3795 m_currentGridWidget->setObjectName("thumbnailWidget");
3796 m_currentGridWidget->style()->unpolish(m_currentGridWidget);
3797 m_currentGridWidget->style()->polish(m_currentGridWidget);
3798 }
3799
3800 m_currentGridWidget = NULL;
3801
3802 path = item->data(Qt::UserRole).toString();
3803
3804 if (path == ALL_PLAYLISTS_TOKEN)
3805 {
3806 unsigned i;
3807 settings_t *settings = config_get_ptr();
3808 QDir playlistDir(settings->paths.directory_playlist);
3809 QStringList playlists;
3810 size_t list_size = (size_t)m_playlistFiles.count();
3811
3812 for (i = 0; i < list_size; i++)
3813 {
3814 const QString &playlist = m_playlistFiles.at(i);
3815 playlists.append(playlistDir.absoluteFilePath(playlist));
3816 }
3817
3818 m_playlistModel->addPlaylistItems(playlists, true);
3819 }
3820 else
3821 m_playlistModel->addPlaylistItems(QStringList() << path);
3822
3823 if (item != m_historyPlaylistsItem)
3824 m_tableView->sortByColumn(0, Qt::AscendingOrder);
3825 else
3826 m_proxyModel->sort(-1);
3827
3828 updateItemsCount();
3829
3830 m_gridView->scrollToTop();
3831 m_gridView->setCurrentIndex(m_proxyModel->index(0, 0));
3832 }
3833
updateItemsCount()3834 void MainWindow::updateItemsCount()
3835 {
3836 m_itemsCountLabel->setText(
3837 m_itemsCountLiteral.arg(m_proxyModel->rowCount()));
3838 }
3839
keyPressEvent(QKeyEvent * event)3840 void MainWindow::keyPressEvent(QKeyEvent *event)
3841 {
3842 QMainWindow::keyPressEvent(event);
3843 }
3844
settings()3845 QSettings* MainWindow::settings()
3846 {
3847 return m_settings;
3848 }
3849
getCurrentViewTypeString()3850 QString MainWindow::getCurrentViewTypeString()
3851 {
3852 switch (m_viewType)
3853 {
3854 case VIEW_TYPE_ICONS:
3855 return QStringLiteral("icons");
3856 case VIEW_TYPE_LIST:
3857 default:
3858 break;
3859 }
3860
3861 return QStringLiteral("list");
3862 }
3863
getCurrentThumbnailTypeString()3864 QString MainWindow::getCurrentThumbnailTypeString()
3865 {
3866 switch (m_thumbnailType)
3867 {
3868 case THUMBNAIL_TYPE_SCREENSHOT:
3869 return QStringLiteral("screenshot");
3870 case THUMBNAIL_TYPE_TITLE_SCREEN:
3871 return QStringLiteral("title");
3872 case THUMBNAIL_TYPE_BOXART:
3873 default:
3874 return QStringLiteral("boxart");
3875 }
3876
3877 return QStringLiteral("list");
3878 }
3879
getThumbnailTypeFromString(QString thumbnailType)3880 ThumbnailType MainWindow::getThumbnailTypeFromString(QString thumbnailType)
3881 {
3882 if (thumbnailType == "boxart")
3883 return THUMBNAIL_TYPE_BOXART;
3884 else if (thumbnailType == "screenshot")
3885 return THUMBNAIL_TYPE_SCREENSHOT;
3886 else if (thumbnailType == "title")
3887 return THUMBNAIL_TYPE_TITLE_SCREEN;
3888
3889 return THUMBNAIL_TYPE_BOXART;
3890 }
3891
closeEvent(QCloseEvent * event)3892 void MainWindow::closeEvent(QCloseEvent *event)
3893 {
3894 if (m_settings->value("save_geometry", false).toBool())
3895 m_settings->setValue("geometry", saveGeometry());
3896 if (m_settings->value("save_dock_positions", false).toBool())
3897 m_settings->setValue("dock_positions", saveState());
3898 if (m_settings->value("save_last_tab", false).toBool())
3899 m_settings->setValue("last_tab", m_browserAndPlaylistTabWidget->currentIndex());
3900
3901 m_settings->setValue("view_type", getCurrentViewTypeString());
3902 m_settings->setValue("file_browser_table_headers", m_fileTableView->horizontalHeader()->saveState());
3903 m_settings->setValue("icon_view_zoom", m_lastZoomSliderValue);
3904 m_settings->setValue("icon_view_thumbnail_type", getCurrentThumbnailTypeString());
3905 m_settings->setValue("options_dialog_geometry", m_viewOptionsDialog->saveGeometry());
3906
3907 QMainWindow::closeEvent(event);
3908 }
3909
onContributorsClicked()3910 void MainWindow::onContributorsClicked()
3911 {
3912 QScopedPointer<QDialog> dialog(new QDialog());
3913 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
3914 QTextEdit *textEdit = new QTextEdit(dialog.data());
3915
3916 connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept()));
3917 connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject()));
3918
3919 dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS));
3920 dialog->setLayout(new QVBoxLayout());
3921
3922 dialog->layout()->addWidget(textEdit);
3923
3924 dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Minimum));
3925 dialog->layout()->addWidget(buttonBox);
3926
3927 textEdit->setReadOnly(true);
3928 textEdit->setHtml(QString("<pre>") + retroarch_contributors_list + "</pre>");
3929
3930 dialog->resize(480, 640);
3931 dialog->exec();
3932 }
3933
showAbout()3934 void MainWindow::showAbout()
3935 {
3936 QScopedPointer<QDialog> dialog(new QDialog());
3937 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
3938 QString text = QString("RetroArch ") + PACKAGE_VERSION +
3939 "<br><br>" + "<a href=\"https://www.libretro.com/\">www.libretro.com</a>"
3940 "<br><br>" + "<a href=\"https://www.retroarch.com/\">www.retroarch.com</a>"
3941 #ifdef HAVE_GIT_VERSION
3942 "<br><br>" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION) + ": " + retroarch_git_version +
3943 #endif
3944 "<br>" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_BUILD_DATE) + ": " + __DATE__;
3945 QLabel *label = new QLabel(text, dialog.data());
3946 QPixmap pix = getInvader();
3947 QLabel *pixLabel = new QLabel(dialog.data());
3948 QPushButton *contributorsPushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS), dialog.data());
3949
3950 connect(contributorsPushButton, SIGNAL(clicked()), this, SLOT(onContributorsClicked()));
3951 connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept()));
3952 connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject()));
3953
3954 label->setTextFormat(Qt::RichText);
3955 label->setAlignment(Qt::AlignCenter);
3956 label->setTextInteractionFlags(Qt::TextBrowserInteraction);
3957 label->setOpenExternalLinks(true);
3958
3959 pixLabel->setAlignment(Qt::AlignCenter);
3960 pixLabel->setPixmap(pix);
3961
3962 dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT));
3963 dialog->setLayout(new QVBoxLayout());
3964
3965 dialog->layout()->addWidget(pixLabel);
3966 dialog->layout()->addWidget(label);
3967 dialog->layout()->addWidget(contributorsPushButton);
3968
3969 dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
3970 dialog->layout()->addWidget(buttonBox);
3971
3972 dialog->exec();
3973 }
3974
showDocs()3975 void MainWindow::showDocs()
3976 {
3977 QDesktopServices::openUrl(QUrl(DOCS_URL));
3978 }
3979
onShowErrorMessage(QString msg)3980 void MainWindow::onShowErrorMessage(QString msg)
3981 {
3982 showMessageBox(msg, MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
3983 }
3984
onShowInfoMessage(QString msg)3985 void MainWindow::onShowInfoMessage(QString msg)
3986 {
3987 showMessageBox(msg, MainWindow::MSGBOX_TYPE_INFO, Qt::ApplicationModal, false);
3988 }
3989
onExtractArchive(QString path,QString extractionDir,QString tempExtension,retro_task_callback_t cb)3990 int MainWindow::onExtractArchive(QString path, QString extractionDir, QString tempExtension, retro_task_callback_t cb)
3991 {
3992 unsigned i;
3993 file_archive_transfer_t state;
3994 struct archive_extract_userdata userdata;
3995 QByteArray pathArray = path.toUtf8();
3996 QByteArray dirArray = extractionDir.toUtf8();
3997 const char *file = pathArray.constData();
3998 const char *dir = dirArray.constData();
3999 struct string_list *file_list = file_archive_get_file_list(file, NULL);
4000 bool returnerr = true;
4001 retro_task_t *decompress_task = NULL;
4002
4003 if (!file_list || file_list->size == 0)
4004 {
4005 showMessageBox("Error: Archive is empty.", MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
4006 RARCH_ERR("[Qt]: Downloaded archive is empty?\n");
4007 return -1;
4008 }
4009
4010 for (i = 0; i < file_list->size; i++)
4011 {
4012 QFile fileObj(file_list->elems[i].data);
4013
4014 if (fileObj.exists())
4015 {
4016 if (!fileObj.remove())
4017 {
4018 /* if we cannot delete the existing file to update it, rename it for now and delete later */
4019 QFile fileTemp(fileObj.fileName() + tempExtension);
4020
4021 if (fileTemp.exists())
4022 {
4023 if (!fileTemp.remove())
4024 {
4025 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
4026 RARCH_ERR("[Qt]: Could not delete file: %s\n", file_list->elems[i].data);
4027 return -1;
4028 }
4029 }
4030
4031 if (!fileObj.rename(fileTemp.fileName()))
4032 {
4033 showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
4034 RARCH_ERR("[Qt]: Could not rename file: %s\n", file_list->elems[i].data);
4035 return -1;
4036 }
4037 }
4038 }
4039 }
4040
4041 string_list_free(file_list);
4042
4043 memset(&state, 0, sizeof(state));
4044 memset(&userdata, 0, sizeof(userdata));
4045
4046 state.type = ARCHIVE_TRANSFER_INIT;
4047
4048 m_updateProgressDialog->setWindowModality(Qt::NonModal);
4049 m_updateProgressDialog->setMinimumDuration(0);
4050 m_updateProgressDialog->setRange(0, 0);
4051 m_updateProgressDialog->setAutoClose(true);
4052 m_updateProgressDialog->setAutoReset(true);
4053 m_updateProgressDialog->setValue(0);
4054 m_updateProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_EXTRACTING)) + "...");
4055 m_updateProgressDialog->setCancelButtonText(QString());
4056 m_updateProgressDialog->show();
4057
4058 decompress_task = (retro_task_t*)task_push_decompress(
4059 file, dir,
4060 NULL, NULL, NULL,
4061 cb, this, NULL, false);
4062
4063 if (!decompress_task)
4064 {
4065 m_updateProgressDialog->cancel();
4066 return -1;
4067 }
4068
4069 return returnerr;
4070 }
4071
getScrubbedString(QString str)4072 QString MainWindow::getScrubbedString(QString str)
4073 {
4074 const QString chars("&*/:`\"<>?\\|");
4075 int i;
4076
4077 for (i = 0; i < chars.count(); i++)
4078 str.replace(chars.at(i), '_');
4079
4080 return str;
4081 }
4082
ui_window_qt_init(void)4083 static void* ui_window_qt_init(void)
4084 {
4085 ui_window.qtWindow = new MainWindow();
4086
4087 return &ui_window;
4088 }
4089
ui_window_qt_destroy(void * data)4090 static void ui_window_qt_destroy(void *data)
4091 {
4092 #if 0
4093 ui_window_qt_t *window = (ui_window_qt_t*)data;
4094
4095 delete window->qtWindow;
4096 #endif
4097 }
4098
ui_window_qt_set_focused(void * data)4099 static void ui_window_qt_set_focused(void *data)
4100 {
4101 #if 0
4102 ui_window_qt_t *window = (ui_window_qt_t*)data;
4103
4104 window->qtWindow->raise();
4105 window->qtWindow->activateWindow();
4106 #endif
4107 }
4108
ui_window_qt_set_visible(void * data,bool set_visible)4109 static void ui_window_qt_set_visible(void *data,
4110 bool set_visible)
4111 {
4112 (void)data;
4113 (void)set_visible;
4114 /* TODO/FIXME */
4115 }
4116
ui_window_qt_set_title(void * data,char * buf)4117 static void ui_window_qt_set_title(void *data, char *buf)
4118 {
4119 #if 0
4120 ui_window_qt_t *window = (ui_window_qt_t*)data;
4121
4122 window->qtWindow->setWindowTitle(QString::fromUtf8(buf));
4123 #endif
4124 }
4125
ui_window_qt_set_droppable(void * data,bool droppable)4126 static void ui_window_qt_set_droppable(void *data, bool droppable)
4127 {
4128 #if 0
4129 ui_window_qt_t *window = (ui_window_qt_t*)data;
4130
4131 window->qtWindow->setAcceptDrops(droppable);
4132 #endif
4133 }
4134
ui_window_qt_focused(void * data)4135 static bool ui_window_qt_focused(void *data)
4136 {
4137 #if 0
4138 ui_window_qt_t *window = (ui_window_qt_t*)data;
4139 return window->qtWindow->isActiveWindow() && !window->qtWindow->isMinimized();
4140 #else
4141 return true;
4142 #endif
4143 }
4144
4145 static ui_window_t ui_window_qt = {
4146 ui_window_qt_init,
4147 ui_window_qt_destroy,
4148 ui_window_qt_set_focused,
4149 ui_window_qt_set_visible,
4150 ui_window_qt_set_title,
4151 ui_window_qt_set_droppable,
4152 ui_window_qt_focused,
4153 "qt"
4154 };
4155
ui_msg_window_qt_response(ui_msg_window_state * state,QMessageBox::StandardButtons response)4156 static enum ui_msg_window_response ui_msg_window_qt_response(ui_msg_window_state *state, QMessageBox::StandardButtons response)
4157 {
4158 switch (response)
4159 {
4160 case QMessageBox::Ok:
4161 return UI_MSG_RESPONSE_OK;
4162 case QMessageBox::Cancel:
4163 return UI_MSG_RESPONSE_CANCEL;
4164 case QMessageBox::Yes:
4165 return UI_MSG_RESPONSE_YES;
4166 case QMessageBox::No:
4167 return UI_MSG_RESPONSE_NO;
4168 default:
4169 break;
4170 }
4171
4172 switch (state->buttons)
4173 {
4174 case UI_MSG_WINDOW_OK:
4175 return UI_MSG_RESPONSE_OK;
4176 case UI_MSG_WINDOW_OKCANCEL:
4177 return UI_MSG_RESPONSE_CANCEL;
4178 case UI_MSG_WINDOW_YESNO:
4179 return UI_MSG_RESPONSE_NO;
4180 case UI_MSG_WINDOW_YESNOCANCEL:
4181 return UI_MSG_RESPONSE_CANCEL;
4182 default:
4183 break;
4184 }
4185
4186 return UI_MSG_RESPONSE_NA;
4187 }
4188
4189 static QFlags<QMessageBox::StandardButton>
ui_msg_window_qt_buttons(ui_msg_window_state * state)4190 ui_msg_window_qt_buttons(ui_msg_window_state *state)
4191 {
4192 switch (state->buttons)
4193 {
4194 case UI_MSG_WINDOW_OK:
4195 return QMessageBox::Ok;
4196 case UI_MSG_WINDOW_OKCANCEL:
4197 return QMessageBox::Cancel;
4198 case UI_MSG_WINDOW_YESNO:
4199 return (QMessageBox::Yes | QMessageBox::No);
4200 case UI_MSG_WINDOW_YESNOCANCEL:
4201 return (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
4202 }
4203
4204 return QMessageBox::NoButton;
4205 }
4206
4207 static enum ui_msg_window_response
ui_msg_window_qt_error(ui_msg_window_state * state)4208 ui_msg_window_qt_error(ui_msg_window_state *state)
4209 {
4210 QFlags<QMessageBox::StandardButton> flags = ui_msg_window_qt_buttons(state);
4211 return ui_msg_window_qt_response(state, QMessageBox::critical((QWidget*)state->window, state->title, state->text, flags));
4212 }
4213
ui_msg_window_qt_information(ui_msg_window_state * state)4214 static enum ui_msg_window_response ui_msg_window_qt_information(ui_msg_window_state *state)
4215 {
4216 QFlags<QMessageBox::StandardButton> flags = ui_msg_window_qt_buttons(state);
4217 return ui_msg_window_qt_response(state, QMessageBox::information((QWidget*)state->window, state->title, state->text, flags));
4218 }
4219
ui_msg_window_qt_question(ui_msg_window_state * state)4220 static enum ui_msg_window_response ui_msg_window_qt_question(ui_msg_window_state *state)
4221 {
4222 QFlags<QMessageBox::StandardButton> flags = ui_msg_window_qt_buttons(state);
4223 return ui_msg_window_qt_response(state, QMessageBox::question((QWidget*)state->window, state->title, state->text, flags));
4224 }
4225
ui_msg_window_qt_warning(ui_msg_window_state * state)4226 static enum ui_msg_window_response ui_msg_window_qt_warning(ui_msg_window_state *state)
4227 {
4228 QFlags<QMessageBox::StandardButton> flags = ui_msg_window_qt_buttons(state);
4229 return ui_msg_window_qt_response(state, QMessageBox::warning((QWidget*)state->window, state->title, state->text, flags));
4230 }
4231
4232 static ui_msg_window_t ui_msg_window_qt = {
4233 ui_msg_window_qt_error,
4234 ui_msg_window_qt_information,
4235 ui_msg_window_qt_question,
4236 ui_msg_window_qt_warning,
4237 "qt"
4238 };
4239
ui_browser_window_qt_open(ui_browser_window_state_t * state)4240 static bool ui_browser_window_qt_open(ui_browser_window_state_t *state)
4241 {
4242 return true;
4243 }
4244
ui_browser_window_qt_save(ui_browser_window_state_t * state)4245 static bool ui_browser_window_qt_save(ui_browser_window_state_t *state)
4246 {
4247 return false;
4248 }
4249
4250 static ui_browser_window_t ui_browser_window_qt = {
4251 ui_browser_window_qt_open,
4252 ui_browser_window_qt_save,
4253 "qt"
4254 };
4255
ui_application_qt_initialize(void)4256 static void* ui_application_qt_initialize(void)
4257 {
4258 /* These must last for the lifetime of the QApplication */
4259 static int app_argc = 1;
4260 static char app_name[] = "retroarch";
4261 static char *app_argv[] = { app_name, NULL };
4262
4263 app_handler = new AppHandler();
4264
4265 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
4266 /* HiDpi supported since Qt 5.6 */
4267 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
4268 #endif
4269
4270 /* Create QApplication() before calling QApplication::setStyle()
4271 * to ensure that plugin path is determined correctly */
4272 ui_application.app = new QApplication(app_argc, app_argv);
4273 QApplication::setStyle("fusion");
4274 ui_application.app->setOrganizationName("libretro");
4275 ui_application.app->setApplicationName("RetroArch");
4276 ui_application.app->setApplicationVersion(PACKAGE_VERSION);
4277 ui_application.app->connect(ui_application.app, SIGNAL(lastWindowClosed()),
4278 app_handler, SLOT(onLastWindowClosed()));
4279
4280 #ifdef Q_OS_UNIX
4281 setlocale(LC_NUMERIC, "C");
4282 #endif
4283 {
4284 /* Can't declare the pixmap at the top, because: "QPixmap: Must construct a QGuiApplication before a QPixmap" */
4285 QImage iconImage(16, 16, QImage::Format_ARGB32);
4286 QPixmap iconPixmap;
4287 unsigned char *bits = iconImage.bits();
4288
4289 memcpy(bits, retroarch_qt_icon_data, 16 * 16 * sizeof(unsigned));
4290
4291 iconPixmap = QPixmap::fromImage(iconImage);
4292
4293 ui_application.app->setWindowIcon(QIcon(iconPixmap));
4294 }
4295
4296 return &ui_application;
4297 }
4298
ui_application_qt_process_events(void)4299 static void ui_application_qt_process_events(void)
4300 {
4301 QAbstractEventDispatcher *dispatcher = QApplication::eventDispatcher();
4302 if (dispatcher && dispatcher->hasPendingEvents())
4303 QApplication::processEvents();
4304 }
4305
ui_application_qt_quit(void)4306 static void ui_application_qt_quit(void)
4307 {
4308 if (app_handler)
4309 app_handler->exit();
4310 }
4311
4312 #ifdef HAVE_MAIN
4313 #if defined(__cplusplus) && !defined(CXX_BUILD)
4314 extern "C"
4315 #endif
main(int argc,char * argv[])4316 int main(int argc, char *argv[])
4317 {
4318 return rarch_main(argc, argv, NULL);
4319 }
4320 #endif
4321
4322 static ui_application_t ui_application_qt = {
4323 ui_application_qt_initialize,
4324 ui_application_qt_process_events,
4325 ui_application_qt_quit,
4326 false,
4327 "qt"
4328 };
4329
4330
AppHandler(QObject * parent)4331 AppHandler::AppHandler(QObject *parent) :
4332 QObject(parent)
4333 {
4334 }
4335
~AppHandler()4336 AppHandler::~AppHandler()
4337 {
4338 }
4339
exit()4340 void AppHandler::exit()
4341 {
4342 ui_application_qt.exiting = true;
4343
4344 if (qApp)
4345 qApp->closeAllWindows();
4346 }
4347
isExiting() const4348 bool AppHandler::isExiting() const
4349 {
4350 return ui_application_qt.exiting;
4351 }
4352
onLastWindowClosed()4353 void AppHandler::onLastWindowClosed()
4354 {
4355 }
4356
4357 typedef struct ui_companion_qt
4358 {
4359 ui_application_qt_t *app;
4360 ui_window_qt_t *window;
4361 } ui_companion_qt_t;
4362
ThumbnailWidget(ThumbnailType type,QWidget * parent)4363 ThumbnailWidget::ThumbnailWidget(ThumbnailType type, QWidget *parent) :
4364 QStackedWidget(parent)
4365 ,m_thumbnailType(type)
4366 ,m_thumbnailLabel(new ThumbnailLabel(this))
4367 ,m_dropIndicator(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DROP_IMAGE_HERE), this))
4368 {
4369 m_dropIndicator->setObjectName("dropIndicator");
4370 m_dropIndicator->setAlignment(Qt::AlignCenter);
4371 addWidget(m_dropIndicator);
4372 addWidget(m_thumbnailLabel);
4373 }
4374
setPixmap(const QPixmap & pixmap,bool acceptDrops)4375 void ThumbnailWidget::setPixmap(const QPixmap &pixmap, bool acceptDrops)
4376 {
4377 m_thumbnailLabel->setPixmap(pixmap);
4378
4379 if (acceptDrops && pixmap.isNull())
4380 setCurrentWidget(m_dropIndicator);
4381 else
4382 setCurrentWidget(m_thumbnailLabel);
4383
4384 m_thumbnailLabel->update();
4385
4386 QWidget::setAcceptDrops(acceptDrops);
4387 }
4388
dragEnterEvent(QDragEnterEvent * event)4389 void ThumbnailWidget::dragEnterEvent(QDragEnterEvent *event)
4390 {
4391 const QMimeData *data = event->mimeData();
4392
4393 if (data->hasUrls())
4394 event->acceptProposedAction();
4395 }
4396
4397 /* Workaround for QTBUG-72844. Without it, you can't
4398 * drop on this if you first drag over another
4399 * widget that doesn't accept drops. */
dragMoveEvent(QDragMoveEvent * event)4400 void ThumbnailWidget::dragMoveEvent(QDragMoveEvent *event)
4401 {
4402 event->acceptProposedAction();
4403 }
4404
dropEvent(QDropEvent * event)4405 void ThumbnailWidget::dropEvent(QDropEvent *event)
4406 {
4407 const QMimeData *data = event->mimeData();
4408
4409 if (data->hasUrls())
4410 {
4411 const QString imageString = data->urls().at(0).toLocalFile();
4412 const QImage image(imageString);
4413
4414 if (!image.isNull())
4415 emit(filesDropped(image, m_thumbnailType));
4416 else
4417 {
4418 const char *string_data = QDir::toNativeSeparators(imageString).toUtf8().constData();
4419 RARCH_ERR("[Qt]: Could not read image: %s\n", string_data);
4420 }
4421 }
4422 }
4423
ThumbnailLabel(QWidget * parent)4424 ThumbnailLabel::ThumbnailLabel(QWidget *parent) :
4425 QWidget(parent)
4426 ,m_pixmap(NULL)
4427 ,m_pixmapWidth(0)
4428 ,m_pixmapHeight(0)
4429 {
4430 }
4431
~ThumbnailLabel()4432 ThumbnailLabel::~ThumbnailLabel()
4433 {
4434 if (m_pixmap)
4435 delete m_pixmap;
4436 }
4437
setPixmap(const QPixmap & pixmap)4438 void ThumbnailLabel::setPixmap(const QPixmap &pixmap)
4439 {
4440 m_pixmapWidth = pixmap.width();
4441 m_pixmapHeight = pixmap.height();
4442
4443 if (m_pixmap)
4444 delete m_pixmap;
4445
4446 m_pixmap = new QPixmap(pixmap);
4447 }
4448
sizeHint() const4449 QSize ThumbnailLabel::sizeHint() const
4450 {
4451 return QSize(256, 256);
4452 }
4453
paintEvent(QPaintEvent * event)4454 void ThumbnailLabel::paintEvent(QPaintEvent *event)
4455 {
4456 QStyleOption o;
4457 QPainter p;
4458 int w = width();
4459 int h = height();
4460
4461 event->accept();
4462
4463 o.initFrom(this);
4464 p.begin(this);
4465 style()->drawPrimitive(
4466 QStyle::PE_Widget, &o, &p, this);
4467 p.end();
4468
4469 if (!m_pixmap || m_pixmap->isNull())
4470 {
4471 if (m_pixmap)
4472 delete m_pixmap;
4473 m_pixmap = new QPixmap(sizeHint());
4474 m_pixmap->fill(QColor(0, 0, 0, 0));
4475 }
4476
4477 if (w > 0 && h > 0 && m_pixmap && !m_pixmap->isNull())
4478 {
4479 int newHeight = (m_pixmap->height() / static_cast<float>(m_pixmap->width())) * width();
4480 QPixmap pixmapScaled = *m_pixmap;
4481 QPixmap pixmap;
4482 QPainter pScale;
4483 int pw = 0;
4484 int ph = 0;
4485 unsigned *buf = new unsigned[w * h];
4486
4487 if (newHeight > h)
4488 pixmapScaled = pixmapScaled.scaledToHeight(h, Qt::SmoothTransformation);
4489 else
4490 pixmapScaled = pixmapScaled.scaledToWidth(w, Qt::SmoothTransformation);
4491
4492 pw = pixmapScaled.width();
4493 ph = pixmapScaled.height();
4494
4495 pixmap = QPixmap(w, h);
4496 pixmap.fill(QColor(0, 0, 0, 0));
4497
4498 pScale.begin(&pixmap);
4499 pScale.drawPixmap(QRect((w - pw) / 2, (h - ph) / 2, pw, ph), pixmapScaled, pixmapScaled.rect());
4500 pScale.end();
4501
4502 if (!pixmap.isNull())
4503 {
4504 p.begin(this);
4505 p.drawPixmap(rect(), pixmap, pixmap.rect());
4506 p.end();
4507 }
4508
4509 delete []buf;
4510 }
4511 else
4512 QWidget::paintEvent(event);
4513 }
4514
resizeEvent(QResizeEvent * event)4515 void ThumbnailLabel::resizeEvent(QResizeEvent *event)
4516 {
4517 QWidget::resizeEvent(event);
4518 }
4519
ui_companion_qt_deinit(void * data)4520 static void ui_companion_qt_deinit(void *data)
4521 {
4522 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
4523
4524 if (!handle)
4525 return;
4526
4527 /* why won't deleteLater() here call the destructor? */
4528 delete handle->window->qtWindow;
4529
4530 free(handle);
4531 }
4532
ui_companion_qt_init(void)4533 static void* ui_companion_qt_init(void)
4534 {
4535 int i = 0;
4536 QString initialPlaylist;
4537 QRect desktopRect;
4538 ui_companion_qt_t *handle = (ui_companion_qt_t*)
4539 calloc(1, sizeof(*handle));
4540 MainWindow *mainwindow = NULL;
4541 QHBoxLayout *browserButtonsHBoxLayout = NULL;
4542 QVBoxLayout *layout = NULL;
4543 QVBoxLayout *launchWithWidgetLayout = NULL;
4544 QHBoxLayout *coreComboBoxLayout = NULL;
4545 QMenuBar *menu = NULL;
4546 QDesktopWidget *desktop = NULL;
4547 QMenu *fileMenu = NULL;
4548 QMenu *editMenu = NULL;
4549 QMenu *viewMenu = NULL;
4550 QMenu *viewClosedDocksMenu = NULL;
4551 QMenu *helpMenu = NULL;
4552 QDockWidget *thumbnailDock = NULL;
4553 QDockWidget *thumbnail2Dock = NULL;
4554 QDockWidget *thumbnail3Dock = NULL;
4555 QDockWidget *browserAndPlaylistTabDock = NULL;
4556 QDockWidget *coreSelectionDock = NULL;
4557 QTabWidget *browserAndPlaylistTabWidget = NULL;
4558 QStackedWidget *centralWidget = NULL;
4559 QStackedWidget *widget = NULL;
4560 QFrame *browserWidget = NULL;
4561 QFrame *playlistWidget = NULL;
4562 QWidget *coreSelectionWidget = NULL;
4563 QWidget *launchWithWidget = NULL;
4564 ThumbnailWidget *thumbnailWidget = NULL;
4565 ThumbnailWidget *thumbnail2Widget = NULL;
4566 ThumbnailWidget *thumbnail3Widget = NULL;
4567 QPushButton *browserDownloadsButton = NULL;
4568 QPushButton *browserUpButton = NULL;
4569 QPushButton *browserStartButton = NULL;
4570 ThumbnailLabel *thumbnail = NULL;
4571 ThumbnailLabel *thumbnail2 = NULL;
4572 ThumbnailLabel *thumbnail3 = NULL;
4573 QAction *editSearchAction = NULL;
4574 QAction *loadCoreAction = NULL;
4575 QAction *unloadCoreAction = NULL;
4576 QAction *exitAction = NULL;
4577 QComboBox *launchWithComboBox = NULL;
4578 QSettings *qsettings = NULL;
4579 QListWidget *listWidget = NULL;
4580 bool foundPlaylist = false;
4581
4582 if (!handle)
4583 return NULL;
4584
4585 handle->app = static_cast<ui_application_qt_t*>
4586 (ui_application_qt.initialize());
4587 handle->window = static_cast<ui_window_qt_t*>(ui_window_qt.init());
4588
4589 desktop = qApp->desktop();
4590 desktopRect = desktop->availableGeometry();
4591
4592 mainwindow = handle->window->qtWindow;
4593
4594 qsettings = mainwindow->settings();
4595
4596 initialPlaylist = qsettings->value("initial_playlist", mainwindow->getSpecialPlaylistPath(SPECIAL_PLAYLIST_HISTORY)).toString();
4597
4598 mainwindow->resize(qMin(desktopRect.width(), INITIAL_WIDTH), qMin(desktopRect.height(), INITIAL_HEIGHT));
4599 mainwindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, mainwindow->size(), desktopRect));
4600
4601 mainwindow->setWindowTitle("RetroArch");
4602 mainwindow->setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | GROUPED_DRAGGING);
4603
4604 listWidget = mainwindow->playlistListWidget();
4605
4606 widget = mainwindow->playlistViews();
4607 widget->setContextMenuPolicy(Qt::CustomContextMenu);
4608
4609 QObject::connect(widget, SIGNAL(filesDropped(QStringList)), mainwindow, SLOT(onPlaylistFilesDropped(QStringList)));
4610 QObject::connect(widget, SIGNAL(enterPressed()), mainwindow, SLOT(onDropWidgetEnterPressed()));
4611 QObject::connect(widget, SIGNAL(deletePressed()), mainwindow, SLOT(deleteCurrentPlaylistItem()));
4612 QObject::connect(widget, SIGNAL(customContextMenuRequested(const QPoint&)), mainwindow, SLOT(onFileDropWidgetContextMenuRequested(const QPoint&)));
4613
4614 centralWidget = mainwindow->centralWidget();
4615
4616 centralWidget->addWidget(mainwindow->playlistViewsAndFooter());
4617 centralWidget->addWidget(mainwindow->fileTableView());
4618
4619 mainwindow->setCentralWidget(centralWidget);
4620
4621 menu = mainwindow->menuBar();
4622
4623 fileMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE));
4624
4625 loadCoreAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_LOAD_CORE), mainwindow, SLOT(onLoadCoreClicked()));
4626 loadCoreAction->setShortcut(QKeySequence("Ctrl+L"));
4627
4628 unloadCoreAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_UNLOAD_CORE), mainwindow, SLOT(onUnloadCoreMenuAction()));
4629 unloadCoreAction->setObjectName("unloadCoreAction");
4630 unloadCoreAction->setEnabled(false);
4631 unloadCoreAction->setShortcut(QKeySequence("Ctrl+U"));
4632
4633 exitAction = fileMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_FILE_EXIT), mainwindow, SLOT(close()));
4634 exitAction->setShortcut(QKeySequence::Quit);
4635
4636 editMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT));
4637 editSearchAction = editMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH), mainwindow->searchLineEdit(), SLOT(setFocus()));
4638 editSearchAction->setShortcut(QKeySequence::Find);
4639
4640 viewMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW));
4641 viewClosedDocksMenu = viewMenu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_CLOSED_DOCKS));
4642 viewClosedDocksMenu->setObjectName("viewClosedDocksMenu");
4643
4644 QObject::connect(viewClosedDocksMenu, SIGNAL(aboutToShow()), mainwindow, SLOT(onViewClosedDocksAboutToShow()));
4645
4646 viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_OPTIONS), mainwindow, SLOT(onCoreOptionsClicked()));
4647 #if defined(HAVE_MENU)
4648 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
4649 viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS), mainwindow, SLOT(onShaderParamsClicked()));
4650 #endif
4651 #endif
4652
4653 viewMenu->addSeparator();
4654 viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS), mainwindow, SLOT(onIconViewClicked()));
4655 viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST), mainwindow, SLOT(onListViewClicked()));
4656 viewMenu->addSeparator();
4657 viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS), mainwindow->viewOptionsDialog(), SLOT(showDialog()));
4658
4659 helpMenu = menu->addMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP));
4660 helpMenu->addAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_DOCUMENTATION)), mainwindow, SLOT(showDocs()));
4661 helpMenu->addAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT)) + "...", mainwindow, SLOT(showAbout()));
4662 helpMenu->addAction("About Qt...", qApp, SLOT(aboutQt()));
4663
4664 playlistWidget = new QFrame();
4665 playlistWidget->setLayout(new QVBoxLayout());
4666 playlistWidget->setObjectName("playlistWidget");
4667 playlistWidget->layout()->setContentsMargins(0, 0, 0, 0);
4668
4669 playlistWidget->layout()->addWidget(mainwindow->playlistListWidget());
4670
4671 browserWidget = new QFrame();
4672 browserWidget->setLayout(new QVBoxLayout());
4673 browserWidget->setObjectName("browserWidget");
4674 browserWidget->layout()->setContentsMargins(0, 0, 0, 0);
4675
4676 browserDownloadsButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIRECTORY));
4677 browserUpButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER_UP));
4678 browserStartButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES));
4679
4680 QObject::connect(browserDownloadsButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserDownloadsClicked()));
4681 QObject::connect(browserUpButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserUpClicked()));
4682 QObject::connect(browserStartButton, SIGNAL(clicked()), mainwindow, SLOT(onBrowserStartClicked()));
4683
4684 browserButtonsHBoxLayout = new QHBoxLayout();
4685 browserButtonsHBoxLayout->addWidget(browserUpButton);
4686 browserButtonsHBoxLayout->addWidget(browserStartButton);
4687 browserButtonsHBoxLayout->addWidget(browserDownloadsButton);
4688
4689 qobject_cast<QVBoxLayout*>(browserWidget->layout())->addLayout(browserButtonsHBoxLayout);
4690 browserWidget->layout()->addWidget(mainwindow->dirTreeView());
4691
4692 browserAndPlaylistTabWidget = mainwindow->browserAndPlaylistTabWidget();
4693 browserAndPlaylistTabWidget->setObjectName("browserAndPlaylistTabWidget");
4694
4695 /* Several functions depend on the same tab title strings here, so if you change these, make sure to change those too
4696 * setCoreActions()
4697 * onTabWidgetIndexChanged()
4698 * onCurrentListItemChanged()
4699 */
4700 browserAndPlaylistTabWidget->addTab(playlistWidget, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS));
4701 browserAndPlaylistTabWidget->addTab(browserWidget, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER));
4702
4703 browserAndPlaylistTabDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER), mainwindow);
4704 browserAndPlaylistTabDock->setObjectName("browserAndPlaylistTabDock");
4705 browserAndPlaylistTabDock->setProperty("default_area", Qt::LeftDockWidgetArea);
4706 browserAndPlaylistTabDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER));
4707 browserAndPlaylistTabDock->setWidget(browserAndPlaylistTabWidget);
4708
4709 mainwindow->addDockWidget(static_cast<Qt::DockWidgetArea>(browserAndPlaylistTabDock->property("default_area").toInt()), browserAndPlaylistTabDock);
4710
4711 browserButtonsHBoxLayout->addItem(new QSpacerItem(browserAndPlaylistTabWidget->tabBar()->width(), 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
4712
4713 thumbnailWidget = new ThumbnailWidget(THUMBNAIL_TYPE_BOXART);
4714 thumbnailWidget->setObjectName("thumbnail");
4715
4716 thumbnail2Widget = new ThumbnailWidget(THUMBNAIL_TYPE_TITLE_SCREEN);
4717 thumbnail2Widget->setObjectName("thumbnail2");
4718
4719 thumbnail3Widget = new ThumbnailWidget(THUMBNAIL_TYPE_SCREENSHOT);
4720 thumbnail3Widget->setObjectName("thumbnail3");
4721
4722 QObject::connect(thumbnailWidget, SIGNAL(filesDropped(const QImage&, ThumbnailType)), mainwindow, SLOT(onThumbnailDropped(const QImage&, ThumbnailType)));
4723 QObject::connect(thumbnail2Widget, SIGNAL(filesDropped(const QImage&, ThumbnailType)), mainwindow, SLOT(onThumbnailDropped(const QImage&, ThumbnailType)));
4724 QObject::connect(thumbnail3Widget, SIGNAL(filesDropped(const QImage&, ThumbnailType)), mainwindow, SLOT(onThumbnailDropped(const QImage&, ThumbnailType)));
4725
4726 thumbnailDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART), mainwindow);
4727 thumbnailDock->setObjectName("thumbnailDock");
4728 thumbnailDock->setProperty("default_area", Qt::RightDockWidgetArea);
4729 thumbnailDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART));
4730 thumbnailDock->setWidget(thumbnailWidget);
4731
4732 mainwindow->addDockWidget(static_cast<Qt::DockWidgetArea>(thumbnailDock->property("default_area").toInt()), thumbnailDock);
4733
4734 thumbnail2Dock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN), mainwindow);
4735 thumbnail2Dock->setObjectName("thumbnail2Dock");
4736 thumbnail2Dock->setProperty("default_area", Qt::RightDockWidgetArea);
4737 thumbnail2Dock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN));
4738 thumbnail2Dock->setWidget(thumbnail2Widget);
4739
4740 mainwindow->addDockWidget(static_cast<Qt::DockWidgetArea>(thumbnail2Dock->property("default_area").toInt()), thumbnail2Dock);
4741
4742 thumbnail3Dock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT), mainwindow);
4743 thumbnail3Dock->setObjectName("thumbnail3Dock");
4744 thumbnail3Dock->setProperty("default_area", Qt::RightDockWidgetArea);
4745 thumbnail3Dock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT));
4746 thumbnail3Dock->setWidget(thumbnail3Widget);
4747
4748 mainwindow->addDockWidget(static_cast<Qt::DockWidgetArea>(thumbnail3Dock->property("default_area").toInt()), thumbnail3Dock);
4749
4750 mainwindow->tabifyDockWidget(thumbnailDock, thumbnail2Dock);
4751 mainwindow->tabifyDockWidget(thumbnailDock, thumbnail3Dock);
4752
4753 /* when tabifying the dock widgets, the last tab added is selected by default, so we need to re-select the first tab */
4754 thumbnailDock->raise();
4755
4756 coreSelectionWidget = new QWidget();
4757 coreSelectionWidget->setLayout(new QVBoxLayout());
4758
4759 launchWithComboBox = mainwindow->launchWithComboBox();
4760
4761 launchWithWidgetLayout = new QVBoxLayout();
4762
4763 launchWithWidget = new QWidget();
4764 launchWithWidget->setLayout(launchWithWidgetLayout);
4765
4766 coreComboBoxLayout = new QHBoxLayout();
4767
4768 mainwindow->runPushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
4769 mainwindow->stopPushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
4770 mainwindow->startCorePushButton()->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
4771
4772 coreComboBoxLayout->addWidget(launchWithComboBox);
4773 coreComboBoxLayout->addWidget(mainwindow->startCorePushButton());
4774 coreComboBoxLayout->addWidget(mainwindow->coreInfoPushButton());
4775 coreComboBoxLayout->addWidget(mainwindow->runPushButton());
4776 coreComboBoxLayout->addWidget(mainwindow->stopPushButton());
4777
4778 mainwindow->stopPushButton()->hide();
4779
4780 coreComboBoxLayout->setStretchFactor(launchWithComboBox, 1);
4781
4782 launchWithWidgetLayout->addLayout(coreComboBoxLayout);
4783
4784 coreSelectionWidget->layout()->addWidget(launchWithWidget);
4785
4786 coreSelectionWidget->layout()->addItem(new QSpacerItem(20, browserAndPlaylistTabWidget->height(), QSizePolicy::Minimum, QSizePolicy::Expanding));
4787
4788 coreSelectionDock = new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE), mainwindow);
4789 coreSelectionDock->setObjectName("coreSelectionDock");
4790 coreSelectionDock->setProperty("default_area", Qt::LeftDockWidgetArea);
4791 coreSelectionDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE));
4792 coreSelectionDock->setWidget(coreSelectionWidget);
4793 coreSelectionDock->setFixedHeight(coreSelectionDock->minimumSizeHint().height());
4794
4795 mainwindow->addDockWidget(static_cast<Qt::DockWidgetArea>(coreSelectionDock->property("default_area").toInt()), coreSelectionDock);
4796
4797 mainwindow->splitDockWidget(browserAndPlaylistTabDock, coreSelectionDock, Qt::Vertical);
4798
4799 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
4800 mainwindow->resizeDocks(QList<QDockWidget*>() << coreSelectionDock, QList<int>() << 1, Qt::Vertical);
4801 #endif
4802
4803 if (qsettings->contains("all_playlists_list_max_count"))
4804 mainwindow->setAllPlaylistsListMaxCount(qsettings->value("all_playlists_list_max_count", 0).toInt());
4805
4806 if (qsettings->contains("all_playlists_grid_max_count"))
4807 mainwindow->setAllPlaylistsGridMaxCount(qsettings->value("all_playlists_grid_max_count", 5000).toInt());
4808
4809 if (qsettings->contains("thumbnail_cache_limit"))
4810 mainwindow->setThumbnailCacheLimit(qsettings->value("thumbnail_cache_limit", 500).toInt());
4811 else
4812 mainwindow->setThumbnailCacheLimit(500);
4813
4814 if (qsettings->contains("geometry"))
4815 if (qsettings->contains("save_geometry"))
4816 mainwindow->restoreGeometry(qsettings->value("geometry").toByteArray());
4817
4818 if (qsettings->contains("options_dialog_geometry"))
4819 mainwindow->viewOptionsDialog()->restoreGeometry(qsettings->value("options_dialog_geometry").toByteArray());
4820
4821 if (qsettings->contains("save_dock_positions"))
4822 if (qsettings->contains("dock_positions"))
4823 mainwindow->restoreState(qsettings->value("dock_positions").toByteArray());
4824
4825 if (qsettings->contains("file_browser_table_headers"))
4826 mainwindow->fileTableView()->horizontalHeader()->restoreState(qsettings->value("file_browser_table_headers").toByteArray());
4827 else
4828 mainwindow->fileTableView()->horizontalHeader()->resizeSection(0, 300);
4829
4830 if (qsettings->contains("icon_view_zoom"))
4831 mainwindow->setIconViewZoom(qsettings->value("icon_view_zoom", 50).toInt());
4832
4833 if (qsettings->contains("theme"))
4834 {
4835 QString themeStr = qsettings->value("theme").toString();
4836 MainWindow::Theme theme = mainwindow->getThemeFromString(themeStr);
4837
4838 if (qsettings->contains("custom_theme") && theme == MainWindow::THEME_CUSTOM)
4839 {
4840 QString customThemeFilePath = qsettings->value("custom_theme").toString();
4841
4842 mainwindow->setCustomThemeFile(customThemeFilePath);
4843 }
4844
4845 mainwindow->setTheme(theme);
4846 }
4847 else
4848 mainwindow->setTheme();
4849
4850 if (qsettings->contains("view_type"))
4851 {
4852 QString viewType = qsettings->value("view_type", "list").toString();
4853
4854 if (viewType == "list")
4855 mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST);
4856 else if (viewType == "icons")
4857 mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_ICONS);
4858 else
4859 mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST);
4860 }
4861 else
4862 mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST);
4863
4864 if (qsettings->contains("icon_view_thumbnail_type"))
4865 {
4866 QString thumbnailType = qsettings->value("icon_view_thumbnail_type", "boxart").toString();
4867
4868 if (thumbnailType == "boxart")
4869 mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART);
4870 else if (thumbnailType == "screenshot")
4871 mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT);
4872 else if (thumbnailType == "title")
4873 mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN);
4874 else
4875 mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART);
4876 }
4877
4878 /* We make sure to hook up the tab widget callback only after the tabs themselves have been added,
4879 * but before changing to a specific one, to avoid the callback firing before the view type is set.
4880 */
4881 QObject::connect(browserAndPlaylistTabWidget, SIGNAL(currentChanged(int)), mainwindow, SLOT(onTabWidgetIndexChanged(int)));
4882
4883 /* setting the last tab must come after setting the view type */
4884 if (qsettings->contains("save_last_tab"))
4885 {
4886 int lastTabIndex = qsettings->value("last_tab", 0).toInt();
4887
4888 if (lastTabIndex >= 0 && browserAndPlaylistTabWidget->count() > lastTabIndex)
4889 {
4890 browserAndPlaylistTabWidget->setCurrentIndex(lastTabIndex);
4891 mainwindow->onTabWidgetIndexChanged(lastTabIndex);
4892 }
4893 }
4894 else
4895 {
4896 browserAndPlaylistTabWidget->setCurrentIndex(0);
4897 mainwindow->onTabWidgetIndexChanged(0);
4898 }
4899
4900 /* the initial playlist that is selected is based on the user's setting (initialPlaylist) */
4901 for (i = 0; listWidget->count() && i < listWidget->count(); i++)
4902 {
4903 QString path;
4904 QListWidgetItem *item = listWidget->item(i);
4905
4906 if (!item)
4907 continue;
4908
4909 path = item->data(Qt::UserRole).toString();
4910
4911 if (path == initialPlaylist)
4912 {
4913 foundPlaylist = true;
4914 listWidget->setRowHidden(i, false);
4915 listWidget->setCurrentRow(i);
4916 break;
4917 }
4918 }
4919
4920 /* couldn't find the user's initial playlist, just find anything */
4921 if (!foundPlaylist)
4922 {
4923 for (i = 0; listWidget->count() && i < listWidget->count(); i++)
4924 {
4925 /* select the first non-hidden row */
4926 if (!listWidget->isRowHidden(i))
4927 {
4928 listWidget->setCurrentRow(i);
4929 break;
4930 }
4931 }
4932 }
4933
4934 mainwindow->initContentTableWidget();
4935
4936 return handle;
4937 }
4938
ui_companion_qt_notify_content_loaded(void * data)4939 static void ui_companion_qt_notify_content_loaded(void *data) { }
4940
ui_companion_qt_toggle(void * data,bool force)4941 static void ui_companion_qt_toggle(void *data, bool force)
4942 {
4943 static bool already_started = false;
4944 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
4945 ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window;
4946 settings_t *settings = config_get_ptr();
4947 bool ui_companion_toggle = settings->bools.ui_companion_toggle;
4948 bool video_fullscreen = settings->bools.video_fullscreen;
4949 bool mouse_grabbed = input_mouse_grabbed();
4950
4951 if (ui_companion_toggle || force)
4952 {
4953 if (mouse_grabbed)
4954 command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL);
4955 video_driver_show_mouse();
4956
4957 if (video_fullscreen)
4958 command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL);
4959
4960 win_handle->qtWindow->activateWindow();
4961 win_handle->qtWindow->raise();
4962 win_handle->qtWindow->show();
4963
4964 if (video_driver_started_fullscreen())
4965 win_handle->qtWindow->lower();
4966
4967 if (!already_started)
4968 {
4969 already_started = true;
4970
4971 if (win_handle->qtWindow->settings()->value(
4972 "show_welcome_screen", true).toBool())
4973 win_handle->qtWindow->showWelcomeScreen();
4974 }
4975 }
4976 }
4977
ui_companion_qt_event_command(void * data,enum event_command cmd)4978 static void ui_companion_qt_event_command(void *data, enum event_command cmd)
4979 {
4980 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
4981 ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window;
4982
4983 if (!handle)
4984 return;
4985
4986 switch (cmd)
4987 {
4988 case CMD_EVENT_SHADERS_APPLY_CHANGES:
4989 case CMD_EVENT_SHADER_PRESET_LOADED:
4990 #if defined(HAVE_MENU)
4991 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
4992 RARCH_LOG("[Qt]: Reloading shader parameters.\n");
4993 win_handle->qtWindow->deferReloadShaderParams();
4994 #endif
4995 #endif
4996 break;
4997 default:
4998 break;
4999 }
5000 }
5001
ui_companion_qt_notify_list_pushed(void * data,file_list_t * list,file_list_t * menu_list)5002 static void ui_companion_qt_notify_list_pushed(void *data, file_list_t *list,
5003 file_list_t *menu_list) { }
5004
ui_companion_qt_notify_refresh(void * data)5005 static void ui_companion_qt_notify_refresh(void *data)
5006 {
5007 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
5008 ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window;
5009
5010 win_handle->qtWindow->deferReloadPlaylists();
5011 }
5012
ui_companion_qt_log_msg(void * data,const char * msg)5013 static void ui_companion_qt_log_msg(void *data, const char *msg)
5014 {
5015 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
5016 ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window;
5017
5018 win_handle->qtWindow->appendLogMessage(msg);
5019 }
5020
ui_companion_qt_is_active(void * data)5021 static bool ui_companion_qt_is_active(void *data)
5022 {
5023 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
5024 ui_window_qt_t *win_handle = (ui_window_qt_t*)handle->window;
5025
5026 return win_handle->qtWindow->isVisible();
5027 }
5028
ui_companion_qt_msg_queue_push(void * data,const char * msg,unsigned priority,unsigned duration,bool flush)5029 void ui_companion_qt_msg_queue_push(void *data,
5030 const char *msg, unsigned priority, unsigned duration, bool flush)
5031 {
5032 ui_companion_qt_t *handle = (ui_companion_qt_t*)data;
5033 ui_window_qt_t *win_handle = NULL;
5034
5035 if (!handle)
5036 return;
5037
5038 win_handle = (ui_window_qt_t*)handle->window;
5039
5040 if (win_handle)
5041 win_handle->qtWindow->showStatusMessage(msg, priority, duration, flush);
5042 }
5043
5044 ui_companion_driver_t ui_companion_qt = {
5045 ui_companion_qt_init,
5046 ui_companion_qt_deinit,
5047 ui_companion_qt_toggle,
5048 ui_companion_qt_event_command,
5049 ui_companion_qt_notify_content_loaded,
5050 ui_companion_qt_notify_list_pushed,
5051 ui_companion_qt_notify_refresh,
5052 ui_companion_qt_msg_queue_push,
5053 NULL,
5054 NULL,
5055 ui_companion_qt_log_msg,
5056 ui_companion_qt_is_active,
5057 &ui_browser_window_qt,
5058 &ui_msg_window_qt,
5059 &ui_window_qt,
5060 &ui_application_qt,
5061 "qt",
5062 };
5063
string_split_to_qt(QString str,char delim)5064 QStringList string_split_to_qt(QString str, char delim)
5065 {
5066 int at;
5067 QStringList list = QStringList();
5068
5069 for (at = 0;;)
5070 {
5071 /* Find next split */
5072 int spl = str.indexOf(delim, at);
5073
5074 /* Store split into list of extensions */
5075 list << str.mid(at, (spl < 0 ? -1 : spl - at));
5076
5077 /* No more splits */
5078 if (spl < 0)
5079 break;
5080
5081 at = spl + 1;
5082 }
5083
5084 return list;
5085 }
5086
5087 #define CORE_NAME_COLUMN 0
5088 #define CORE_VERSION_COLUMN 1
5089
LoadCoreTableWidget(QWidget * parent)5090 LoadCoreTableWidget::LoadCoreTableWidget(QWidget *parent) :
5091 QTableWidget(parent)
5092 {
5093 }
5094
keyPressEvent(QKeyEvent * event)5095 void LoadCoreTableWidget::keyPressEvent(QKeyEvent *event)
5096 {
5097 if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
5098 {
5099 event->accept();
5100 emit enterPressed();
5101 }
5102 else
5103 QTableWidget::keyPressEvent(event);
5104 }
5105
LoadCoreWindow(QWidget * parent)5106 LoadCoreWindow::LoadCoreWindow(QWidget *parent) :
5107 QMainWindow(parent)
5108 ,m_layout()
5109 ,m_table(new LoadCoreTableWidget())
5110 ,m_statusLabel(new QLabel())
5111 {
5112 QHBoxLayout *hbox = new QHBoxLayout();
5113 QPushButton *customCoreButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CUSTOM_CORE));
5114
5115 connect(customCoreButton, SIGNAL(clicked()), this, SLOT(onLoadCustomCoreClicked()));
5116 connect(m_table, SIGNAL(enterPressed()), this, SLOT(onCoreEnterPressed()));
5117 connect(m_table, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(onCellDoubleClicked(int,int)));
5118
5119 setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE));
5120
5121 setCentralWidget(new QWidget());
5122
5123 centralWidget()->setLayout(&m_layout);
5124
5125 hbox->addWidget(customCoreButton);
5126 hbox->addItem(new QSpacerItem(width(),
5127 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
5128
5129 m_layout.addWidget(m_table);
5130 m_layout.addLayout(hbox);
5131
5132 statusBar()->addPermanentWidget(m_statusLabel);
5133 }
5134
closeEvent(QCloseEvent * event)5135 void LoadCoreWindow::closeEvent(QCloseEvent *event)
5136 {
5137 emit windowClosed();
5138
5139 QWidget::closeEvent(event);
5140 }
5141
keyPressEvent(QKeyEvent * event)5142 void LoadCoreWindow::keyPressEvent(QKeyEvent *event)
5143 {
5144 if (event->key() == Qt::Key_Escape)
5145 {
5146 event->accept();
5147 close();
5148 }
5149 else
5150 QMainWindow::keyPressEvent(event);
5151 }
5152
setStatusLabel(QString label)5153 void LoadCoreWindow::setStatusLabel(QString label)
5154 {
5155 m_statusLabel->setText(label);
5156 }
5157
onCellDoubleClicked(int,int)5158 void LoadCoreWindow::onCellDoubleClicked(int, int)
5159 {
5160 onCoreEnterPressed();
5161 }
5162
loadCore(const char * path)5163 void LoadCoreWindow::loadCore(const char *path)
5164 {
5165 QProgressDialog progress(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOADING_CORE), QString(), 0, 0, this);
5166 progress.setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE));
5167 progress.setMinimumDuration(0);
5168 progress.setValue(progress.minimum());
5169 progress.show();
5170
5171 /* Because core loading will block, we need to go ahead and process pending events that would allow the progress dialog to fully show its contents before actually starting the core loading process. Must call processEvents() twice. */
5172 qApp->processEvents();
5173 qApp->processEvents();
5174
5175 #ifdef HAVE_DYNAMIC
5176 path_set(RARCH_PATH_CORE, path);
5177
5178 command_event(CMD_EVENT_CORE_INFO_DEINIT, NULL);
5179 command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
5180
5181 core_info_init_current_core();
5182
5183 if (!command_event(CMD_EVENT_LOAD_CORE, NULL))
5184 {
5185 QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_OPEN_LIBRETRO_CORE));
5186 return;
5187 }
5188
5189 setProperty("last_launch_with_index", -1);
5190
5191 emit coreLoaded();
5192 #endif
5193 }
5194
onCoreEnterPressed()5195 void LoadCoreWindow::onCoreEnterPressed()
5196 {
5197 QByteArray pathArray;
5198 const char *pathData = NULL;
5199 QTableWidgetItem *selectedCoreItem =
5200 m_table->item(m_table->currentRow(), CORE_NAME_COLUMN);
5201 QVariantHash hash = selectedCoreItem->data(
5202 Qt::UserRole).toHash();
5203 QString path = hash["path"].toString();
5204
5205 pathArray.append(path);
5206 pathData = pathArray.constData();
5207
5208 loadCore(pathData);
5209 }
5210
onLoadCustomCoreClicked()5211 void LoadCoreWindow::onLoadCustomCoreClicked()
5212 {
5213 QString path;
5214 QByteArray pathArray;
5215 char core_ext[255] = {0};
5216 char filters[PATH_MAX_LENGTH] = {0};
5217 const char *pathData = NULL;
5218 settings_t *settings = config_get_ptr();
5219 const char *path_dir_libretro = settings->paths.directory_libretro;
5220
5221 frontend_driver_get_core_extension(core_ext, sizeof(core_ext));
5222
5223 strlcpy(filters, "Cores (*.", sizeof(filters));
5224 strlcat(filters, core_ext, sizeof(filters));
5225 strlcat(filters, ");;All Files (*.*)", sizeof(filters));
5226
5227 path = QFileDialog::getOpenFileName(
5228 this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE),
5229 path_dir_libretro, filters, NULL);
5230
5231 if (path.isEmpty())
5232 return;
5233
5234 pathArray.append(path);
5235 pathData = pathArray.constData();
5236
5237 loadCore(pathData);
5238 }
5239
initCoreList(const QStringList & extensionFilters)5240 void LoadCoreWindow::initCoreList(const QStringList &extensionFilters)
5241 {
5242 int j;
5243 unsigned i;
5244 QStringList horizontal_header_labels;
5245 core_info_list_t *cores = NULL;
5246 QDesktopWidget *desktop = qApp->desktop();
5247 QRect desktopRect = desktop->availableGeometry();
5248
5249 horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME);
5250 horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_VERSION);
5251
5252 core_info_get_list(&cores);
5253
5254 m_table->clear();
5255 m_table->setColumnCount(0);
5256 m_table->setRowCount(0);
5257 m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
5258 m_table->setSelectionMode(QAbstractItemView::SingleSelection);
5259 m_table->setSortingEnabled(false);
5260 m_table->setColumnCount(2);
5261 m_table->setHorizontalHeaderLabels(horizontal_header_labels);
5262
5263 if (cores)
5264 {
5265 m_table->setRowCount(cores->count);
5266
5267 for (i = 0; i < cores->count; i++)
5268 {
5269 QVariantHash hash;
5270 core_info_t *core = core_info_get(cores, i);
5271 QTableWidgetItem *name_item = NULL;
5272 QTableWidgetItem *version_item = new QTableWidgetItem(core->display_version);
5273 const char *name = core->display_name;
5274
5275 if (string_is_empty(name))
5276 name = path_basename(core->path);
5277
5278 name_item = new QTableWidgetItem(name);
5279
5280 hash["path"] = core->path;
5281 hash["extensions"] = string_split_to_qt(QString(core->supported_extensions), '|');
5282
5283 name_item->setData(Qt::UserRole, hash);
5284 name_item->setFlags(name_item->flags() & ~Qt::ItemIsEditable);
5285 version_item->setFlags(version_item->flags() & ~Qt::ItemIsEditable);
5286
5287 m_table->setItem(i, CORE_NAME_COLUMN, name_item);
5288 m_table->setItem(i, CORE_VERSION_COLUMN, version_item);
5289 }
5290 }
5291
5292 if (!extensionFilters.isEmpty())
5293 {
5294 QVector<int> rowsToHide;
5295
5296 for (j = 0; j < m_table->rowCount(); j++)
5297 {
5298 int k;
5299 QVariantHash hash;
5300 QStringList extensions;
5301 bool found = false;
5302 QTableWidgetItem *item = m_table->item(j, CORE_NAME_COLUMN);
5303
5304 if (!item)
5305 continue;
5306
5307 hash = item->data(Qt::UserRole).toHash();
5308 extensions = hash["extensions"].toStringList();
5309
5310 if (!extensions.isEmpty())
5311 {
5312 for (k = 0; k < extensions.size(); k++)
5313 {
5314 QString ext = extensions.at(k).toLower();
5315
5316 if (extensionFilters.contains(ext, Qt::CaseInsensitive))
5317 {
5318 found = true;
5319 break;
5320 }
5321 }
5322
5323 if (!found)
5324 rowsToHide.append(j);
5325 }
5326 }
5327
5328 if (rowsToHide.size() != m_table->rowCount())
5329 {
5330 int i = 0;
5331
5332 for (i = 0; i < rowsToHide.count() && rowsToHide.count() > 0; i++)
5333 {
5334 const int &row = rowsToHide.at(i);
5335 m_table->setRowHidden(row, true);
5336 }
5337 }
5338 }
5339
5340 m_table->setSortingEnabled(true);
5341 m_table->resizeColumnsToContents();
5342 m_table->sortByColumn(0, Qt::AscendingOrder);
5343 m_table->selectRow(0);
5344 m_table->setAlternatingRowColors(true);
5345
5346 resize(qMin(desktopRect.width(), contentsMargins().left() + m_table->horizontalHeader()->length() + contentsMargins().right()), height());
5347 }
5348