/* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis * Copyright (C) 2018 - 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 #include "../ui_qt.h" #include "ui_qt_load_core_window.h" #include "ui_qt_themes.h" #include "flowlayout.h" #include "invader_png.h" extern "C" { #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" #include "../../../menu/menu_driver.h" #include "../../../tasks/tasks_internal.h" #include "../../../config.def.h" #include #include #include #include #include #include } #define TIMER_MSEC 1000 /* periodic timer for gathering statistics */ #define STATUS_MSG_THROTTLE_MSEC 250 #ifndef COLLECTION_SIZE #define COLLECTION_SIZE 99999 #endif #define GENERIC_FOLDER_ICON "/xmb/dot-art/png/folder.png" #define ICON_PATH "/xmb/dot-art/png/" #define THUMBNAIL_BOXART "Named_Boxarts" #define THUMBNAIL_SCREENSHOT "Named_Snaps" #define THUMBNAIL_TITLE "Named_Titles" #define ALL_PLAYLISTS_TOKEN "|||ALL|||" #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 USER_AGENT "RetroArch-WIMP/1.0" #define DOCS_URL "http://docs.libretro.com/" #define PARTIAL_EXTENSION ".partial" #define TEMP_EXTENSION ".update_tmp" #define RETROARCH_NIGHTLY_UPDATE_PATH "../RetroArch_update.zip" static ui_window_qt_t ui_window = {0}; enum CoreSelection { CORE_SELECTION_CURRENT, CORE_SELECTION_PLAYLIST_SAVED, CORE_SELECTION_PLAYLIST_DEFAULT, CORE_SELECTION_ASK, CORE_SELECTION_LOAD_CORE }; static double lerp(double x, double y, double a, double b, double d) { return a + (b - a) * ((double)(d - x) / (double)(y - x)); } /* 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 returnValue = 0; double M = maxValue / midValue; double C = log(pow(M - 1, 2)); double B = maxValue / (exp(C) - 1); double A = -1 * B; returnValue = A + B * exp(C * inputValue); return returnValue; } #ifdef HAVE_LIBRETRODB static void scan_finished_handler(void *task_data, void *user_data, const char *err) { bool dontAsk = false; bool answer = false; menu_ctx_environment_t menu_environ; menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; menu_environ.data = NULL; (void)task_data; (void)user_data; (void)err; menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); 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 inline static bool comp_string_lower(const QString &lhs, const QString &rhs) { return lhs.toLower() < rhs.toLower(); } inline static bool comp_hash_ui_display_name_key_lower(const QHash &lhs, const QHash &rhs) { return lhs.value("ui_display_name").toLower() < rhs.value("ui_display_name").toLower(); } inline static bool comp_hash_label_key_lower(const QHash &lhs, const QHash &rhs) { return lhs.value("label").toLower() < rhs.value("label").toLower(); } static void addDirectoryFilesToList(QStringList &list, QDir &dir) { QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name); int i; for (i = 0; i < dirList.count(); i++) { QString path(dir.path() + "/" + dirList.at(i)); QFileInfo fileInfo(path); if (fileInfo.isDir()) { QDir fileInfoDir(path); addDirectoryFilesToList(list, fileInfoDir); continue; } if (fileInfo.isFile()) { list.append(fileInfo.absoluteFilePath()); } } } /* https://gist.github.com/andrey-str/0f9c7709cbf0c9c49ef9 */ static void setElidedText(QLabel *label, QWidget *clipWidget, int padding, const QString &text) { QFontMetrics metrix(label->font()); int width = clipWidget->width() - padding; QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); label->setText(clippedText); } const QPixmap getInvader(); GridItem::GridItem() : QObject() ,widget(NULL) ,label(NULL) ,hash() ,image() ,pixmap() ,imageWatcher() ,labelText() { } 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); } FileDropWidget::FileDropWidget(QWidget *parent) : QWidget(parent) { setAcceptDrops(true); } void FileDropWidget::paintEvent(QPaintEvent *event) { QStyleOption o; QPainter p; o.initFrom(this); p.begin(this); style()->drawPrimitive( QStyle::PE_Widget, &o, &p, this); p.end(); QWidget::paintEvent(event); } void FileDropWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete) { event->accept(); emit deletePressed(); } else QWidget::keyPressEvent(event); } void FileDropWidget::dragEnterEvent(QDragEnterEvent *event) { const QMimeData *data = event->mimeData(); if (data->hasUrls()) event->acceptProposedAction(); } void FileDropWidget::dropEvent(QDropEvent *event) { const QMimeData *data = event->mimeData(); if (data->hasUrls()) { QList urls = data->urls(); QStringList files; int i; for (i = 0; i < urls.count(); i++) { QString path(urls.at(i).toLocalFile()); files.append(path); } emit filesDropped(files); } } TableWidget::TableWidget(QWidget *parent) : QTableWidget(parent) { } void TableWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { event->accept(); emit enterPressed(); } else if (event->key() == Qt::Key_Delete) { event->accept(); emit deletePressed(); } else QTableWidget::keyPressEvent(event); } CoreInfoLabel::CoreInfoLabel(QString text, QWidget *parent) : QLabel(text, parent) { setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); } CoreInfoDialog::CoreInfoDialog(MainWindow *mainwindow, QWidget *parent) : QDialog(parent) ,m_formLayout(new QFormLayout()) ,m_mainwindow(mainwindow) { QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION)); m_formLayout->setFormAlignment(Qt::AlignCenter); m_formLayout->setLabelAlignment(Qt::AlignCenter); setLayout(new QVBoxLayout()); qobject_cast(layout())->addLayout(m_formLayout); layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); layout()->addWidget(buttonBox); } void CoreInfoDialog::showCoreInfo() { int row = 0; int rowCount = m_formLayout->rowCount(); int i = 0; QVector > infoList = m_mainwindow->getCoreInfo(); if (rowCount > 0) { for (row = 0; row < rowCount; row++) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) /* removeRow() and takeRow() was only added in 5.8! */ m_formLayout->removeRow(0); #else /* something is buggy here... sometimes items appear duplicated, and other times not */ QLayoutItem *item = m_formLayout->itemAt(0); QWidget *w = NULL; if (item) { w = item->widget(); if (w) { QWidget *label = m_formLayout->labelForField(w); if (label) delete label; m_formLayout->removeWidget(w); delete w; } } #endif } } if (infoList.count() == 0) return; for (i = 0; i < infoList.count(); i++) { const QHash &line = infoList.at(i); QLabel *label = new QLabel(line.value("key")); CoreInfoLabel *value = new CoreInfoLabel(line.value("value")); QString labelStyle = line.value("label_style"); QString valueStyle = line.value("value_style"); if (!labelStyle.isEmpty()) label->setStyleSheet(labelStyle); if (!valueStyle.isEmpty()) value->setStyleSheet(valueStyle); m_formLayout->addRow(label, value); } show(); } PlaylistEntryDialog::PlaylistEntryDialog(MainWindow *mainwindow, QWidget *parent) : QDialog(parent) ,m_mainwindow(mainwindow) ,m_settings(mainwindow->settings()) ,m_nameLineEdit(new QLineEdit(this)) ,m_pathLineEdit(new QLineEdit(this)) ,m_coreComboBox(new QComboBox(this)) ,m_databaseComboBox(new QComboBox(this)) { QFormLayout *form = new QFormLayout(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QVBoxLayout *databaseVBoxLayout = new QVBoxLayout(); QHBoxLayout *pathHBoxLayout = new QHBoxLayout(); QLabel *databaseLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS), this); QToolButton *pathPushButton = new QToolButton(this); pathPushButton->setText("..."); pathHBoxLayout->addWidget(m_pathLineEdit); pathHBoxLayout->addWidget(pathPushButton); databaseVBoxLayout->addWidget(m_databaseComboBox); databaseVBoxLayout->addWidget(databaseLabel); setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY)); form->setFormAlignment(Qt::AlignCenter); form->setLabelAlignment(Qt::AlignCenter); setLayout(new QVBoxLayout(this)); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(this, SIGNAL(accepted()), this, SLOT(onAccepted())); connect(this, SIGNAL(rejected()), this, SLOT(onRejected())); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_NAME), m_nameLineEdit); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_PATH), pathHBoxLayout); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE), m_coreComboBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE), databaseVBoxLayout); qobject_cast(layout())->addLayout(form); layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); layout()->addWidget(buttonBox); connect(pathPushButton, SIGNAL(clicked()), this, SLOT(onPathClicked())); } void PlaylistEntryDialog::onPathClicked() { QString filePath = QFileDialog::getOpenFileName(this); if (filePath.isEmpty()) return; m_pathLineEdit->setText(filePath); } void PlaylistEntryDialog::loadPlaylistOptions() { core_info_list_t *core_info_list = NULL; const core_info_t *core_info = NULL; unsigned i = 0; int j = 0; m_nameLineEdit->clear(); m_pathLineEdit->clear(); m_coreComboBox->clear(); m_databaseComboBox->clear(); m_coreComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK)); m_databaseComboBox->addItem(QString("<") + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE) + ">", QFileInfo(m_mainwindow->getCurrentPlaylistPath()).fileName().remove(file_path_str(FILE_PATH_LPL_EXTENSION))); core_info_get_list(&core_info_list); if (core_info_list && core_info_list->count > 0) { QVector > allCores; QStringList allDatabases; for (i = 0; i < core_info_list->count; i++) { const core_info_t *core = &core_info_list->list[i]; QStringList databases = QString(core->databases).split("|"); QHash hash; QString ui_display_name; hash["core_name"] = core->core_name; hash["core_display_name"] = core->display_name; hash["core_path"] = core->path; hash["core_databases"] = core->databases; ui_display_name = hash.value("core_name"); if (ui_display_name.isEmpty()) ui_display_name = hash.value("core_display_name"); if (ui_display_name.isEmpty()) ui_display_name = QFileInfo(hash.value("core_path")).fileName(); if (ui_display_name.isEmpty()) continue; hash["ui_display_name"] = ui_display_name; for (j = 0; j < databases.count(); j++) { QString database = databases.at(j); if (database.isEmpty()) continue; if (!allDatabases.contains(database)) allDatabases.append(database); } if (!allCores.contains(hash)) allCores.append(hash); } std::sort(allCores.begin(), allCores.end(), comp_hash_ui_display_name_key_lower); std::sort(allDatabases.begin(), allDatabases.end(), comp_string_lower); for (j = 0; j < allCores.count(); j++) { const QHash &hash = allCores.at(j); m_coreComboBox->addItem(hash.value("ui_display_name"), QVariant::fromValue(hash)); } for (j = 0; j < allDatabases.count(); j++) { QString database = allDatabases.at(j); m_databaseComboBox->addItem(database, database); } } } void PlaylistEntryDialog::setEntryValues(const QHash &contentHash) { QString db; QString coreName = contentHash.value("core_name"); int foundDB = 0; int i = 0; loadPlaylistOptions(); if (contentHash.isEmpty()) { m_nameLineEdit->setText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE)); m_pathLineEdit->setText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE)); m_nameLineEdit->setEnabled(false); m_pathLineEdit->setEnabled(false); } else { m_nameLineEdit->setEnabled(true); m_pathLineEdit->setEnabled(true); m_nameLineEdit->setText(contentHash.value("label")); m_pathLineEdit->setText(contentHash.value("path")); } for (i = 0; i < m_coreComboBox->count(); i++) { const QHash hash = m_coreComboBox->itemData(i, Qt::UserRole).value >(); if (hash.isEmpty() || coreName.isEmpty()) continue; if (hash.value("core_name") == coreName) { m_coreComboBox->setCurrentIndex(i); break; } } db = contentHash.value("db_name"); if (!db.isEmpty()) { foundDB = m_databaseComboBox->findText(db); if (foundDB >= 0) m_databaseComboBox->setCurrentIndex(foundDB); } } const QHash PlaylistEntryDialog::getSelectedCore() { return m_coreComboBox->currentData(Qt::UserRole).value >(); } const QString PlaylistEntryDialog::getSelectedName() { return m_nameLineEdit->text(); } const QString PlaylistEntryDialog::getSelectedPath() { return m_pathLineEdit->text(); } const QString PlaylistEntryDialog::getSelectedDatabase() { return m_databaseComboBox->currentData(Qt::UserRole).toString(); } void PlaylistEntryDialog::onAccepted() { } void PlaylistEntryDialog::onRejected() { } bool PlaylistEntryDialog::showDialog(const QHash &hash) { loadPlaylistOptions(); setEntryValues(hash); if (exec() == QDialog::Accepted) return true; return false; } void PlaylistEntryDialog::hideDialog() { reject(); } ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : QDialog(parent) ,m_mainwindow(mainwindow) ,m_settings(mainwindow->settings()) ,m_saveGeometryCheckBox(new QCheckBox(this)) ,m_saveDockPositionsCheckBox(new QCheckBox(this)) ,m_saveLastTabCheckBox(new QCheckBox(this)) ,m_showHiddenFilesCheckBox(new QCheckBox(this)) ,m_themeComboBox(new QComboBox(this)) ,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this)) ,m_highlightColor() ,m_highlightColorLabel(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR), this)) ,m_customThemePath() ,m_suggestLoadedCoreFirstCheckBox(new QCheckBox(this)) ,m_allPlaylistsListMaxCountSpinBox(new QSpinBox(this)) ,m_allPlaylistsGridMaxCountSpinBox(new QSpinBox(this)) { QFormLayout *form = new QFormLayout(); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE)); m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT), MainWindow::THEME_SYSTEM_DEFAULT); m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK); m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM); m_allPlaylistsListMaxCountSpinBox->setRange(0, 99999); m_allPlaylistsGridMaxCountSpinBox->setRange(0, 99999); form->setFormAlignment(Qt::AlignCenter); form->setLabelAlignment(Qt::AlignCenter); setLayout(new QVBoxLayout(this)); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(this, SIGNAL(accepted()), this, SLOT(onAccepted())); connect(this, SIGNAL(rejected()), this, SLOT(onRejected())); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY), m_saveGeometryCheckBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS), m_saveDockPositionsCheckBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB), m_saveLastTabCheckBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SHOW_HIDDEN_FILES), m_showHiddenFilesCheckBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST), m_suggestLoadedCoreFirstCheckBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT), m_allPlaylistsListMaxCountSpinBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT), m_allPlaylistsGridMaxCountSpinBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox); form->addRow(m_highlightColorLabel, m_highlightColorPushButton); qobject_cast(layout())->addLayout(form); layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); layout()->addWidget(buttonBox); loadViewOptions(); connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose())); } void ViewOptionsDialog::onThemeComboBoxIndexChanged(int) { MainWindow::Theme theme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); if (theme == MainWindow::THEME_CUSTOM) { QString filePath = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME)); if (filePath.isEmpty()) { int oldThemeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString())); if (m_themeComboBox->count() > oldThemeIndex) { disconnect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); m_themeComboBox->setCurrentIndex(oldThemeIndex); connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); } } else { m_customThemePath = filePath; if (m_mainwindow->setCustomThemeFile(filePath)) m_mainwindow->setTheme(theme); } } else m_mainwindow->setTheme(theme); showOrHideHighlightColor(); } void ViewOptionsDialog::onHighlightColorChoose() { QPixmap highlightPixmap(m_highlightColorPushButton->iconSize()); QColor currentHighlightColor = m_settings->value("highlight_color", QApplication::palette().highlight().color()).value(); QColor newHighlightColor = QColorDialog::getColor(currentHighlightColor, this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR)); if (newHighlightColor.isValid()) { MainWindow::Theme theme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); m_highlightColor = newHighlightColor; m_settings->setValue("highlight_color", m_highlightColor); highlightPixmap.fill(m_highlightColor); m_highlightColorPushButton->setIcon(highlightPixmap); m_mainwindow->setTheme(theme); } } void ViewOptionsDialog::loadViewOptions() { QColor highlightColor = m_settings->value("highlight_color", QApplication::palette().highlight().color()).value(); QPixmap highlightPixmap(m_highlightColorPushButton->iconSize()); int themeIndex = 0; m_saveGeometryCheckBox->setChecked(m_settings->value("save_geometry", false).toBool()); m_saveDockPositionsCheckBox->setChecked(m_settings->value("save_dock_positions", false).toBool()); m_saveLastTabCheckBox->setChecked(m_settings->value("save_last_tab", false).toBool()); m_showHiddenFilesCheckBox->setChecked(m_settings->value("show_hidden_files", true).toBool()); m_suggestLoadedCoreFirstCheckBox->setChecked(m_settings->value("suggest_loaded_core_first", false).toBool()); m_allPlaylistsListMaxCountSpinBox->setValue(m_settings->value("all_playlists_list_max_count", 0).toInt()); m_allPlaylistsGridMaxCountSpinBox->setValue(m_settings->value("all_playlists_grid_max_count", 5000).toInt()); themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString())); if (m_themeComboBox->count() > themeIndex) m_themeComboBox->setCurrentIndex(themeIndex); if (highlightColor.isValid()) { m_highlightColor = highlightColor; highlightPixmap.fill(m_highlightColor); m_highlightColorPushButton->setIcon(highlightPixmap); } showOrHideHighlightColor(); } void ViewOptionsDialog::showOrHideHighlightColor() { if (m_mainwindow->theme() == MainWindow::THEME_DARK) { m_highlightColorLabel->show(); m_highlightColorPushButton->show(); } else { m_highlightColorLabel->hide(); m_highlightColorPushButton->hide(); } } void ViewOptionsDialog::saveViewOptions() { m_settings->setValue("save_geometry", m_saveGeometryCheckBox->isChecked()); m_settings->setValue("save_dock_positions", m_saveDockPositionsCheckBox->isChecked()); m_settings->setValue("save_last_tab", m_saveLastTabCheckBox->isChecked()); m_settings->setValue("theme", m_mainwindow->getThemeString(static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()))); m_settings->setValue("show_hidden_files", m_showHiddenFilesCheckBox->isChecked()); m_settings->setValue("highlight_color", m_highlightColor); m_settings->setValue("suggest_loaded_core_first", m_suggestLoadedCoreFirstCheckBox->isChecked()); m_settings->setValue("all_playlists_list_max_count", m_allPlaylistsListMaxCountSpinBox->value()); m_settings->setValue("all_playlists_grid_max_count", m_allPlaylistsGridMaxCountSpinBox->value()); if (!m_mainwindow->customThemeString().isEmpty()) m_settings->setValue("custom_theme", m_customThemePath); m_mainwindow->setAllPlaylistsListMaxCount(m_allPlaylistsListMaxCountSpinBox->value()); m_mainwindow->setAllPlaylistsGridMaxCount(m_allPlaylistsGridMaxCountSpinBox->value()); } void ViewOptionsDialog::onAccepted() { MainWindow::Theme newTheme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); m_mainwindow->setTheme(newTheme); saveViewOptions(); } void ViewOptionsDialog::onRejected() { loadViewOptions(); } void ViewOptionsDialog::showDialog() { loadViewOptions(); show(); } void ViewOptionsDialog::hideDialog() { reject(); } CoreInfoWidget::CoreInfoWidget(CoreInfoLabel *label, QWidget *parent) : QWidget(parent) ,m_label(label) ,m_scrollArea(new QScrollArea(this)) { //m_scrollArea->setFrameShape(QFrame::NoFrame); 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()); } 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_listWidget(new QListWidget(this)) ,m_tableWidget(new TableWidget(this)) ,m_searchWidget(new QWidget(this)) ,m_searchLineEdit(new QLineEdit(this)) ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_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_fileSanitizerRegex("[&*/:`<>?\\|]") ,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 QWidget(this)) ,m_logTextEdit(new LogTextEdit(m_logWidget)) ,m_imageFormats() ,m_historyPlaylistsItem(NULL) ,m_folderIcon() ,m_customThemeString() ,m_gridLayout(NULL) ,m_gridWidget(new QWidget(this)) ,m_gridScrollArea(new QScrollArea(m_gridWidget)) ,m_gridItems() ,m_gridLayoutWidget(new FileDropWidget()) ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) ,m_pendingItemUpdates() ,m_viewType(VIEW_TYPE_LIST) ,m_gridProgressBar(NULL) ,m_gridProgressWidget(NULL) ,m_currentGridHash() ,m_lastViewType(m_viewType) ,m_currentGridWidget(NULL) ,m_allPlaylistsListMaxCount(0) ,m_allPlaylistsGridMaxCount(0) ,m_playlistEntryDialog(NULL) ,m_statusMessageElapsedTimer() ,m_networkManager(new QNetworkAccessManager(this)) ,m_updateProgressDialog(new QProgressDialog()) ,m_updateFile() ,m_updateReply() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); QToolButton *searchResetButton = NULL; QWidget *zoomWidget = new QWidget(); QHBoxLayout *zoomLayout = new QHBoxLayout(); QLabel *zoomLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ZOOM), zoomWidget); QPushButton *viewTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW), zoomWidget); QMenu *viewTypeMenu = new QMenu(viewTypePushButton); QAction *viewTypeIconsAction = NULL; QAction *viewTypeListAction = NULL; QHBoxLayout *gridProgressLayout = new QHBoxLayout(); QLabel *gridProgressLabel = NULL; QHBoxLayout *gridFooterLayout = NULL; int i = 0; qRegisterMetaType >("ThumbnailWidget"); m_updateProgressDialog->cancel(); m_gridProgressWidget = new QWidget(); gridProgressLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PROGRESS), m_gridProgressWidget); 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, 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_gridWidget->setLayout(new QVBoxLayout()); m_gridLayout = new FlowLayout(m_gridLayoutWidget); m_gridScrollArea->setAlignment(Qt::AlignCenter); m_gridScrollArea->setFrameShape(QFrame::NoFrame); m_gridScrollArea->setWidgetResizable(true); m_gridScrollArea->setWidget(m_gridLayoutWidget); m_gridWidget->layout()->addWidget(m_gridScrollArea); m_gridWidget->layout()->setAlignment(Qt::AlignCenter); m_gridWidget->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_gridWidget->layout()->addWidget(m_gridProgressWidget); zoomWidget->setLayout(zoomLayout); zoomLayout->setContentsMargins(0, 0, 0, 0); zoomLayout->addWidget(zoomLabel); zoomLayout->addWidget(m_zoomSlider); zoomLayout->addWidget(viewTypePushButton); gridFooterLayout = new QHBoxLayout(); gridFooterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); gridFooterLayout->addWidget(m_gridProgressWidget); gridFooterLayout->addWidget(zoomWidget); static_cast(m_gridWidget->layout())->addLayout(gridFooterLayout); m_gridProgressWidget->hide(); m_tableWidget->setAlternatingRowColors(true); m_logWidget->setObjectName("logWidget"); m_folderIcon = QIcon(QString(settings->paths.directory_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))); #if defined(Q_OS_WIN) m_dirModel->setRootPath(""); #else m_dirModel->setRootPath("/"); #endif m_dirTree->setModel(m_dirModel); m_dirTree->setSelectionMode(QAbstractItemView::SingleSelection); if (m_dirModel->columnCount() > 3) { /* size */ m_dirTree->hideColumn(1); /* type */ m_dirTree->hideColumn(2); /* date modified */ m_dirTree->hideColumn(3); } m_dirTree->setCurrentIndex(m_dirModel->index(settings->paths.directory_menu_content)); m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); m_dirTree->expand(m_dirTree->currentIndex()); 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); 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_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); m_gridLayoutWidget->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_tableWidget, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(onCurrentTableItemChanged(QTableWidgetItem*, QTableWidgetItem*))); connect(m_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onContentItemDoubleClicked(QTableWidgetItem*))); connect(m_tableWidget, SIGNAL(enterPressed()), this, SLOT(onTableWidgetEnterPressed())); connect(m_tableWidget, SIGNAL(deletePressed()), this, SLOT(onTableWidgetDeletePressed())); 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(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked())); connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked())); connect(m_gridLayoutWidget, SIGNAL(filesDropped(QStringList)), this, SLOT(onPlaylistFilesDropped(QStringList))); connect(m_gridLayoutWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileDropWidgetContextMenuRequested(const QPoint&))); /* 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())); connect(this, SIGNAL(showErrorMessageDeferred(QString)), this, SLOT(onShowErrorMessage(QString)), Qt::QueuedConnection); connect(this, SIGNAL(extractArchiveDeferred(QString)), this, SLOT(onExtractArchive(QString)), 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 removeUpdateTempFiles(); } MainWindow::~MainWindow() { if (m_thumbnailPixmap) delete m_thumbnailPixmap; if (m_thumbnailPixmap2) delete m_thumbnailPixmap2; if (m_thumbnailPixmap3) delete m_thumbnailPixmap3; removeGridItems(); } void MainWindow::removeUpdateTempFiles() { /* a QDir with no path means the current working directory */ QDir dir; QStringList dirList = dir.entryList(QStringList(), QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System, QDir::Name); int i; for (i = 0; i < dirList.count(); i++) { QString path(dir.path() + "/" + dirList.at(i)); QFile file(path); if (path.endsWith(TEMP_EXTENSION)) { QByteArray pathArray = path.toUtf8(); const char *pathData = pathArray.constData(); if (file.remove()) RARCH_LOG("[Qt]: removed temporary update file %s\n", pathData); else RARCH_LOG("[Qt]: could not remove temporary update file %s\n", pathData); } } } void MainWindow::onPlaylistFilesDropped(QStringList files) { addFilesToPlaylist(files); } /* Takes a list of files and folders and adds them to the currently selected playlist. Folders will have their contents added recursively. */ void MainWindow::addFilesToPlaylist(QStringList files) { QStringList list; QString currentPlaylistPath; QListWidgetItem *currentItem = m_listWidget->currentItem(); QByteArray currentPlaylistArray; QScopedPointer dialog(NULL); PlaylistEntryDialog *playlistDialog = playlistEntryDialog(); QHash selectedCore; QHash itemToAdd; QString selectedDatabase; QString selectedName; QString selectedPath; const char *currentPlaylistData = NULL; playlist_t *playlist = NULL; int i; /* Assume a blank list means we will manually enter in all fields. */ if (files.isEmpty()) { /* Make sure hash isn't blank, that would mean there's multiple entries to add at once. */ itemToAdd["label"] = ""; itemToAdd["path"] = ""; } else if (files.count() == 1) { QString path = files.at(0); QFileInfo info(path); if (info.isFile()) { itemToAdd["label"] = info.completeBaseName(); itemToAdd["path"] = path; } } if (currentItem) { currentPlaylistPath = currentItem->data(Qt::UserRole).toString(); if (!currentPlaylistPath.isEmpty()) { currentPlaylistArray = currentPlaylistPath.toUtf8(); currentPlaylistData = currentPlaylistArray.constData(); } } if (currentPlaylistPath == ALL_PLAYLISTS_TOKEN) { showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CANNOT_ADD_TO_ALL_PLAYLISTS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); return; } /* a blank itemToAdd means there will be multiple */ if (!playlistDialog->showDialog(itemToAdd)) return; selectedName = m_playlistEntryDialog->getSelectedName(); selectedPath = m_playlistEntryDialog->getSelectedPath(); selectedCore = m_playlistEntryDialog->getSelectedCore(); selectedDatabase = m_playlistEntryDialog->getSelectedDatabase(); if (selectedDatabase.isEmpty()) selectedDatabase = QFileInfo(currentPlaylistPath).fileName(); else selectedDatabase += file_path_str(FILE_PATH_LPL_EXTENSION); dialog.reset(new QProgressDialog(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_GATHERING_LIST_OF_FILES), "Cancel", 0, 0, this)); dialog->setWindowModality(Qt::ApplicationModal); if (selectedName.isEmpty() || selectedPath.isEmpty() || selectedDatabase.isEmpty()) { showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLEASE_FILL_OUT_REQUIRED_FIELDS), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); return; } if (files.isEmpty()) files.append(selectedPath); for (i = 0; i < files.count(); i++) { QString path(files.at(i)); QFileInfo fileInfo(path); if (dialog->wasCanceled()) return; if (i % 25 == 0) { /* Needed to update progress dialog while doing a lot of stuff on the main thread. */ qApp->processEvents(); } if (fileInfo.isDir()) { QDir dir(path); addDirectoryFilesToList(list, dir); continue; } if (fileInfo.isFile()) list.append(fileInfo.absoluteFilePath()); else if (files.count() == 1) { /* If adding a single file, tell user that it doesn't exist. */ showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); return; } } dialog->setLabelText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADDING_FILES_TO_PLAYLIST)); dialog->setMaximum(list.count()); playlist = playlist_init(currentPlaylistData, COLLECTION_SIZE); for (i = 0; i < list.count(); i++) { QString fileName = list.at(i); QFileInfo fileInfo; QByteArray fileBaseNameArray; QByteArray pathArray; QByteArray corePathArray; QByteArray coreNameArray; QByteArray databaseArray; const char *pathData = NULL; const char *fileNameNoExten = NULL; const char *corePathData = NULL; const char *coreNameData = NULL; const char *databaseData = NULL; if (dialog->wasCanceled()) { playlist_free(playlist); return; } if (fileName.isEmpty()) continue; fileInfo = fileName; if (files.count() == 1 && list.count() == 1 && i == 0) { fileBaseNameArray = selectedName.toUtf8(); pathArray = QDir::toNativeSeparators(selectedPath).toUtf8(); } else { fileBaseNameArray = fileInfo.completeBaseName().toUtf8(); pathArray = QDir::toNativeSeparators(fileName).toUtf8(); } fileNameNoExten = fileBaseNameArray.constData(); /* a modal QProgressDialog calls processEvents() automatically in setValue() */ dialog->setValue(i + 1); pathData = pathArray.constData(); if (selectedCore.isEmpty()) { corePathData = "DETECT"; coreNameData = "DETECT"; } else { corePathArray = QDir::toNativeSeparators(selectedCore.value("core_path")).toUtf8(); coreNameArray = selectedCore.value("core_name").toUtf8(); corePathData = corePathArray.constData(); coreNameData = coreNameArray.constData(); } databaseArray = selectedDatabase.toUtf8(); databaseData = databaseArray.constData(); if (path_is_compressed_file(pathData)) { struct string_list *list = file_archive_get_file_list(pathData, NULL); if (list) { if (list->size == 1) { /* assume archives with one file should have that file loaded directly */ pathArray = QDir::toNativeSeparators(QString(pathData) + "#" + list->elems[0].data).toUtf8(); pathData = pathArray.constData(); } string_list_free(list); } } playlist_push(playlist, pathData, fileNameNoExten, corePathData, coreNameData, "00000000|crc", databaseData); } playlist_write_file(playlist); playlist_free(playlist); reloadPlaylists(); } void MainWindow::onGridItemClicked(ThumbnailWidget *widget) { QHash hash; ThumbnailWidget *w = static_cast(sender()); if (!w) { if (widget) w = widget; else return; } if (m_currentGridWidget) { m_currentGridWidget->setObjectName("thumbnailWidget"); //m_currentGridWidget->setFrameStyle(QFrame::Plain); m_currentGridWidget->style()->unpolish(m_currentGridWidget); m_currentGridWidget->style()->polish(m_currentGridWidget); } hash = w->property("hash").value >(); w->setObjectName("thumbnailWidgetSelected"); w->style()->unpolish(w); w->style()->polish(w); m_currentGridWidget = w; m_currentGridHash = hash; currentItemChanged(hash); } void MainWindow::onGridItemDoubleClicked() { QHash hash; ThumbnailWidget *w = static_cast(sender()); if (!w) return; hash = w->property("hash").value >(); loadContent(hash); } void MainWindow::onIconViewClicked() { setCurrentViewType(VIEW_TYPE_ICONS); onCurrentListItemChanged(m_listWidget->currentItem(), NULL); } void MainWindow::onListViewClicked() { setCurrentViewType(VIEW_TYPE_LIST); onCurrentListItemChanged(m_listWidget->currentItem(), NULL); } inline void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) { int newSize = 0; QLabel *label = NULL; if (zoomValue < 50) newSize = expScale(lerp(0, 49, 25, 49, zoomValue) / 50.0, 102, 256); else newSize = expScale(zoomValue / 100.0, 256, 1024); item->widget->setFixedSize(QSize(newSize, newSize)); label = item->widget->findChild("thumbnailQLabel"); if (label) setElidedText(label, item->widget, item->widget->layout()->contentsMargins().left() + item->widget->layout()->spacing() + 2, item->labelText); } void MainWindow::onZoomValueChanged(int value) { int i; for (i = 0; i < m_gridItems.count(); i++) { GridItem *item = m_gridItems.at(i); calcGridItemSize(item, value); } m_lastZoomSliderValue = value; } 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; } else { QFile file(filePath); if (file.exists()) { bool opened = file.open(QIODevice::ReadOnly); if (opened) { 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; } else 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_READ_OPEN_FAILED)); return false; } } 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) { QPointer msgBoxPtr; QMessageBox *msgBox = NULL; QCheckBox *checkBox = NULL; msgBoxPtr = new QMessageBox(this); 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; } bool MainWindow::updateCurrentPlaylistEntry(const QHash &contentHash) { QString playlistPath = getCurrentPlaylistPath(); QString path; QString label; QString corePath; QString coreName; QString dbName; QString crc32; QByteArray playlistPathArray; QByteArray pathArray; QByteArray labelArray; QByteArray corePathArray; QByteArray coreNameArray; QByteArray dbNameArray; QByteArray crc32Array; const char *playlistPathData = NULL; const char *pathData = NULL; const char *labelData = NULL; const char *corePathData = NULL; const char *coreNameData = NULL; const char *dbNameData = NULL; const char *crc32Data = NULL; playlist_t *playlist = NULL; unsigned index = 0; bool ok = false; if (playlistPath.isEmpty() || contentHash.isEmpty() || !contentHash.contains("index")) return false; index = contentHash.value("index").toUInt(&ok); if (!ok) return false; path = contentHash.value("path"); label = contentHash.value("label"); coreName = contentHash.value("core_name"); corePath = contentHash.value("core_path"); dbName = contentHash.value("db_name"); crc32 = contentHash.value("crc32"); if (path.isEmpty() || label.isEmpty() || coreName.isEmpty() || corePath.isEmpty() || dbName.isEmpty() || crc32.isEmpty() ) return false; playlistPathArray = playlistPath.toUtf8(); pathArray = QDir::toNativeSeparators(path).toUtf8(); labelArray = label.toUtf8(); coreNameArray = coreName.toUtf8(); corePathArray = QDir::toNativeSeparators(corePath).toUtf8(); dbNameArray = (dbName + file_path_str(FILE_PATH_LPL_EXTENSION)).toUtf8(); crc32Array = crc32.toUtf8(); playlistPathData = playlistPathArray.constData(); pathData = pathArray.constData(); labelData = labelArray.constData(); coreNameData = coreNameArray.constData(); corePathData = corePathArray.constData(); dbNameData = dbNameArray.constData(); crc32Data = crc32Array.constData(); if (path_is_compressed_file(pathData)) { struct string_list *list = file_archive_get_file_list(pathData, NULL); if (list) { if (list->size == 1) { /* assume archives with one file should have that file loaded directly */ pathArray = QDir::toNativeSeparators(QString(pathData) + "#" + list->elems[0].data).toUtf8(); pathData = pathArray.constData(); } string_list_free(list); } } playlist = playlist_init(playlistPathData, COLLECTION_SIZE); playlist_update(playlist, index, pathData, labelData, corePathData, coreNameData, crc32Data, dbNameData); playlist_write_file(playlist); playlist_free(playlist); reloadPlaylists(); return true; } void MainWindow::onFileDropWidgetContextMenuRequested(const QPoint &pos) { QScopedPointer menu; QScopedPointer addEntryAction; QScopedPointer addFilesAction; QScopedPointer addFolderAction; QScopedPointer editAction; QScopedPointer deleteAction; QPointer selectedAction; QPoint cursorPos = QCursor::pos(); QHash contentHash = getCurrentContentHash(); menu.reset(new QMenu(this)); addEntryAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_ENTRY)), this)); addFilesAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FILES)), this)); addFolderAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FOLDER)), this)); editAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_EDIT)), this)); deleteAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DELETE)), this)); menu->addAction(addEntryAction.data()); menu->addAction(addFilesAction.data()); menu->addAction(addFolderAction.data()); if (!contentHash.isEmpty()) { menu->addAction(editAction.data()); menu->addAction(deleteAction.data()); } selectedAction = menu->exec(cursorPos); if (!selectedAction) return; if (selectedAction == addFilesAction.data()) { QStringList filePaths = QFileDialog::getOpenFileNames(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FILES)); if (!filePaths.isEmpty()) addFilesToPlaylist(filePaths); } else if (selectedAction == addEntryAction.data()) { addFilesToPlaylist(QStringList()); } else if (selectedAction == addFolderAction.data()) { QString dirPath = QFileDialog::getExistingDirectory(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FOLDER), QString(), QFileDialog::ShowDirsOnly); if (!dirPath.isEmpty()) addFilesToPlaylist(QStringList() << dirPath); } else if (selectedAction == editAction.data()) { PlaylistEntryDialog *playlistDialog = playlistEntryDialog(); QHash selectedCore; QString selectedDatabase; QString selectedName; QString selectedPath; QString currentPlaylistPath = getCurrentPlaylistPath(); if (!playlistDialog->showDialog(contentHash)) return; selectedName = m_playlistEntryDialog->getSelectedName(); selectedPath = m_playlistEntryDialog->getSelectedPath(); selectedCore = m_playlistEntryDialog->getSelectedCore(); selectedDatabase = m_playlistEntryDialog->getSelectedDatabase(); if (selectedCore.isEmpty()) { selectedCore["core_name"] = "DETECT"; selectedCore["core_path"] = "DETECT"; } if (selectedDatabase.isEmpty()) { selectedDatabase = QFileInfo(currentPlaylistPath).fileName().remove(file_path_str(FILE_PATH_LPL_EXTENSION)); } contentHash["label"] = selectedName; contentHash["path"] = selectedPath; contentHash["core_name"] = selectedCore.value("core_name"); contentHash["core_path"] = selectedCore.value("core_path"); contentHash["db_name"] = selectedDatabase; if (!updateCurrentPlaylistEntry(contentHash)) { showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_UPDATE_PLAYLIST_ENTRY), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); return; } } else if (selectedAction == deleteAction.data()) { deleteCurrentPlaylistItem(); } } void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) { settings_t *settings = config_get_ptr(); QScopedPointer menu; QScopedPointer associateMenu; QScopedPointer hiddenPlaylistsMenu; QScopedPointer hideAction; QScopedPointer newPlaylistAction; QScopedPointer deletePlaylistAction; QPointer selectedAction; QPoint cursorPos = QCursor::pos(); QListWidgetItem *selectedItem = m_listWidget->itemAt(m_listWidget->viewport()->mapFromGlobal(cursorPos)); QDir playlistDir(settings->paths.directory_playlist); QString playlistDirAbsPath = playlistDir.absolutePath(); QString currentPlaylistDirPath; QString currentPlaylistPath; QString currentPlaylistFileName; QFile currentPlaylistFile; QByteArray currentPlaylistFileNameArray; QFileInfo currentPlaylistFileInfo; QMap coreList; core_info_list_t *core_info_list = NULL; union string_list_elem_attr attr = {0}; struct string_list *stnames = NULL; struct string_list *stcores = NULL; unsigned i = 0; int j = 0; size_t found = 0; const char *currentPlaylistFileNameData = NULL; char new_playlist_names[PATH_MAX_LENGTH]; char new_playlist_cores[PATH_MAX_LENGTH]; bool specialPlaylist = false; bool foundHiddenPlaylist = false; new_playlist_names[0] = new_playlist_cores[0] = '\0'; stnames = string_split(settings->arrays.playlist_names, ";"); stcores = string_split(settings->arrays.playlist_cores, ";"); if (selectedItem) { currentPlaylistPath = selectedItem->data(Qt::UserRole).toString(); currentPlaylistFile.setFileName(currentPlaylistPath); currentPlaylistFileInfo = QFileInfo(currentPlaylistPath); currentPlaylistFileName = currentPlaylistFileInfo.fileName(); currentPlaylistDirPath = currentPlaylistFileInfo.absoluteDir().absolutePath(); currentPlaylistFileNameArray.append(currentPlaylistFileName); currentPlaylistFileNameData = currentPlaylistFileNameArray.constData(); } menu.reset(new QMenu(this)); menu->setObjectName("menu"); hiddenPlaylistsMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDDEN_PLAYLISTS), this)); newPlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST)) + "...", this)); hiddenPlaylistsMenu->setObjectName("hiddenPlaylistsMenu"); menu->addAction(newPlaylistAction.data()); if (currentPlaylistFile.exists()) { deletePlaylistAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DELETE_PLAYLIST)) + "...", this)); menu->addAction(deletePlaylistAction.data()); } if (selectedItem) { hideAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_HIDE), this)); menu->addAction(hideAction.data()); } for (j = 0; j < m_listWidget->count(); j++) { QListWidgetItem *item = m_listWidget->item(j); bool hidden = m_listWidget->isItemHidden(item); if (hidden) { QAction *action = hiddenPlaylistsMenu->addAction(item->text()); action->setProperty("row", j); action->setProperty("core_path", item->data(Qt::UserRole).toString()); foundHiddenPlaylist = true; } } if (!foundHiddenPlaylist) { QAction *action = hiddenPlaylistsMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); action->setProperty("row", -1); } menu->addMenu(hiddenPlaylistsMenu.data()); if (currentPlaylistDirPath != playlistDirAbsPath) { /* special playlists like history etc. can't have an association */ specialPlaylist = true; } if (!specialPlaylist) { associateMenu.reset(new QMenu(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ASSOCIATE_CORE), this)); associateMenu->setObjectName("associateMenu"); core_info_get_list(&core_info_list); for (i = 0; i < core_info_list->count && core_info_list->count > 0; i++) { const core_info_t *core = &core_info_list->list[i]; coreList[core->core_name] = core; } { QMapIterator coreListIterator(coreList); while (coreListIterator.hasNext()) { QString key; const core_info_t *core = NULL; QAction *action = NULL; coreListIterator.next(); key = coreListIterator.key(); core = coreList.value(key); action = associateMenu->addAction(core->core_name); action->setProperty("core_path", core->path); } } menu->addMenu(associateMenu.data()); } selectedAction = menu->exec(cursorPos); if (!selectedAction) goto end; if (!specialPlaylist && selectedAction->parent() == associateMenu.data()) { found = string_list_find_elem(stnames, currentPlaylistFileNameData); if (found) string_list_set(stcores, static_cast(found - 1), selectedAction->property("core_path").toString().toUtf8().constData()); else { string_list_append(stnames, currentPlaylistFileNameData, attr); string_list_append(stcores, "DETECT", attr); found = string_list_find_elem(stnames, currentPlaylistFileNameData); if (found) string_list_set(stcores, static_cast(found - 1), selectedAction->property("core_path").toString().toUtf8().constData()); } string_list_join_concat(new_playlist_names, sizeof(new_playlist_names), stnames, ";"); string_list_join_concat(new_playlist_cores, sizeof(new_playlist_cores), stcores, ";"); strlcpy(settings->arrays.playlist_names, new_playlist_names, sizeof(settings->arrays.playlist_names)); strlcpy(settings->arrays.playlist_cores, new_playlist_cores, sizeof(settings->arrays.playlist_cores)); } else if (selectedAction == deletePlaylistAction.data()) { if (currentPlaylistFile.exists()) { if (showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST)).arg(selectedItem->text()), MainWindow::MSGBOX_TYPE_QUESTION_YESNO, Qt::ApplicationModal, false)) { if (currentPlaylistFile.remove()) reloadPlaylists(); else showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_DELETE_FILE), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); } } } else if (selectedAction == newPlaylistAction.data()) { QString name = QInputDialog::getText(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NEW_PLAYLIST), msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ENTER_NEW_PLAYLIST_NAME)); QString newPlaylistPath = playlistDirAbsPath + "/" + name + file_path_str(FILE_PATH_LPL_EXTENSION); QFile file(newPlaylistPath); if (file.open(QIODevice::WriteOnly)) file.close(); reloadPlaylists(); } else if (selectedAction == hideAction.data()) { int row = m_listWidget->row(selectedItem); if (row >= 0) { QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); if (!hiddenPlaylists.contains(currentPlaylistFileName)) { hiddenPlaylists.append(currentPlaylistFileName); m_settings->setValue("hidden_playlists", hiddenPlaylists); } m_listWidget->setRowHidden(row, true); } } else if (selectedAction->parent() == hiddenPlaylistsMenu.data()) { QVariant rowVariant = selectedAction->property("row"); if (rowVariant.isValid()) { QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); int row = rowVariant.toInt(); if (row >= 0) { QString playlistPath = selectedAction->property("core_path").toString(); QFileInfo playlistFileInfo(playlistPath); QString playlistFileName = playlistFileInfo.fileName(); if (hiddenPlaylists.contains(playlistFileName)) { hiddenPlaylists.removeOne(playlistFileName); m_settings->setValue("hidden_playlists", hiddenPlaylists); } m_listWidget->setRowHidden(row, false); } } } setCoreActions(); end: if (stnames) string_list_free(stnames); if (stcores) string_list_free(stcores); } void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&) { #ifdef HAVE_LIBRETRODB QPointer action; QList actions; QScopedPointer scanAction; QDir dir; QString currentDirString = m_dirModel->filePath(m_dirTree->currentIndex()); settings_t *settings = config_get_ptr(); QByteArray dirArray; const char *fullpath = NULL; 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( settings->paths.directory_playlist, settings->paths.path_content_database, 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::deferReloadPlaylists() { emit gotReloadPlaylists(); } void MainWindow::onGotReloadPlaylists() { reloadPlaylists(); } void MainWindow::reloadPlaylists() { QListWidgetItem *allPlaylistsItem = NULL; QListWidgetItem *favoritesPlaylistsItem = NULL; QListWidgetItem *imagePlaylistsItem = NULL; QListWidgetItem *musicPlaylistsItem = NULL; QListWidgetItem *videoPlaylistsItem = NULL; QListWidgetItem *firstItem = NULL; QListWidgetItem *currentItem = NULL; settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QString currentPlaylistPath; QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); int i = 0; currentItem = m_listWidget->currentItem(); if (currentItem) { currentPlaylistPath = currentItem->data(Qt::UserRole).toString(); } getPlaylistFiles(); m_listWidget->clear(); allPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ALL_PLAYLISTS)); allPlaylistsItem->setData(Qt::UserRole, ALL_PLAYLISTS_TOKEN); favoritesPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FAVORITES_TAB)); favoritesPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_favorites); m_historyPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_HISTORY_TAB)); m_historyPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_history); imagePlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_IMAGES_TAB)); imagePlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_image_history); musicPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MUSIC_TAB)); musicPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_music_history); videoPlaylistsItem = new QListWidgetItem(m_folderIcon, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_TAB)); videoPlaylistsItem->setData(Qt::UserRole, settings->paths.path_content_video_history); m_listWidget->addItem(allPlaylistsItem); m_listWidget->addItem(favoritesPlaylistsItem); m_listWidget->addItem(m_historyPlaylistsItem); m_listWidget->addItem(imagePlaylistsItem); m_listWidget->addItem(musicPlaylistsItem); m_listWidget->addItem(videoPlaylistsItem); if (hiddenPlaylists.contains(ALL_PLAYLISTS_TOKEN)) m_listWidget->setRowHidden(m_listWidget->row(allPlaylistsItem), true); if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_favorites).fileName())) m_listWidget->setRowHidden(m_listWidget->row(favoritesPlaylistsItem), true); if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(m_historyPlaylistsItem), true); if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_image_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(imagePlaylistsItem), true); if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_music_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(musicPlaylistsItem), true); if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true); for (i = 0; i < m_playlistFiles.count(); i++) { QListWidgetItem *item = NULL; const QString &file = m_playlistFiles.at(i); QString fileDisplayName = file; QString fileName = file; bool hasIcon = false; QIcon icon; QString iconPath; fileDisplayName.remove(file_path_str(FILE_PATH_LPL_EXTENSION)); iconPath = QString(settings->paths.directory_assets) + ICON_PATH + fileDisplayName + ".png"; hasIcon = QFile::exists(iconPath); if (hasIcon) icon = QIcon(iconPath); else icon = m_folderIcon; item = new QListWidgetItem(icon, fileDisplayName); item->setData(Qt::UserRole, playlistDir.absoluteFilePath(file)); m_listWidget->addItem(item); if (hiddenPlaylists.contains(fileName)) { int row = m_listWidget->row(item); if (row >= 0) m_listWidget->setRowHidden(row, true); } } if (m_listWidget->count() > 0) { firstItem = m_listWidget->item(0); if (firstItem) { bool found = false; for (i = 0; i < m_listWidget->count(); i++) { QListWidgetItem *item = m_listWidget->item(i); QString path; if (item) { path = item->data(Qt::UserRole).toString(); if (!currentPlaylistPath.isEmpty() && !path.isEmpty()) { if (path == currentPlaylistPath) { found = true; m_listWidget->setCurrentItem(item); break; } } } } /* the previous playlist must be gone now, just select the first one */ if (!found) m_listWidget->setCurrentItem(firstItem); } } } 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) { QVector > infoList = getCoreInfo(); QString coreInfoText; QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); CoreSelection 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; 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; } } QVector > MainWindow::getCoreInfo() { QVector > infoList; QHash currentCore = getSelectedCore(); core_info_list_t *core_info_list = NULL; const core_info_t *core_info = NULL; unsigned i = 0; core_info_get_list(&core_info_list); if (!core_info_list || core_info_list->count == 0) return infoList; for (i = 0; i < core_info_list->count; i++) { const core_info_t *core = &core_info_list->list[i]; if (currentCore["core_path"] == core->path) { core_info = core; break; } } 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 labelText = "(!) "; QString valueText; QHash hash; 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::selectBrowserDir(QString path) { QStringList horizontal_header_labels; QDir dir = path; QStringList dirList; int i = 0; horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_tableWidget->setSortingEnabled(false); m_tableWidget->setColumnCount(1); m_tableWidget->setRowCount(0); m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); dirList = dir.entryList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files | (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0)), QDir::Name); if (dirList.count() == 0) return; m_tableWidget->setRowCount(dirList.count()); for (i = 0; i < dirList.count(); i++) { QString fileName = dirList.at(i); QTableWidgetItem *item = new QTableWidgetItem(fileName); QHash hash; QString filePath(dir.absoluteFilePath(fileName)); QFileInfo fileInfo(filePath); hash["path"] = filePath; hash["label"] = hash["path"]; hash["label_noext"] = fileInfo.completeBaseName(); hash["db_name"] = fileInfo.dir().dirName(); item->setData(Qt::UserRole, QVariant::fromValue >(hash)); item->setFlags(item->flags() & ~Qt::ItemIsEditable); m_tableWidget->setItem(i, 0, item); } m_tableWidget->setSortingEnabled(true); m_tableWidget->resizeColumnsToContents(); m_tableWidget->sortByColumn(0, Qt::AscendingOrder); m_tableWidget->selectRow(0); onSearchEnterPressed(); } QTabWidget* MainWindow::browserAndPlaylistTabWidget() { return m_browserAndPlaylistTabWidget; } void MainWindow::onTableWidgetEnterPressed() { onRunClicked(); } void MainWindow::onTableWidgetDeletePressed() { deleteCurrentPlaylistItem(); } QString MainWindow::getCurrentPlaylistPath() { QListWidgetItem *playlistItem = m_listWidget->currentItem(); QHash contentHash; QString playlistPath; if (!playlistItem) return playlistPath; playlistPath = playlistItem->data(Qt::UserRole).toString(); return playlistPath; } QHash MainWindow::getCurrentContentHash() { QTableWidgetItem *contentItem = m_tableWidget->currentItem(); QListWidgetItem *playlistItem = m_listWidget->currentItem(); QHash contentHash; ViewType viewType = getCurrentViewType(); if (viewType == VIEW_TYPE_LIST) { if (!contentItem) return contentHash; contentHash = contentItem->data(Qt::UserRole).value >(); } else if (viewType == VIEW_TYPE_ICONS) contentHash = m_currentGridHash; return contentHash; } void MainWindow::deleteCurrentPlaylistItem() { QString playlistPath = getCurrentPlaylistPath(); QByteArray playlistArray; QHash contentHash = getCurrentContentHash(); playlist_t *playlist = NULL; const char *playlistData = NULL; unsigned index = 0; bool ok = false; if (playlistPath.isEmpty()) return; if (contentHash.isEmpty()) return; playlistArray = playlistPath.toUtf8(); playlistData = playlistArray.constData(); index = contentHash.value("index").toUInt(&ok); if (!ok) return; if (!showMessageBox(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CONFIRM_DELETE_PLAYLIST_ITEM)).arg(contentHash["label"]), MainWindow::MSGBOX_TYPE_QUESTION_YESNO, Qt::ApplicationModal, false)) return; playlist = playlist_init(playlistData, COLLECTION_SIZE); playlist_delete_index(playlist, index); playlist_write_file(playlist); playlist_free(playlist); reloadPlaylists(); } void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) { 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(); CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); QHash coreHash; QHash contentHash; QTableWidgetItem *contentItem = m_tableWidget->currentItem(); ViewType viewType = getCurrentViewType(); if (viewType == VIEW_TYPE_LIST && contentItem) contentHash = contentItem->data(Qt::UserRole).value >(); else if (viewType == VIEW_TYPE_ICONS) contentHash = m_currentGridHash; 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: { QVector > cores; int i = 0; if (contentHash.isEmpty() || contentHash["db_name"].isEmpty()) break; cores = getPlaylistDefaultCores(); for (i = 0; i < cores.count(); i++) { if (cores[i]["playlist_filename"] == contentHash["db_name"]) { if (cores[i]["core_path"].isEmpty()) break; coreHash["core_path"] = cores[i]["core_path"]; break; } } 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) { #ifdef HAVE_MENU content_ctx_info_t content_info; QByteArray corePathArray; QByteArray contentPathArray; QByteArray contentLabelArray; const char *corePath = NULL; const char *contentPath = NULL; const char *contentLabel = NULL; QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); if (m_pendingRun) coreSelection = CORE_SELECTION_CURRENT; if (coreSelection == CORE_SELECTION_ASK) { QStringList extensionFilters; if (contentHash.contains("path")) { int lastIndex = contentHash["path"].lastIndexOf('.'); QString extensionStr; 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: { QVector > cores = getPlaylistDefaultCores(); int i = 0; for (i = 0; i < cores.count(); i++) { if (cores[i]["playlist_filename"] == contentHash["db_name"]) { corePathArray = cores[i]["core_path"].toUtf8(); contentPathArray = contentHash["path"].toUtf8(); contentLabelArray = contentHash["label_noext"].toUtf8(); break; } } break; } default: return; } corePath = corePathArray.constData(); contentPath = contentPathArray.constData(); contentLabel = contentLabelArray.constData(); content_info.argc = 0; content_info.argv = NULL; content_info.args = NULL; content_info.environ_get = NULL; menu_navigation_set_selection(0); command_event(CMD_EVENT_UNLOAD_CORE, NULL); if (!task_push_load_content_from_playlist_from_menu( corePath, contentPath, contentLabel, &content_info, NULL, NULL)) { QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); return; } menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, NULL); #endif } void MainWindow::onRunClicked() { #ifdef HAVE_MENU QTableWidgetItem *item = m_tableWidget->currentItem(); ViewType viewType = getCurrentViewType(); QHash contentHash; if (viewType == VIEW_TYPE_LIST) { if (!item) return; contentHash = item->data(Qt::UserRole).value >(); } else if (viewType == VIEW_TYPE_ICONS) { contentHash = m_currentGridHash; if (contentHash.isEmpty()) return; } else return; loadContent(contentHash); #endif } 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; } QVector > MainWindow::getPlaylistDefaultCores() { settings_t *settings = config_get_ptr(); struct string_list *playlists = string_split(settings->arrays.playlist_names, ";"); struct string_list *cores = string_split(settings->arrays.playlist_cores, ";"); unsigned i = 0; QVector > coreList; if (!playlists || !cores) { RARCH_WARN("[Qt]: Could not parse one of playlist_names or playlist_cores\n"); goto finish; } else if (playlists->size != cores->size) { RARCH_WARN("[Qt]: playlist_names array size differs from playlist_cores\n"); goto finish; } if (playlists->size == 0) goto finish; for (i = 0; i < playlists->size; i++) { const char *playlist = playlists->elems[i].data; const char *core = cores->elems[i].data; QHash hash; hash["playlist_filename"] = playlist; hash["playlist_filename"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); hash["core_path"] = core; coreList.append(hash); } finish: if (playlists) string_list_free(playlists); if (cores) string_list_free(cores); return coreList; } void MainWindow::setCoreActions() { QTableWidgetItem *currentContentItem = m_tableWidget->currentItem(); QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); ViewType viewType = getCurrentViewType(); QHash hash; 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 (viewType == VIEW_TYPE_LIST && currentContentItem) hash = currentContentItem->data(Qt::UserRole).value >(); else if (viewType == VIEW_TYPE_ICONS) hash = m_currentGridHash; if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) { if (!hash.isEmpty()) { QString coreName = hash["core_name"]; if (coreName.isEmpty()) { coreName = ""; } else { const char *detect_str = file_path_str(FILE_PATH_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)); } } } } } } if (!hash["db_name"].isEmpty()) { QVector > defaultCores = getPlaylistDefaultCores(); int i = 0; if (defaultCores.count() > 0) { QString currentPlaylistItemDataString; bool allPlaylists = false; int row = 0; if (currentPlaylistItem) { currentPlaylistItemDataString = currentPlaylistItem->data(Qt::UserRole).toString(); allPlaylists = (currentPlaylistItemDataString == ALL_PLAYLISTS_TOKEN); } for (row = 0; row < m_listWidget->count(); row++) { if (allPlaylists) { QListWidgetItem *listItem = m_listWidget->item(row); QString listItemString = listItem->data(Qt::UserRole).toString(); QFileInfo info; info.setFile(listItemString); if (listItemString == ALL_PLAYLISTS_TOKEN) continue; } for (i = 0; i < defaultCores.count(); i++) { QString playlist = defaultCores.at(i)["playlist_filename"]; QString core = defaultCores.at(i)["core_path"]; QString currentPlaylistFileName = hash["db_name"]; playlist.remove(file_path_str(FILE_PATH_LPL_EXTENSION)); if (currentPlaylistFileName == playlist) { core_info_list_t *coreInfoList = NULL; unsigned j = 0; core_info_get_list(&coreInfoList); for (j = 0; j < coreInfoList->count; j++) { const core_info_t *info = &coreInfoList->list[j]; if (core == info->path) { 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(); if (map.value("core_path").toString() == info->path || map.value("core_name").toString() == info->core_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) { Q_UNUSED(index) if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_FILE_BROWSER)) { QModelIndex index = m_dirTree->currentIndex(); /* force list view for file browser, will set it back to whatever the user had when switching back to playlist tab */ setCurrentViewType(VIEW_TYPE_LIST); m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); if (index.isValid()) { m_dirTree->clearSelection(); m_dirTree->setCurrentIndex(index); } } else if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) { QListWidgetItem *item = m_listWidget->currentItem(); if (m_lastViewType != getCurrentViewType()) setCurrentViewType(m_lastViewType); m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); if (item) { m_listWidget->setCurrentItem(NULL); m_listWidget->setCurrentItem(item); } } 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::getPlaylistFiles() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); m_playlistFiles = playlistDir.entryList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name); } void MainWindow::onSearchLineEditEdited(const QString &text) { int i = 0; QList items; QVector > gridItems; QVector textUnicode = text.toUcs4(); QVector textHiraToKata; QVector textKataToHira; ViewType viewType = getCurrentViewType(); 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; } } switch(viewType) { case VIEW_TYPE_LIST: { if (text.isEmpty()) { for (i = 0; i < m_tableWidget->rowCount(); i++) { m_tableWidget->setRowHidden(i, false); } return; } items.append(m_tableWidget->findItems(text, Qt::MatchContains)); if (foundHira) { items.append(m_tableWidget->findItems(QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::MatchContains)); } if (foundKata) { items.append(m_tableWidget->findItems(QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::MatchContains)); } if (items.isEmpty()) { for (i = 0; i < m_tableWidget->rowCount(); i++) { m_tableWidget->setRowHidden(i, true); } return; } else { for (i = 0; i < m_tableWidget->rowCount(); i++) { if (items.contains(m_tableWidget->item(i, 0))) m_tableWidget->setRowHidden(i, false); else m_tableWidget->setRowHidden(i, true); } } break; } case VIEW_TYPE_ICONS: { int i; if (text.isEmpty()) { for (i = 0; i < m_gridItems.size(); i++) { m_gridItems.at(i)->widget->show(); } return; } for (i = 0; i < m_gridItems.count(); i++) { const QPointer &item = m_gridItems.at(i); if (item->hash.value("label").contains(text, Qt::CaseInsensitive)) gridItems.append(item); if (foundHira) { if (item->hash.value("label").contains(QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::CaseInsensitive)) gridItems.append(item); } if (foundKata) { if (item->hash.value("label").contains(QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::CaseInsensitive)) gridItems.append(item); } } if (gridItems.isEmpty()) { for (i = 0; i < m_gridItems.size(); i++) { m_gridItems.at(i)->widget->hide(); } return; } else { for (i = 0; i < m_gridItems.size(); i++) { const QPointer &item = m_gridItems.at(i); if (gridItems.contains(item)) item->widget->show(); else item->widget->hide(); } } break; } default: break; } } void MainWindow::onViewClosedDocksAboutToShow() { QMenu *menu = qobject_cast(sender()); QList dockWidgets; bool found = false; int i = 0; 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::onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *) { QHash hash; if (!current) return; hash = current->data(Qt::UserRole).value >(); currentItemChanged(hash); } void MainWindow::currentItemChanged(const QHash &hash) { settings_t *settings = config_get_ptr(); QString label; QString playlist_name; QByteArray extension; QString extensionStr; int lastIndex = -1; label = hash["label_noext"]; label.replace(m_fileSanitizerRegex, "_"); lastIndex = hash["path"].lastIndexOf('.'); if (lastIndex >= 0) { extensionStr = hash["path"].mid(lastIndex + 1); if (!extensionStr.isEmpty()) { extension = extensionStr.toLower().toUtf8(); } } playlist_name = hash["db_name"]; if (m_thumbnailPixmap) delete m_thumbnailPixmap; if (m_thumbnailPixmap2) delete m_thumbnailPixmap2; if (m_thumbnailPixmap3) delete m_thumbnailPixmap3; if (!extension.isEmpty() && m_imageFormats.contains(extension)) { /* use thumbnail widgets to show regular image files */ m_thumbnailPixmap = new QPixmap(hash["path"]); m_thumbnailPixmap2 = new QPixmap(*m_thumbnailPixmap); m_thumbnailPixmap3 = new QPixmap(*m_thumbnailPixmap); } else { m_thumbnailPixmap = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_BOXART + "/" + label + ".png"); m_thumbnailPixmap2 = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_TITLE + "/" + label + ".png"); m_thumbnailPixmap3 = new QPixmap(QString(settings->paths.directory_thumbnails) + "/" + playlist_name + "/" + THUMBNAIL_SCREENSHOT + "/" + label + ".png"); } resizeThumbnails(true, true, true); setCoreActions(); } void MainWindow::onResizeThumbnailOne() { resizeThumbnails(true, false, false); } void MainWindow::onResizeThumbnailTwo() { resizeThumbnails(false, true, false); } void MainWindow::onResizeThumbnailThree() { resizeThumbnails(false, false, true); } void MainWindow::resizeThumbnails(bool one, bool two, bool three) { QPixmap pixmap; QPixmap pixmap2; QPixmap pixmap3; ThumbnailLabel *thumbnail = NULL; ThumbnailLabel *thumbnail2 = NULL; ThumbnailLabel *thumbnail3 = NULL; if (m_thumbnailPixmap) pixmap = *m_thumbnailPixmap; if (m_thumbnailPixmap2) pixmap2 = *m_thumbnailPixmap2; if (m_thumbnailPixmap3) pixmap3 = *m_thumbnailPixmap3; thumbnail = findChild("thumbnail"); thumbnail2 = findChild("thumbnail2"); thumbnail3 = findChild("thumbnail3"); if (thumbnail && one) { if (pixmap.isNull()) thumbnail->hide(); else { thumbnail->show(); emit thumbnailChanged(pixmap); thumbnail->update(); } } if (thumbnail2 && two) { if (pixmap2.isNull()) thumbnail2->hide(); else { thumbnail2->show(); emit thumbnail2Changed(pixmap2); thumbnail2->update(); } } if (thumbnail3 && three) { if (pixmap3.isNull()) thumbnail3->hide(); else { thumbnail3->show(); emit thumbnail3Changed(pixmap3); thumbnail3->update(); } } } void MainWindow::setCurrentViewType(ViewType viewType) { m_lastViewType = m_viewType; m_viewType = viewType; switch (viewType) { case VIEW_TYPE_ICONS: { m_tableWidget->hide(); m_gridWidget->show(); break; } case VIEW_TYPE_LIST: default: { m_viewType = VIEW_TYPE_LIST; m_gridWidget->hide(); m_tableWidget->show(); break; } } } MainWindow::ViewType MainWindow::getCurrentViewType() { return m_viewType; } void MainWindow::onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { ViewType viewType = getCurrentViewType(); Q_UNUSED(current) Q_UNUSED(previous) if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) != msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) return; switch (viewType) { case VIEW_TYPE_ICONS: { initContentGridLayout(); break; } case VIEW_TYPE_LIST: default: { initContentTableWidget(); break; } } setCoreActions(); } TableWidget* MainWindow::contentTableWidget() { return m_tableWidget; } QWidget* MainWindow::contentGridWidget() { return m_gridWidget; } FlowLayout* MainWindow::contentGridLayout() { return m_gridLayout; } void MainWindow::onBrowserDownloadsClicked() { settings_t *settings = config_get_ptr(); QDir dir(settings->paths.directory_core_assets); m_dirTree->setCurrentIndex(m_dirModel->index(dir.absolutePath())); /* for some reason, scrollTo only seems to work right when the button is clicked twice (only tested on Linux) */ m_dirTree->scrollTo(m_dirTree->currentIndex(), QAbstractItemView::PositionAtTop); } 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); } QListWidget* 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() { menu_navigation_set_selection(0); command_event(CMD_EVENT_UNLOAD_CORE, NULL); setCurrentCoreLabel(); activateWindow(); raise(); } void MainWindow::setCurrentCoreLabel() { rarch_system_info_t *system = runloop_get_system_info(); bool update = false; QString libraryName = system->info.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->info.library_name; m_currentCoreVersion = (string_is_empty(system->info.library_version) ? "" : system->info.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()); menu_navigation_set_selection(0); if (!command_event(CMD_EVENT_UNLOAD_CORE, NULL)) { /* TODO */ 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::removeGridItems() { if (m_gridItems.count() > 0) { QMutableVectorIterator > items(m_gridItems); m_pendingItemUpdates.clear(); while (items.hasNext()) { QPointer item = items.next(); if (item) { item->imageWatcher.waitForFinished(); items.remove(); m_gridLayout->removeWidget(item->widget); delete item->widget; delete item; } } } } void MainWindow::onDeferredImageLoaded() { const QFutureWatcher *watcher = static_cast*>(sender()); GridItem *item = NULL; if (!watcher) return; item = watcher->result(); if (!item) return; if (m_gridItems.contains(item)) { if (!item->image.isNull()) { m_pendingItemUpdates.append(item); QTimer::singleShot(0, this, SLOT(onPendingItemUpdates())); } } } void MainWindow::onPendingItemUpdates() { QMutableListIterator list(m_pendingItemUpdates); while (list.hasNext()) { GridItem *item = list.next(); if (!item) continue; if (m_gridItems.contains(item)) onUpdateGridItemPixmapFromImage(item); list.remove(); } } void MainWindow::onUpdateGridItemPixmapFromImage(GridItem *item) { if (!item) return; if (!m_gridItems.contains(item)) return; item->label->setPixmap(QPixmap::fromImage(item->image)); item->label->update(); } void MainWindow::loadImageDeferred(GridItem *item, QString path) { connect(&item->imageWatcher, SIGNAL(finished()), this, SLOT(onDeferredImageLoaded()), Qt::QueuedConnection); item->imageWatcher.setFuture(QtConcurrent::run(this, &MainWindow::doDeferredImageLoad, item, path)); } GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) { /* this runs in another thread */ if (!item) return NULL; /* While we are indeed writing across thread boundaries here, the image is never accessed until after * its thread finishes, and the item is never deleted without first waiting for the thread to finish. */ item->image = QImage(path); return item; } void MainWindow::addPlaylistItemsToGrid(const QStringList &paths, bool add) { QVector > items; int i; if (paths.isEmpty()) return; for (i = 0; i < paths.size(); i++) { int j; QVector > vec = getPlaylistItems(paths.at(i)); /* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */ for (j = 0; j < vec.size(); j++) { if (add && m_allPlaylistsGridMaxCount > 0 && items.size() >= m_allPlaylistsGridMaxCount) goto finish; items.append(vec.at(j)); } } finish: std::sort(items.begin(), items.end(), comp_hash_label_key_lower); addPlaylistHashToGrid(items); } void MainWindow::addPlaylistHashToGrid(const QVector > &items) { QScreen *screen = qApp->primaryScreen(); QSize screenSize = screen->size(); QListWidgetItem *currentItem = m_listWidget->currentItem(); settings_t *settings = config_get_ptr(); int i = 0; int zoomValue = m_zoomSlider->value(); m_gridProgressBar->setMinimum(0); m_gridProgressBar->setMaximum(qMax(0, items.count() - 1)); m_gridProgressBar->setValue(0); for (i = 0; i < items.count(); i++) { const QHash &hash = items.at(i); QPointer item; QPointer label; QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8); QByteArray extension; QString extensionStr; QString imagePath; int lastIndex = -1; if (m_listWidget->currentItem() != currentItem) { /* user changed the current playlist before we finished loading... abort */ m_gridProgressWidget->hide(); break; } item = new GridItem(); lastIndex = hash["path"].lastIndexOf('.'); if (lastIndex >= 0) { extensionStr = hash["path"].mid(lastIndex + 1); if (!extensionStr.isEmpty()) { extension = extensionStr.toLower().toUtf8(); } } if (!extension.isEmpty() && m_imageFormats.contains(extension)) { /* use thumbnail widgets to show regular image files */ imagePath = hash["path"]; } else { thumbnailFileNameNoExt = hash["label_noext"]; thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); imagePath = QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"; } item->hash = hash; item->widget = new ThumbnailWidget(); item->widget->setSizeHint(thumbnailWidgetSizeHint); item->widget->setFixedSize(item->widget->sizeHint()); item->widget->setLayout(new QVBoxLayout()); item->widget->setObjectName("thumbnailWidget"); item->widget->setProperty("hash", QVariant::fromValue >(hash)); connect(item->widget, SIGNAL(mouseDoubleClicked()), this, SLOT(onGridItemDoubleClicked())); connect(item->widget, SIGNAL(mousePressed()), this, SLOT(onGridItemClicked())); label = new ThumbnailLabel(item->widget); label->setObjectName("thumbnailGridLabel"); item->label = label; item->labelText = hash.value("label"); newLabel = new QLabel(item->labelText, item->widget); newLabel->setObjectName("thumbnailQLabel"); newLabel->setAlignment(Qt::AlignCenter); newLabel->setToolTip(item->labelText); calcGridItemSize(item, zoomValue); item->widget->layout()->addWidget(label); item->widget->layout()->addWidget(newLabel); qobject_cast(item->widget->layout())->setStretchFactor(label, 1); m_gridLayout->addWidgetDeferred(item->widget); m_gridItems.append(item); loadImageDeferred(item, imagePath); if (i % 25 == 0) { /* Needed to update progress dialog while doing a lot of stuff on the main thread. */ qApp->processEvents(); } m_gridProgressBar->setValue(i); } /* If there's only one entry, a min/max/value of all zero would make an indeterminate progress bar that never ends... so just hide it when we are done. */ if (m_gridProgressBar->value() == m_gridProgressBar->maximum()) m_gridProgressWidget->hide(); } void MainWindow::initContentGridLayout() { QListWidgetItem *item = m_listWidget->currentItem(); QString path; if (!item) return; m_gridProgressBar->setMinimum(0); m_gridProgressBar->setMaximum(0); m_gridProgressBar->setValue(0); m_gridProgressWidget->show(); removeGridItems(); 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) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QStringList playlists; int i = 0; for (i = 0; i < m_playlistFiles.count(); i++) { const QString &playlist = m_playlistFiles.at(i); playlists.append(playlistDir.absoluteFilePath(playlist)); } addPlaylistItemsToGrid(playlists, true); } else addPlaylistItemsToGrid(QStringList() << path); QTimer::singleShot(0, this, SLOT(onContentGridInited())); } void MainWindow::onContentGridInited() { ThumbnailWidget *thumbnailWidget = NULL; m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); onZoomValueChanged(m_zoomSlider->value()); onSearchEnterPressed(); if (m_gridItems.count() > 0) { GridItem *gridItem = m_gridItems.at(0); if (gridItem) { thumbnailWidget = m_gridItems.at(0)->widget.data(); if (thumbnailWidget) onGridItemClicked(thumbnailWidget); } } } void MainWindow::initContentTableWidget() { QListWidgetItem *item = m_listWidget->currentItem(); QStringList horizontal_header_labels; QString path; int i = 0; 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; horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_tableWidget->setSortingEnabled(false); m_tableWidget->setColumnCount(1); m_tableWidget->setRowCount(0); m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); path = item->data(Qt::UserRole).toString(); if (path == ALL_PLAYLISTS_TOKEN) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QStringList playlists; int i = 0; for (i = 0; i < m_playlistFiles.count(); i++) { const QString &playlist = m_playlistFiles.at(i); playlists.append(playlistDir.absoluteFilePath(playlist)); } addPlaylistItemsToTable(playlists, true); } else addPlaylistItemsToTable(QStringList() << path); m_tableWidget->setSortingEnabled(true); if (item != m_historyPlaylistsItem) m_tableWidget->sortByColumn(0, Qt::AscendingOrder); m_tableWidget->resizeColumnsToContents(); for (i = 0; i < m_tableWidget->rowCount(); i++) { /* select the first non-hidden row */ if (!m_tableWidget->isRowHidden(i)) { m_tableWidget->selectRow(i); break; } } onSearchEnterPressed(); } QVector > MainWindow::getPlaylistItems(QString pathString) { QByteArray pathArray; QVector > items; const char *pathData = NULL; playlist_t *playlist = NULL; unsigned playlistSize = 0; unsigned i = 0; pathArray.append(pathString); pathData = pathArray.constData(); playlist = playlist_init(pathData, COLLECTION_SIZE); playlistSize = playlist_get_size(playlist); for (i = 0; i < playlistSize; i++) { const char *path = NULL; const char *label = NULL; const char *core_path = NULL; const char *core_name = NULL; const char *crc32 = NULL; const char *db_name = NULL; QHash hash; playlist_get_index(playlist, i, &path, &label, &core_path, &core_name, &crc32, &db_name); if (string_is_empty(path)) continue; else hash["path"] = path; hash["index"] = QString::number(i); if (string_is_empty(label)) { hash["label"] = path; hash["label_noext"] = path; } else { hash["label"] = label; hash["label_noext"] = label; } if (!string_is_empty(core_path)) hash["core_path"] = core_path; if (!string_is_empty(core_name)) hash["core_name"] = core_name; if (!string_is_empty(crc32)) hash["crc32"] = crc32; if (!string_is_empty(db_name)) { hash["db_name"] = db_name; hash["db_name"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); } items.append(hash); } playlist_free(playlist); playlist = NULL; return items; } void MainWindow::addPlaylistItemsToTable(const QStringList &paths, bool add) { QVector > items; int i; if (paths.isEmpty()) return; for (i = 0; i < paths.size(); i++) { int j; QVector > vec = getPlaylistItems(paths.at(i)); /* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */ for (j = 0; j < vec.size(); j++) { if (add && m_allPlaylistsListMaxCount > 0 && items.size() >= m_allPlaylistsListMaxCount) goto finish; items.append(vec.at(j)); } } finish: addPlaylistHashToTable(items); } void MainWindow::addPlaylistHashToTable(const QVector > &items) { int i = 0; int oldRowCount = m_tableWidget->rowCount(); m_tableWidget->setRowCount(oldRowCount + items.count()); for (i = 0; i < items.count(); i++) { QTableWidgetItem *labelItem = NULL; const QHash &hash = items.at(i); labelItem = new QTableWidgetItem(hash.value("label")); labelItem->setData(Qt::UserRole, QVariant::fromValue >(hash)); labelItem->setFlags(labelItem->flags() & ~Qt::ItemIsEditable); m_tableWidget->setItem(oldRowCount + i, 0, labelItem); } } void MainWindow::keyPressEvent(QKeyEvent *event) { /* if (event->key() == Qt::Key_F5) { event->accept(); hide(); return; } */ 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: { return QStringLiteral("list"); } } return QStringLiteral("list"); } 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()); QMainWindow::closeEvent(event); } void MainWindow::setAllPlaylistsListMaxCount(int count) { if (count < 1) count = 0; m_allPlaylistsListMaxCount = count; } void MainWindow::setAllPlaylistsGridMaxCount(int count) { if (count < 1) count = 0; m_allPlaylistsGridMaxCount = count; } 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"; QLabel *label = new QLabel(text, dialog.data()); QPixmap pix = getInvader(); QLabel *pixLabel = new QLabel(); label->setTextFormat(Qt::RichText); label->setAlignment(Qt::AlignCenter); label->setTextInteractionFlags(Qt::TextBrowserInteraction); label->setOpenExternalLinks(true); pixLabel->setAlignment(Qt::AlignCenter); pixLabel->setPixmap(pix); 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)); dialog->setLayout(new QVBoxLayout()); dialog->layout()->addWidget(pixLabel); dialog->layout()->addWidget(label); 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::onUpdateNetworkError(QNetworkReply::NetworkError code) { QNetworkReply *reply = m_updateReply.data(); QByteArray errorStringArray; const char *errorStringData = NULL; m_updateProgressDialog->cancel(); if (!reply) return; errorStringArray = reply->errorString().toUtf8(); errorStringData = errorStringArray.constData(); RARCH_ERR("[Qt]: Network error code %d received: %s\n", code, errorStringData); /* Deleting the reply here seems to cause a strange heap-use-after-free crash. */ /* reply->disconnect(); reply->abort(); reply->deleteLater(); */ } void MainWindow::onUpdateNetworkSslErrors(const QList &errors) { QNetworkReply *reply = m_updateReply.data(); int i; if (!reply) return; for (i = 0; i < errors.count(); i++) { const QSslError &error = errors.at(i); QString string = QString("Ignoring SSL error code ") + QString::number(error.error()) + ": " + error.errorString(); QByteArray stringArray = string.toUtf8(); const char *stringData = stringArray.constData(); RARCH_ERR("[Qt]: %s\n", stringData); } /* ignore all SSL errors for now, like self-signed, expired etc. */ reply->ignoreSslErrors(); } void MainWindow::onUpdateDownloadCanceled() { m_updateProgressDialog->cancel(); } void MainWindow::onShowErrorMessage(QString msg) { showMessageBox(msg, MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); } void MainWindow::onRetroArchUpdateDownloadFinished() { QNetworkReply *reply = m_updateReply.data(); QNetworkReply::NetworkError error; int code; m_updateProgressDialog->cancel(); /* At least on Linux, the progress dialog will refuse to hide itself and will stay on screen in a corrupted way if we happen to show an error message in this function. processEvents() will sometimes fix it, other times not... seems random. */ qApp->processEvents(); if (!reply) return; error = reply->error(); code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (m_updateFile.isOpen()) m_updateFile.close(); if (code != 200) { emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": HTTP Code " + QString::number(code)); RARCH_ERR("[Qt]: RetroArch update failed with HTTP status code: %d\n", code); reply->disconnect(); reply->abort(); reply->deleteLater(); return; } if (error == QNetworkReply::NoError) { int index = m_updateFile.fileName().lastIndexOf(PARTIAL_EXTENSION); QString newFileName = m_updateFile.fileName().left(index); QFile newFile(newFileName); /* rename() requires the old file to be deleted first if it exists */ if (newFile.exists() && !newFile.remove()) RARCH_ERR("[Qt]: RetroArch update finished, but old file could not be deleted.\n"); else { if (m_updateFile.rename(newFileName)) { RARCH_LOG("[Qt]: RetroArch update finished downloading successfully.\n"); emit extractArchiveDeferred(newFileName); } else { RARCH_ERR("[Qt]: RetroArch update finished, but temp file could not be renamed.\n"); emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_RENAME_FILE)); } } } else { QByteArray errorArray = reply->errorString().toUtf8(); const char *errorData = errorArray.constData(); RARCH_ERR("[Qt]: RetroArch update ended prematurely: %s\n", errorData); emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": Code " + QString::number(code) + ": " + errorData); } reply->disconnect(); reply->close(); reply->deleteLater(); } static void extractCB(void *task_data, void *user_data, const char *err) { decompress_task_data_t *dec = (decompress_task_data_t*)task_data; MainWindow *mainwindow = (MainWindow*)user_data; if (err) RARCH_ERR("%s", err); if (dec) { if (filestream_exists(dec->source_file)) filestream_delete(dec->source_file); free(dec->source_file); free(dec); } mainwindow->onUpdateRetroArchFinished(string_is_empty(err)); } void MainWindow::onUpdateRetroArchFinished(bool success) { m_updateProgressDialog->cancel(); if (!success) { RARCH_ERR("[Qt]: RetroArch update failed.\n"); emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FAILED)); return; } RARCH_LOG("[Qt]: RetroArch update finished successfully.\n"); emit showErrorMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_UPDATE_RETROARCH_FINISHED)); } int MainWindow::onExtractArchive(QString path) { QByteArray pathArray = path.toUtf8(); const char *file = pathArray.constData(); file_archive_transfer_t state; struct archive_extract_userdata userdata; struct string_list *file_list = file_archive_get_file_list(file, NULL); bool returnerr = true; unsigned i; 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() + TEMP_EXTENSION); 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(); if (!task_push_decompress(file, ".", NULL, NULL, NULL, extractCB, this)) { m_updateProgressDialog->cancel(); return -1; } return returnerr; } void MainWindow::onUpdateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { QNetworkReply *reply = m_updateReply.data(); int progress = (bytesReceived / (float)bytesTotal) * 100.0f; if (!reply) return; m_updateProgressDialog->setValue(progress); } void MainWindow::onUpdateDownloadReadyRead() { QNetworkReply *reply = m_updateReply.data(); if (!reply) return; m_updateFile.write(reply->readAll()); } void MainWindow::updateRetroArchNightly() { QUrl url(QUrl(buildbot_server_url).resolved(QUrl(RETROARCH_NIGHTLY_UPDATE_PATH))); QNetworkRequest request(url); QNetworkReply *reply = NULL; QByteArray urlArray = url.toString().toUtf8(); const char *urlData = urlArray.constData(); if (m_updateFile.isOpen()) { RARCH_ERR("[Qt]: File is already open.\n"); return; } else { QString fileName = QFileInfo(url.toString()).fileName() + PARTIAL_EXTENSION; QByteArray fileNameArray = fileName.toUtf8(); const char *fileNameData = fileNameArray.constData(); m_updateFile.setFileName(fileName); if (!m_updateFile.open(QIODevice::WriteOnly)) { showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FILE_WRITE_OPEN_FAILED), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false); RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData); return; } } RARCH_LOG("[Qt]: Starting update of RetroArch...\n"); RARCH_LOG("[Qt]: Downloading URL %s\n", urlData); request.setHeader(QNetworkRequest::UserAgentHeader, USER_AGENT); m_updateProgressDialog->setWindowModality(Qt::NonModal); m_updateProgressDialog->setMinimumDuration(0); m_updateProgressDialog->setRange(0, 100); m_updateProgressDialog->setAutoClose(true); m_updateProgressDialog->setAutoReset(true); m_updateProgressDialog->setValue(0); m_updateProgressDialog->setLabelText(QString(msg_hash_to_str(MSG_DOWNLOADING)) + "..."); m_updateProgressDialog->setCancelButtonText(tr("Cancel")); m_updateProgressDialog->show(); m_updateReply = m_networkManager->get(request); reply = m_updateReply.data(); /* make sure any previous connection is removed first */ disconnect(m_updateProgressDialog, SIGNAL(canceled()), reply, SLOT(abort())); disconnect(m_updateProgressDialog, SIGNAL(canceled()), m_updateProgressDialog, SLOT(cancel())); connect(m_updateProgressDialog, SIGNAL(canceled()), reply, SLOT(abort())); connect(m_updateProgressDialog, SIGNAL(canceled()), m_updateProgressDialog, SLOT(cancel())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onUpdateNetworkError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(sslErrors(const QList&)), this, SLOT(onUpdateNetworkSslErrors(const QList&))); connect(reply, SIGNAL(finished()), this, SLOT(onRetroArchUpdateDownloadFinished())); connect(reply, SIGNAL(readyRead()), this, SLOT(onUpdateDownloadReadyRead())); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onUpdateDownloadProgress(qint64, qint64))); } const QPixmap getInvader() { QPixmap pix; pix.loadFromData(invader_png, invader_png_len, "PNG"); return pix; } static void* ui_window_qt_init(void) { ui_window.qtWindow = new MainWindow(); return &ui_window; } static void ui_window_qt_destroy(void *data) { (void)data; /* ui_window_qt_t *window = (ui_window_qt_t*)data; delete window->qtWindow; */ } static void ui_window_qt_set_focused(void *data) { (void)data; /* ui_window_qt_t *window = (ui_window_qt_t*)data; window->qtWindow->raise(); window->qtWindow->activateWindow(); */ } 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) { (void)data; (void)buf; /* ui_window_qt_t *window = (ui_window_qt_t*)data; window->qtWindow->setWindowTitle(QString::fromUtf8(buf)); */ } static void ui_window_qt_set_droppable(void *data, bool droppable) { (void)data; (void)droppable; /* ui_window_qt_t *window = (ui_window_qt_t*)data; window->qtWindow->setAcceptDrops(droppable); */ } static bool ui_window_qt_focused(void *data) { (void)data; /* ui_window_qt_t *window = (ui_window_qt_t*)data; return window->qtWindow->isActiveWindow() && !window->qtWindow->isMinimized(); */ return true; } 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" };