diff --git a/Makefile.common b/Makefile.common index 07b7e2f836..e0fb91745c 100644 --- a/Makefile.common +++ b/Makefile.common @@ -487,11 +487,7 @@ endif ifeq ($(HAVE_QT), 1) OBJ += ui/drivers/ui_qt.o \ - ui/drivers/qt/ui_qt_application.o \ - ui/drivers/qt/ui_qt_window.o \ - ui/drivers/qt/ui_qt_browser_window.o \ ui/drivers/qt/ui_qt_load_core_window.o \ - ui/drivers/qt/ui_qt_msg_window.o \ ui/drivers/qt/gridview.o \ ui/drivers/qt/coreoptionsdialog.o \ ui/drivers/qt/filedropwidget.o \ diff --git a/griffin/griffin_cpp.cpp b/griffin/griffin_cpp.cpp index 5407f13017..3ab89521de 100644 --- a/griffin/griffin_cpp.cpp +++ b/griffin/griffin_cpp.cpp @@ -51,11 +51,7 @@ UI #include "../ui/drivers/ui_qt.cpp" -#include "../ui/drivers/qt/ui_qt_window.cpp" #include "../ui/drivers/qt/ui_qt_load_core_window.cpp" -#include "../ui/drivers/qt/ui_qt_browser_window.cpp" -#include "../ui/drivers/qt/ui_qt_msg_window.cpp" -#include "../ui/drivers/qt/ui_qt_application.cpp" #include "../ui/drivers/qt/gridview.cpp" #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) #include "../ui/drivers/qt/shaderparamsdialog.cpp" diff --git a/ui/drivers/qt/ui_qt_application.cpp b/ui/drivers/qt/ui_qt_application.cpp deleted file mode 100644 index 0f69c8e068..0000000000 --- a/ui/drivers/qt/ui_qt_application.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2016-2019 - Brad Parker - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include - -#ifndef CXX_BUILD -extern "C" { -#endif - -#include "../../ui_companion_driver.h" -#include "../../../retroarch.h" -#include "../../../verbosity.h" -#include "../../../version.h" -#include "../../../frontend/frontend.h" -#include "../../../tasks/tasks_internal.h" -#include -#ifdef Q_OS_UNIX -#include -#endif - -#ifndef CXX_BUILD -} -#endif - -#include "../ui_qt.h" - -static AppHandler *app_handler; -static ui_application_qt_t ui_application; - -/* ARGB 16x16 */ -static const unsigned retroarch_qt_icon_data[] = { -0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, -0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000, -0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000 -}; - -AppHandler::AppHandler(QObject *parent) : - QObject(parent) -{ -} - -AppHandler::~AppHandler() -{ -} - -void AppHandler::exit() -{ - ui_application_qt.exiting = true; - - if (qApp) - qApp->closeAllWindows(); -} - -bool AppHandler::isExiting() const -{ - return ui_application_qt.exiting; -} - -void AppHandler::onLastWindowClosed() -{ -} - -static void* ui_application_qt_initialize(void) -{ - /* These must last for the lifetime of the QApplication */ - static int app_argc = 1; - static char app_name[] = "retroarch"; - static char *app_argv[] = { app_name, NULL }; - - app_handler = new AppHandler(); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - /* HiDpi supported since Qt 5.6 */ - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif - - /* Create QApplication() before calling QApplication::setStyle() - * to ensure that plugin path is determined correctly */ - ui_application.app = new QApplication(app_argc, app_argv); - QApplication::setStyle("fusion"); - ui_application.app->setOrganizationName("libretro"); - ui_application.app->setApplicationName("RetroArch"); - ui_application.app->setApplicationVersion(PACKAGE_VERSION); - ui_application.app->connect(ui_application.app, SIGNAL(lastWindowClosed()), - app_handler, SLOT(onLastWindowClosed())); - -#ifdef Q_OS_UNIX - setlocale(LC_NUMERIC, "C"); -#endif - { - /* Can't declare the pixmap at the top, because: "QPixmap: Must construct a QGuiApplication before a QPixmap" */ - QImage iconImage(16, 16, QImage::Format_ARGB32); - QPixmap iconPixmap; - unsigned char *bits = iconImage.bits(); - - memcpy(bits, retroarch_qt_icon_data, 16 * 16 * sizeof(unsigned)); - - iconPixmap = QPixmap::fromImage(iconImage); - - ui_application.app->setWindowIcon(QIcon(iconPixmap)); - } - - return &ui_application; -} - -static void ui_application_qt_process_events(void) -{ - QAbstractEventDispatcher *dispatcher = QApplication::eventDispatcher(); - if (dispatcher && dispatcher->hasPendingEvents()) - QApplication::processEvents(); -} - -static void ui_application_qt_quit(void) -{ - if (app_handler) - app_handler->exit(); -} - -#ifdef HAVE_MAIN -#if defined(__cplusplus) && !defined(CXX_BUILD) -extern "C" -#endif -int main(int argc, char *argv[]) -{ - return rarch_main(argc, argv, NULL); -} -#endif - -ui_application_t ui_application_qt = { - ui_application_qt_initialize, - ui_application_qt_process_events, - ui_application_qt_quit, - false, - "qt" -}; diff --git a/ui/drivers/qt/ui_qt_browser_window.cpp b/ui/drivers/qt/ui_qt_browser_window.cpp deleted file mode 100644 index 3fdce75ef2..0000000000 --- a/ui/drivers/qt/ui_qt_browser_window.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2016-2019 - Brad Parker - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "../../ui_companion_driver.h" - -static bool ui_browser_window_qt_open(ui_browser_window_state_t *state) -{ - return true; -} - -static bool ui_browser_window_qt_save(ui_browser_window_state_t *state) -{ - return false; -} - -ui_browser_window_t ui_browser_window_qt = { - ui_browser_window_qt_open, - ui_browser_window_qt_save, - "qt" -}; diff --git a/ui/drivers/qt/ui_qt_msg_window.cpp b/ui/drivers/qt/ui_qt_msg_window.cpp deleted file mode 100644 index 01817abd3e..0000000000 --- a/ui/drivers/qt/ui_qt_msg_window.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2016-2019 - Brad Parker - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include - -#include - -#include "../../ui_companion_driver.h" - -static enum ui_msg_window_response ui_msg_window_qt_response(ui_msg_window_state *state, QMessageBox::StandardButtons response) -{ - switch (response) - { - case QMessageBox::Ok: - return UI_MSG_RESPONSE_OK; - case QMessageBox::Cancel: - return UI_MSG_RESPONSE_CANCEL; - case QMessageBox::Yes: - return UI_MSG_RESPONSE_YES; - case QMessageBox::No: - return UI_MSG_RESPONSE_NO; - default: - break; - } - - switch (state->buttons) - { - case UI_MSG_WINDOW_OK: - return UI_MSG_RESPONSE_OK; - case UI_MSG_WINDOW_OKCANCEL: - return UI_MSG_RESPONSE_CANCEL; - case UI_MSG_WINDOW_YESNO: - return UI_MSG_RESPONSE_NO; - case UI_MSG_WINDOW_YESNOCANCEL: - return UI_MSG_RESPONSE_CANCEL; - default: - break; - } - - return UI_MSG_RESPONSE_NA; -} - -static QFlags -ui_msg_window_qt_buttons(ui_msg_window_state *state) -{ - switch (state->buttons) - { - case UI_MSG_WINDOW_OK: - return QMessageBox::Ok; - case UI_MSG_WINDOW_OKCANCEL: - return QMessageBox::Cancel; - case UI_MSG_WINDOW_YESNO: - return (QMessageBox::Yes | QMessageBox::No); - case UI_MSG_WINDOW_YESNOCANCEL: - return (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); - } - - return QMessageBox::NoButton; -} - -static enum ui_msg_window_response -ui_msg_window_qt_error(ui_msg_window_state *state) -{ - QFlags flags = ui_msg_window_qt_buttons(state); - return ui_msg_window_qt_response(state, QMessageBox::critical((QWidget*)state->window, state->title, state->text, flags)); -} - -static enum ui_msg_window_response ui_msg_window_qt_information(ui_msg_window_state *state) -{ - QFlags flags = ui_msg_window_qt_buttons(state); - return ui_msg_window_qt_response(state, QMessageBox::information((QWidget*)state->window, state->title, state->text, flags)); -} - -static enum ui_msg_window_response ui_msg_window_qt_question(ui_msg_window_state *state) -{ - QFlags flags = ui_msg_window_qt_buttons(state); - return ui_msg_window_qt_response(state, QMessageBox::question((QWidget*)state->window, state->title, state->text, flags)); -} - -static enum ui_msg_window_response ui_msg_window_qt_warning(ui_msg_window_state *state) -{ - QFlags flags = ui_msg_window_qt_buttons(state); - return ui_msg_window_qt_response(state, QMessageBox::warning((QWidget*)state->window, state->title, state->text, flags)); -} - -ui_msg_window_t ui_msg_window_qt = { - ui_msg_window_qt_error, - ui_msg_window_qt_information, - ui_msg_window_qt_question, - ui_msg_window_qt_warning, - "qt" -}; diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp deleted file mode 100644 index 6635cd4ef4..0000000000 --- a/ui/drivers/qt/ui_qt_window.cpp +++ /dev/null @@ -1,3256 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2011-2017 - Daniel De Matteis - * Copyright (C) 2016-2019 - Brad Parker - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../ui_qt.h" -#include "invader_png.h" -#include "ui_qt_load_core_window.h" -#include "ui_qt_themes.h" -#include "gridview.h" -#include "coreoptionsdialog.h" -#include "filedropwidget.h" -#include "coreinfodialog.h" -#include "playlistentrydialog.h" -#include "viewoptionsdialog.h" - -#ifndef CXX_BUILD -extern "C" { -#endif - -#ifdef HAVE_CONFIG_H -#include "../../../config.h" -#endif - -#include "../../../version.h" -#include "../../../verbosity.h" -#include "../../../retroarch.h" -#include "../../../msg_hash.h" -#include "../../../core_info.h" -#include "../../../content.h" -#include "../../../paths.h" -#include "../../../configuration.h" -#include "../../../file_path_special.h" -#include "../../../playlist.h" -#include "../../../content.h" -#ifdef HAVE_MENU -#include "../../../menu/menu_driver.h" -#endif -#include "../../../config.def.h" -#include "../../../tasks/task_content.h" -#include "../../../tasks/tasks_internal.h" -#ifdef HAVE_GIT_VERSION -#include "../../../version_git.h" -#endif -#include -#include -#include -#include -#include -#ifdef HAVE_OPENSSL -#include -#include -#endif - -#ifndef CXX_BUILD -} -#endif - -#include "shaderparamsdialog.h" -#include "../../../AUTHORS.h" - -#define TIMER_MSEC 1000 /* periodic timer for gathering statistics */ -#define STATUS_MSG_THROTTLE_MSEC 250 - -#define GENERIC_FOLDER_ICON "/xmb/dot-art/png/folder.png" -#define HIRAGANA_START 0x3041U -#define HIRAGANA_END 0x3096U -#define KATAKANA_START 0x30A1U -#define KATAKANA_END 0x30F6U -#define HIRA_KATA_OFFSET (KATAKANA_START - HIRAGANA_START) -#define DOCS_URL "http://docs.libretro.com/" - -static ui_window_qt_t ui_window = {0}; - -enum core_selection -{ - CORE_SELECTION_CURRENT = 0, - CORE_SELECTION_PLAYLIST_SAVED, - CORE_SELECTION_PLAYLIST_DEFAULT, - CORE_SELECTION_ASK, - CORE_SELECTION_LOAD_CORE -}; - -static const QPixmap getInvader(void) -{ - QPixmap pix; - pix.loadFromData(invader_png, invader_png_len, "PNG"); - - return pix; -} - -#ifdef HAVE_LIBRETRODB -static void scan_finished_handler(retro_task_t *task, - void *task_data, void *user_data, const char *err) -{ - bool dontAsk = false; - bool answer = false; -#ifdef HAVE_MENU - menu_ctx_environment_t menu_environ; - menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; - menu_environ.data = NULL; - - menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); -#endif - if (!ui_window.qtWindow->settings()->value( - "scan_finish_confirm", true).toBool()) - return; - - 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); - - if (answer && dontAsk) - ui_window.qtWindow->settings()->setValue("scan_finish_confirm", false); -} -#endif - -/* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */ -static double expScale(double inputValue, double midValue, double maxValue) -{ - double M = maxValue / midValue; - double C = log(pow(M - 1, 2)); - double B = maxValue / (exp(C) - 1); - double A = -1 * B; - double returnValue = A + B * exp(C * inputValue); - return returnValue; -} - -TreeView::TreeView(QWidget *parent) : - QTreeView(parent) -{ -} - -void TreeView::columnCountChanged(int oldCount, int newCount) -{ - QTreeView::columnCountChanged(oldCount, newCount); -} - -void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ - QModelIndexList list = selected.indexes(); - - QTreeView::selectionChanged(selected, deselected); - - emit itemsSelected(list); -} - -TableView::TableView(QWidget *parent) : - QTableView(parent) -{ -} - -bool TableView::isEditorOpen() -{ - return (state() == QAbstractItemView::EditingState); -} - -ListWidget::ListWidget(QWidget *parent) : - QListWidget(parent) -{ -} - -bool ListWidget::isEditorOpen() -{ - return (state() == QAbstractItemView::EditingState); -} - -void ListWidget::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) - emit enterPressed(); - else if (event->key() == Qt::Key_Delete) - emit deletePressed(); - - QListWidget::keyPressEvent(event); -} - -CoreInfoLabel::CoreInfoLabel(QString text, QWidget *parent) : - QLabel(text, parent) -{ - setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); -} - -CoreInfoWidget::CoreInfoWidget(CoreInfoLabel *label, QWidget *parent) : - QWidget(parent) - ,m_label(label) - ,m_scrollArea(new QScrollArea(this)) -{ - m_scrollArea->setWidgetResizable(true); - m_scrollArea->setWidget(m_label); -} - -QSize CoreInfoWidget::sizeHint() const -{ - return QSize(256, 256); -} - -void CoreInfoWidget::resizeEvent(QResizeEvent *event) -{ - QWidget::resizeEvent(event); - m_scrollArea->resize(event->size()); -} - -LogTextEdit::LogTextEdit(QWidget *parent) : - QPlainTextEdit(parent) -{ - -} - -void LogTextEdit::appendMessage(const QString& text) -{ - if (text.isEmpty()) - return; - - appendPlainText(text); - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); -} - -/* only accept indexes from current path. https://www.qtcentre.org/threads/50700-QFileSystemModel-and-QSortFilterProxyModel-don-t-work-well-together */ -bool FileSystemProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - QFileSystemModel *sm = qobject_cast(sourceModel()); - QModelIndex rootIndex = sm->index(sm->rootPath()); - - if (sourceParent == rootIndex) - return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); - return true; -} - -void FileSystemProxyModel::sort(int column, Qt::SortOrder order) -{ - /* sort the source (QFileSystemModel to keep directories before files) */ - sourceModel()->sort(column, order); -} - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent) - ,m_loadCoreWindow(new LoadCoreWindow(this)) - ,m_timer(new QTimer(this)) - ,m_currentCore() - ,m_currentCoreVersion() - ,m_statusLabel(new QLabel(this)) - ,m_dirTree(new TreeView(this)) - ,m_dirModel(new QFileSystemModel(m_dirTree)) - ,m_fileModel(new QFileSystemModel(this)) - ,m_listWidget(new ListWidget(this)) - ,m_centralWidget(new QStackedWidget(this)) - ,m_tableView(new TableView(this)) - ,m_fileTableView(new QTableView(this)) - ,m_playlistViews(new FileDropWidget(this)) - ,m_searchWidget(new QWidget(this)) - ,m_searchLineEdit(new QLineEdit(this)) - ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), this)) - ,m_playlistFiles() - ,m_launchWithComboBox(new QComboBox(this)) - ,m_startCorePushButton(new QToolButton(this)) - ,m_coreInfoPushButton(new QToolButton(this)) - ,m_runPushButton(new QToolButton(this)) - ,m_stopPushButton(new QToolButton(this)) - ,m_browserAndPlaylistTabWidget(new QTabWidget(this)) - ,m_pendingRun(false) - ,m_thumbnailPixmap(NULL) - ,m_thumbnailPixmap2(NULL) - ,m_thumbnailPixmap3(NULL) - ,m_settings(NULL) - ,m_viewOptionsDialog(NULL) - ,m_coreInfoDialog(new CoreInfoDialog(this, NULL)) - ,m_defaultStyle(NULL) - ,m_defaultPalette() - ,m_currentTheme(THEME_SYSTEM_DEFAULT) - ,m_coreInfoDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO), this)) - ,m_coreInfoLabel(new CoreInfoLabel(QString(), this)) - ,m_coreInfoWidget(new CoreInfoWidget(m_coreInfoLabel, this)) - ,m_logDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG), this)) - ,m_logWidget(new QFrame(this)) - ,m_logTextEdit(new LogTextEdit(m_logWidget)) - ,m_historyPlaylistsItem(NULL) - ,m_folderIcon() - ,m_customThemeString() - ,m_gridView(new GridView(this)) - ,m_playlistViewsAndFooter(new QWidget(this)) - ,m_zoomSlider(NULL) - ,m_lastZoomSliderValue(0) - ,m_viewType(VIEW_TYPE_LIST) - ,m_thumbnailType(THUMBNAIL_TYPE_BOXART) - ,m_gridProgressBar(NULL) - ,m_gridProgressWidget(NULL) - ,m_currentGridHash() - ,m_currentGridWidget(NULL) - ,m_allPlaylistsListMaxCount(0) - ,m_allPlaylistsGridMaxCount(0) - ,m_playlistEntryDialog(NULL) - ,m_statusMessageElapsedTimer() -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - ,m_shaderParamsDialog(new ShaderParamsDialog()) -#endif - ,m_coreOptionsDialog(new CoreOptionsDialog()) - ,m_networkManager(new QNetworkAccessManager(this)) - ,m_updateProgressDialog(new QProgressDialog()) - ,m_updateFile() - ,m_updateReply() - ,m_thumbnailDownloadProgressDialog(new QProgressDialog()) - ,m_thumbnailDownloadFile() - ,m_thumbnailDownloadReply() - ,m_pendingThumbnailDownloadTypes() - ,m_thumbnailPackDownloadProgressDialog(new QProgressDialog()) - ,m_thumbnailPackDownloadFile() - ,m_thumbnailPackDownloadReply() - ,m_playlistThumbnailDownloadProgressDialog(new QProgressDialog()) - ,m_playlistThumbnailDownloadFile() - ,m_playlistThumbnailDownloadReply() - ,m_pendingPlaylistThumbnails() - ,m_downloadedThumbnails(0) - ,m_failedThumbnails(0) - ,m_playlistThumbnailDownloadWasCanceled(false) - ,m_pendingDirScrollPath() - ,m_thumbnailTimer(new QTimer(this)) - ,m_gridItem(this) - ,m_currentBrowser(BROWSER_TYPE_PLAYLISTS) - ,m_searchRegExp() - ,m_zoomWidget(new QWidget(this)) - ,m_itemsCountLiteral(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ITEMS_COUNT)) - ,m_itemsCountLabel(new QLabel(this)) -{ - settings_t *settings = config_get_ptr(); - const char *path_dir_playlist = settings->paths.directory_playlist; - const char *path_dir_assets = settings->paths.directory_assets; - const char *path_dir_menu_content = settings->paths.directory_menu_content; - QDir playlistDir(path_dir_playlist); - QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); - QToolButton *searchResetButton = NULL; - QHBoxLayout *zoomLayout = new QHBoxLayout(); - QLabel *zoomLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ZOOM), m_zoomWidget); - QPushButton *thumbnailTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE), m_zoomWidget); - QMenu *thumbnailTypeMenu = new QMenu(thumbnailTypePushButton); - QAction *thumbnailTypeBoxartAction = NULL; - QAction *thumbnailTypeScreenshotAction = NULL; - QAction *thumbnailTypeTitleAction = NULL; - QPushButton *viewTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW), m_zoomWidget); - QMenu *viewTypeMenu = new QMenu(viewTypePushButton); - QAction *viewTypeIconsAction = NULL; - QAction *viewTypeListAction = NULL; - QHBoxLayout *gridProgressLayout = new QHBoxLayout(); - QLabel *gridProgressLabel = NULL; - QHBoxLayout *gridFooterLayout = NULL; - - qRegisterMetaType >("ThumbnailWidget"); - qRegisterMetaType("retro_task_callback_t"); - - /* Cancel all progress dialogs immediately since - * they show as soon as they're constructed. */ - m_updateProgressDialog->cancel(); - m_thumbnailDownloadProgressDialog->cancel(); - m_thumbnailPackDownloadProgressDialog->cancel(); - m_playlistThumbnailDownloadProgressDialog->cancel(); - - m_gridProgressWidget = new QWidget(); - gridProgressLabel = new QLabel( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PROGRESS), - m_gridProgressWidget); - - thumbnailTypePushButton->setObjectName("thumbnailTypePushButton"); - thumbnailTypePushButton->setFlat(true); - - thumbnailTypeBoxartAction = thumbnailTypeMenu->addAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART)); - thumbnailTypeScreenshotAction = thumbnailTypeMenu->addAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT)); - thumbnailTypeTitleAction = thumbnailTypeMenu->addAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN)); - - thumbnailTypePushButton->setMenu(thumbnailTypeMenu); - - viewTypePushButton->setObjectName("viewTypePushButton"); - viewTypePushButton->setFlat(true); - - viewTypeIconsAction = viewTypeMenu->addAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS)); - viewTypeListAction = viewTypeMenu->addAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST)); - - viewTypePushButton->setMenu(viewTypeMenu); - - gridProgressLabel->setObjectName("gridProgressLabel"); - - m_gridProgressBar = new QProgressBar( - m_gridProgressWidget); - - m_gridProgressBar->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); - - zoomLabel->setObjectName("zoomLabel"); - - m_zoomSlider = new QSlider( - Qt::Horizontal, m_zoomWidget); - - m_zoomSlider->setMinimum(0); - m_zoomSlider->setMaximum(100); - m_zoomSlider->setValue(50); - m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); - - m_lastZoomSliderValue = m_zoomSlider->value(); - - m_playlistViewsAndFooter->setLayout(new QVBoxLayout()); - - m_gridView->setSelectionMode(QAbstractItemView::SingleSelection); - m_gridView->setEditTriggers(QAbstractItemView::NoEditTriggers); - - m_playlistViews->addWidget(m_gridView); - m_playlistViews->addWidget(m_tableView); - m_centralWidget->setObjectName("centralWidget"); - - m_playlistViewsAndFooter->layout()->addWidget(m_playlistViews); - m_playlistViewsAndFooter->layout()->setAlignment(Qt::AlignCenter); - m_playlistViewsAndFooter->layout()->setContentsMargins(0, 0, 0, 0); - - m_gridProgressWidget->setLayout(gridProgressLayout); - gridProgressLayout->setContentsMargins(0, 0, 0, 0); - gridProgressLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); - gridProgressLayout->addWidget(gridProgressLabel); - gridProgressLayout->addWidget(m_gridProgressBar); - - m_playlistViewsAndFooter->layout()->addWidget(m_gridProgressWidget); - - m_zoomWidget->setLayout(zoomLayout); - zoomLayout->setContentsMargins(0, 0, 0, 0); - zoomLayout->addWidget(zoomLabel); - zoomLayout->addWidget(m_zoomSlider); - - m_itemsCountLabel->setObjectName("itemsCountLabel"); - - gridFooterLayout = new QHBoxLayout(); - gridFooterLayout->addWidget(m_itemsCountLabel); - gridFooterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); - gridFooterLayout->addWidget(m_gridProgressWidget); - gridFooterLayout->addWidget(m_zoomWidget); - gridFooterLayout->addWidget(thumbnailTypePushButton); - gridFooterLayout->addWidget(viewTypePushButton); - - static_cast(m_playlistViewsAndFooter->layout())->addLayout(gridFooterLayout); - - m_gridProgressWidget->hide(); - - m_playlistModel = new PlaylistModel(this); - m_proxyModel = new QSortFilterProxyModel(this); - m_proxyModel->setSourceModel(m_playlistModel); - m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); - - m_proxyFileModel = new FileSystemProxyModel(); - m_proxyFileModel->setSourceModel(m_fileModel); - m_proxyFileModel->setSortCaseSensitivity(Qt::CaseInsensitive); - - m_tableView->setAlternatingRowColors(true); - m_tableView->setModel(m_proxyModel); - m_tableView->setSortingEnabled(true); - m_tableView->verticalHeader()->setVisible(false); - m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); - m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); - m_tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_tableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - m_tableView->horizontalHeader()->setStretchLastSection(true); - m_tableView->setWordWrap(false); - - m_fileTableView->setModel(m_fileModel); - m_fileTableView->sortByColumn(0, Qt::AscendingOrder); - m_fileTableView->setSortingEnabled(true); - m_fileTableView->setAlternatingRowColors(true); - m_fileTableView->verticalHeader()->setVisible(false); - m_fileTableView->setSelectionMode(QAbstractItemView::SingleSelection); - m_fileTableView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_fileTableView->horizontalHeader()->setStretchLastSection(true); - m_fileTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - m_fileTableView->setWordWrap(false); - - m_gridView->setItemDelegate(new ThumbnailDelegate(m_gridItem, this)); - m_gridView->setModel(m_proxyModel); - - m_gridView->setSelectionModel(m_tableView->selectionModel()); - - m_logWidget->setObjectName("logWidget"); - - m_folderIcon = QIcon(QString(path_dir_assets) + GENERIC_FOLDER_ICON); - m_imageFormats = QVector::fromList(QImageReader::supportedImageFormats()); - m_defaultStyle = QApplication::style(); - m_defaultPalette = QApplication::palette(); - - /* ViewOptionsDialog needs m_settings set before it's constructed */ - m_settings = new QSettings(configDir + "/retroarch_qt.cfg", QSettings::IniFormat, this); - m_viewOptionsDialog = new ViewOptionsDialog(this, 0); - m_playlistEntryDialog = new PlaylistEntryDialog(this, 0); - - /* default NULL parameter for parent wasn't added until 5.7 */ - m_startCorePushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_START_CORE), m_startCorePushButton)); - m_startCorePushButton->setFixedSize(m_startCorePushButton->sizeHint()); - - m_runPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN), m_runPushButton)); - m_runPushButton->setFixedSize(m_runPushButton->sizeHint()); - - m_stopPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_STOP), m_stopPushButton)); - m_stopPushButton->setFixedSize(m_stopPushButton->sizeHint()); - - m_coreInfoPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFO), m_coreInfoPushButton)); - m_coreInfoPushButton->setFixedSize(m_coreInfoPushButton->sizeHint()); - - searchResetButton = new QToolButton(m_searchWidget); - searchResetButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_SEARCH_CLEAR), searchResetButton)); - searchResetButton->setFixedSize(searchResetButton->sizeHint()); - - connect(searchResetButton, SIGNAL(clicked()), this, SLOT(onSearchResetClicked())); - - m_dirModel->setFilter(QDir::NoDotAndDotDot | - QDir::AllDirs | - QDir::Drives | - (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0))); - - m_fileModel->setFilter(QDir::NoDot | - QDir::AllEntries | - (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0))); - -#if defined(Q_OS_WIN) - m_dirModel->setRootPath(""); - m_fileModel->setRootPath(""); -#else - m_dirModel->setRootPath("/"); - m_fileModel->setRootPath("/"); -#endif - - m_dirTree->setModel(m_dirModel); - m_dirTree->setSelectionMode(QAbstractItemView::SingleSelection); - m_dirTree->header()->setVisible(false); - - m_fileTableView->setModel(m_proxyFileModel); - - if (m_dirModel->columnCount() > 3) - { - /* size */ - m_dirTree->hideColumn(1); - /* type */ - m_dirTree->hideColumn(2); - /* date modified */ - m_dirTree->hideColumn(3); - } - - reloadPlaylists(); - - m_searchWidget->setLayout(new QHBoxLayout()); - m_searchWidget->layout()->addWidget(m_searchLineEdit); - m_searchWidget->layout()->addWidget(searchResetButton); - - m_searchDock->setObjectName("searchDock"); - m_searchDock->setProperty("default_area", Qt::LeftDockWidgetArea); - m_searchDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH)); - m_searchDock->setWidget(m_searchWidget); - m_searchDock->setFixedHeight(m_searchDock->minimumSizeHint().height()); - - addDockWidget(static_cast(m_searchDock->property("default_area").toInt()), m_searchDock); - - m_coreInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); - m_coreInfoLabel->setTextFormat(Qt::RichText); - m_coreInfoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_coreInfoLabel->setOpenExternalLinks(true); - - m_coreInfoDock->setObjectName("coreInfoDock"); - m_coreInfoDock->setProperty("default_area", Qt::RightDockWidgetArea); - m_coreInfoDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO)); - m_coreInfoDock->setWidget(m_coreInfoWidget); - - addDockWidget(static_cast(m_coreInfoDock->property("default_area").toInt()), m_coreInfoDock); - - m_logWidget->setLayout(new QVBoxLayout()); - m_logWidget->layout()->addWidget(m_logTextEdit); - m_logWidget->layout()->setContentsMargins(0, 0, 0, 0); - - m_logDock->setObjectName("logDock"); - m_logDock->setProperty("default_area", Qt::BottomDockWidgetArea); - m_logDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG)); - m_logDock->setWidget(m_logWidget); - - addDockWidget(static_cast(m_logDock->property("default_area").toInt()), m_logDock); - - /* Hide the log by default. If user has saved their dock positions with the log visible, - * then this hide() call will be reversed later by restoreState(). - * FIXME: If user unchecks "save dock positions", the log will not be unhidden even if - * it was previously saved in the config. - */ - m_logDock->hide(); - - m_dirTree->setContextMenuPolicy(Qt::CustomContextMenu); - m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(onSearchEnterPressed())); - connect(m_searchLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onSearchLineEditEdited(const QString&))); - connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); - connect(m_loadCoreWindow, SIGNAL(coreLoaded()), this, SLOT(onCoreLoaded())); - connect(m_loadCoreWindow, SIGNAL(windowClosed()), this, SLOT(onCoreLoadWindowClosed())); - connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(onCurrentListItemChanged(QListWidgetItem*, QListWidgetItem*))); - connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked())); - connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); - connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); - connect(m_stopPushButton, SIGNAL(clicked()), this, SLOT(onStopClicked())); - connect(m_dirTree, SIGNAL(itemsSelected(QModelIndexList)), this, SLOT(onTreeViewItemsSelected(QModelIndexList))); - connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&))); - connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&))); - connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int))); - connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int))); - connect(thumbnailTypeBoxartAction, SIGNAL(triggered()), this, SLOT(onBoxartThumbnailClicked())); - connect(thumbnailTypeScreenshotAction, SIGNAL(triggered()), this, SLOT(onScreenshotThumbnailClicked())); - connect(thumbnailTypeTitleAction, SIGNAL(triggered()), this, SLOT(onTitleThumbnailClicked())); - connect(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked())); - connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked())); - connect(m_dirModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileSystemDirLoaded(const QString&))); - connect(m_fileModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileBrowserTableDirLoaded(const QString&))); - - m_dirTree->setCurrentIndex(m_dirModel->index(path_dir_menu_content)); - m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); - m_dirTree->expand(m_dirTree->currentIndex()); - - /* must use queued connection */ - connect(this, SIGNAL(scrollToDownloads(QString)), this, SLOT(onDownloadScroll(QString)), Qt::QueuedConnection); - connect(this, SIGNAL(scrollToDownloadsAgain(QString)), this, SLOT(onDownloadScrollAgain(QString)), Qt::QueuedConnection); - - connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), m_playlistThumbnailDownloadProgressDialog, SLOT(cancel())); - connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onPlaylistThumbnailDownloadCanceled())); - - connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailDownloadProgressDialog, SLOT(cancel())); - connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailDownloadCanceled())); - - connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailPackDownloadProgressDialog, SLOT(cancel())); - connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailPackDownloadCanceled())); - - connect(this, SIGNAL(itemChanged()), this, SLOT(onItemChanged())); - connect(this, SIGNAL(gotThumbnailDownload(QString,QString)), this, SLOT(onDownloadThumbnail(QString,QString))); - - m_thumbnailTimer->setSingleShot(true); - connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems())); - connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems())); - - /* TODO: Handle scroll and resize differently. */ - connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer())); - - connect(m_tableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentItemChanged(const QModelIndex&))); - connect(m_fileTableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentFileChanged(const QModelIndex&))); - - connect(m_gridView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); - connect(m_tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); - connect(m_fileTableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onFileDoubleClicked(const QModelIndex&))); - - connect(m_playlistModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), this, SLOT(onCurrentTableItemDataChanged(const QModelIndex&, const QModelIndex&, const QVector&))); - - /* 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) */ - connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); - connect(this, SIGNAL(gotStatusMessage(QString,unsigned,unsigned,bool)), this, SLOT(onGotStatusMessage(QString,unsigned,unsigned,bool)), Qt::AutoConnection); - connect(this, SIGNAL(gotReloadPlaylists()), this, SLOT(onGotReloadPlaylists()), Qt::AutoConnection); -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - connect(this, SIGNAL(gotReloadShaderParams()), this, SLOT(onGotReloadShaderParams()), Qt::AutoConnection); -#endif - connect(this, SIGNAL(gotReloadCoreOptions()), this, SLOT(onGotReloadCoreOptions()), Qt::AutoConnection); - - /* these are always queued */ - connect(this, SIGNAL(showErrorMessageDeferred(QString)), this, SLOT(onShowErrorMessage(QString)), Qt::QueuedConnection); - connect(this, SIGNAL(showInfoMessageDeferred(QString)), this, SLOT(onShowInfoMessage(QString)), Qt::QueuedConnection); - connect(this, SIGNAL(extractArchiveDeferred(QString,QString,QString,retro_task_callback_t)), this, SLOT(onExtractArchive(QString,QString,QString,retro_task_callback_t)), Qt::QueuedConnection); - - m_timer->start(TIMER_MSEC); - - statusBar()->addPermanentWidget(m_statusLabel); - - setCurrentCoreLabel(); - setCoreActions(); - - /* both of these are necessary to get the folder to scroll to the top of the view */ - qApp->processEvents(); - QTimer::singleShot(0, this, SLOT(onBrowserStartClicked())); - - m_searchLineEdit->setFocus(); - m_loadCoreWindow->setWindowModality(Qt::ApplicationModal); - - m_statusMessageElapsedTimer.start(); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - resizeDocks(QList() << m_searchDock, QList() << 1, Qt::Vertical); -#endif - -#ifdef HAVE_OPENSSL - { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - const SSL_METHOD *method = TLS_method(); - SSL_CTX *ctx = SSL_CTX_new(method); - - if (ctx) - SSL_CTX_free(ctx); -#else - const SSL_METHOD *method = TLSv1_method(); - RARCH_LOG("[Qt]: TLS supports %d ciphers.\n", method->num_ciphers()); -#endif - RARCH_LOG("[Qt]: Using %s\n", OPENSSL_VERSION_TEXT); - } -#endif -} - -MainWindow::~MainWindow() -{ - if (m_thumbnailPixmap) - delete m_thumbnailPixmap; - if (m_thumbnailPixmap2) - delete m_thumbnailPixmap2; - if (m_thumbnailPixmap3) - delete m_thumbnailPixmap3; - if (m_proxyFileModel) - delete m_proxyFileModel; -} - -void MainWindow::startTimer() -{ - if (m_thumbnailTimer->isActive()) - { - m_thumbnailTimer->stop(); - m_thumbnailTimer->start(50); - } - else - m_thumbnailTimer->start(50); -} - -void MainWindow::updateVisibleItems() -{ - if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS && m_viewType == VIEW_TYPE_ICONS) - { - unsigned i; - QVector indexes = m_gridView->visibleIndexes(); - size_t size = indexes.size(); - - for (i = 0; i < size; i++) - m_playlistModel->loadThumbnail(m_proxyModel->mapToSource(indexes.at(i))); - } -} - -void MainWindow::setThumbnailCacheLimit(int count) -{ - if (count < 1) - count = 0; - - m_playlistModel->setThumbnailCacheLimit(count); -} - -void MainWindow::onFileSystemDirLoaded(const QString &path) -{ - if (path.isEmpty() || m_pendingDirScrollPath.isEmpty()) - return; - - if (QDir(path) == QDir(m_pendingDirScrollPath)) - { - m_pendingDirScrollPath = QString(); - - emit scrollToDownloads(path); - } -} - -/* workaround for columns being resized */ -void MainWindow::onFileBrowserTableDirLoaded(const QString &path) -{ - if (path.isEmpty()) - return; - - m_fileTableView->horizontalHeader()->restoreState(m_fileTableHeaderState); -} - -QVector > MainWindow::getPlaylists() -{ - unsigned i; - QVector > playlists; - size_t size = m_listWidget->count(); - - for (i = 0; i < size; i++) - { - QString label, path; - QPair pair; - QListWidgetItem *item = m_listWidget->item(i); - - if (!item) - continue; - - label = item->text(); - path = item->data(Qt::UserRole).toString(); - - pair.first = label; - pair.second = path; - - playlists.append(pair); - } - - return playlists; -} - -void MainWindow::onItemChanged() -{ - QModelIndex index = getCurrentContentIndex(); - m_playlistModel->reloadThumbnail(index); - onCurrentItemChanged(index); -} - -QString MainWindow::getSpecialPlaylistPath(SpecialPlaylist playlist) -{ - switch (playlist) - { - case SPECIAL_PLAYLIST_HISTORY: - if (m_historyPlaylistsItem) - return m_historyPlaylistsItem->data(Qt::UserRole).toString(); - break; - default: - break; - } - - return QString(); -} - -double MainWindow::lerp(double x, double y, double a, double b, double d) -{ - return a + (b - a) * ((double)(d - x) / (double)(y - x)); -} - -void MainWindow::onIconViewClicked() -{ - setCurrentViewType(VIEW_TYPE_ICONS); -} - -void MainWindow::onListViewClicked() -{ - setCurrentViewType(VIEW_TYPE_LIST); -} - -void MainWindow::onBoxartThumbnailClicked() -{ - setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART); -} - -void MainWindow::onScreenshotThumbnailClicked() -{ - setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT); -} - -void MainWindow::onTitleThumbnailClicked() -{ - setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN); -} - -void MainWindow::setIconViewZoom(int zoomValue) -{ - m_zoomSlider->setValue(zoomValue); -} - -void MainWindow::onZoomValueChanged(int zoomValue) -{ - int newSize = 0; - - if (zoomValue < 50) - newSize = expScale( - lerp(0, 49, 25, 49, zoomValue) / 50.0, 102, 256); - else - newSize = expScale(zoomValue / 100.0, 256, 1024); - - m_gridView->setGridSize(newSize); - - m_lastZoomSliderValue = zoomValue; -} - -void MainWindow::showWelcomeScreen() -{ - bool dontAsk = false; - bool answer = false; - const QString welcomeText = QStringLiteral("" - "Welcome to the RetroArch Desktop Menu!
\n" - "
\n" - "Many settings and actions are currently only available in the familiar Big Picture menu, " - "but this Desktop Menu should be functional for launching content and managing playlists.
\n" - "
\n" - "Some useful hotkeys for interacting with the Big Picture menu include:\n" - "
  • F1 - Bring up the Big Picture menu
  • \n" - "
  • F - Switch between fullscreen and windowed modes
  • \n" - "
  • F5 - Bring the Desktop Menu back if closed
  • \n" - "
  • Esc - Exit RetroArch
\n" - "\n" - "For more hotkeys and their assignments, see:
\n" - "Settings -> Input -> Input Hotkey Binds
\n" - "
\n" - "Documentation for RetroArch, libretro and cores:
\n" - "https://docs.libretro.com/"); - - if (!m_settings->value("show_welcome_screen", true).toBool()) - return; - - answer = showMessageBox(welcomeText, MainWindow::MSGBOX_TYPE_QUESTION_OKCANCEL, Qt::ApplicationModal, true, &dontAsk); - - if (answer && dontAsk) - m_settings->setValue("show_welcome_screen", false); -} - -const QString& MainWindow::customThemeString() const -{ - return m_customThemeString; -} - -bool MainWindow::setCustomThemeFile(QString filePath) -{ - if (filePath.isEmpty()) - { - 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)); - return false; - } - - QFile file(filePath); - - if (file.exists()) - { - bool opened = file.open(QIODevice::ReadOnly); - - if (!opened) - { - 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)); - return false; - } - - { - QByteArray fileArray = file.readAll(); - QString fileStr = QString::fromUtf8(fileArray); - - file.close(); - - if (fileStr.isEmpty()) - { - 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)); - return false; - } - - setCustomThemeString(fileStr); - } - } - else - { - 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)); - return false; - } - - return true; -} - -void MainWindow::setCustomThemeString(QString qss) -{ - m_customThemeString = qss; -} - -bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality, bool showDontAsk, bool *dontAsk) -{ - QCheckBox *checkBox = NULL; - - QPointer msgBoxPtr = new QMessageBox(this); - QMessageBox *msgBox = msgBoxPtr.data(); - - msgBox->setWindowModality(modality); - msgBox->setTextFormat(Qt::RichText); - msgBox->setTextInteractionFlags(Qt::TextBrowserInteraction); - - if (showDontAsk) - { - checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); - /* QMessageBox::setCheckBox() is available since 5.2 */ - msgBox->setCheckBox(checkBox); - } - - switch (msgType) - { - case MSGBOX_TYPE_INFO: - msgBox->setIcon(QMessageBox::Information); - msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFORMATION)); - break; - case MSGBOX_TYPE_WARNING: - msgBox->setIcon(QMessageBox::Warning); - msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_WARNING)); - break; - case MSGBOX_TYPE_ERROR: - msgBox->setIcon(QMessageBox::Critical); - msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ERROR)); - break; - case MSGBOX_TYPE_QUESTION_YESNO: - msgBox->setIcon(QMessageBox::Question); - msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION)); - msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); - break; - case MSGBOX_TYPE_QUESTION_OKCANCEL: - msgBox->setIcon(QMessageBox::Question); - msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION)); - msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - break; - default: - break; - } - - msgBox->setText(msg); - msgBox->exec(); - - if (!msgBoxPtr) - return true; - - if ( - msgBox->result() != QMessageBox::Ok - && msgBox->result() != QMessageBox::Yes) - return false; - - if (checkBox) - if (dontAsk) - *dontAsk = checkBox->isChecked(); - - return true; -} - -void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&) -{ -#ifdef HAVE_LIBRETRODB - QDir dir; - QByteArray dirArray; - QPointer action; - QList actions; - QScopedPointer scanAction; - QString currentDirString = QDir::toNativeSeparators( - m_dirModel->filePath(m_dirTree->currentIndex())); - settings_t *settings = config_get_ptr(); - const char *fullpath = NULL; - const char *path_dir_playlist = settings->paths.directory_playlist; - const char *path_content_db = settings->paths.path_content_database; - - if (currentDirString.isEmpty()) - return; - - dir = currentDirString; - - if (!dir.exists()) - return; - - /* default NULL parameter for parent wasn't added until 5.7 */ - scanAction.reset(new QAction( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY), 0)); - - actions.append(scanAction.data()); - - action = QMenu::exec(actions, QCursor::pos(), NULL, m_dirTree); - - if (!action) - return; - - dirArray = currentDirString.toUtf8(); - fullpath = dirArray.constData(); - - task_push_dbscan( - path_dir_playlist, - path_content_db, - fullpath, true, - m_settings->value("show_hidden_files", true).toBool(), - scan_finished_handler); -#endif -} - -void MainWindow::showStatusMessage(QString msg, - unsigned priority, unsigned duration, bool flush) -{ - emit gotStatusMessage(msg, priority, duration, flush); -} - -void MainWindow::onGotStatusMessage( - QString msg, unsigned priority, unsigned duration, bool flush) -{ - int msecDuration = 0; - QScreen *screen = qApp->primaryScreen(); - QStatusBar *status = statusBar(); - - Q_UNUSED(priority) - - if (msg.isEmpty()) - return; - - if (!status) - return; - - if (screen) - msecDuration = (duration / screen->refreshRate()) * 1000; - - if (msecDuration <= 0) - msecDuration = 1000; - - if (status->currentMessage().isEmpty() || flush) - { - if (m_statusMessageElapsedTimer.elapsed() >= STATUS_MSG_THROTTLE_MSEC) - { - qint64 msgDuration = qMax(msecDuration, STATUS_MSG_THROTTLE_MSEC); - m_statusMessageElapsedTimer.restart(); - status->showMessage(msg, msgDuration); - } - } -} - -void MainWindow::deferReloadShaderParams() -{ -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - emit gotReloadShaderParams(); -#endif -} - -void MainWindow::onShaderParamsClicked() -{ -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - if (!m_shaderParamsDialog) - return; - - m_shaderParamsDialog->show(); - - onGotReloadShaderParams(); -#endif -} - -void MainWindow::onGotReloadShaderParams() -{ -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - if (m_shaderParamsDialog && m_shaderParamsDialog->isVisible()) - m_shaderParamsDialog->reload(); -#endif -} - -void MainWindow::onCoreOptionsClicked() -{ - if (!m_coreOptionsDialog) - return; - - m_coreOptionsDialog->show(); - - onGotReloadCoreOptions(); -} - -void MainWindow::onGotReloadCoreOptions() -{ - if (m_coreOptionsDialog && m_coreOptionsDialog->isVisible()) - m_coreOptionsDialog->reload(); -} - -void MainWindow::appendLogMessage(const QString &msg) -{ - emit gotLogMessage(msg); -} - -void MainWindow::onGotLogMessage(const QString &msg) -{ - QString newMsg = msg; - - if (newMsg.at(newMsg.size() - 1) == '\n') - newMsg.chop(1); - - m_logTextEdit->appendMessage(newMsg); -} - -void MainWindow::onLaunchWithComboBoxIndexChanged(int) -{ - QString coreInfoText; - QVector > - infoList = getCoreInfo(); - QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); - core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); - int i = 0; - - if (infoList.count() == 0) - return; - - for (i = 0; i < infoList.count(); i++) - { - const QHash &hash = infoList.at(i); - const QString &key = - hash.value("html_key", hash.value("key")); - const QString &value = - hash.value("html_value", hash.value("value")); - - if (!key.isEmpty()) - coreInfoText += key; - - if (!value.isEmpty()) - { - if (!key.isEmpty()) - coreInfoText += " "; - - coreInfoText += value; - } - - if (i < infoList.count() - 1) - coreInfoText += "
\n"; - } - - m_coreInfoLabel->setText(coreInfoText); - - if (coreSelection == CORE_SELECTION_LOAD_CORE) - onLoadCoreClicked(); - else - m_loadCoreWindow->setProperty("last_launch_with_index", - m_launchWithComboBox->currentIndex()); -} - -MainWindow::Theme MainWindow::getThemeFromString(QString themeString) -{ - if (themeString == "default") - return THEME_SYSTEM_DEFAULT; - else if (themeString == "dark") - return THEME_DARK; - else if (themeString == "custom") - return THEME_CUSTOM; - - return THEME_SYSTEM_DEFAULT; -} - -QString MainWindow::getThemeString(Theme theme) -{ - switch (theme) - { - case THEME_SYSTEM_DEFAULT: - return "default"; - case THEME_DARK: - return "dark"; - case THEME_CUSTOM: - return "custom"; - default: - break; - } - - return "default"; -} - -MainWindow::Theme MainWindow::theme() -{ - return m_currentTheme; -} - -void MainWindow::setTheme(Theme theme) -{ - m_currentTheme = theme; - - setDefaultCustomProperties(); - - switch(theme) - { - case THEME_SYSTEM_DEFAULT: - qApp->setStyleSheet(qt_theme_default_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString())); - break; - case THEME_DARK: - qApp->setStyleSheet(qt_theme_dark_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString())); - break; - case THEME_CUSTOM: - qApp->setStyleSheet(m_customThemeString); - break; - default: - break; - } -#ifdef HAVE_MENU - m_viewOptionsDialog->repaintIcons(); -#endif -} - -void MainWindow::setDefaultCustomProperties() -{ - m_gridView->setLayout(QString(DEFAULT_GRID_LAYOUT)); - m_gridView->setSpacing(DEFAULT_GRID_SPACING); - m_gridItem.setThumbnailVerticalAlign(QString(DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT)); - m_gridItem.setPadding(DEFAULT_GRID_ITEM_MARGIN); -} - -void MainWindow::changeThumbnailType(ThumbnailType type) -{ - m_playlistModel->setThumbnailType(type); - updateVisibleItems(); - m_gridView->viewport()->update(); -} - -QString MainWindow::changeThumbnail(const QImage &image, QString type) -{ - QHash hash = getCurrentContentHash(); - QString dirString = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]) + "/" + type; - QString thumbPath = dirString + "/" + m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]); - QByteArray dirArray = QDir::toNativeSeparators(dirString).toUtf8(); - const char *dirData = dirArray.constData(); - QByteArray thumbArray = QDir::toNativeSeparators(thumbPath).toUtf8(); - const char *thumbData = thumbArray.constData(); - int quality = -1; - QDir dir(dirString); - QImage scaledImage(image); - - if (!dir.exists()) - { - if (dir.mkpath(".")) - RARCH_LOG("[Qt]: Created directory: %s\n", dirData); - else - { - RARCH_ERR("[Qt]: Could not create directory: %s\n", dirData); - return QString(); - } - } - - if (m_settings->contains("thumbnail_max_size")) - { - int size = m_settings->value("thumbnail_max_size", 0).toInt(); - - if (size != 0 && (image.height() > size || image.width() > size)) - scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - - if (m_settings->contains("thumbnail_quality")) - quality = m_settings->value("thumbnail_quality", -1).toInt(); - - if (scaledImage.save(thumbPath, "png", quality)) - { - RARCH_LOG("[Qt]: Saved image: %s\n", thumbData); - m_playlistModel->reloadThumbnailPath(thumbPath); - updateVisibleItems(); - - return thumbPath; - } - - RARCH_ERR("[Qt]: Could not save image: %s\n", thumbData); - return QString(); -} - -void MainWindow::onThumbnailDropped(const QImage &image, - ThumbnailType thumbnailType) -{ - switch (thumbnailType) - { - case THUMBNAIL_TYPE_BOXART: - { - QString path = changeThumbnail(image, THUMBNAIL_BOXART); - - if (path.isNull()) - return; - - if (m_thumbnailPixmap) - delete m_thumbnailPixmap; - - m_thumbnailPixmap = new QPixmap(path); - - onResizeThumbnailOne(*m_thumbnailPixmap, true); - break; - } - - case THUMBNAIL_TYPE_TITLE_SCREEN: - { - QString path = changeThumbnail(image, THUMBNAIL_TITLE); - - if (path.isNull()) - return; - - if (m_thumbnailPixmap2) - delete m_thumbnailPixmap2; - - m_thumbnailPixmap2 = new QPixmap(path); - - onResizeThumbnailTwo(*m_thumbnailPixmap2, true); - break; - } - - case THUMBNAIL_TYPE_SCREENSHOT: - { - QString path = changeThumbnail(image, THUMBNAIL_SCREENSHOT); - - if (path.isNull()) - return; - - if (m_thumbnailPixmap3) - delete m_thumbnailPixmap3; - - m_thumbnailPixmap3 = new QPixmap(path); - - onResizeThumbnailThree(*m_thumbnailPixmap3, true); - break; - } - } -} - -QVector > MainWindow::getCoreInfo() -{ - unsigned i; - QVector > infoList; - core_info_ctx_find_t core_info_finder; - QHash currentCore = getSelectedCore(); - const core_info_t *core_info = NULL; - QByteArray currentCorePathArray = currentCore["core_path"].toUtf8(); - const char *current_core_path_data = currentCorePathArray.constData(); - - /* Search for current core */ - core_info_finder.inf = NULL; - core_info_finder.path = current_core_path_data; - - if (core_info_find(&core_info_finder)) - core_info = core_info_finder.inf; - - if ( currentCore["core_path"].isEmpty() - || !core_info - || !core_info->config_data) - { - QHash hash; - - hash["key"] = msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE); - hash["value"] = ""; - - infoList.append(hash); - - return infoList; - } - - if (core_info->core_name) - { - QHash hash; - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME)) + ":"; - hash["value"] = core_info->core_name; - - infoList.append(hash); - } - - if (core_info->display_name) - { - QHash hash; - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL)) + ":"; - hash["value"] = core_info->display_name; - - infoList.append(hash); - } - - if (core_info->systemname) - { - QHash hash; - - hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME)) + ":"; - hash["value"] = core_info->systemname; - - infoList.append(hash); - } - - if (core_info->system_manufacturer) - { - QHash hash; - - hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER)) + ":"; - hash["value"] = core_info->system_manufacturer; - - infoList.append(hash); - } - - if (core_info->categories_list) - { - QHash hash; - QString categories; - - for (i = 0; i < core_info->categories_list->size; i++) - { - categories += core_info->categories_list->elems[i].data; - - if (i < core_info->categories_list->size - 1) - categories += ", "; - } - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES)) + ":"; - hash["value"] = categories; - - infoList.append(hash); - } - - if (core_info->authors_list) - { - QHash hash; - QString authors; - - for (i = 0; i < core_info->authors_list->size; i++) - { - authors += core_info->authors_list->elems[i].data; - - if (i < core_info->authors_list->size - 1) - authors += ", "; - } - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS)) + ":"; - hash["value"] = authors; - - infoList.append(hash); - } - - if (core_info->permissions_list) - { - QHash hash; - QString permissions; - - for (i = 0; i < core_info->permissions_list->size; i++) - { - permissions += core_info->permissions_list->elems[i].data; - - if (i < core_info->permissions_list->size - 1) - permissions += ", "; - } - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS)) + ":"; - hash["value"] = permissions; - - infoList.append(hash); - } - - if (core_info->licenses_list) - { - QHash hash; - QString licenses; - - for (i = 0; i < core_info->licenses_list->size; i++) - { - licenses += core_info->licenses_list->elems[i].data; - - if (i < core_info->licenses_list->size - 1) - licenses += ", "; - } - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES)) + ":"; - hash["value"] = licenses; - - infoList.append(hash); - } - - if (core_info->supported_extensions_list) - { - QHash hash; - QString supported_extensions; - - for (i = 0; i < core_info->supported_extensions_list->size; i++) - { - supported_extensions += core_info->supported_extensions_list->elems[i].data; - - if (i < core_info->supported_extensions_list->size - 1) - supported_extensions += ", "; - } - - hash["key"] = QString( - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS)) + ":"; - hash["value"] = supported_extensions; - - infoList.append(hash); - } - - if (core_info->firmware_count > 0) - { - core_info_ctx_firmware_t firmware_info; - bool update_missing_firmware = false; - bool set_missing_firmware = false; - settings_t *settings = config_get_ptr(); - - firmware_info.path = core_info->path; - firmware_info.directory.system = settings->paths.directory_system; - - rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); - - update_missing_firmware = core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware); - - if (set_missing_firmware) - rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); - - if (update_missing_firmware) - { - QHash hash; - - hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE)) + ":"; - hash["value"] = ""; - - infoList.append(hash); - - /* FIXME: This looks hacky and probably - * needs to be improved for good translation support. */ - - for (i = 0; i < core_info->firmware_count; i++) - { - if (core_info->firmware[i].desc) - { - QString valueText; - QHash hash; - QString labelText = "(!) "; - bool missing = false; - - if (core_info->firmware[i].missing) - { - missing = true; - labelText += msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_MISSING); - } - else - labelText += msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_PRESENT); - - labelText += ", "; - - if (core_info->firmware[i].optional) - labelText += msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_OPTIONAL); - else - labelText += msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_REQUIRED); - - labelText += ":"; - - if (core_info->firmware[i].desc) - valueText = core_info->firmware[i].desc; - else - valueText = msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME); - - hash["key"] = labelText; - hash["value"] = valueText; - - if (missing) - { - QString style = "font-weight: bold; color: #ff0000"; - hash["label_style"] = style; - hash["value_style"] = style; - hash["html_key"] = "" + hash["key"] + ""; - hash["html_value"] = "" + hash["value"] + ""; - } - else - { - QString style = "font-weight: bold; color: rgb(0, 175, 0)"; - hash["label_style"] = style; - hash["value_style"] = style; - hash["html_key"] = "" + hash["key"] + ""; - hash["html_value"] = "" + hash["value"] + ""; - } - - infoList.append(hash); - } - } - } - } - - if (core_info->notes) - { - for (i = 0; i < core_info->note_list->size; i++) - { - QHash hash; - - hash["key"] = ""; - hash["value"] = core_info->note_list->elems[i].data; - - infoList.append(hash); - } - } - - return infoList; -} - -void MainWindow::onSearchResetClicked() -{ - m_searchLineEdit->clear(); - onSearchEnterPressed(); -} - -QToolButton* MainWindow::coreInfoPushButton() -{ - return m_coreInfoPushButton; -} - -void MainWindow::onTreeViewItemsSelected(QModelIndexList selectedIndexes) -{ - QString dir; - - if (selectedIndexes.isEmpty()) - return; - - dir = m_dirModel->filePath(selectedIndexes.first()); - - selectBrowserDir(dir); -} - -void MainWindow::onFileDoubleClicked(const QModelIndex &proxyIndex) -{ - const QModelIndex index = m_proxyFileModel->mapToSource(proxyIndex); - - if (m_fileModel->isDir(index)) - m_dirTree->setCurrentIndex(m_dirModel->index(m_fileModel->filePath(index))); - else - loadContent(getFileContentHash(index)); -} - -void MainWindow::selectBrowserDir(QString path) -{ - if (!path.isEmpty()) - { - QModelIndex sourceIndex = m_fileModel->setRootPath(path); - QModelIndex proxyIndex = m_proxyFileModel->mapFromSource(sourceIndex); - m_fileTableHeaderState = m_fileTableView->horizontalHeader()->saveState(); - - if (proxyIndex.isValid()) - m_fileTableView->setRootIndex(proxyIndex); - else - { - /* the directory is filtered out. Remove the filter for a moment. - * FIXME: Find a way to not have to do this - * (not filtering dirs is one). */ - m_proxyFileModel->setFilterRegExp(QRegExp()); - m_fileTableView->setRootIndex(m_proxyFileModel->mapFromSource(sourceIndex)); - m_proxyFileModel->setFilterRegExp(m_searchRegExp); - } - } - setCoreActions(); -} - -QTabWidget* MainWindow::browserAndPlaylistTabWidget() -{ - return m_browserAndPlaylistTabWidget; -} - -void MainWindow::onDropWidgetEnterPressed() -{ -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - /* entry is being renamed, ignore this enter press */ - if (m_tableView->isPersistentEditorOpen(m_tableView->currentIndex())) -#else - /* we can only check if any editor at all is open */ - if (m_tableView->isEditorOpen()) -#endif - return; - onRunClicked(); -} - -QModelIndex MainWindow::getCurrentContentIndex() -{ - if (m_viewType == VIEW_TYPE_LIST) - return m_tableView->currentIndex(); - else if (m_viewType == VIEW_TYPE_ICONS) - return m_gridView->currentIndex(); - return QModelIndex(); -} - -QHash MainWindow::getCurrentContentHash() -{ - return getCurrentContentIndex().data(PlaylistModel::HASH).value >(); -} - -QHash MainWindow::getFileContentHash(const QModelIndex &index) -{ - QHash hash; - QFileInfo fileInfo = m_fileModel->fileInfo(index); - - hash["path"] = QDir::toNativeSeparators(m_fileModel->filePath(index)); - hash["label"] = hash["path"]; - hash["label_noext"] = fileInfo.completeBaseName(); - hash["db_name"] = fileInfo.dir().dirName(); - - return hash; -} - -void MainWindow::onContentItemDoubleClicked(const QModelIndex &index) -{ - Q_UNUSED(index); - onRunClicked(); -} - -void MainWindow::onStartCoreClicked() -{ - content_ctx_info_t content_info; - - content_info.argc = 0; - content_info.argv = NULL; - content_info.args = NULL; - content_info.environ_get = NULL; - - path_clear(RARCH_PATH_BASENAME); - - if (!task_push_start_current_core(&content_info)) - QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); -} - -QHash MainWindow::getSelectedCore() -{ - QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); - core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); - QHash coreHash; - QHash contentHash; - ViewType viewType = getCurrentViewType(); - - if (viewType == VIEW_TYPE_LIST) - contentHash = m_tableView->currentIndex().data(PlaylistModel::HASH).value >(); - else if (viewType == VIEW_TYPE_ICONS) - contentHash = m_gridView->currentIndex().data(PlaylistModel::HASH).value >(); - else - return coreHash; - - switch(coreSelection) - { - case CORE_SELECTION_CURRENT: - coreHash["core_path"] = path_get(RARCH_PATH_CORE); - break; - case CORE_SELECTION_PLAYLIST_SAVED: - if (contentHash.isEmpty() || contentHash["core_path"].isEmpty()) - break; - - coreHash["core_path"] = contentHash["core_path"]; - - break; - case CORE_SELECTION_PLAYLIST_DEFAULT: - { - QString defaultCorePath; - - if (contentHash.isEmpty() || contentHash["db_name"].isEmpty()) - break; - - defaultCorePath = getPlaylistDefaultCore(contentHash["db_name"]); - - if (!defaultCorePath.isEmpty()) - coreHash["core_path"] = defaultCorePath; - - break; - } - default: - break; - } - - return coreHash; -} - -/* the hash typically has the following keys: -path - absolute path to the content file -core_path - absolute path to the core, or "DETECT" to ask the user -db_name - the display name of the rdb database this content is from -label - the display name of the content, usually comes from the database -crc32 - an upper-case, 8 byte string representation of the hex CRC32 checksum (e.g. ABCDEF12) followed by "|crc" -core_name - the display name of the core, or "DETECT" if unknown -label_noext - the display name of the content that is guaranteed not to contain a file extension -*/ -void MainWindow::loadContent(const QHash &contentHash) -{ - content_ctx_info_t content_info; - QByteArray corePathArray; - QByteArray contentPathArray; - QByteArray contentLabelArray; - QByteArray contentDbNameArray; - QByteArray contentCrc32Array; - char contentDbNameFull[PATH_MAX_LENGTH]; - const char *corePath = NULL; - const char *contentPath = NULL; - const char *contentLabel = NULL; - const char *contentDbName = NULL; - const char *contentCrc32 = NULL; - QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); - core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); - core_info_ctx_find_t core_info; - - contentDbNameFull[0] = '\0'; - - if (m_pendingRun) - coreSelection = CORE_SELECTION_CURRENT; - - if (coreSelection == CORE_SELECTION_ASK) - { - QStringList extensionFilters; - - if (contentHash.contains("path")) - { - QString extensionStr; - int lastIndex = contentHash["path"].lastIndexOf('.'); - QByteArray pathArray = contentHash["path"].toUtf8(); - const char *pathData = pathArray.constData(); - - if (lastIndex >= 0) - { - extensionStr = contentHash["path"].mid(lastIndex + 1); - - if (!extensionStr.isEmpty()) - extensionFilters.append(extensionStr.toLower()); - } - - if (path_is_compressed_file(pathData)) - { - unsigned i = 0; - struct string_list *list = file_archive_get_file_list(pathData, NULL); - - if (list) - { - if (list->size > 0) - { - for (i = 0; i < list->size; i++) - { - const char *filePath = list->elems[i].data; - const char *extension = path_get_extension(filePath); - - if (!extensionFilters.contains(extension, Qt::CaseInsensitive)) - extensionFilters.append(extension); - } - } - - string_list_free(list); - } - } - } - - m_pendingRun = true; - onLoadCoreClicked(extensionFilters); - - return; - } - - switch (coreSelection) - { - case CORE_SELECTION_CURRENT: - corePathArray = path_get(RARCH_PATH_CORE); - contentPathArray = contentHash["path"].toUtf8(); - contentLabelArray = contentHash["label_noext"].toUtf8(); - break; - case CORE_SELECTION_PLAYLIST_SAVED: - corePathArray = contentHash["core_path"].toUtf8(); - contentPathArray = contentHash["path"].toUtf8(); - contentLabelArray = contentHash["label_noext"].toUtf8(); - break; - case CORE_SELECTION_PLAYLIST_DEFAULT: - { - QString defaultCorePath = getPlaylistDefaultCore(contentHash["db_name"]); - - if (!defaultCorePath.isEmpty()) - { - corePathArray = defaultCorePath.toUtf8(); - contentPathArray = contentHash["path"].toUtf8(); - contentLabelArray = contentHash["label_noext"].toUtf8(); - } - - break; - } - default: - return; - } - - contentDbNameArray = contentHash["db_name"].toUtf8(); - contentCrc32Array = contentHash["crc32"].toUtf8(); - - corePath = corePathArray.constData(); - contentPath = contentPathArray.constData(); - contentLabel = contentLabelArray.constData(); - contentDbName = contentDbNameArray.constData(); - contentCrc32 = contentCrc32Array.constData(); - - /* Search for specified core - ensures path - * is 'sanitised' */ - core_info.inf = NULL; - core_info.path = corePath; - - if (core_info_find(&core_info) && - !string_is_empty(core_info.inf->path)) - corePath = core_info.inf->path; - - /* Add lpl extension to db_name, if required */ - if (!string_is_empty(contentDbName)) - { - const char *extension = NULL; - - strlcpy(contentDbNameFull, contentDbName, sizeof(contentDbNameFull)); - extension = path_get_extension(contentDbNameFull); - - if ( string_is_empty(extension) - || !string_is_equal_noncase( - extension, FILE_PATH_LPL_EXTENSION_NO_DOT)) - strlcat( - contentDbNameFull, FILE_PATH_LPL_EXTENSION, - sizeof(contentDbNameFull)); - } - - content_info.argc = 0; - content_info.argv = NULL; - content_info.args = NULL; - content_info.environ_get = NULL; - -#ifdef HAVE_MENU - menu_navigation_set_selection(0); -#endif - - command_event(CMD_EVENT_UNLOAD_CORE, NULL); - - if (!task_push_load_content_with_new_core_from_companion_ui( - corePath, contentPath, contentLabel, contentDbNameFull, contentCrc32, - &content_info, NULL, NULL)) - { - QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), - msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); - return; - } - -#ifdef HAVE_MENU - menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL); -#endif -} - -void MainWindow::onRunClicked() -{ - QHash contentHash; - - switch (m_currentBrowser) - { - case BROWSER_TYPE_FILES: - contentHash = getFileContentHash( - m_proxyFileModel->mapToSource(m_fileTableView->currentIndex())); - break; - case BROWSER_TYPE_PLAYLISTS: - contentHash = getCurrentContentHash(); - break; - } - - if (contentHash.isEmpty()) - return; - - loadContent(contentHash); -} - -bool MainWindow::isContentLessCore() -{ - rarch_system_info_t *system = runloop_get_system_info(); - - return system->load_no_content; -} - -bool MainWindow::isCoreLoaded() -{ - if ( m_currentCore.isEmpty() || - m_currentCore == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE)) - return false; - - return true; -} - -PlaylistEntryDialog* MainWindow::playlistEntryDialog() -{ - return m_playlistEntryDialog; -} - -ViewOptionsDialog* MainWindow::viewOptionsDialog() -{ - return m_viewOptionsDialog; -} - -void MainWindow::setCoreActions() -{ - QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); - ViewType viewType = getCurrentViewType(); - QHash hash = getCurrentContentHash(); - QString currentPlaylistFileName = QString(); - - m_launchWithComboBox->clear(); - - if (isContentLessCore()) - m_startCorePushButton->show(); - else - m_startCorePushButton->hide(); - - if ( isCoreLoaded() - && m_settings->value("suggest_loaded_core_first", false).toBool()) - { - QVariantMap comboBoxMap; - comboBoxMap["core_name"] = m_currentCore; - comboBoxMap["core_path"] = path_get(RARCH_PATH_CORE); - comboBoxMap["core_selection"] = CORE_SELECTION_CURRENT; - m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap)); - } - - if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS) - { - if (!hash.isEmpty()) - { - QString coreName = hash["core_name"]; - - if (coreName.isEmpty()) - coreName = ""; - else - { - const char *detect_str = "DETECT"; - - if (coreName != detect_str) - { - if (m_launchWithComboBox->findText(coreName) == -1) - { - int i = 0; - bool found_existing = false; - - for (i = 0; i < m_launchWithComboBox->count(); i++) - { - QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); - - if (map.value("core_path").toString() == hash["core_path"] || map.value("core_name").toString() == coreName) - { - found_existing = true; - break; - } - } - - if (!found_existing) - { - QVariantMap comboBoxMap; - comboBoxMap["core_name"] = coreName; - comboBoxMap["core_path"] = hash["core_path"]; - comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_SAVED; - m_launchWithComboBox->addItem(coreName, QVariant::fromValue(comboBoxMap)); - } - } - } - } - } - } - - switch(m_currentBrowser) - { - case BROWSER_TYPE_PLAYLISTS: - currentPlaylistFileName = hash["db_name"]; - break; - case BROWSER_TYPE_FILES: - currentPlaylistFileName = m_fileModel->rootDirectory().dirName(); - break; - } - - if (!currentPlaylistFileName.isEmpty()) - { - QString defaultCorePath = getPlaylistDefaultCore(currentPlaylistFileName); - - if (!defaultCorePath.isEmpty()) - { - QString currentPlaylistItemDataString; - core_info_ctx_find_t core_info_finder; - bool allPlaylists = false; - int row = 0; - QByteArray defaultCorePathArray = defaultCorePath.toUtf8(); - const char *default_core_path_data = defaultCorePathArray.constData(); - - if (currentPlaylistItem) - { - currentPlaylistItemDataString = currentPlaylistItem->data(Qt::UserRole).toString(); - allPlaylists = ( - currentPlaylistItemDataString == ALL_PLAYLISTS_TOKEN); - } - - for (row = 0; row < m_listWidget->count(); row++) - { - if (allPlaylists) - { - QFileInfo info; - QListWidgetItem *listItem = m_listWidget->item(row); - QString listItemString = listItem->data(Qt::UserRole).toString(); - - info.setFile(listItemString); - - if (listItemString == ALL_PLAYLISTS_TOKEN) - continue; - } - - /* Search for default core */ - core_info_finder.inf = NULL; - core_info_finder.path = default_core_path_data; - - if (core_info_find(&core_info_finder)) - { - const core_info_t *info = core_info_finder.inf; - - if (m_launchWithComboBox->findText(info->core_name) == -1) - { - int i = 0; - bool found_existing = false; - - for (i = 0; i < m_launchWithComboBox->count(); i++) - { - QVariantMap map = - m_launchWithComboBox->itemData( - i, Qt::UserRole).toMap(); - QByteArray CorePathArray = - map.value("core_path").toString().toUtf8(); - const char *core_path_data = CorePathArray.constData(); - - if (string_starts_with(path_basename(core_path_data), - info->core_file_id.str) || - map.value("core_name").toString() == info->core_name || - map.value("core_name").toString() == info->display_name) - { - found_existing = true; - break; - } - } - - if (!found_existing) - { - QVariantMap comboBoxMap; - comboBoxMap["core_name"] = info->core_name; - comboBoxMap["core_path"] = info->path; - comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT; - m_launchWithComboBox->addItem(info->core_name, QVariant::fromValue(comboBoxMap)); - } - } - } - - if (!allPlaylists) - break; - } - } - } - - { - QVariantMap comboBoxMap; - comboBoxMap["core_selection"] = CORE_SELECTION_ASK; - m_launchWithComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK), QVariant::fromValue(comboBoxMap)); - m_launchWithComboBox->insertSeparator(m_launchWithComboBox->count()); - comboBoxMap["core_selection"] = CORE_SELECTION_LOAD_CORE; - m_launchWithComboBox->addItem(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)) + "...", QVariant::fromValue(comboBoxMap)); - } -} - -void MainWindow::onTabWidgetIndexChanged(int index) -{ - if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER)) - { - m_currentBrowser = BROWSER_TYPE_FILES; - - m_centralWidget->setCurrentWidget(m_fileTableView); - - onCurrentFileChanged(m_fileTableView->currentIndex()); - } - else if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) - { - m_currentBrowser = BROWSER_TYPE_PLAYLISTS; - - m_centralWidget->setCurrentWidget(m_playlistViewsAndFooter); - - onCurrentItemChanged(m_tableView->currentIndex()); - } - - applySearch(); - - setCoreActions(); -} - -QToolButton* MainWindow::runPushButton() -{ - return m_runPushButton; -} - -QToolButton* MainWindow::stopPushButton() -{ - return m_stopPushButton; -} - -QToolButton* MainWindow::startCorePushButton() -{ - return m_startCorePushButton; -} - -QComboBox* MainWindow::launchWithComboBox() -{ - return m_launchWithComboBox; -} - -void MainWindow::onSearchLineEditEdited(const QString &text) -{ - int i = 0; - QVector textUnicode = text.toUcs4(); - QVector textHiraToKata; - QVector textKataToHira; - bool foundHira = false; - bool foundKata = false; - - for (i = 0; i < textUnicode.size(); i++) - { - unsigned code = textUnicode.at(i); - - if (code >= HIRAGANA_START && code <= HIRAGANA_END) - { - foundHira = true; - textHiraToKata += code + HIRA_KATA_OFFSET; - } - else if (code >= KATAKANA_START && code <= KATAKANA_END) - { - foundKata = true; - textKataToHira += code - HIRA_KATA_OFFSET; - } - else - { - textHiraToKata += code; - textKataToHira += code; - } - } - - if (!foundHira && !foundKata) - m_searchRegExp = QRegExp(text, Qt::CaseInsensitive); - else if (foundHira && !foundKata) - m_searchRegExp = QRegExp(text + "|" - + QString::fromUcs4(textHiraToKata.constData(), - textHiraToKata.size()), Qt::CaseInsensitive); - else if (!foundHira && foundKata) - m_searchRegExp = QRegExp(text + "|" - + QString::fromUcs4(textKataToHira.constData(), - textKataToHira.size()), Qt::CaseInsensitive); - else - m_searchRegExp = QRegExp(text + "|" - + QString::fromUcs4(textHiraToKata.constData(), - textHiraToKata.size()) + "|" + - QString::fromUcs4(textKataToHira.constData(), - textKataToHira.size()), Qt::CaseInsensitive); - - applySearch(); -} - -void MainWindow::applySearch() -{ - switch (m_currentBrowser) - { - case BROWSER_TYPE_PLAYLISTS: - if (m_proxyModel->filterRegExp() != m_searchRegExp) - { - m_proxyModel->setFilterRegExp(m_searchRegExp); - updateItemsCount(); - } - break; - case BROWSER_TYPE_FILES: - if (m_proxyFileModel->filterRegExp() != m_searchRegExp) - m_proxyFileModel->setFilterRegExp(m_searchRegExp); - break; - } -} - -void MainWindow::onViewClosedDocksAboutToShow() -{ - QList dockWidgets; - int i = 0; - QMenu *menu = qobject_cast(sender()); - bool found = false; - - if (!menu) - return; - - dockWidgets = findChildren(); - - menu->clear(); - - if (dockWidgets.isEmpty()) - { - menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); - return; - } - - for (i = 0; i < dockWidgets.count(); i++) - { - const QDockWidget *dock = dockWidgets.at(i); - - if (!dock->isVisible()) - { - QAction *action = menu->addAction( - dock->property("menu_text").toString(), - this, SLOT(onShowHiddenDockWidgetAction())); - action->setProperty("dock_name", dock->objectName()); - found = true; - } - } - - if (!found) - menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); -} - -void MainWindow::onShowHiddenDockWidgetAction() -{ - QAction *action = qobject_cast(sender()); - QDockWidget *dock = NULL; - - if (!action) - return; - - dock = findChild(action->property("dock_name").toString()); - - if (!dock) - return; - - if (!dock->isVisible()) - { - addDockWidget(static_cast( - dock->property("default_area").toInt()), dock); - dock->setVisible(true); - dock->setFloating(false); - } -} - -QWidget* MainWindow::searchWidget() -{ - return m_searchWidget; -} - -QLineEdit* MainWindow::searchLineEdit() -{ - return m_searchLineEdit; -} - -void MainWindow::onSearchEnterPressed() -{ - onSearchLineEditEdited(m_searchLineEdit->text()); -} - -void MainWindow::onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) -{ - QHash hash; - - if (!roles.contains(Qt::EditRole)) - return; - - if (topLeft != bottomRight) - return; - - hash = topLeft.data(PlaylistModel::HASH).value>(); - - updateCurrentPlaylistEntry(hash); - - onCurrentItemChanged(topLeft); -} - -void MainWindow::onCurrentListItemDataChanged(QListWidgetItem *item) -{ - renamePlaylistItem(item, item->text()); -} - -void MainWindow::renamePlaylistItem(QListWidgetItem *item, QString newName) -{ - QString oldPath; - QString newPath; - QString extension; - QString oldName; - QFile file; - QFileInfo info; - QFileInfo playlistInfo; - QString playlistPath; - bool specialPlaylist = false; - settings_t *settings = config_get_ptr(); - const char *path_dir_playlist = settings->paths.directory_playlist; - QDir playlistDir(path_dir_playlist); - - if (!item) - return; - - playlistPath = item->data(Qt::UserRole).toString(); - playlistInfo = playlistPath; - oldName = playlistInfo.completeBaseName(); - - /* Don't just compare strings in case there are - * case differences on Windows that should be ignored. */ - if (QDir(playlistInfo.absoluteDir()) != QDir(playlistDir)) - { - /* special playlists like history etc. can't have an association */ - specialPlaylist = true; - } - - if (specialPlaylist) - { - /* special playlists shouldn't be editable already, - * but just in case, set the old name back and - * early return if they rename it */ - item->setText(oldName); - return; - } - - /* block this signal because setData() would trigger - * an infinite loop here */ - disconnect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); - - oldPath = item->data(Qt::UserRole).toString(); - - file.setFileName(oldPath); - info = file; - - extension = info.suffix(); - - newPath = info.absolutePath(); - - /* absolutePath() will always use / even on Windows */ - if (newPath.at(newPath.count() - 1) != '/') - /* add trailing slash if the path doesn't have one */ - newPath += '/'; - - newPath += newName + "." + extension; - - item->setData(Qt::UserRole, newPath); - - if (!file.rename(newPath)) - { - RARCH_ERR("[Qt]: Could not rename playlist.\n"); - item->setText(oldName); - } - - connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); -} - -void MainWindow::onCurrentItemChanged(const QModelIndex &index) -{ - onCurrentItemChanged(index.data(PlaylistModel::HASH).value>()); -} - -void MainWindow::onCurrentFileChanged(const QModelIndex &index) -{ - onCurrentItemChanged(getFileContentHash(m_proxyFileModel->mapToSource(index))); -} - -void MainWindow::onCurrentItemChanged(const QHash &hash) -{ - QString path = hash["path"]; - bool acceptDrop = false; - - if (m_thumbnailPixmap) - delete m_thumbnailPixmap; - if (m_thumbnailPixmap2) - delete m_thumbnailPixmap2; - if (m_thumbnailPixmap3) - delete m_thumbnailPixmap3; - - if (m_playlistModel->isSupportedImage(path)) - { - /* use thumbnail widgets to show regular image files */ - m_thumbnailPixmap = new QPixmap(path); - m_thumbnailPixmap2 = new QPixmap(*m_thumbnailPixmap); - m_thumbnailPixmap3 = new QPixmap(*m_thumbnailPixmap); - } - else - { - QString thumbnailsDir = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]); - QString thumbnailName = m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]); - - m_thumbnailPixmap = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_BOXART + "/" + thumbnailName); - m_thumbnailPixmap2 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_TITLE + "/" + thumbnailName); - m_thumbnailPixmap3 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_SCREENSHOT + "/" + thumbnailName); - - if ( m_currentBrowser == BROWSER_TYPE_PLAYLISTS - && !currentPlaylistIsSpecial()) - acceptDrop = true; - } - - onResizeThumbnailOne(*m_thumbnailPixmap, acceptDrop); - onResizeThumbnailTwo(*m_thumbnailPixmap2, acceptDrop); - onResizeThumbnailThree(*m_thumbnailPixmap3, acceptDrop); - - setCoreActions(); -} - -void MainWindow::setThumbnail(QString widgetName, - QPixmap &pixmap, bool acceptDrop) -{ - ThumbnailWidget *thumbnail = findChild(widgetName); - - if (thumbnail) - thumbnail->setPixmap(pixmap, acceptDrop); -} - -void MainWindow::onResizeThumbnailOne(QPixmap &pixmap, bool acceptDrop) -{ - setThumbnail("thumbnail", pixmap, acceptDrop); -} - -void MainWindow::onResizeThumbnailTwo(QPixmap &pixmap, bool acceptDrop) -{ - setThumbnail("thumbnail2", pixmap, acceptDrop); -} - -void MainWindow::onResizeThumbnailThree(QPixmap &pixmap, bool acceptDrop) -{ - setThumbnail("thumbnail3", pixmap, acceptDrop); -} - -void MainWindow::setCurrentViewType(ViewType viewType) -{ - m_viewType = viewType; - - switch (viewType) - { - case VIEW_TYPE_ICONS: - m_playlistViews->setCurrentWidget(m_gridView); - m_zoomWidget->show(); - break; - case VIEW_TYPE_LIST: - default: - m_playlistViews->setCurrentWidget(m_tableView); - m_zoomWidget->hide(); - break; - } -} - -void MainWindow::setCurrentThumbnailType(ThumbnailType thumbnailType) -{ - m_thumbnailType = thumbnailType; - - m_playlistModel->setThumbnailType(thumbnailType); - updateVisibleItems(); - m_gridView->viewport()->update(); -} - -MainWindow::ViewType MainWindow::getCurrentViewType() -{ - return m_viewType; -} - -ThumbnailType MainWindow::getCurrentThumbnailType() -{ - return m_thumbnailType; -} - -void MainWindow::onCurrentListItemChanged( - QListWidgetItem *current, QListWidgetItem *previous) -{ - Q_UNUSED(current) - Q_UNUSED(previous) - - initContentTableWidget(); - - setCoreActions(); -} - -TableView* MainWindow::contentTableView() -{ - return m_tableView; -} - -QTableView* MainWindow::fileTableView() -{ - return m_fileTableView; -} - -QStackedWidget* MainWindow::centralWidget() -{ - return m_centralWidget; -} - -FileDropWidget* MainWindow::playlistViews() -{ - return m_playlistViews; -} - -QWidget* MainWindow::playlistViewsAndFooter() -{ - return m_playlistViewsAndFooter; -} - -GridView* MainWindow::contentGridView() -{ - return m_gridView; -} - -void MainWindow::onBrowserDownloadsClicked() -{ - settings_t *settings = config_get_ptr(); - QDir dir(settings->paths.directory_core_assets); - QString path = dir.absolutePath(); - QModelIndex index; - - m_pendingDirScrollPath = path; - - index = m_dirModel->index(path); - - m_dirTree->setCurrentIndex(index); - - onDownloadScroll(path); -} - -void MainWindow::onDownloadScroll(QString path) -{ - QModelIndex index = m_dirModel->index(path); - m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop); - m_dirTree->expand(index); - - /* FIXME: Find a way to make this unnecessary */ - emit scrollToDownloadsAgain(path); -} - -void MainWindow::onDownloadScrollAgain(QString path) -{ - QModelIndex index = m_dirModel->index(path); - m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop); - m_dirTree->expand(index); -} - -void MainWindow::onBrowserUpClicked() -{ - QDir dir(m_dirModel->filePath(m_dirTree->currentIndex())); - - dir.cdUp(); - - m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath())); - m_dirTree->scrollTo(m_dirTree->currentIndex(), - QAbstractItemView::EnsureVisible); -} - -void MainWindow::onBrowserStartClicked() -{ - settings_t *settings = config_get_ptr(); - - m_dirTree->setCurrentIndex( - m_dirModel->index(settings->paths.directory_menu_content)); - m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); -} - -ListWidget* MainWindow::playlistListWidget() -{ - return m_listWidget; -} - -TreeView* MainWindow::dirTreeView() -{ - return m_dirTree; -} - -void MainWindow::onTimeout() -{ - bool contentless = false; - bool is_inited = false; - - content_get_status(&contentless, &is_inited); - - if (is_inited) - { - if (m_runPushButton->isVisible()) - m_runPushButton->hide(); - if (!m_stopPushButton->isVisible()) - m_stopPushButton->show(); - } - else - { - if (!m_runPushButton->isVisible()) - m_runPushButton->show(); - if (m_stopPushButton->isVisible()) - m_stopPushButton->hide(); - } - - setCurrentCoreLabel(); -} - -void MainWindow::onStopClicked() -{ -#ifdef HAVE_MENU - menu_navigation_set_selection(0); -#endif - command_event(CMD_EVENT_UNLOAD_CORE, NULL); - setCurrentCoreLabel(); - activateWindow(); - raise(); -} - -void MainWindow::setCurrentCoreLabel() -{ - bool update = false; - struct retro_system_info *system = runloop_get_libretro_system_info(); - QString libraryName = system->library_name; - const char *no_core_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE); - - if ( (m_statusLabel->text().isEmpty()) - || (m_currentCore != no_core_str && libraryName.isEmpty()) - ) - { - m_currentCore = no_core_str; - m_currentCoreVersion = ""; - update = true; - } - else - { - if (m_currentCore != libraryName && !libraryName.isEmpty()) - { - m_currentCore = system->library_name; - m_currentCoreVersion = (string_is_empty(system->library_version) ? "" : system->library_version); - update = true; - } - } - - if (update) - { - QAction *unloadCoreAction = findChild("unloadCoreAction"); - QString text = QString(PACKAGE_VERSION) + " - " + m_currentCore + " " + m_currentCoreVersion; - m_statusLabel->setText(text); - m_loadCoreWindow->setStatusLabel(text); - setCoreActions(); - - if (unloadCoreAction) - { - if (libraryName.isEmpty()) - unloadCoreAction->setEnabled(false); - else - unloadCoreAction->setEnabled(true); - } - } -} - -void MainWindow::onCoreLoadWindowClosed() -{ - QVariant lastLaunchWithVariant = m_loadCoreWindow->property("last_launch_with_index"); - int lastLaunchWithIndex = lastLaunchWithVariant.toInt(); - - m_pendingRun = false; - - if (lastLaunchWithVariant.isValid() && lastLaunchWithIndex >= 0) - { - m_launchWithComboBox->setCurrentIndex(lastLaunchWithIndex); - m_loadCoreWindow->setProperty("last_launch_with_index", -1); - } -} - -void MainWindow::onCoreLoaded() -{ - QAction *unloadAction = findChild("unloadCoreAction"); - - activateWindow(); - raise(); - setCurrentCoreLabel(); - setCoreActions(); - - if (unloadAction) - unloadAction->setEnabled(true); - - m_loadCoreWindow->hide(); - - if (m_pendingRun) - { - onRunClicked(); - m_pendingRun = false; - } -} - -void MainWindow::onUnloadCoreMenuAction() -{ - QAction *action = qobject_cast(sender()); - -#ifdef HAVE_MENU - menu_navigation_set_selection(0); -#endif - - /* TODO */ - if (!command_event(CMD_EVENT_UNLOAD_CORE, NULL)) - return; - - setCurrentCoreLabel(); - setCoreActions(); - - if (!action) - return; - - action->setEnabled(false); - activateWindow(); - raise(); -} - -void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters) -{ - m_loadCoreWindow->show(); - m_loadCoreWindow->resize(width() / 2, height()); - m_loadCoreWindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, m_loadCoreWindow->size(), geometry())); - m_loadCoreWindow->initCoreList(extensionFilters); -} - -void MainWindow::initContentTableWidget() -{ - QString path; - QListWidgetItem *item = m_listWidget->currentItem(); - - if (!item) - return; - - m_currentGridHash.clear(); - - if (m_currentGridWidget) - { - m_currentGridWidget->setObjectName("thumbnailWidget"); - m_currentGridWidget->style()->unpolish(m_currentGridWidget); - m_currentGridWidget->style()->polish(m_currentGridWidget); - } - - m_currentGridWidget = NULL; - - path = item->data(Qt::UserRole).toString(); - - if (path == ALL_PLAYLISTS_TOKEN) - { - unsigned i; - settings_t *settings = config_get_ptr(); - QDir playlistDir(settings->paths.directory_playlist); - QStringList playlists; - - for (i = 0; i < m_playlistFiles.count(); i++) - { - const QString &playlist = m_playlistFiles.at(i); - playlists.append(playlistDir.absoluteFilePath(playlist)); - } - - m_playlistModel->addPlaylistItems(playlists, true); - } - else - m_playlistModel->addPlaylistItems(QStringList() << path); - - if (item != m_historyPlaylistsItem) - m_tableView->sortByColumn(0, Qt::AscendingOrder); - else - m_proxyModel->sort(-1); - - updateItemsCount(); - - m_gridView->scrollToTop(); - m_gridView->setCurrentIndex(m_proxyModel->index(0, 0)); -} - -void MainWindow::updateItemsCount() -{ - m_itemsCountLabel->setText( - m_itemsCountLiteral.arg(m_proxyModel->rowCount())); -} - -void MainWindow::keyPressEvent(QKeyEvent *event) -{ - QMainWindow::keyPressEvent(event); -} - -QSettings* MainWindow::settings() -{ - return m_settings; -} - -QString MainWindow::getCurrentViewTypeString() -{ - switch (m_viewType) - { - case VIEW_TYPE_ICONS: - return QStringLiteral("icons"); - case VIEW_TYPE_LIST: - default: - break; - } - - return QStringLiteral("list"); -} - -QString MainWindow::getCurrentThumbnailTypeString() -{ - switch (m_thumbnailType) - { - case THUMBNAIL_TYPE_SCREENSHOT: - return QStringLiteral("screenshot"); - case THUMBNAIL_TYPE_TITLE_SCREEN: - return QStringLiteral("title"); - case THUMBNAIL_TYPE_BOXART: - default: - return QStringLiteral("boxart"); - } - - return QStringLiteral("list"); -} - -ThumbnailType MainWindow::getThumbnailTypeFromString(QString thumbnailType) -{ - if (thumbnailType == "boxart") - return THUMBNAIL_TYPE_BOXART; - else if (thumbnailType == "screenshot") - return THUMBNAIL_TYPE_SCREENSHOT; - else if (thumbnailType == "title") - return THUMBNAIL_TYPE_TITLE_SCREEN; - - return THUMBNAIL_TYPE_BOXART; -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - if (m_settings->value("save_geometry", false).toBool()) - m_settings->setValue("geometry", saveGeometry()); - if (m_settings->value("save_dock_positions", false).toBool()) - m_settings->setValue("dock_positions", saveState()); - if (m_settings->value("save_last_tab", false).toBool()) - m_settings->setValue("last_tab", m_browserAndPlaylistTabWidget->currentIndex()); - - m_settings->setValue("view_type", getCurrentViewTypeString()); - m_settings->setValue("file_browser_table_headers", m_fileTableView->horizontalHeader()->saveState()); - m_settings->setValue("icon_view_zoom", m_lastZoomSliderValue); - m_settings->setValue("icon_view_thumbnail_type", getCurrentThumbnailTypeString()); - m_settings->setValue("options_dialog_geometry", m_viewOptionsDialog->saveGeometry()); - - QMainWindow::closeEvent(event); -} - -void MainWindow::onContributorsClicked() -{ - QScopedPointer dialog(new QDialog()); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); - QTextEdit *textEdit = new QTextEdit(dialog.data()); - - connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject())); - - dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS)); - dialog->setLayout(new QVBoxLayout()); - - dialog->layout()->addWidget(textEdit); - - dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Minimum)); - dialog->layout()->addWidget(buttonBox); - - textEdit->setReadOnly(true); - textEdit->setHtml(QString("
") + retroarch_contributors_list + "
"); - - dialog->resize(480, 640); - dialog->exec(); -} - -void MainWindow::showAbout() -{ - QScopedPointer dialog(new QDialog()); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); - QString text = QString("RetroArch ") + PACKAGE_VERSION + - "

" + "www.libretro.com" - "

" + "www.retroarch.com" -#ifdef HAVE_GIT_VERSION - "

" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION) + ": " + retroarch_git_version + -#endif - "
" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_BUILD_DATE) + ": " + __DATE__; - QLabel *label = new QLabel(text, dialog.data()); - QPixmap pix = getInvader(); - QLabel *pixLabel = new QLabel(dialog.data()); - QPushButton *contributorsPushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS), dialog.data()); - - connect(contributorsPushButton, SIGNAL(clicked()), this, SLOT(onContributorsClicked())); - connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject())); - - label->setTextFormat(Qt::RichText); - label->setAlignment(Qt::AlignCenter); - label->setTextInteractionFlags(Qt::TextBrowserInteraction); - label->setOpenExternalLinks(true); - - pixLabel->setAlignment(Qt::AlignCenter); - pixLabel->setPixmap(pix); - - dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT)); - dialog->setLayout(new QVBoxLayout()); - - dialog->layout()->addWidget(pixLabel); - dialog->layout()->addWidget(label); - dialog->layout()->addWidget(contributorsPushButton); - - dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); - dialog->layout()->addWidget(buttonBox); - - dialog->exec(); -} - -void MainWindow::showDocs() -{ - QDesktopServices::openUrl(QUrl(DOCS_URL)); -} - -void MainWindow::onShowErrorMessage(QString msg) -{ - showMessageBox(msg, MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); -} - -void MainWindow::onShowInfoMessage(QString msg) -{ - showMessageBox(msg, MainWindow::MSGBOX_TYPE_INFO, Qt::ApplicationModal, false); -} - -int MainWindow::onExtractArchive(QString path, QString extractionDir, QString tempExtension, retro_task_callback_t cb) -{ - unsigned i; - file_archive_transfer_t state; - struct archive_extract_userdata userdata; - QByteArray pathArray = path.toUtf8(); - QByteArray dirArray = extractionDir.toUtf8(); - const char *file = pathArray.constData(); - const char *dir = dirArray.constData(); - struct string_list *file_list = file_archive_get_file_list(file, NULL); - bool returnerr = true; - retro_task_t *decompress_task = NULL; - - if (!file_list || file_list->size == 0) - { - showMessageBox("Error: Archive is empty.", MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); - RARCH_ERR("[Qt]: Downloaded archive is empty?\n"); - return -1; - } - - for (i = 0; i < file_list->size; i++) - { - QFile fileObj(file_list->elems[i].data); - - if (fileObj.exists()) - { - if (!fileObj.remove()) - { - /* if we cannot delete the existing file to update it, rename it for now and delete later */ - QFile fileTemp(fileObj.fileName() + tempExtension); - - if (fileTemp.exists()) - { - if (!fileTemp.remove()) - { - showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); - RARCH_ERR("[Qt]: Could not delete file: %s\n", file_list->elems[i].data); - return -1; - } - } - - if (!fileObj.rename(fileTemp.fileName())) - { - showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); - RARCH_ERR("[Qt]: Could not rename file: %s\n", file_list->elems[i].data); - return -1; - } - } - } - } - - string_list_free(file_list); - - memset(&state, 0, sizeof(state)); - memset(&userdata, 0, sizeof(userdata)); - - state.type = ARCHIVE_TRANSFER_INIT; - - m_updateProgressDialog->setWindowModality(Qt::NonModal); - m_updateProgressDialog->setMinimumDuration(0); - m_updateProgressDialog->setRange(0, 0); - m_updateProgressDialog->setAutoClose(true); - m_updateProgressDialog->setAutoReset(true); - m_updateProgressDialog->setValue(0); - m_updateProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_EXTRACTING)) + "..."); - m_updateProgressDialog->setCancelButtonText(QString()); - m_updateProgressDialog->show(); - - decompress_task = (retro_task_t*)task_push_decompress( - file, dir, - NULL, NULL, NULL, - cb, this, NULL, false); - - if (!decompress_task) - { - m_updateProgressDialog->cancel(); - return -1; - } - - return returnerr; -} - -QString MainWindow::getScrubbedString(QString str) -{ - const QString chars("&*/:`\"<>?\\|"); - int i; - - for (i = 0; i < chars.count(); i++) - str.replace(chars.at(i), '_'); - - return str; -} - -static void* ui_window_qt_init(void) -{ - ui_window.qtWindow = new MainWindow(); - - return &ui_window; -} - -static void ui_window_qt_destroy(void *data) -{ -#if 0 - ui_window_qt_t *window = (ui_window_qt_t*)data; - - delete window->qtWindow; -#endif -} - -static void ui_window_qt_set_focused(void *data) -{ -#if 0 - ui_window_qt_t *window = (ui_window_qt_t*)data; - - window->qtWindow->raise(); - window->qtWindow->activateWindow(); -#endif -} - -static void ui_window_qt_set_visible(void *data, - bool set_visible) -{ - (void)data; - (void)set_visible; - /* TODO/FIXME */ -} - -static void ui_window_qt_set_title(void *data, char *buf) -{ -#if 0 - ui_window_qt_t *window = (ui_window_qt_t*)data; - - window->qtWindow->setWindowTitle(QString::fromUtf8(buf)); -#endif -} - -static void ui_window_qt_set_droppable(void *data, bool droppable) -{ -#if 0 - ui_window_qt_t *window = (ui_window_qt_t*)data; - - window->qtWindow->setAcceptDrops(droppable); -#endif -} - -static bool ui_window_qt_focused(void *data) -{ -#if 0 - ui_window_qt_t *window = (ui_window_qt_t*)data; - return window->qtWindow->isActiveWindow() && !window->qtWindow->isMinimized(); -#else - return true; -#endif -} - -ui_window_t ui_window_qt = { - ui_window_qt_init, - ui_window_qt_destroy, - ui_window_qt_set_focused, - ui_window_qt_set_visible, - ui_window_qt_set_title, - ui_window_qt_set_droppable, - ui_window_qt_focused, - "qt" -}; diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index aa475840e6..fe700ef81f 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -19,30 +19,86 @@ extern "C" { #endif #include +#include +#include #include +#ifdef Q_OS_UNIX +#include +#endif + +#ifdef HAVE_OPENSSL +#include +#include +#endif + #ifdef HAVE_CONFIG_H #include "../../config.h" #endif +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + #include "../ui_companion_driver.h" #include "../../configuration.h" +#include "../../frontend/frontend.h" +#include "../../paths.h" +#include "../../retroarch.h" #include "../../verbosity.h" +#include "../../version.h" #include "../../msg_hash.h" +#include "../../tasks/task_content.h" #include "../../tasks/tasks_internal.h" +#include "../../AUTHORS.h" +#ifdef HAVE_GIT_VERSION +#include "../../version_git.h" +#endif #ifndef CXX_BUILD } #endif #include "ui_qt.h" +#include "qt/invader_png.h" +#include "qt/gridview.h" +#include "qt/ui_qt_themes.h" +#include "qt/ui_qt_load_core_window.h" +#include "qt/coreinfodialog.h" +#include "qt/playlistentrydialog.h" +#include "qt/shaderparamsdialog.h" +#include "qt/coreoptionsdialog.h" #include "qt/viewoptionsdialog.h" #include +#include #include +#include +#include +#include #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define INITIAL_WIDTH 1280 #define INITIAL_HEIGHT 720 @@ -53,6 +109,3393 @@ extern "C" { #define GROUPED_DRAGGING static_cast(0) #endif +#define TIMER_MSEC 1000 /* periodic timer for gathering statistics */ +#define STATUS_MSG_THROTTLE_MSEC 250 + +#define GENERIC_FOLDER_ICON "/xmb/dot-art/png/folder.png" +#define HIRAGANA_START 0x3041U +#define HIRAGANA_END 0x3096U +#define KATAKANA_START 0x30A1U +#define KATAKANA_END 0x30F6U +#define HIRA_KATA_OFFSET (KATAKANA_START - HIRAGANA_START) +#define DOCS_URL "http://docs.libretro.com/" + +enum core_selection +{ + CORE_SELECTION_CURRENT = 0, + CORE_SELECTION_PLAYLIST_SAVED, + CORE_SELECTION_PLAYLIST_DEFAULT, + CORE_SELECTION_ASK, + CORE_SELECTION_LOAD_CORE +}; + +static AppHandler *app_handler; +static ui_application_qt_t ui_application; + +/* ARGB 16x16 */ +static const unsigned retroarch_qt_icon_data[] = { +0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xfff2f2f2,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000, +0x00000000,0x00000000,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0xff333333,0x00000000,0x00000000,0x00000000, +0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000 +}; + +static ui_window_qt_t ui_window = {0}; + +static const QPixmap getInvader(void) +{ + QPixmap pix; + pix.loadFromData(invader_png, invader_png_len, "PNG"); + + return pix; +} + +#ifdef HAVE_LIBRETRODB +static void scan_finished_handler(retro_task_t *task, + void *task_data, void *user_data, const char *err) +{ + bool dontAsk = false; + bool answer = false; +#ifdef HAVE_MENU + menu_ctx_environment_t menu_environ; + menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; + menu_environ.data = NULL; + + menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); +#endif + if (!ui_window.qtWindow->settings()->value( + "scan_finish_confirm", true).toBool()) + return; + + 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); + + if (answer && dontAsk) + ui_window.qtWindow->settings()->setValue("scan_finish_confirm", false); +} +#endif + +/* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */ +static double expScale(double inputValue, double midValue, double maxValue) +{ + double M = maxValue / midValue; + double C = log(pow(M - 1, 2)); + double B = maxValue / (exp(C) - 1); + double A = -1 * B; + double returnValue = A + B * exp(C * inputValue); + return returnValue; +} + +TreeView::TreeView(QWidget *parent) : + QTreeView(parent) +{ +} + +void TreeView::columnCountChanged(int oldCount, int newCount) +{ + QTreeView::columnCountChanged(oldCount, newCount); +} + +void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QModelIndexList list = selected.indexes(); + + QTreeView::selectionChanged(selected, deselected); + + emit itemsSelected(list); +} + +TableView::TableView(QWidget *parent) : + QTableView(parent) +{ +} + +bool TableView::isEditorOpen() +{ + return (state() == QAbstractItemView::EditingState); +} + +ListWidget::ListWidget(QWidget *parent) : + QListWidget(parent) +{ +} + +bool ListWidget::isEditorOpen() +{ + return (state() == QAbstractItemView::EditingState); +} + +void ListWidget::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + emit enterPressed(); + else if (event->key() == Qt::Key_Delete) + emit deletePressed(); + + QListWidget::keyPressEvent(event); +} + +CoreInfoLabel::CoreInfoLabel(QString text, QWidget *parent) : + QLabel(text, parent) +{ + setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +} + +CoreInfoWidget::CoreInfoWidget(CoreInfoLabel *label, QWidget *parent) : + QWidget(parent) + ,m_label(label) + ,m_scrollArea(new QScrollArea(this)) +{ + m_scrollArea->setWidgetResizable(true); + m_scrollArea->setWidget(m_label); +} + +QSize CoreInfoWidget::sizeHint() const +{ + return QSize(256, 256); +} + +void CoreInfoWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + m_scrollArea->resize(event->size()); +} + +LogTextEdit::LogTextEdit(QWidget *parent) : + QPlainTextEdit(parent) +{ + +} + +void LogTextEdit::appendMessage(const QString& text) +{ + if (text.isEmpty()) + return; + + appendPlainText(text); + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +/* only accept indexes from current path. https://www.qtcentre.org/threads/50700-QFileSystemModel-and-QSortFilterProxyModel-don-t-work-well-together */ +bool FileSystemProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QFileSystemModel *sm = qobject_cast(sourceModel()); + QModelIndex rootIndex = sm->index(sm->rootPath()); + + if (sourceParent == rootIndex) + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + return true; +} + +void FileSystemProxyModel::sort(int column, Qt::SortOrder order) +{ + /* sort the source (QFileSystemModel to keep directories before files) */ + sourceModel()->sort(column, order); +} + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent) + ,m_loadCoreWindow(new LoadCoreWindow(this)) + ,m_timer(new QTimer(this)) + ,m_currentCore() + ,m_currentCoreVersion() + ,m_statusLabel(new QLabel(this)) + ,m_dirTree(new TreeView(this)) + ,m_dirModel(new QFileSystemModel(m_dirTree)) + ,m_fileModel(new QFileSystemModel(this)) + ,m_listWidget(new ListWidget(this)) + ,m_centralWidget(new QStackedWidget(this)) + ,m_tableView(new TableView(this)) + ,m_fileTableView(new QTableView(this)) + ,m_playlistViews(new FileDropWidget(this)) + ,m_searchWidget(new QWidget(this)) + ,m_searchLineEdit(new QLineEdit(this)) + ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), this)) + ,m_playlistFiles() + ,m_launchWithComboBox(new QComboBox(this)) + ,m_startCorePushButton(new QToolButton(this)) + ,m_coreInfoPushButton(new QToolButton(this)) + ,m_runPushButton(new QToolButton(this)) + ,m_stopPushButton(new QToolButton(this)) + ,m_browserAndPlaylistTabWidget(new QTabWidget(this)) + ,m_pendingRun(false) + ,m_thumbnailPixmap(NULL) + ,m_thumbnailPixmap2(NULL) + ,m_thumbnailPixmap3(NULL) + ,m_settings(NULL) + ,m_viewOptionsDialog(NULL) + ,m_coreInfoDialog(new CoreInfoDialog(this, NULL)) + ,m_defaultStyle(NULL) + ,m_defaultPalette() + ,m_currentTheme(THEME_SYSTEM_DEFAULT) + ,m_coreInfoDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO), this)) + ,m_coreInfoLabel(new CoreInfoLabel(QString(), this)) + ,m_coreInfoWidget(new CoreInfoWidget(m_coreInfoLabel, this)) + ,m_logDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG), this)) + ,m_logWidget(new QFrame(this)) + ,m_logTextEdit(new LogTextEdit(m_logWidget)) + ,m_historyPlaylistsItem(NULL) + ,m_folderIcon() + ,m_customThemeString() + ,m_gridView(new GridView(this)) + ,m_playlistViewsAndFooter(new QWidget(this)) + ,m_zoomSlider(NULL) + ,m_lastZoomSliderValue(0) + ,m_viewType(VIEW_TYPE_LIST) + ,m_thumbnailType(THUMBNAIL_TYPE_BOXART) + ,m_gridProgressBar(NULL) + ,m_gridProgressWidget(NULL) + ,m_currentGridHash() + ,m_currentGridWidget(NULL) + ,m_allPlaylistsListMaxCount(0) + ,m_allPlaylistsGridMaxCount(0) + ,m_playlistEntryDialog(NULL) + ,m_statusMessageElapsedTimer() +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + ,m_shaderParamsDialog(new ShaderParamsDialog()) +#endif + ,m_coreOptionsDialog(new CoreOptionsDialog()) + ,m_networkManager(new QNetworkAccessManager(this)) + ,m_updateProgressDialog(new QProgressDialog()) + ,m_updateFile() + ,m_updateReply() + ,m_thumbnailDownloadProgressDialog(new QProgressDialog()) + ,m_thumbnailDownloadFile() + ,m_thumbnailDownloadReply() + ,m_pendingThumbnailDownloadTypes() + ,m_thumbnailPackDownloadProgressDialog(new QProgressDialog()) + ,m_thumbnailPackDownloadFile() + ,m_thumbnailPackDownloadReply() + ,m_playlistThumbnailDownloadProgressDialog(new QProgressDialog()) + ,m_playlistThumbnailDownloadFile() + ,m_playlistThumbnailDownloadReply() + ,m_pendingPlaylistThumbnails() + ,m_downloadedThumbnails(0) + ,m_failedThumbnails(0) + ,m_playlistThumbnailDownloadWasCanceled(false) + ,m_pendingDirScrollPath() + ,m_thumbnailTimer(new QTimer(this)) + ,m_gridItem(this) + ,m_currentBrowser(BROWSER_TYPE_PLAYLISTS) + ,m_searchRegExp() + ,m_zoomWidget(new QWidget(this)) + ,m_itemsCountLiteral(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ITEMS_COUNT)) + ,m_itemsCountLabel(new QLabel(this)) +{ + settings_t *settings = config_get_ptr(); + const char *path_dir_playlist = settings->paths.directory_playlist; + const char *path_dir_assets = settings->paths.directory_assets; + const char *path_dir_menu_content = settings->paths.directory_menu_content; + QDir playlistDir(path_dir_playlist); + QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); + QToolButton *searchResetButton = NULL; + QHBoxLayout *zoomLayout = new QHBoxLayout(); + QLabel *zoomLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ZOOM), m_zoomWidget); + QPushButton *thumbnailTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE), m_zoomWidget); + QMenu *thumbnailTypeMenu = new QMenu(thumbnailTypePushButton); + QAction *thumbnailTypeBoxartAction = NULL; + QAction *thumbnailTypeScreenshotAction = NULL; + QAction *thumbnailTypeTitleAction = NULL; + QPushButton *viewTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW), m_zoomWidget); + QMenu *viewTypeMenu = new QMenu(viewTypePushButton); + QAction *viewTypeIconsAction = NULL; + QAction *viewTypeListAction = NULL; + QHBoxLayout *gridProgressLayout = new QHBoxLayout(); + QLabel *gridProgressLabel = NULL; + QHBoxLayout *gridFooterLayout = NULL; + + qRegisterMetaType >("ThumbnailWidget"); + qRegisterMetaType("retro_task_callback_t"); + + /* Cancel all progress dialogs immediately since + * they show as soon as they're constructed. */ + m_updateProgressDialog->cancel(); + m_thumbnailDownloadProgressDialog->cancel(); + m_thumbnailPackDownloadProgressDialog->cancel(); + m_playlistThumbnailDownloadProgressDialog->cancel(); + + m_gridProgressWidget = new QWidget(); + gridProgressLabel = new QLabel( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PROGRESS), + m_gridProgressWidget); + + thumbnailTypePushButton->setObjectName("thumbnailTypePushButton"); + thumbnailTypePushButton->setFlat(true); + + thumbnailTypeBoxartAction = thumbnailTypeMenu->addAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART)); + thumbnailTypeScreenshotAction = thumbnailTypeMenu->addAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT)); + thumbnailTypeTitleAction = thumbnailTypeMenu->addAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN)); + + thumbnailTypePushButton->setMenu(thumbnailTypeMenu); + + viewTypePushButton->setObjectName("viewTypePushButton"); + viewTypePushButton->setFlat(true); + + viewTypeIconsAction = viewTypeMenu->addAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS)); + viewTypeListAction = viewTypeMenu->addAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST)); + + viewTypePushButton->setMenu(viewTypeMenu); + + gridProgressLabel->setObjectName("gridProgressLabel"); + + m_gridProgressBar = new QProgressBar( + m_gridProgressWidget); + + m_gridProgressBar->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); + + zoomLabel->setObjectName("zoomLabel"); + + m_zoomSlider = new QSlider( + Qt::Horizontal, m_zoomWidget); + + m_zoomSlider->setMinimum(0); + m_zoomSlider->setMaximum(100); + m_zoomSlider->setValue(50); + m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); + + m_lastZoomSliderValue = m_zoomSlider->value(); + + m_playlistViewsAndFooter->setLayout(new QVBoxLayout()); + + m_gridView->setSelectionMode(QAbstractItemView::SingleSelection); + m_gridView->setEditTriggers(QAbstractItemView::NoEditTriggers); + + m_playlistViews->addWidget(m_gridView); + m_playlistViews->addWidget(m_tableView); + m_centralWidget->setObjectName("centralWidget"); + + m_playlistViewsAndFooter->layout()->addWidget(m_playlistViews); + m_playlistViewsAndFooter->layout()->setAlignment(Qt::AlignCenter); + m_playlistViewsAndFooter->layout()->setContentsMargins(0, 0, 0, 0); + + m_gridProgressWidget->setLayout(gridProgressLayout); + gridProgressLayout->setContentsMargins(0, 0, 0, 0); + gridProgressLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); + gridProgressLayout->addWidget(gridProgressLabel); + gridProgressLayout->addWidget(m_gridProgressBar); + + m_playlistViewsAndFooter->layout()->addWidget(m_gridProgressWidget); + + m_zoomWidget->setLayout(zoomLayout); + zoomLayout->setContentsMargins(0, 0, 0, 0); + zoomLayout->addWidget(zoomLabel); + zoomLayout->addWidget(m_zoomSlider); + + m_itemsCountLabel->setObjectName("itemsCountLabel"); + + gridFooterLayout = new QHBoxLayout(); + gridFooterLayout->addWidget(m_itemsCountLabel); + gridFooterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); + gridFooterLayout->addWidget(m_gridProgressWidget); + gridFooterLayout->addWidget(m_zoomWidget); + gridFooterLayout->addWidget(thumbnailTypePushButton); + gridFooterLayout->addWidget(viewTypePushButton); + + static_cast(m_playlistViewsAndFooter->layout())->addLayout(gridFooterLayout); + + m_gridProgressWidget->hide(); + + m_playlistModel = new PlaylistModel(this); + m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel->setSourceModel(m_playlistModel); + m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + m_proxyFileModel = new FileSystemProxyModel(); + m_proxyFileModel->setSourceModel(m_fileModel); + m_proxyFileModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + m_tableView->setAlternatingRowColors(true); + m_tableView->setModel(m_proxyModel); + m_tableView->setSortingEnabled(true); + m_tableView->verticalHeader()->setVisible(false); + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); + m_tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_tableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_tableView->horizontalHeader()->setStretchLastSection(true); + m_tableView->setWordWrap(false); + + m_fileTableView->setModel(m_fileModel); + m_fileTableView->sortByColumn(0, Qt::AscendingOrder); + m_fileTableView->setSortingEnabled(true); + m_fileTableView->setAlternatingRowColors(true); + m_fileTableView->verticalHeader()->setVisible(false); + m_fileTableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_fileTableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_fileTableView->horizontalHeader()->setStretchLastSection(true); + m_fileTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_fileTableView->setWordWrap(false); + + m_gridView->setItemDelegate(new ThumbnailDelegate(m_gridItem, this)); + m_gridView->setModel(m_proxyModel); + + m_gridView->setSelectionModel(m_tableView->selectionModel()); + + m_logWidget->setObjectName("logWidget"); + + m_folderIcon = QIcon(QString(path_dir_assets) + GENERIC_FOLDER_ICON); + m_imageFormats = QVector::fromList(QImageReader::supportedImageFormats()); + m_defaultStyle = QApplication::style(); + m_defaultPalette = QApplication::palette(); + + /* ViewOptionsDialog needs m_settings set before it's constructed */ + m_settings = new QSettings(configDir + "/retroarch_qt.cfg", QSettings::IniFormat, this); + m_viewOptionsDialog = new ViewOptionsDialog(this, 0); + m_playlistEntryDialog = new PlaylistEntryDialog(this, 0); + + /* default NULL parameter for parent wasn't added until 5.7 */ + m_startCorePushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_START_CORE), m_startCorePushButton)); + m_startCorePushButton->setFixedSize(m_startCorePushButton->sizeHint()); + + m_runPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RUN), m_runPushButton)); + m_runPushButton->setFixedSize(m_runPushButton->sizeHint()); + + m_stopPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_STOP), m_stopPushButton)); + m_stopPushButton->setFixedSize(m_stopPushButton->sizeHint()); + + m_coreInfoPushButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFO), m_coreInfoPushButton)); + m_coreInfoPushButton->setFixedSize(m_coreInfoPushButton->sizeHint()); + + searchResetButton = new QToolButton(m_searchWidget); + searchResetButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_SEARCH_CLEAR), searchResetButton)); + searchResetButton->setFixedSize(searchResetButton->sizeHint()); + + connect(searchResetButton, SIGNAL(clicked()), this, SLOT(onSearchResetClicked())); + + m_dirModel->setFilter(QDir::NoDotAndDotDot | + QDir::AllDirs | + QDir::Drives | + (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0))); + + m_fileModel->setFilter(QDir::NoDot | + QDir::AllEntries | + (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0))); + +#if defined(Q_OS_WIN) + m_dirModel->setRootPath(""); + m_fileModel->setRootPath(""); +#else + m_dirModel->setRootPath("/"); + m_fileModel->setRootPath("/"); +#endif + + m_dirTree->setModel(m_dirModel); + m_dirTree->setSelectionMode(QAbstractItemView::SingleSelection); + m_dirTree->header()->setVisible(false); + + m_fileTableView->setModel(m_proxyFileModel); + + if (m_dirModel->columnCount() > 3) + { + /* size */ + m_dirTree->hideColumn(1); + /* type */ + m_dirTree->hideColumn(2); + /* date modified */ + m_dirTree->hideColumn(3); + } + + reloadPlaylists(); + + m_searchWidget->setLayout(new QHBoxLayout()); + m_searchWidget->layout()->addWidget(m_searchLineEdit); + m_searchWidget->layout()->addWidget(searchResetButton); + + m_searchDock->setObjectName("searchDock"); + m_searchDock->setProperty("default_area", Qt::LeftDockWidgetArea); + m_searchDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH)); + m_searchDock->setWidget(m_searchWidget); + m_searchDock->setFixedHeight(m_searchDock->minimumSizeHint().height()); + + addDockWidget(static_cast(m_searchDock->property("default_area").toInt()), m_searchDock); + + m_coreInfoLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); + m_coreInfoLabel->setTextFormat(Qt::RichText); + m_coreInfoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_coreInfoLabel->setOpenExternalLinks(true); + + m_coreInfoDock->setObjectName("coreInfoDock"); + m_coreInfoDock->setProperty("default_area", Qt::RightDockWidgetArea); + m_coreInfoDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_INFO)); + m_coreInfoDock->setWidget(m_coreInfoWidget); + + addDockWidget(static_cast(m_coreInfoDock->property("default_area").toInt()), m_coreInfoDock); + + m_logWidget->setLayout(new QVBoxLayout()); + m_logWidget->layout()->addWidget(m_logTextEdit); + m_logWidget->layout()->setContentsMargins(0, 0, 0, 0); + + m_logDock->setObjectName("logDock"); + m_logDock->setProperty("default_area", Qt::BottomDockWidgetArea); + m_logDock->setProperty("menu_text", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG)); + m_logDock->setWidget(m_logWidget); + + addDockWidget(static_cast(m_logDock->property("default_area").toInt()), m_logDock); + + /* Hide the log by default. If user has saved their dock positions with the log visible, + * then this hide() call will be reversed later by restoreState(). + * FIXME: If user unchecks "save dock positions", the log will not be unhidden even if + * it was previously saved in the config. + */ + m_logDock->hide(); + + m_dirTree->setContextMenuPolicy(Qt::CustomContextMenu); + m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(onSearchEnterPressed())); + connect(m_searchLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(onSearchLineEditEdited(const QString&))); + connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); + connect(m_loadCoreWindow, SIGNAL(coreLoaded()), this, SLOT(onCoreLoaded())); + connect(m_loadCoreWindow, SIGNAL(windowClosed()), this, SLOT(onCoreLoadWindowClosed())); + connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(onCurrentListItemChanged(QListWidgetItem*, QListWidgetItem*))); + connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked())); + connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); + connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); + connect(m_stopPushButton, SIGNAL(clicked()), this, SLOT(onStopClicked())); + connect(m_dirTree, SIGNAL(itemsSelected(QModelIndexList)), this, SLOT(onTreeViewItemsSelected(QModelIndexList))); + connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&))); + connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&))); + connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int))); + connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int))); + connect(thumbnailTypeBoxartAction, SIGNAL(triggered()), this, SLOT(onBoxartThumbnailClicked())); + connect(thumbnailTypeScreenshotAction, SIGNAL(triggered()), this, SLOT(onScreenshotThumbnailClicked())); + connect(thumbnailTypeTitleAction, SIGNAL(triggered()), this, SLOT(onTitleThumbnailClicked())); + connect(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked())); + connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked())); + connect(m_dirModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileSystemDirLoaded(const QString&))); + connect(m_fileModel, SIGNAL(directoryLoaded(const QString&)), this, SLOT(onFileBrowserTableDirLoaded(const QString&))); + + m_dirTree->setCurrentIndex(m_dirModel->index(path_dir_menu_content)); + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); + m_dirTree->expand(m_dirTree->currentIndex()); + + /* must use queued connection */ + connect(this, SIGNAL(scrollToDownloads(QString)), this, SLOT(onDownloadScroll(QString)), Qt::QueuedConnection); + connect(this, SIGNAL(scrollToDownloadsAgain(QString)), this, SLOT(onDownloadScrollAgain(QString)), Qt::QueuedConnection); + + connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), m_playlistThumbnailDownloadProgressDialog, SLOT(cancel())); + connect(m_playlistThumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onPlaylistThumbnailDownloadCanceled())); + + connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailDownloadProgressDialog, SLOT(cancel())); + connect(m_thumbnailDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailDownloadCanceled())); + + connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), m_thumbnailPackDownloadProgressDialog, SLOT(cancel())); + connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailPackDownloadCanceled())); + + connect(this, SIGNAL(itemChanged()), this, SLOT(onItemChanged())); + connect(this, SIGNAL(gotThumbnailDownload(QString,QString)), this, SLOT(onDownloadThumbnail(QString,QString))); + + m_thumbnailTimer->setSingleShot(true); + connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems())); + connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems())); + + /* TODO: Handle scroll and resize differently. */ + connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer())); + + connect(m_tableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentItemChanged(const QModelIndex&))); + connect(m_fileTableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(onCurrentFileChanged(const QModelIndex&))); + + connect(m_gridView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); + connect(m_tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); + connect(m_fileTableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onFileDoubleClicked(const QModelIndex&))); + + connect(m_playlistModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), this, SLOT(onCurrentTableItemDataChanged(const QModelIndex&, const QModelIndex&, const QVector&))); + + /* 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) */ + connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); + connect(this, SIGNAL(gotStatusMessage(QString,unsigned,unsigned,bool)), this, SLOT(onGotStatusMessage(QString,unsigned,unsigned,bool)), Qt::AutoConnection); + connect(this, SIGNAL(gotReloadPlaylists()), this, SLOT(onGotReloadPlaylists()), Qt::AutoConnection); +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + connect(this, SIGNAL(gotReloadShaderParams()), this, SLOT(onGotReloadShaderParams()), Qt::AutoConnection); +#endif + connect(this, SIGNAL(gotReloadCoreOptions()), this, SLOT(onGotReloadCoreOptions()), Qt::AutoConnection); + + /* these are always queued */ + connect(this, SIGNAL(showErrorMessageDeferred(QString)), this, SLOT(onShowErrorMessage(QString)), Qt::QueuedConnection); + connect(this, SIGNAL(showInfoMessageDeferred(QString)), this, SLOT(onShowInfoMessage(QString)), Qt::QueuedConnection); + connect(this, SIGNAL(extractArchiveDeferred(QString,QString,QString,retro_task_callback_t)), this, SLOT(onExtractArchive(QString,QString,QString,retro_task_callback_t)), Qt::QueuedConnection); + + m_timer->start(TIMER_MSEC); + + statusBar()->addPermanentWidget(m_statusLabel); + + setCurrentCoreLabel(); + setCoreActions(); + + /* both of these are necessary to get the folder to scroll to the top of the view */ + qApp->processEvents(); + QTimer::singleShot(0, this, SLOT(onBrowserStartClicked())); + + m_searchLineEdit->setFocus(); + m_loadCoreWindow->setWindowModality(Qt::ApplicationModal); + + m_statusMessageElapsedTimer.start(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + resizeDocks(QList() << m_searchDock, QList() << 1, Qt::Vertical); +#endif + +#ifdef HAVE_OPENSSL + { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const SSL_METHOD *method = TLS_method(); + SSL_CTX *ctx = SSL_CTX_new(method); + + if (ctx) + SSL_CTX_free(ctx); +#else + const SSL_METHOD *method = TLSv1_method(); + RARCH_LOG("[Qt]: TLS supports %d ciphers.\n", method->num_ciphers()); +#endif + RARCH_LOG("[Qt]: Using %s\n", OPENSSL_VERSION_TEXT); + } +#endif +} + +MainWindow::~MainWindow() +{ + if (m_thumbnailPixmap) + delete m_thumbnailPixmap; + if (m_thumbnailPixmap2) + delete m_thumbnailPixmap2; + if (m_thumbnailPixmap3) + delete m_thumbnailPixmap3; + if (m_proxyFileModel) + delete m_proxyFileModel; +} + +void MainWindow::startTimer() +{ + if (m_thumbnailTimer->isActive()) + { + m_thumbnailTimer->stop(); + m_thumbnailTimer->start(50); + } + else + m_thumbnailTimer->start(50); +} + +void MainWindow::updateVisibleItems() +{ + if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS && m_viewType == VIEW_TYPE_ICONS) + { + unsigned i; + QVector indexes = m_gridView->visibleIndexes(); + size_t size = indexes.size(); + + for (i = 0; i < size; i++) + m_playlistModel->loadThumbnail(m_proxyModel->mapToSource(indexes.at(i))); + } +} + +void MainWindow::setThumbnailCacheLimit(int count) +{ + if (count < 1) + count = 0; + + m_playlistModel->setThumbnailCacheLimit(count); +} + +void MainWindow::onFileSystemDirLoaded(const QString &path) +{ + if (path.isEmpty() || m_pendingDirScrollPath.isEmpty()) + return; + + if (QDir(path) == QDir(m_pendingDirScrollPath)) + { + m_pendingDirScrollPath = QString(); + + emit scrollToDownloads(path); + } +} + +/* workaround for columns being resized */ +void MainWindow::onFileBrowserTableDirLoaded(const QString &path) +{ + if (path.isEmpty()) + return; + + m_fileTableView->horizontalHeader()->restoreState(m_fileTableHeaderState); +} + +QVector > MainWindow::getPlaylists() +{ + unsigned i; + QVector > playlists; + size_t size = m_listWidget->count(); + + for (i = 0; i < size; i++) + { + QString label, path; + QPair pair; + QListWidgetItem *item = m_listWidget->item(i); + + if (!item) + continue; + + label = item->text(); + path = item->data(Qt::UserRole).toString(); + + pair.first = label; + pair.second = path; + + playlists.append(pair); + } + + return playlists; +} + +void MainWindow::onItemChanged() +{ + QModelIndex index = getCurrentContentIndex(); + m_playlistModel->reloadThumbnail(index); + onCurrentItemChanged(index); +} + +QString MainWindow::getSpecialPlaylistPath(SpecialPlaylist playlist) +{ + switch (playlist) + { + case SPECIAL_PLAYLIST_HISTORY: + if (m_historyPlaylistsItem) + return m_historyPlaylistsItem->data(Qt::UserRole).toString(); + break; + default: + break; + } + + return QString(); +} + +double MainWindow::lerp(double x, double y, double a, double b, double d) +{ + return a + (b - a) * ((double)(d - x) / (double)(y - x)); +} + +void MainWindow::onIconViewClicked() +{ + setCurrentViewType(VIEW_TYPE_ICONS); +} + +void MainWindow::onListViewClicked() +{ + setCurrentViewType(VIEW_TYPE_LIST); +} + +void MainWindow::onBoxartThumbnailClicked() +{ + setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART); +} + +void MainWindow::onScreenshotThumbnailClicked() +{ + setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT); +} + +void MainWindow::onTitleThumbnailClicked() +{ + setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN); +} + +void MainWindow::setIconViewZoom(int zoomValue) +{ + m_zoomSlider->setValue(zoomValue); +} + +void MainWindow::onZoomValueChanged(int zoomValue) +{ + int newSize = 0; + + if (zoomValue < 50) + newSize = expScale( + lerp(0, 49, 25, 49, zoomValue) / 50.0, 102, 256); + else + newSize = expScale(zoomValue / 100.0, 256, 1024); + + m_gridView->setGridSize(newSize); + + m_lastZoomSliderValue = zoomValue; +} + +void MainWindow::showWelcomeScreen() +{ + bool dontAsk = false; + bool answer = false; + const QString welcomeText = QStringLiteral("" + "Welcome to the RetroArch Desktop Menu!
\n" + "
\n" + "Many settings and actions are currently only available in the familiar Big Picture menu, " + "but this Desktop Menu should be functional for launching content and managing playlists.
\n" + "
\n" + "Some useful hotkeys for interacting with the Big Picture menu include:\n" + "
  • F1 - Bring up the Big Picture menu
  • \n" + "
  • F - Switch between fullscreen and windowed modes
  • \n" + "
  • F5 - Bring the Desktop Menu back if closed
  • \n" + "
  • Esc - Exit RetroArch
\n" + "\n" + "For more hotkeys and their assignments, see:
\n" + "Settings -> Input -> Input Hotkey Binds
\n" + "
\n" + "Documentation for RetroArch, libretro and cores:
\n" + "https://docs.libretro.com/"); + + if (!m_settings->value("show_welcome_screen", true).toBool()) + return; + + answer = showMessageBox(welcomeText, MainWindow::MSGBOX_TYPE_QUESTION_OKCANCEL, Qt::ApplicationModal, true, &dontAsk); + + if (answer && dontAsk) + m_settings->setValue("show_welcome_screen", false); +} + +const QString& MainWindow::customThemeString() const +{ + return m_customThemeString; +} + +bool MainWindow::setCustomThemeFile(QString filePath) +{ + if (filePath.isEmpty()) + { + 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)); + return false; + } + + QFile file(filePath); + + if (file.exists()) + { + bool opened = file.open(QIODevice::ReadOnly); + + if (!opened) + { + 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)); + return false; + } + + { + QByteArray fileArray = file.readAll(); + QString fileStr = QString::fromUtf8(fileArray); + + file.close(); + + if (fileStr.isEmpty()) + { + 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)); + return false; + } + + setCustomThemeString(fileStr); + } + } + else + { + 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)); + return false; + } + + return true; +} + +void MainWindow::setCustomThemeString(QString qss) +{ + m_customThemeString = qss; +} + +bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality, bool showDontAsk, bool *dontAsk) +{ + QCheckBox *checkBox = NULL; + + QPointer msgBoxPtr = new QMessageBox(this); + QMessageBox *msgBox = msgBoxPtr.data(); + + msgBox->setWindowModality(modality); + msgBox->setTextFormat(Qt::RichText); + msgBox->setTextInteractionFlags(Qt::TextBrowserInteraction); + + if (showDontAsk) + { + checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); + /* QMessageBox::setCheckBox() is available since 5.2 */ + msgBox->setCheckBox(checkBox); + } + + switch (msgType) + { + case MSGBOX_TYPE_INFO: + msgBox->setIcon(QMessageBox::Information); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_INFORMATION)); + break; + case MSGBOX_TYPE_WARNING: + msgBox->setIcon(QMessageBox::Warning); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_WARNING)); + break; + case MSGBOX_TYPE_ERROR: + msgBox->setIcon(QMessageBox::Critical); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ERROR)); + break; + case MSGBOX_TYPE_QUESTION_YESNO: + msgBox->setIcon(QMessageBox::Question); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION)); + msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + break; + case MSGBOX_TYPE_QUESTION_OKCANCEL: + msgBox->setIcon(QMessageBox::Question); + msgBox->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_QUESTION)); + msgBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + break; + default: + break; + } + + msgBox->setText(msg); + msgBox->exec(); + + if (!msgBoxPtr) + return true; + + if ( + msgBox->result() != QMessageBox::Ok + && msgBox->result() != QMessageBox::Yes) + return false; + + if (checkBox) + if (dontAsk) + *dontAsk = checkBox->isChecked(); + + return true; +} + +void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&) +{ +#ifdef HAVE_LIBRETRODB + QDir dir; + QByteArray dirArray; + QPointer action; + QList actions; + QScopedPointer scanAction; + QString currentDirString = QDir::toNativeSeparators( + m_dirModel->filePath(m_dirTree->currentIndex())); + settings_t *settings = config_get_ptr(); + const char *fullpath = NULL; + const char *path_dir_playlist = settings->paths.directory_playlist; + const char *path_content_db = settings->paths.path_content_database; + + if (currentDirString.isEmpty()) + return; + + dir = currentDirString; + + if (!dir.exists()) + return; + + /* default NULL parameter for parent wasn't added until 5.7 */ + scanAction.reset(new QAction( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCAN_DIRECTORY), 0)); + + actions.append(scanAction.data()); + + action = QMenu::exec(actions, QCursor::pos(), NULL, m_dirTree); + + if (!action) + return; + + dirArray = currentDirString.toUtf8(); + fullpath = dirArray.constData(); + + task_push_dbscan( + path_dir_playlist, + path_content_db, + fullpath, true, + m_settings->value("show_hidden_files", true).toBool(), + scan_finished_handler); +#endif +} + +void MainWindow::showStatusMessage(QString msg, + unsigned priority, unsigned duration, bool flush) +{ + emit gotStatusMessage(msg, priority, duration, flush); +} + +void MainWindow::onGotStatusMessage( + QString msg, unsigned priority, unsigned duration, bool flush) +{ + int msecDuration = 0; + QScreen *screen = qApp->primaryScreen(); + QStatusBar *status = statusBar(); + + Q_UNUSED(priority) + + if (msg.isEmpty()) + return; + + if (!status) + return; + + if (screen) + msecDuration = (duration / screen->refreshRate()) * 1000; + + if (msecDuration <= 0) + msecDuration = 1000; + + if (status->currentMessage().isEmpty() || flush) + { + if (m_statusMessageElapsedTimer.elapsed() >= STATUS_MSG_THROTTLE_MSEC) + { + qint64 msgDuration = qMax(msecDuration, STATUS_MSG_THROTTLE_MSEC); + m_statusMessageElapsedTimer.restart(); + status->showMessage(msg, msgDuration); + } + } +} + +void MainWindow::deferReloadShaderParams() +{ +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + emit gotReloadShaderParams(); +#endif +} + +void MainWindow::onShaderParamsClicked() +{ +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + if (!m_shaderParamsDialog) + return; + + m_shaderParamsDialog->show(); + + onGotReloadShaderParams(); +#endif +} + +void MainWindow::onGotReloadShaderParams() +{ +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + if (m_shaderParamsDialog && m_shaderParamsDialog->isVisible()) + m_shaderParamsDialog->reload(); +#endif +} + +void MainWindow::onCoreOptionsClicked() +{ + if (!m_coreOptionsDialog) + return; + + m_coreOptionsDialog->show(); + + onGotReloadCoreOptions(); +} + +void MainWindow::onGotReloadCoreOptions() +{ + if (m_coreOptionsDialog && m_coreOptionsDialog->isVisible()) + m_coreOptionsDialog->reload(); +} + +void MainWindow::appendLogMessage(const QString &msg) +{ + emit gotLogMessage(msg); +} + +void MainWindow::onGotLogMessage(const QString &msg) +{ + QString newMsg = msg; + + if (newMsg.at(newMsg.size() - 1) == '\n') + newMsg.chop(1); + + m_logTextEdit->appendMessage(newMsg); +} + +void MainWindow::onLaunchWithComboBoxIndexChanged(int) +{ + QString coreInfoText; + QVector > + infoList = getCoreInfo(); + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + int i = 0; + + if (infoList.count() == 0) + return; + + for (i = 0; i < infoList.count(); i++) + { + const QHash &hash = infoList.at(i); + const QString &key = + hash.value("html_key", hash.value("key")); + const QString &value = + hash.value("html_value", hash.value("value")); + + if (!key.isEmpty()) + coreInfoText += key; + + if (!value.isEmpty()) + { + if (!key.isEmpty()) + coreInfoText += " "; + + coreInfoText += value; + } + + if (i < infoList.count() - 1) + coreInfoText += "
\n"; + } + + m_coreInfoLabel->setText(coreInfoText); + + if (coreSelection == CORE_SELECTION_LOAD_CORE) + onLoadCoreClicked(); + else + m_loadCoreWindow->setProperty("last_launch_with_index", + m_launchWithComboBox->currentIndex()); +} + +MainWindow::Theme MainWindow::getThemeFromString(QString themeString) +{ + if (themeString == "default") + return THEME_SYSTEM_DEFAULT; + else if (themeString == "dark") + return THEME_DARK; + else if (themeString == "custom") + return THEME_CUSTOM; + + return THEME_SYSTEM_DEFAULT; +} + +QString MainWindow::getThemeString(Theme theme) +{ + switch (theme) + { + case THEME_SYSTEM_DEFAULT: + return "default"; + case THEME_DARK: + return "dark"; + case THEME_CUSTOM: + return "custom"; + default: + break; + } + + return "default"; +} + +MainWindow::Theme MainWindow::theme() +{ + return m_currentTheme; +} + +void MainWindow::setTheme(Theme theme) +{ + m_currentTheme = theme; + + setDefaultCustomProperties(); + + switch(theme) + { + case THEME_SYSTEM_DEFAULT: + qApp->setStyleSheet(qt_theme_default_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString())); + break; + case THEME_DARK: + qApp->setStyleSheet(qt_theme_dark_stylesheet.arg(m_settings->value("highlight_color", "palette(highlight)").toString())); + break; + case THEME_CUSTOM: + qApp->setStyleSheet(m_customThemeString); + break; + default: + break; + } +#ifdef HAVE_MENU + m_viewOptionsDialog->repaintIcons(); +#endif +} + +void MainWindow::setDefaultCustomProperties() +{ + m_gridView->setLayout(QString(DEFAULT_GRID_LAYOUT)); + m_gridView->setSpacing(DEFAULT_GRID_SPACING); + m_gridItem.setThumbnailVerticalAlign(QString(DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT)); + m_gridItem.setPadding(DEFAULT_GRID_ITEM_MARGIN); +} + +void MainWindow::changeThumbnailType(ThumbnailType type) +{ + m_playlistModel->setThumbnailType(type); + updateVisibleItems(); + m_gridView->viewport()->update(); +} + +QString MainWindow::changeThumbnail(const QImage &image, QString type) +{ + QHash hash = getCurrentContentHash(); + QString dirString = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]) + "/" + type; + QString thumbPath = dirString + "/" + m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]); + QByteArray dirArray = QDir::toNativeSeparators(dirString).toUtf8(); + const char *dirData = dirArray.constData(); + QByteArray thumbArray = QDir::toNativeSeparators(thumbPath).toUtf8(); + const char *thumbData = thumbArray.constData(); + int quality = -1; + QDir dir(dirString); + QImage scaledImage(image); + + if (!dir.exists()) + { + if (dir.mkpath(".")) + RARCH_LOG("[Qt]: Created directory: %s\n", dirData); + else + { + RARCH_ERR("[Qt]: Could not create directory: %s\n", dirData); + return QString(); + } + } + + if (m_settings->contains("thumbnail_max_size")) + { + int size = m_settings->value("thumbnail_max_size", 0).toInt(); + + if (size != 0 && (image.height() > size || image.width() > size)) + scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + + if (m_settings->contains("thumbnail_quality")) + quality = m_settings->value("thumbnail_quality", -1).toInt(); + + if (scaledImage.save(thumbPath, "png", quality)) + { + RARCH_LOG("[Qt]: Saved image: %s\n", thumbData); + m_playlistModel->reloadThumbnailPath(thumbPath); + updateVisibleItems(); + + return thumbPath; + } + + RARCH_ERR("[Qt]: Could not save image: %s\n", thumbData); + return QString(); +} + +void MainWindow::onThumbnailDropped(const QImage &image, + ThumbnailType thumbnailType) +{ + switch (thumbnailType) + { + case THUMBNAIL_TYPE_BOXART: + { + QString path = changeThumbnail(image, THUMBNAIL_BOXART); + + if (path.isNull()) + return; + + if (m_thumbnailPixmap) + delete m_thumbnailPixmap; + + m_thumbnailPixmap = new QPixmap(path); + + onResizeThumbnailOne(*m_thumbnailPixmap, true); + break; + } + + case THUMBNAIL_TYPE_TITLE_SCREEN: + { + QString path = changeThumbnail(image, THUMBNAIL_TITLE); + + if (path.isNull()) + return; + + if (m_thumbnailPixmap2) + delete m_thumbnailPixmap2; + + m_thumbnailPixmap2 = new QPixmap(path); + + onResizeThumbnailTwo(*m_thumbnailPixmap2, true); + break; + } + + case THUMBNAIL_TYPE_SCREENSHOT: + { + QString path = changeThumbnail(image, THUMBNAIL_SCREENSHOT); + + if (path.isNull()) + return; + + if (m_thumbnailPixmap3) + delete m_thumbnailPixmap3; + + m_thumbnailPixmap3 = new QPixmap(path); + + onResizeThumbnailThree(*m_thumbnailPixmap3, true); + break; + } + } +} + +QVector > MainWindow::getCoreInfo() +{ + unsigned i; + QVector > infoList; + core_info_ctx_find_t core_info_finder; + QHash currentCore = getSelectedCore(); + const core_info_t *core_info = NULL; + QByteArray currentCorePathArray = currentCore["core_path"].toUtf8(); + const char *current_core_path_data = currentCorePathArray.constData(); + + /* Search for current core */ + core_info_finder.inf = NULL; + core_info_finder.path = current_core_path_data; + + if (core_info_find(&core_info_finder)) + core_info = core_info_finder.inf; + + if ( currentCore["core_path"].isEmpty() + || !core_info + || !core_info->config_data) + { + QHash hash; + + hash["key"] = msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_NO_CORE_INFORMATION_AVAILABLE); + hash["value"] = ""; + + infoList.append(hash); + + return infoList; + } + + if (core_info->core_name) + { + QHash hash; + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_NAME)) + ":"; + hash["value"] = core_info->core_name; + + infoList.append(hash); + } + + if (core_info->display_name) + { + QHash hash; + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CORE_LABEL)) + ":"; + hash["value"] = core_info->display_name; + + infoList.append(hash); + } + + if (core_info->systemname) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME)) + ":"; + hash["value"] = core_info->systemname; + + infoList.append(hash); + } + + if (core_info->system_manufacturer) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_MANUFACTURER)) + ":"; + hash["value"] = core_info->system_manufacturer; + + infoList.append(hash); + } + + if (core_info->categories_list) + { + QHash hash; + QString categories; + + for (i = 0; i < core_info->categories_list->size; i++) + { + categories += core_info->categories_list->elems[i].data; + + if (i < core_info->categories_list->size - 1) + categories += ", "; + } + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_CATEGORIES)) + ":"; + hash["value"] = categories; + + infoList.append(hash); + } + + if (core_info->authors_list) + { + QHash hash; + QString authors; + + for (i = 0; i < core_info->authors_list->size; i++) + { + authors += core_info->authors_list->elems[i].data; + + if (i < core_info->authors_list->size - 1) + authors += ", "; + } + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_AUTHORS)) + ":"; + hash["value"] = authors; + + infoList.append(hash); + } + + if (core_info->permissions_list) + { + QHash hash; + QString permissions; + + for (i = 0; i < core_info->permissions_list->size; i++) + { + permissions += core_info->permissions_list->elems[i].data; + + if (i < core_info->permissions_list->size - 1) + permissions += ", "; + } + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_PERMISSIONS)) + ":"; + hash["value"] = permissions; + + infoList.append(hash); + } + + if (core_info->licenses_list) + { + QHash hash; + QString licenses; + + for (i = 0; i < core_info->licenses_list->size; i++) + { + licenses += core_info->licenses_list->elems[i].data; + + if (i < core_info->licenses_list->size - 1) + licenses += ", "; + } + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES)) + ":"; + hash["value"] = licenses; + + infoList.append(hash); + } + + if (core_info->supported_extensions_list) + { + QHash hash; + QString supported_extensions; + + for (i = 0; i < core_info->supported_extensions_list->size; i++) + { + supported_extensions += core_info->supported_extensions_list->elems[i].data; + + if (i < core_info->supported_extensions_list->size - 1) + supported_extensions += ", "; + } + + hash["key"] = QString( + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_SUPPORTED_EXTENSIONS)) + ":"; + hash["value"] = supported_extensions; + + infoList.append(hash); + } + + if (core_info->firmware_count > 0) + { + core_info_ctx_firmware_t firmware_info; + bool update_missing_firmware = false; + bool set_missing_firmware = false; + settings_t *settings = config_get_ptr(); + + firmware_info.path = core_info->path; + firmware_info.directory.system = settings->paths.directory_system; + + rarch_ctl(RARCH_CTL_UNSET_MISSING_BIOS, NULL); + + update_missing_firmware = core_info_list_update_missing_firmware(&firmware_info, &set_missing_firmware); + + if (set_missing_firmware) + rarch_ctl(RARCH_CTL_SET_MISSING_BIOS, NULL); + + if (update_missing_firmware) + { + QHash hash; + + hash["key"] = QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_FIRMWARE)) + ":"; + hash["value"] = ""; + + infoList.append(hash); + + /* FIXME: This looks hacky and probably + * needs to be improved for good translation support. */ + + for (i = 0; i < core_info->firmware_count; i++) + { + if (core_info->firmware[i].desc) + { + QString valueText; + QHash hash; + QString labelText = "(!) "; + bool missing = false; + + if (core_info->firmware[i].missing) + { + missing = true; + labelText += msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_MISSING); + } + else + labelText += msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_PRESENT); + + labelText += ", "; + + if (core_info->firmware[i].optional) + labelText += msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_OPTIONAL); + else + labelText += msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_REQUIRED); + + labelText += ":"; + + if (core_info->firmware[i].desc) + valueText = core_info->firmware[i].desc; + else + valueText = msg_hash_to_str( + MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME); + + hash["key"] = labelText; + hash["value"] = valueText; + + if (missing) + { + QString style = "font-weight: bold; color: #ff0000"; + hash["label_style"] = style; + hash["value_style"] = style; + hash["html_key"] = "" + hash["key"] + ""; + hash["html_value"] = "" + hash["value"] + ""; + } + else + { + QString style = "font-weight: bold; color: rgb(0, 175, 0)"; + hash["label_style"] = style; + hash["value_style"] = style; + hash["html_key"] = "" + hash["key"] + ""; + hash["html_value"] = "" + hash["value"] + ""; + } + + infoList.append(hash); + } + } + } + } + + if (core_info->notes) + { + for (i = 0; i < core_info->note_list->size; i++) + { + QHash hash; + + hash["key"] = ""; + hash["value"] = core_info->note_list->elems[i].data; + + infoList.append(hash); + } + } + + return infoList; +} + +void MainWindow::onSearchResetClicked() +{ + m_searchLineEdit->clear(); + onSearchEnterPressed(); +} + +QToolButton* MainWindow::coreInfoPushButton() +{ + return m_coreInfoPushButton; +} + +void MainWindow::onTreeViewItemsSelected(QModelIndexList selectedIndexes) +{ + QString dir; + + if (selectedIndexes.isEmpty()) + return; + + dir = m_dirModel->filePath(selectedIndexes.first()); + + selectBrowserDir(dir); +} + +void MainWindow::onFileDoubleClicked(const QModelIndex &proxyIndex) +{ + const QModelIndex index = m_proxyFileModel->mapToSource(proxyIndex); + + if (m_fileModel->isDir(index)) + m_dirTree->setCurrentIndex(m_dirModel->index(m_fileModel->filePath(index))); + else + loadContent(getFileContentHash(index)); +} + +void MainWindow::selectBrowserDir(QString path) +{ + if (!path.isEmpty()) + { + QModelIndex sourceIndex = m_fileModel->setRootPath(path); + QModelIndex proxyIndex = m_proxyFileModel->mapFromSource(sourceIndex); + m_fileTableHeaderState = m_fileTableView->horizontalHeader()->saveState(); + + if (proxyIndex.isValid()) + m_fileTableView->setRootIndex(proxyIndex); + else + { + /* the directory is filtered out. Remove the filter for a moment. + * FIXME: Find a way to not have to do this + * (not filtering dirs is one). */ + m_proxyFileModel->setFilterRegExp(QRegExp()); + m_fileTableView->setRootIndex(m_proxyFileModel->mapFromSource(sourceIndex)); + m_proxyFileModel->setFilterRegExp(m_searchRegExp); + } + } + setCoreActions(); +} + +QTabWidget* MainWindow::browserAndPlaylistTabWidget() +{ + return m_browserAndPlaylistTabWidget; +} + +void MainWindow::onDropWidgetEnterPressed() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + /* entry is being renamed, ignore this enter press */ + if (m_tableView->isPersistentEditorOpen(m_tableView->currentIndex())) +#else + /* we can only check if any editor at all is open */ + if (m_tableView->isEditorOpen()) +#endif + return; + onRunClicked(); +} + +QModelIndex MainWindow::getCurrentContentIndex() +{ + if (m_viewType == VIEW_TYPE_LIST) + return m_tableView->currentIndex(); + else if (m_viewType == VIEW_TYPE_ICONS) + return m_gridView->currentIndex(); + return QModelIndex(); +} + +QHash MainWindow::getCurrentContentHash() +{ + return getCurrentContentIndex().data(PlaylistModel::HASH).value >(); +} + +QHash MainWindow::getFileContentHash(const QModelIndex &index) +{ + QHash hash; + QFileInfo fileInfo = m_fileModel->fileInfo(index); + + hash["path"] = QDir::toNativeSeparators(m_fileModel->filePath(index)); + hash["label"] = hash["path"]; + hash["label_noext"] = fileInfo.completeBaseName(); + hash["db_name"] = fileInfo.dir().dirName(); + + return hash; +} + +void MainWindow::onContentItemDoubleClicked(const QModelIndex &index) +{ + Q_UNUSED(index); + onRunClicked(); +} + +void MainWindow::onStartCoreClicked() +{ + content_ctx_info_t content_info; + + content_info.argc = 0; + content_info.argv = NULL; + content_info.args = NULL; + content_info.environ_get = NULL; + + path_clear(RARCH_PATH_BASENAME); + + if (!task_push_start_current_core(&content_info)) + QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); +} + +QHash MainWindow::getSelectedCore() +{ + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + QHash coreHash; + QHash contentHash; + ViewType viewType = getCurrentViewType(); + + if (viewType == VIEW_TYPE_LIST) + contentHash = m_tableView->currentIndex().data(PlaylistModel::HASH).value >(); + else if (viewType == VIEW_TYPE_ICONS) + contentHash = m_gridView->currentIndex().data(PlaylistModel::HASH).value >(); + else + return coreHash; + + switch(coreSelection) + { + case CORE_SELECTION_CURRENT: + coreHash["core_path"] = path_get(RARCH_PATH_CORE); + break; + case CORE_SELECTION_PLAYLIST_SAVED: + if (contentHash.isEmpty() || contentHash["core_path"].isEmpty()) + break; + + coreHash["core_path"] = contentHash["core_path"]; + + break; + case CORE_SELECTION_PLAYLIST_DEFAULT: + { + QString defaultCorePath; + + if (contentHash.isEmpty() || contentHash["db_name"].isEmpty()) + break; + + defaultCorePath = getPlaylistDefaultCore(contentHash["db_name"]); + + if (!defaultCorePath.isEmpty()) + coreHash["core_path"] = defaultCorePath; + + break; + } + default: + break; + } + + return coreHash; +} + +/* the hash typically has the following keys: +path - absolute path to the content file +core_path - absolute path to the core, or "DETECT" to ask the user +db_name - the display name of the rdb database this content is from +label - the display name of the content, usually comes from the database +crc32 - an upper-case, 8 byte string representation of the hex CRC32 checksum (e.g. ABCDEF12) followed by "|crc" +core_name - the display name of the core, or "DETECT" if unknown +label_noext - the display name of the content that is guaranteed not to contain a file extension +*/ +void MainWindow::loadContent(const QHash &contentHash) +{ + content_ctx_info_t content_info; + QByteArray corePathArray; + QByteArray contentPathArray; + QByteArray contentLabelArray; + QByteArray contentDbNameArray; + QByteArray contentCrc32Array; + char contentDbNameFull[PATH_MAX_LENGTH]; + const char *corePath = NULL; + const char *contentPath = NULL; + const char *contentLabel = NULL; + const char *contentDbName = NULL; + const char *contentCrc32 = NULL; + QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); + core_selection coreSelection = static_cast(coreMap.value("core_selection").toInt()); + core_info_ctx_find_t core_info; + + contentDbNameFull[0] = '\0'; + + if (m_pendingRun) + coreSelection = CORE_SELECTION_CURRENT; + + if (coreSelection == CORE_SELECTION_ASK) + { + QStringList extensionFilters; + + if (contentHash.contains("path")) + { + QString extensionStr; + int lastIndex = contentHash["path"].lastIndexOf('.'); + QByteArray pathArray = contentHash["path"].toUtf8(); + const char *pathData = pathArray.constData(); + + if (lastIndex >= 0) + { + extensionStr = contentHash["path"].mid(lastIndex + 1); + + if (!extensionStr.isEmpty()) + extensionFilters.append(extensionStr.toLower()); + } + + if (path_is_compressed_file(pathData)) + { + unsigned i = 0; + struct string_list *list = file_archive_get_file_list(pathData, NULL); + + if (list) + { + if (list->size > 0) + { + for (i = 0; i < list->size; i++) + { + const char *filePath = list->elems[i].data; + const char *extension = path_get_extension(filePath); + + if (!extensionFilters.contains(extension, Qt::CaseInsensitive)) + extensionFilters.append(extension); + } + } + + string_list_free(list); + } + } + } + + m_pendingRun = true; + onLoadCoreClicked(extensionFilters); + + return; + } + + switch (coreSelection) + { + case CORE_SELECTION_CURRENT: + corePathArray = path_get(RARCH_PATH_CORE); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + break; + case CORE_SELECTION_PLAYLIST_SAVED: + corePathArray = contentHash["core_path"].toUtf8(); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + break; + case CORE_SELECTION_PLAYLIST_DEFAULT: + { + QString defaultCorePath = getPlaylistDefaultCore(contentHash["db_name"]); + + if (!defaultCorePath.isEmpty()) + { + corePathArray = defaultCorePath.toUtf8(); + contentPathArray = contentHash["path"].toUtf8(); + contentLabelArray = contentHash["label_noext"].toUtf8(); + } + + break; + } + default: + return; + } + + contentDbNameArray = contentHash["db_name"].toUtf8(); + contentCrc32Array = contentHash["crc32"].toUtf8(); + + corePath = corePathArray.constData(); + contentPath = contentPathArray.constData(); + contentLabel = contentLabelArray.constData(); + contentDbName = contentDbNameArray.constData(); + contentCrc32 = contentCrc32Array.constData(); + + /* Search for specified core - ensures path + * is 'sanitised' */ + core_info.inf = NULL; + core_info.path = corePath; + + if (core_info_find(&core_info) && + !string_is_empty(core_info.inf->path)) + corePath = core_info.inf->path; + + /* Add lpl extension to db_name, if required */ + if (!string_is_empty(contentDbName)) + { + const char *extension = NULL; + + strlcpy(contentDbNameFull, contentDbName, sizeof(contentDbNameFull)); + extension = path_get_extension(contentDbNameFull); + + if ( string_is_empty(extension) + || !string_is_equal_noncase( + extension, FILE_PATH_LPL_EXTENSION_NO_DOT)) + strlcat( + contentDbNameFull, FILE_PATH_LPL_EXTENSION, + sizeof(contentDbNameFull)); + } + + content_info.argc = 0; + content_info.argv = NULL; + content_info.args = NULL; + content_info.environ_get = NULL; + +#ifdef HAVE_MENU + menu_navigation_set_selection(0); +#endif + + command_event(CMD_EVENT_UNLOAD_CORE, NULL); + + if (!task_push_load_content_with_new_core_from_companion_ui( + corePath, contentPath, contentLabel, contentDbNameFull, contentCrc32, + &content_info, NULL, NULL)) + { + QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), + msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); + return; + } + +#ifdef HAVE_MENU + menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL); +#endif +} + +void MainWindow::onRunClicked() +{ + QHash contentHash; + + switch (m_currentBrowser) + { + case BROWSER_TYPE_FILES: + contentHash = getFileContentHash( + m_proxyFileModel->mapToSource(m_fileTableView->currentIndex())); + break; + case BROWSER_TYPE_PLAYLISTS: + contentHash = getCurrentContentHash(); + break; + } + + if (contentHash.isEmpty()) + return; + + loadContent(contentHash); +} + +bool MainWindow::isContentLessCore() +{ + rarch_system_info_t *system = runloop_get_system_info(); + + return system->load_no_content; +} + +bool MainWindow::isCoreLoaded() +{ + if ( m_currentCore.isEmpty() || + m_currentCore == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE)) + return false; + + return true; +} + +PlaylistEntryDialog* MainWindow::playlistEntryDialog() +{ + return m_playlistEntryDialog; +} + +ViewOptionsDialog* MainWindow::viewOptionsDialog() +{ + return m_viewOptionsDialog; +} + +void MainWindow::setCoreActions() +{ + QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); + ViewType viewType = getCurrentViewType(); + QHash hash = getCurrentContentHash(); + QString currentPlaylistFileName = QString(); + + m_launchWithComboBox->clear(); + + if (isContentLessCore()) + m_startCorePushButton->show(); + else + m_startCorePushButton->hide(); + + if ( isCoreLoaded() + && m_settings->value("suggest_loaded_core_first", false).toBool()) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = m_currentCore; + comboBoxMap["core_path"] = path_get(RARCH_PATH_CORE); + comboBoxMap["core_selection"] = CORE_SELECTION_CURRENT; + m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap)); + } + + if (m_currentBrowser == BROWSER_TYPE_PLAYLISTS) + { + if (!hash.isEmpty()) + { + QString coreName = hash["core_name"]; + + if (coreName.isEmpty()) + coreName = ""; + else + { + const char *detect_str = "DETECT"; + + if (coreName != detect_str) + { + if (m_launchWithComboBox->findText(coreName) == -1) + { + int i = 0; + bool found_existing = false; + + for (i = 0; i < m_launchWithComboBox->count(); i++) + { + QVariantMap map = m_launchWithComboBox->itemData(i, Qt::UserRole).toMap(); + + if (map.value("core_path").toString() == hash["core_path"] || map.value("core_name").toString() == coreName) + { + found_existing = true; + break; + } + } + + if (!found_existing) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = coreName; + comboBoxMap["core_path"] = hash["core_path"]; + comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_SAVED; + m_launchWithComboBox->addItem(coreName, QVariant::fromValue(comboBoxMap)); + } + } + } + } + } + } + + switch(m_currentBrowser) + { + case BROWSER_TYPE_PLAYLISTS: + currentPlaylistFileName = hash["db_name"]; + break; + case BROWSER_TYPE_FILES: + currentPlaylistFileName = m_fileModel->rootDirectory().dirName(); + break; + } + + if (!currentPlaylistFileName.isEmpty()) + { + QString defaultCorePath = getPlaylistDefaultCore(currentPlaylistFileName); + + if (!defaultCorePath.isEmpty()) + { + QString currentPlaylistItemDataString; + core_info_ctx_find_t core_info_finder; + bool allPlaylists = false; + int row = 0; + QByteArray defaultCorePathArray = defaultCorePath.toUtf8(); + const char *default_core_path_data = defaultCorePathArray.constData(); + + if (currentPlaylistItem) + { + currentPlaylistItemDataString = currentPlaylistItem->data(Qt::UserRole).toString(); + allPlaylists = ( + currentPlaylistItemDataString == ALL_PLAYLISTS_TOKEN); + } + + for (row = 0; row < m_listWidget->count(); row++) + { + if (allPlaylists) + { + QFileInfo info; + QListWidgetItem *listItem = m_listWidget->item(row); + QString listItemString = listItem->data(Qt::UserRole).toString(); + + info.setFile(listItemString); + + if (listItemString == ALL_PLAYLISTS_TOKEN) + continue; + } + + /* Search for default core */ + core_info_finder.inf = NULL; + core_info_finder.path = default_core_path_data; + + if (core_info_find(&core_info_finder)) + { + const core_info_t *info = core_info_finder.inf; + + if (m_launchWithComboBox->findText(info->core_name) == -1) + { + int i = 0; + bool found_existing = false; + + for (i = 0; i < m_launchWithComboBox->count(); i++) + { + QVariantMap map = + m_launchWithComboBox->itemData( + i, Qt::UserRole).toMap(); + QByteArray CorePathArray = + map.value("core_path").toString().toUtf8(); + const char *core_path_data = CorePathArray.constData(); + + if (string_starts_with(path_basename(core_path_data), + info->core_file_id.str) || + map.value("core_name").toString() == info->core_name || + map.value("core_name").toString() == info->display_name) + { + found_existing = true; + break; + } + } + + if (!found_existing) + { + QVariantMap comboBoxMap; + comboBoxMap["core_name"] = info->core_name; + comboBoxMap["core_path"] = info->path; + comboBoxMap["core_selection"] = CORE_SELECTION_PLAYLIST_DEFAULT; + m_launchWithComboBox->addItem(info->core_name, QVariant::fromValue(comboBoxMap)); + } + } + } + + if (!allPlaylists) + break; + } + } + } + + { + QVariantMap comboBoxMap; + comboBoxMap["core_selection"] = CORE_SELECTION_ASK; + m_launchWithComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK), QVariant::fromValue(comboBoxMap)); + m_launchWithComboBox->insertSeparator(m_launchWithComboBox->count()); + comboBoxMap["core_selection"] = CORE_SELECTION_LOAD_CORE; + m_launchWithComboBox->addItem(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD_CORE)) + "...", QVariant::fromValue(comboBoxMap)); + } +} + +void MainWindow::onTabWidgetIndexChanged(int index) +{ + if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER)) + { + m_currentBrowser = BROWSER_TYPE_FILES; + + m_centralWidget->setCurrentWidget(m_fileTableView); + + onCurrentFileChanged(m_fileTableView->currentIndex()); + } + else if (m_browserAndPlaylistTabWidget->tabText(index) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) + { + m_currentBrowser = BROWSER_TYPE_PLAYLISTS; + + m_centralWidget->setCurrentWidget(m_playlistViewsAndFooter); + + onCurrentItemChanged(m_tableView->currentIndex()); + } + + applySearch(); + + setCoreActions(); +} + +QToolButton* MainWindow::runPushButton() +{ + return m_runPushButton; +} + +QToolButton* MainWindow::stopPushButton() +{ + return m_stopPushButton; +} + +QToolButton* MainWindow::startCorePushButton() +{ + return m_startCorePushButton; +} + +QComboBox* MainWindow::launchWithComboBox() +{ + return m_launchWithComboBox; +} + +void MainWindow::onSearchLineEditEdited(const QString &text) +{ + int i = 0; + QVector textUnicode = text.toUcs4(); + QVector textHiraToKata; + QVector textKataToHira; + bool foundHira = false; + bool foundKata = false; + + for (i = 0; i < textUnicode.size(); i++) + { + unsigned code = textUnicode.at(i); + + if (code >= HIRAGANA_START && code <= HIRAGANA_END) + { + foundHira = true; + textHiraToKata += code + HIRA_KATA_OFFSET; + } + else if (code >= KATAKANA_START && code <= KATAKANA_END) + { + foundKata = true; + textKataToHira += code - HIRA_KATA_OFFSET; + } + else + { + textHiraToKata += code; + textKataToHira += code; + } + } + + if (!foundHira && !foundKata) + m_searchRegExp = QRegExp(text, Qt::CaseInsensitive); + else if (foundHira && !foundKata) + m_searchRegExp = QRegExp(text + "|" + + QString::fromUcs4(textHiraToKata.constData(), + textHiraToKata.size()), Qt::CaseInsensitive); + else if (!foundHira && foundKata) + m_searchRegExp = QRegExp(text + "|" + + QString::fromUcs4(textKataToHira.constData(), + textKataToHira.size()), Qt::CaseInsensitive); + else + m_searchRegExp = QRegExp(text + "|" + + QString::fromUcs4(textHiraToKata.constData(), + textHiraToKata.size()) + "|" + + QString::fromUcs4(textKataToHira.constData(), + textKataToHira.size()), Qt::CaseInsensitive); + + applySearch(); +} + +void MainWindow::applySearch() +{ + switch (m_currentBrowser) + { + case BROWSER_TYPE_PLAYLISTS: + if (m_proxyModel->filterRegExp() != m_searchRegExp) + { + m_proxyModel->setFilterRegExp(m_searchRegExp); + updateItemsCount(); + } + break; + case BROWSER_TYPE_FILES: + if (m_proxyFileModel->filterRegExp() != m_searchRegExp) + m_proxyFileModel->setFilterRegExp(m_searchRegExp); + break; + } +} + +void MainWindow::onViewClosedDocksAboutToShow() +{ + QList dockWidgets; + int i = 0; + QMenu *menu = qobject_cast(sender()); + bool found = false; + + if (!menu) + return; + + dockWidgets = findChildren(); + + menu->clear(); + + if (dockWidgets.isEmpty()) + { + menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); + return; + } + + for (i = 0; i < dockWidgets.count(); i++) + { + const QDockWidget *dock = dockWidgets.at(i); + + if (!dock->isVisible()) + { + QAction *action = menu->addAction( + dock->property("menu_text").toString(), + this, SLOT(onShowHiddenDockWidgetAction())); + action->setProperty("dock_name", dock->objectName()); + found = true; + } + } + + if (!found) + menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); +} + +void MainWindow::onShowHiddenDockWidgetAction() +{ + QAction *action = qobject_cast(sender()); + QDockWidget *dock = NULL; + + if (!action) + return; + + dock = findChild(action->property("dock_name").toString()); + + if (!dock) + return; + + if (!dock->isVisible()) + { + addDockWidget(static_cast( + dock->property("default_area").toInt()), dock); + dock->setVisible(true); + dock->setFloating(false); + } +} + +QWidget* MainWindow::searchWidget() +{ + return m_searchWidget; +} + +QLineEdit* MainWindow::searchLineEdit() +{ + return m_searchLineEdit; +} + +void MainWindow::onSearchEnterPressed() +{ + onSearchLineEditEdited(m_searchLineEdit->text()); +} + +void MainWindow::onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) +{ + QHash hash; + + if (!roles.contains(Qt::EditRole)) + return; + + if (topLeft != bottomRight) + return; + + hash = topLeft.data(PlaylistModel::HASH).value>(); + + updateCurrentPlaylistEntry(hash); + + onCurrentItemChanged(topLeft); +} + +void MainWindow::onCurrentListItemDataChanged(QListWidgetItem *item) +{ + renamePlaylistItem(item, item->text()); +} + +void MainWindow::renamePlaylistItem(QListWidgetItem *item, QString newName) +{ + QString oldPath; + QString newPath; + QString extension; + QString oldName; + QFile file; + QFileInfo info; + QFileInfo playlistInfo; + QString playlistPath; + bool specialPlaylist = false; + settings_t *settings = config_get_ptr(); + const char *path_dir_playlist = settings->paths.directory_playlist; + QDir playlistDir(path_dir_playlist); + + if (!item) + return; + + playlistPath = item->data(Qt::UserRole).toString(); + playlistInfo = playlistPath; + oldName = playlistInfo.completeBaseName(); + + /* Don't just compare strings in case there are + * case differences on Windows that should be ignored. */ + if (QDir(playlistInfo.absoluteDir()) != QDir(playlistDir)) + { + /* special playlists like history etc. can't have an association */ + specialPlaylist = true; + } + + if (specialPlaylist) + { + /* special playlists shouldn't be editable already, + * but just in case, set the old name back and + * early return if they rename it */ + item->setText(oldName); + return; + } + + /* block this signal because setData() would trigger + * an infinite loop here */ + disconnect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); + + oldPath = item->data(Qt::UserRole).toString(); + + file.setFileName(oldPath); + info = file; + + extension = info.suffix(); + + newPath = info.absolutePath(); + + /* absolutePath() will always use / even on Windows */ + if (newPath.at(newPath.count() - 1) != '/') + /* add trailing slash if the path doesn't have one */ + newPath += '/'; + + newPath += newName + "." + extension; + + item->setData(Qt::UserRole, newPath); + + if (!file.rename(newPath)) + { + RARCH_ERR("[Qt]: Could not rename playlist.\n"); + item->setText(oldName); + } + + connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); +} + +void MainWindow::onCurrentItemChanged(const QModelIndex &index) +{ + onCurrentItemChanged(index.data(PlaylistModel::HASH).value>()); +} + +void MainWindow::onCurrentFileChanged(const QModelIndex &index) +{ + onCurrentItemChanged(getFileContentHash(m_proxyFileModel->mapToSource(index))); +} + +void MainWindow::onCurrentItemChanged(const QHash &hash) +{ + QString path = hash["path"]; + bool acceptDrop = false; + + if (m_thumbnailPixmap) + delete m_thumbnailPixmap; + if (m_thumbnailPixmap2) + delete m_thumbnailPixmap2; + if (m_thumbnailPixmap3) + delete m_thumbnailPixmap3; + + if (m_playlistModel->isSupportedImage(path)) + { + /* use thumbnail widgets to show regular image files */ + m_thumbnailPixmap = new QPixmap(path); + m_thumbnailPixmap2 = new QPixmap(*m_thumbnailPixmap); + m_thumbnailPixmap3 = new QPixmap(*m_thumbnailPixmap); + } + else + { + QString thumbnailsDir = m_playlistModel->getPlaylistThumbnailsDir(hash["db_name"]); + QString thumbnailName = m_playlistModel->getSanitizedThumbnailName(hash["label_noext"]); + + m_thumbnailPixmap = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_BOXART + "/" + thumbnailName); + m_thumbnailPixmap2 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_TITLE + "/" + thumbnailName); + m_thumbnailPixmap3 = new QPixmap(thumbnailsDir + "/" + THUMBNAIL_SCREENSHOT + "/" + thumbnailName); + + if ( m_currentBrowser == BROWSER_TYPE_PLAYLISTS + && !currentPlaylistIsSpecial()) + acceptDrop = true; + } + + onResizeThumbnailOne(*m_thumbnailPixmap, acceptDrop); + onResizeThumbnailTwo(*m_thumbnailPixmap2, acceptDrop); + onResizeThumbnailThree(*m_thumbnailPixmap3, acceptDrop); + + setCoreActions(); +} + +void MainWindow::setThumbnail(QString widgetName, + QPixmap &pixmap, bool acceptDrop) +{ + ThumbnailWidget *thumbnail = findChild(widgetName); + + if (thumbnail) + thumbnail->setPixmap(pixmap, acceptDrop); +} + +void MainWindow::onResizeThumbnailOne(QPixmap &pixmap, bool acceptDrop) +{ + setThumbnail("thumbnail", pixmap, acceptDrop); +} + +void MainWindow::onResizeThumbnailTwo(QPixmap &pixmap, bool acceptDrop) +{ + setThumbnail("thumbnail2", pixmap, acceptDrop); +} + +void MainWindow::onResizeThumbnailThree(QPixmap &pixmap, bool acceptDrop) +{ + setThumbnail("thumbnail3", pixmap, acceptDrop); +} + +void MainWindow::setCurrentViewType(ViewType viewType) +{ + m_viewType = viewType; + + switch (viewType) + { + case VIEW_TYPE_ICONS: + m_playlistViews->setCurrentWidget(m_gridView); + m_zoomWidget->show(); + break; + case VIEW_TYPE_LIST: + default: + m_playlistViews->setCurrentWidget(m_tableView); + m_zoomWidget->hide(); + break; + } +} + +void MainWindow::setCurrentThumbnailType(ThumbnailType thumbnailType) +{ + m_thumbnailType = thumbnailType; + + m_playlistModel->setThumbnailType(thumbnailType); + updateVisibleItems(); + m_gridView->viewport()->update(); +} + +MainWindow::ViewType MainWindow::getCurrentViewType() +{ + return m_viewType; +} + +ThumbnailType MainWindow::getCurrentThumbnailType() +{ + return m_thumbnailType; +} + +void MainWindow::onCurrentListItemChanged( + QListWidgetItem *current, QListWidgetItem *previous) +{ + Q_UNUSED(current) + Q_UNUSED(previous) + + initContentTableWidget(); + + setCoreActions(); +} + +TableView* MainWindow::contentTableView() +{ + return m_tableView; +} + +QTableView* MainWindow::fileTableView() +{ + return m_fileTableView; +} + +QStackedWidget* MainWindow::centralWidget() +{ + return m_centralWidget; +} + +FileDropWidget* MainWindow::playlistViews() +{ + return m_playlistViews; +} + +QWidget* MainWindow::playlistViewsAndFooter() +{ + return m_playlistViewsAndFooter; +} + +GridView* MainWindow::contentGridView() +{ + return m_gridView; +} + +void MainWindow::onBrowserDownloadsClicked() +{ + settings_t *settings = config_get_ptr(); + QDir dir(settings->paths.directory_core_assets); + QString path = dir.absolutePath(); + QModelIndex index; + + m_pendingDirScrollPath = path; + + index = m_dirModel->index(path); + + m_dirTree->setCurrentIndex(index); + + onDownloadScroll(path); +} + +void MainWindow::onDownloadScroll(QString path) +{ + QModelIndex index = m_dirModel->index(path); + m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop); + m_dirTree->expand(index); + + /* FIXME: Find a way to make this unnecessary */ + emit scrollToDownloadsAgain(path); +} + +void MainWindow::onDownloadScrollAgain(QString path) +{ + QModelIndex index = m_dirModel->index(path); + m_dirTree->scrollTo(index, QAbstractItemView::PositionAtTop); + m_dirTree->expand(index); +} + +void MainWindow::onBrowserUpClicked() +{ + QDir dir(m_dirModel->filePath(m_dirTree->currentIndex())); + + dir.cdUp(); + + m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath())); + m_dirTree->scrollTo(m_dirTree->currentIndex(), + QAbstractItemView::EnsureVisible); +} + +void MainWindow::onBrowserStartClicked() +{ + settings_t *settings = config_get_ptr(); + + m_dirTree->setCurrentIndex( + m_dirModel->index(settings->paths.directory_menu_content)); + m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); +} + +ListWidget* MainWindow::playlistListWidget() +{ + return m_listWidget; +} + +TreeView* MainWindow::dirTreeView() +{ + return m_dirTree; +} + +void MainWindow::onTimeout() +{ + bool contentless = false; + bool is_inited = false; + + content_get_status(&contentless, &is_inited); + + if (is_inited) + { + if (m_runPushButton->isVisible()) + m_runPushButton->hide(); + if (!m_stopPushButton->isVisible()) + m_stopPushButton->show(); + } + else + { + if (!m_runPushButton->isVisible()) + m_runPushButton->show(); + if (m_stopPushButton->isVisible()) + m_stopPushButton->hide(); + } + + setCurrentCoreLabel(); +} + +void MainWindow::onStopClicked() +{ +#ifdef HAVE_MENU + menu_navigation_set_selection(0); +#endif + command_event(CMD_EVENT_UNLOAD_CORE, NULL); + setCurrentCoreLabel(); + activateWindow(); + raise(); +} + +void MainWindow::setCurrentCoreLabel() +{ + bool update = false; + struct retro_system_info *system = runloop_get_libretro_system_info(); + QString libraryName = system->library_name; + const char *no_core_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORE); + + if ( (m_statusLabel->text().isEmpty()) + || (m_currentCore != no_core_str && libraryName.isEmpty()) + ) + { + m_currentCore = no_core_str; + m_currentCoreVersion = ""; + update = true; + } + else + { + if (m_currentCore != libraryName && !libraryName.isEmpty()) + { + m_currentCore = system->library_name; + m_currentCoreVersion = (string_is_empty(system->library_version) ? "" : system->library_version); + update = true; + } + } + + if (update) + { + QAction *unloadCoreAction = findChild("unloadCoreAction"); + QString text = QString(PACKAGE_VERSION) + " - " + m_currentCore + " " + m_currentCoreVersion; + m_statusLabel->setText(text); + m_loadCoreWindow->setStatusLabel(text); + setCoreActions(); + + if (unloadCoreAction) + { + if (libraryName.isEmpty()) + unloadCoreAction->setEnabled(false); + else + unloadCoreAction->setEnabled(true); + } + } +} + +void MainWindow::onCoreLoadWindowClosed() +{ + QVariant lastLaunchWithVariant = m_loadCoreWindow->property("last_launch_with_index"); + int lastLaunchWithIndex = lastLaunchWithVariant.toInt(); + + m_pendingRun = false; + + if (lastLaunchWithVariant.isValid() && lastLaunchWithIndex >= 0) + { + m_launchWithComboBox->setCurrentIndex(lastLaunchWithIndex); + m_loadCoreWindow->setProperty("last_launch_with_index", -1); + } +} + +void MainWindow::onCoreLoaded() +{ + QAction *unloadAction = findChild("unloadCoreAction"); + + activateWindow(); + raise(); + setCurrentCoreLabel(); + setCoreActions(); + + if (unloadAction) + unloadAction->setEnabled(true); + + m_loadCoreWindow->hide(); + + if (m_pendingRun) + { + onRunClicked(); + m_pendingRun = false; + } +} + +void MainWindow::onUnloadCoreMenuAction() +{ + QAction *action = qobject_cast(sender()); + +#ifdef HAVE_MENU + menu_navigation_set_selection(0); +#endif + + /* TODO */ + if (!command_event(CMD_EVENT_UNLOAD_CORE, NULL)) + return; + + setCurrentCoreLabel(); + setCoreActions(); + + if (!action) + return; + + action->setEnabled(false); + activateWindow(); + raise(); +} + +void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters) +{ + m_loadCoreWindow->show(); + m_loadCoreWindow->resize(width() / 2, height()); + m_loadCoreWindow->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, m_loadCoreWindow->size(), geometry())); + m_loadCoreWindow->initCoreList(extensionFilters); +} + +void MainWindow::initContentTableWidget() +{ + QString path; + QListWidgetItem *item = m_listWidget->currentItem(); + + if (!item) + return; + + m_currentGridHash.clear(); + + if (m_currentGridWidget) + { + m_currentGridWidget->setObjectName("thumbnailWidget"); + m_currentGridWidget->style()->unpolish(m_currentGridWidget); + m_currentGridWidget->style()->polish(m_currentGridWidget); + } + + m_currentGridWidget = NULL; + + path = item->data(Qt::UserRole).toString(); + + if (path == ALL_PLAYLISTS_TOKEN) + { + unsigned i; + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + QStringList playlists; + + for (i = 0; i < m_playlistFiles.count(); i++) + { + const QString &playlist = m_playlistFiles.at(i); + playlists.append(playlistDir.absoluteFilePath(playlist)); + } + + m_playlistModel->addPlaylistItems(playlists, true); + } + else + m_playlistModel->addPlaylistItems(QStringList() << path); + + if (item != m_historyPlaylistsItem) + m_tableView->sortByColumn(0, Qt::AscendingOrder); + else + m_proxyModel->sort(-1); + + updateItemsCount(); + + m_gridView->scrollToTop(); + m_gridView->setCurrentIndex(m_proxyModel->index(0, 0)); +} + +void MainWindow::updateItemsCount() +{ + m_itemsCountLabel->setText( + m_itemsCountLiteral.arg(m_proxyModel->rowCount())); +} + +void MainWindow::keyPressEvent(QKeyEvent *event) +{ + QMainWindow::keyPressEvent(event); +} + +QSettings* MainWindow::settings() +{ + return m_settings; +} + +QString MainWindow::getCurrentViewTypeString() +{ + switch (m_viewType) + { + case VIEW_TYPE_ICONS: + return QStringLiteral("icons"); + case VIEW_TYPE_LIST: + default: + break; + } + + return QStringLiteral("list"); +} + +QString MainWindow::getCurrentThumbnailTypeString() +{ + switch (m_thumbnailType) + { + case THUMBNAIL_TYPE_SCREENSHOT: + return QStringLiteral("screenshot"); + case THUMBNAIL_TYPE_TITLE_SCREEN: + return QStringLiteral("title"); + case THUMBNAIL_TYPE_BOXART: + default: + return QStringLiteral("boxart"); + } + + return QStringLiteral("list"); +} + +ThumbnailType MainWindow::getThumbnailTypeFromString(QString thumbnailType) +{ + if (thumbnailType == "boxart") + return THUMBNAIL_TYPE_BOXART; + else if (thumbnailType == "screenshot") + return THUMBNAIL_TYPE_SCREENSHOT; + else if (thumbnailType == "title") + return THUMBNAIL_TYPE_TITLE_SCREEN; + + return THUMBNAIL_TYPE_BOXART; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (m_settings->value("save_geometry", false).toBool()) + m_settings->setValue("geometry", saveGeometry()); + if (m_settings->value("save_dock_positions", false).toBool()) + m_settings->setValue("dock_positions", saveState()); + if (m_settings->value("save_last_tab", false).toBool()) + m_settings->setValue("last_tab", m_browserAndPlaylistTabWidget->currentIndex()); + + m_settings->setValue("view_type", getCurrentViewTypeString()); + m_settings->setValue("file_browser_table_headers", m_fileTableView->horizontalHeader()->saveState()); + m_settings->setValue("icon_view_zoom", m_lastZoomSliderValue); + m_settings->setValue("icon_view_thumbnail_type", getCurrentThumbnailTypeString()); + m_settings->setValue("options_dialog_geometry", m_viewOptionsDialog->saveGeometry()); + + QMainWindow::closeEvent(event); +} + +void MainWindow::onContributorsClicked() +{ + QScopedPointer dialog(new QDialog()); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + QTextEdit *textEdit = new QTextEdit(dialog.data()); + + connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject())); + + dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS)); + dialog->setLayout(new QVBoxLayout()); + + dialog->layout()->addWidget(textEdit); + + dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Minimum)); + dialog->layout()->addWidget(buttonBox); + + textEdit->setReadOnly(true); + textEdit->setHtml(QString("
") + retroarch_contributors_list + "
"); + + dialog->resize(480, 640); + dialog->exec(); +} + +void MainWindow::showAbout() +{ + QScopedPointer dialog(new QDialog()); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + QString text = QString("RetroArch ") + PACKAGE_VERSION + + "

" + "www.libretro.com" + "

" + "www.retroarch.com" +#ifdef HAVE_GIT_VERSION + "

" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_GIT_VERSION) + ": " + retroarch_git_version + +#endif + "
" + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_BUILD_DATE) + ": " + __DATE__; + QLabel *label = new QLabel(text, dialog.data()); + QPixmap pix = getInvader(); + QLabel *pixLabel = new QLabel(dialog.data()); + QPushButton *contributorsPushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT_CONTRIBUTORS), dialog.data()); + + connect(contributorsPushButton, SIGNAL(clicked()), this, SLOT(onContributorsClicked())); + connect(buttonBox, SIGNAL(accepted()), dialog.data(), SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), dialog.data(), SLOT(reject())); + + label->setTextFormat(Qt::RichText); + label->setAlignment(Qt::AlignCenter); + label->setTextInteractionFlags(Qt::TextBrowserInteraction); + label->setOpenExternalLinks(true); + + pixLabel->setAlignment(Qt::AlignCenter); + pixLabel->setPixmap(pix); + + dialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_HELP_ABOUT)); + dialog->setLayout(new QVBoxLayout()); + + dialog->layout()->addWidget(pixLabel); + dialog->layout()->addWidget(label); + dialog->layout()->addWidget(contributorsPushButton); + + dialog->layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + dialog->layout()->addWidget(buttonBox); + + dialog->exec(); +} + +void MainWindow::showDocs() +{ + QDesktopServices::openUrl(QUrl(DOCS_URL)); +} + +void MainWindow::onShowErrorMessage(QString msg) +{ + showMessageBox(msg, MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); +} + +void MainWindow::onShowInfoMessage(QString msg) +{ + showMessageBox(msg, MainWindow::MSGBOX_TYPE_INFO, Qt::ApplicationModal, false); +} + +int MainWindow::onExtractArchive(QString path, QString extractionDir, QString tempExtension, retro_task_callback_t cb) +{ + unsigned i; + file_archive_transfer_t state; + struct archive_extract_userdata userdata; + QByteArray pathArray = path.toUtf8(); + QByteArray dirArray = extractionDir.toUtf8(); + const char *file = pathArray.constData(); + const char *dir = dirArray.constData(); + struct string_list *file_list = file_archive_get_file_list(file, NULL); + bool returnerr = true; + retro_task_t *decompress_task = NULL; + + if (!file_list || file_list->size == 0) + { + showMessageBox("Error: Archive is empty.", MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); + RARCH_ERR("[Qt]: Downloaded archive is empty?\n"); + return -1; + } + + for (i = 0; i < file_list->size; i++) + { + QFile fileObj(file_list->elems[i].data); + + if (fileObj.exists()) + { + if (!fileObj.remove()) + { + /* if we cannot delete the existing file to update it, rename it for now and delete later */ + QFile fileTemp(fileObj.fileName() + tempExtension); + + if (fileTemp.exists()) + { + if (!fileTemp.remove()) + { + showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); + RARCH_ERR("[Qt]: Could not delete file: %s\n", file_list->elems[i].data); + return -1; + } + } + + if (!fileObj.rename(fileTemp.fileName())) + { + showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); + RARCH_ERR("[Qt]: Could not rename file: %s\n", file_list->elems[i].data); + return -1; + } + } + } + } + + string_list_free(file_list); + + memset(&state, 0, sizeof(state)); + memset(&userdata, 0, sizeof(userdata)); + + state.type = ARCHIVE_TRANSFER_INIT; + + m_updateProgressDialog->setWindowModality(Qt::NonModal); + m_updateProgressDialog->setMinimumDuration(0); + m_updateProgressDialog->setRange(0, 0); + m_updateProgressDialog->setAutoClose(true); + m_updateProgressDialog->setAutoReset(true); + m_updateProgressDialog->setValue(0); + m_updateProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_EXTRACTING)) + "..."); + m_updateProgressDialog->setCancelButtonText(QString()); + m_updateProgressDialog->show(); + + decompress_task = (retro_task_t*)task_push_decompress( + file, dir, + NULL, NULL, NULL, + cb, this, NULL, false); + + if (!decompress_task) + { + m_updateProgressDialog->cancel(); + return -1; + } + + return returnerr; +} + +QString MainWindow::getScrubbedString(QString str) +{ + const QString chars("&*/:`\"<>?\\|"); + int i; + + for (i = 0; i < chars.count(); i++) + str.replace(chars.at(i), '_'); + + return str; +} + +static void* ui_window_qt_init(void) +{ + ui_window.qtWindow = new MainWindow(); + + return &ui_window; +} + +static void ui_window_qt_destroy(void *data) +{ +#if 0 + ui_window_qt_t *window = (ui_window_qt_t*)data; + + delete window->qtWindow; +#endif +} + +static void ui_window_qt_set_focused(void *data) +{ +#if 0 + ui_window_qt_t *window = (ui_window_qt_t*)data; + + window->qtWindow->raise(); + window->qtWindow->activateWindow(); +#endif +} + +static void ui_window_qt_set_visible(void *data, + bool set_visible) +{ + (void)data; + (void)set_visible; + /* TODO/FIXME */ +} + +static void ui_window_qt_set_title(void *data, char *buf) +{ +#if 0 + ui_window_qt_t *window = (ui_window_qt_t*)data; + + window->qtWindow->setWindowTitle(QString::fromUtf8(buf)); +#endif +} + +static void ui_window_qt_set_droppable(void *data, bool droppable) +{ +#if 0 + ui_window_qt_t *window = (ui_window_qt_t*)data; + + window->qtWindow->setAcceptDrops(droppable); +#endif +} + +static bool ui_window_qt_focused(void *data) +{ +#if 0 + ui_window_qt_t *window = (ui_window_qt_t*)data; + return window->qtWindow->isActiveWindow() && !window->qtWindow->isMinimized(); +#else + return true; +#endif +} + +static ui_window_t ui_window_qt = { + ui_window_qt_init, + ui_window_qt_destroy, + ui_window_qt_set_focused, + ui_window_qt_set_visible, + ui_window_qt_set_title, + ui_window_qt_set_droppable, + ui_window_qt_focused, + "qt" +}; + +static enum ui_msg_window_response ui_msg_window_qt_response(ui_msg_window_state *state, QMessageBox::StandardButtons response) +{ + switch (response) + { + case QMessageBox::Ok: + return UI_MSG_RESPONSE_OK; + case QMessageBox::Cancel: + return UI_MSG_RESPONSE_CANCEL; + case QMessageBox::Yes: + return UI_MSG_RESPONSE_YES; + case QMessageBox::No: + return UI_MSG_RESPONSE_NO; + default: + break; + } + + switch (state->buttons) + { + case UI_MSG_WINDOW_OK: + return UI_MSG_RESPONSE_OK; + case UI_MSG_WINDOW_OKCANCEL: + return UI_MSG_RESPONSE_CANCEL; + case UI_MSG_WINDOW_YESNO: + return UI_MSG_RESPONSE_NO; + case UI_MSG_WINDOW_YESNOCANCEL: + return UI_MSG_RESPONSE_CANCEL; + default: + break; + } + + return UI_MSG_RESPONSE_NA; +} + +static QFlags +ui_msg_window_qt_buttons(ui_msg_window_state *state) +{ + switch (state->buttons) + { + case UI_MSG_WINDOW_OK: + return QMessageBox::Ok; + case UI_MSG_WINDOW_OKCANCEL: + return QMessageBox::Cancel; + case UI_MSG_WINDOW_YESNO: + return (QMessageBox::Yes | QMessageBox::No); + case UI_MSG_WINDOW_YESNOCANCEL: + return (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + } + + return QMessageBox::NoButton; +} + +static enum ui_msg_window_response +ui_msg_window_qt_error(ui_msg_window_state *state) +{ + QFlags flags = ui_msg_window_qt_buttons(state); + return ui_msg_window_qt_response(state, QMessageBox::critical((QWidget*)state->window, state->title, state->text, flags)); +} + +static enum ui_msg_window_response ui_msg_window_qt_information(ui_msg_window_state *state) +{ + QFlags flags = ui_msg_window_qt_buttons(state); + return ui_msg_window_qt_response(state, QMessageBox::information((QWidget*)state->window, state->title, state->text, flags)); +} + +static enum ui_msg_window_response ui_msg_window_qt_question(ui_msg_window_state *state) +{ + QFlags flags = ui_msg_window_qt_buttons(state); + return ui_msg_window_qt_response(state, QMessageBox::question((QWidget*)state->window, state->title, state->text, flags)); +} + +static enum ui_msg_window_response ui_msg_window_qt_warning(ui_msg_window_state *state) +{ + QFlags flags = ui_msg_window_qt_buttons(state); + return ui_msg_window_qt_response(state, QMessageBox::warning((QWidget*)state->window, state->title, state->text, flags)); +} + +static ui_msg_window_t ui_msg_window_qt = { + ui_msg_window_qt_error, + ui_msg_window_qt_information, + ui_msg_window_qt_question, + ui_msg_window_qt_warning, + "qt" +}; + +static bool ui_browser_window_qt_open(ui_browser_window_state_t *state) +{ + return true; +} + +static bool ui_browser_window_qt_save(ui_browser_window_state_t *state) +{ + return false; +} + +static ui_browser_window_t ui_browser_window_qt = { + ui_browser_window_qt_open, + ui_browser_window_qt_save, + "qt" +}; + +static void* ui_application_qt_initialize(void) +{ + /* These must last for the lifetime of the QApplication */ + static int app_argc = 1; + static char app_name[] = "retroarch"; + static char *app_argv[] = { app_name, NULL }; + + app_handler = new AppHandler(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + /* HiDpi supported since Qt 5.6 */ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + /* Create QApplication() before calling QApplication::setStyle() + * to ensure that plugin path is determined correctly */ + ui_application.app = new QApplication(app_argc, app_argv); + QApplication::setStyle("fusion"); + ui_application.app->setOrganizationName("libretro"); + ui_application.app->setApplicationName("RetroArch"); + ui_application.app->setApplicationVersion(PACKAGE_VERSION); + ui_application.app->connect(ui_application.app, SIGNAL(lastWindowClosed()), + app_handler, SLOT(onLastWindowClosed())); + +#ifdef Q_OS_UNIX + setlocale(LC_NUMERIC, "C"); +#endif + { + /* Can't declare the pixmap at the top, because: "QPixmap: Must construct a QGuiApplication before a QPixmap" */ + QImage iconImage(16, 16, QImage::Format_ARGB32); + QPixmap iconPixmap; + unsigned char *bits = iconImage.bits(); + + memcpy(bits, retroarch_qt_icon_data, 16 * 16 * sizeof(unsigned)); + + iconPixmap = QPixmap::fromImage(iconImage); + + ui_application.app->setWindowIcon(QIcon(iconPixmap)); + } + + return &ui_application; +} + +static void ui_application_qt_process_events(void) +{ + QAbstractEventDispatcher *dispatcher = QApplication::eventDispatcher(); + if (dispatcher && dispatcher->hasPendingEvents()) + QApplication::processEvents(); +} + +static void ui_application_qt_quit(void) +{ + if (app_handler) + app_handler->exit(); +} + +#ifdef HAVE_MAIN +#if defined(__cplusplus) && !defined(CXX_BUILD) +extern "C" +#endif +int main(int argc, char *argv[]) +{ + return rarch_main(argc, argv, NULL); +} +#endif + +static ui_application_t ui_application_qt = { + ui_application_qt_initialize, + ui_application_qt_process_events, + ui_application_qt_quit, + false, + "qt" +}; + + +AppHandler::AppHandler(QObject *parent) : + QObject(parent) +{ +} + +AppHandler::~AppHandler() +{ +} + +void AppHandler::exit() +{ + ui_application_qt.exiting = true; + + if (qApp) + qApp->closeAllWindows(); +} + +bool AppHandler::isExiting() const +{ + return ui_application_qt.exiting; +} + +void AppHandler::onLastWindowClosed() +{ +} + typedef struct ui_companion_qt { ui_application_qt_t *app; diff --git a/ui/ui_companion_driver.h b/ui/ui_companion_driver.h index 8de54772d1..442518f044 100644 --- a/ui/ui_companion_driver.h +++ b/ui/ui_companion_driver.h @@ -144,11 +144,6 @@ typedef struct ui_companion_driver const char *ident; } ui_companion_driver_t; -extern ui_browser_window_t ui_browser_window_qt; -extern ui_msg_window_t ui_msg_window_qt; -extern ui_window_t ui_window_qt; -extern ui_application_t ui_application_qt; - extern ui_companion_driver_t ui_companion_cocoa; extern ui_companion_driver_t ui_companion_cocoatouch; extern ui_companion_driver_t ui_companion_qt;