diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 0507fbfaf4..558cf92eb9 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -345,6 +345,7 @@ void game_list_frame::doubleClickedSlot(const QModelIndex& index) { const std::string& path = Emu.GetGameDir() + m_game_data[i].root; emit RequestIconPathSet(path); + emit RequestAddRecentGame(qstr(path), qstr("[" + m_game_data[i].serial + "] " + m_game_data[i].name)); Emu.Stop(); @@ -438,6 +439,7 @@ void game_list_frame::Boot(int row) { const std::string& path = Emu.GetGameDir() + m_game_data[row].root; emit RequestIconPathSet(path); + emit RequestAddRecentGame(qstr(path), qstr("[" + m_game_data[row].serial + "] " + m_game_data[row].name)); Emu.Stop(); diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 6e797a9a2e..a4e45960f4 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -109,6 +109,7 @@ private slots: signals: void game_list_frameClosed(); void RequestIconPathSet(const std::string path); + void RequestAddRecentGame(const QString path, const QString name); protected: /** Override inherited method from Qt to allow signalling when close happened.*/ void closeEvent(QCloseEvent* event); diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index aca7854caf..cb222062fe 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -45,6 +45,10 @@ namespace GUI const QString logger = "Logger"; const QString meta = "Meta"; + const GUI_SAVE rg_freeze = GUI_SAVE(main_window, "recentGamesFrozen", false); + const GUI_SAVE rg_names = GUI_SAVE(main_window, "recentGamesNames", QStringList()); + const GUI_SAVE rg_paths = GUI_SAVE(main_window, "recentGamesPaths", QStringList()); + const GUI_SAVE ib_pkg_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPKG", true ); const GUI_SAVE ib_pup_success = GUI_SAVE( main_window, "infoBoxEnabledInstallPUP", true ); const GUI_SAVE ib_show_welcome = GUI_SAVE(main_window, "infoBoxEnabledWelcome", true); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index a27c5b27a2..a143adbfea 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -131,7 +131,7 @@ QIcon main_window::GetAppIcon() void main_window::SetAppIconFromPath(const std::string path) { // get Icon for the gs_frame from path. this handles presumably all possible use cases - QString qpath = QString::fromUtf8(path.data(), path.size()); + QString qpath = qstr(path); std::string icon_list[] = { "/ICON0.PNG", "/PS3_GAME/ICON0.PNG" }; std::string path_list[] = { path, sstr(qpath.section("/", 0, -2)) ,sstr(qpath.section("/", 0, -3)) }; for (std::string pth : path_list) @@ -142,7 +142,7 @@ void main_window::SetAppIconFromPath(const std::string path) if (fs::is_file(ico)) { // load the image from path. It will most likely be a rectangle - QImage source = QImage(QString::fromUtf8(ico.data(), ico.size())); + QImage source = QImage(qstr(ico)); int edgeMax = std::max(source.width(), source.height()); // create a new transparent image with square size and same format as source (maybe handle other formats than RGB32 as well?) @@ -203,6 +203,9 @@ void main_window::BootElf() guiSettings->SetValue(GUI::fd_boot_elf, filePath); const std::string path = sstr(QFileInfo(filePath).canonicalFilePath()); + const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] "; + AddRecentAction(qstr(path), qstr(serial + Emu.GetTitle())); + SetAppIconFromPath(path); Emu.Stop(); Emu.SetPath(path); @@ -234,6 +237,9 @@ void main_window::BootGame() const std::string path = sstr(dirPath); SetAppIconFromPath(path); + const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] "; + AddRecentAction(qstr(path), qstr(serial + Emu.GetTitle())); + if (!Emu.BootGame(path)) { LOG_ERROR(GENERAL, "PS3 executable not found in selected folder (%s)", path); @@ -674,6 +680,15 @@ void main_window::OnEmuRun() menu_run->setText(tr("&Pause")); menu_run->setIcon(icon_pause); EnableMenus(true); + + // Disable Recent Games + for (auto act : m_bootRecentMenu->actions()) + { + if (act != freezeRecentAct && act != clearRecentAct) + { + act->setEnabled(false); + } + } } void main_window::OnEmuResume() @@ -725,6 +740,15 @@ void main_window::OnEmuStop() sysPauseAct->setText(Emu.IsReady() ? tr("&Start\tCtrl+E") : tr("&Resume\tCtrl+E")); sysPauseAct->setIcon(icon_play); } + + // Enable Recent Games + for (auto act : m_bootRecentMenu->actions()) + { + if (act != freezeRecentAct && act != clearRecentAct) + { + act->setEnabled(true); + } + } } void main_window::OnEmuReady() @@ -771,6 +795,173 @@ void main_window::EnableMenus(bool enabled) toolsStringSearchAct->setEnabled(enabled); } +void main_window::BootRecentAction(const QAction* act) +{ + if (Emu.IsRunning()) + { + return; + } + + const QString pth = act->data().toString(); + + // path is invalid: remove action from list return + if (!QFileInfo(pth).isDir() && !QFileInfo(pth).isFile()) + { + if (m_rg_paths.contains(pth)) + { + // clear menu of actions + for (auto act : m_recentGameActs) + { + m_bootRecentMenu->removeAction(act); + } + + // remove action from list + int idx = m_rg_paths.indexOf(pth); + m_rg_names.removeAt(idx); + m_rg_paths.removeAt(idx); + m_recentGameActs.removeAt(idx); + + guiSettings->SetValue(GUI::rg_paths, m_rg_paths); + guiSettings->SetValue(GUI::rg_names, m_rg_names); + + LOG_ERROR(GENERAL, "Recent Game not valid, removed from Boot Recent list: %s", sstr(pth)); + + // refill menu with actions + for (uint i = 0; i < m_recentGameActs.count(); i++) + { + m_recentGameActs[i]->setShortcut(tr("Ctrl+%1").arg(i + 1)); + m_recentGameActs[i]->setToolTip(m_rg_names[i]); + m_bootRecentMenu->addAction(m_recentGameActs[i]); + } + + LOG_WARNING(GENERAL, "Boot Recent list refreshed"); + + return; + } + + LOG_ERROR(GENERAL, "Path invalid and not in m_rg_paths: %s", sstr(pth)); + + return; + } + + SetAppIconFromPath(sstr(pth)); + + Emu.Stop(); + + AddRecentAction(act->property("path").toString(), act->property("shown_name").toString()); + + if (!Emu.BootGame(sstr(pth), true)) + { + LOG_ERROR(LOADER, "Failed to boot %s", sstr(pth)); + } +}; + +QAction* main_window::CreateRecentAction(const QString& path, const QString& name, const uint& sc_idx) +{ + // if path is not valid remove from list + if (!QFileInfo(path).isDir() && !QFileInfo(path).isFile()) + { + if (m_rg_paths.contains(path)) + { + int idx = m_rg_paths.indexOf(path); + m_rg_names.removeAt(idx); + m_rg_paths.removeAt(idx); + + guiSettings->SetValue(GUI::rg_names, m_rg_names); + guiSettings->SetValue(GUI::rg_paths, m_rg_paths); + + LOG_ERROR(GENERAL, "Recent Game not valid, removed from Boot Recent list: %s", sstr(path)); + } + return nullptr; + } + + // if name is a path get filename + QString shown_name = name; + if (QFileInfo(name).isFile()) + { + shown_name = name.section('/', -1); + } + + // create new action + QAction* act = new QAction(shown_name, this); + act->setData(path); + act->setToolTip(name); + act->setShortcut(tr("Ctrl+%1").arg(sc_idx)); + + // truncate if too long + if (shown_name.length() > 60) + { + act->setText(shown_name.left(27) + "(....)" + shown_name.right(27)); + } + + // connect boot + connect(act, &QAction::triggered, [=]() {BootRecentAction(act); }); + + return act; +}; + +void main_window::AddRecentAction(const QString path, QString name) +{ + // don't change list on freeze + if (freezeRecentAct->isChecked()) + { + return; + } + + // create new action, return if not valid + QAction* act = CreateRecentAction(path, name, 1); + if (!act) + { + return; + } + + // clear menu of actions + for (auto act : m_recentGameActs) + { + m_bootRecentMenu->removeAction(act); + } + + // if path already exists, remove it in order to get it to beginning + if (m_rg_paths.contains(path)) + { + int idx = m_rg_paths.indexOf(path); + m_rg_names.removeAt(idx); + m_rg_paths.removeAt(idx); + m_recentGameActs.removeAt(idx); + } + + // remove oldest action at the end if needed + if (m_rg_names.count() == 9 && m_rg_paths.count() == 9) + { + m_rg_names.removeLast(); + m_rg_paths.removeLast(); + m_recentGameActs.removeLast(); + } + else if (m_rg_names.count() != m_rg_paths.count()) + { + LOG_ERROR(LOADER, "Recent games pathlist and namelist have different count"); + } + + if (m_rg_paths.count() < 9) + { + // add new action at the beginning + m_rg_names.prepend(name); + m_rg_paths.prepend(path); + m_recentGameActs.prepend(act); + } + + // refill menu with actions + for (uint i = 0; i < m_recentGameActs.count(); i++) + { + m_recentGameActs[i]->setShortcut(tr("Ctrl+%1").arg(i+1)); + m_recentGameActs[i]->setToolTip(m_rg_names[i]); + m_bootRecentMenu->addAction(m_recentGameActs[i]); + } + + guiSettings->SetValue(GUI::rg_names, m_rg_names); + guiSettings->SetValue(GUI::rg_paths, m_rg_paths); +} + void main_window::CreateActions() { bootElfAct = new QAction(tr("Boot (S)ELF file"), this); @@ -778,6 +969,10 @@ void main_window::CreateActions() bootInstallPkgAct = new QAction(tr("&Install PKG"), this); bootInstallPupAct = new QAction(tr("&Install Firmware"), this); + clearRecentAct = new QAction(tr("&Clear List"), this); + freezeRecentAct = new QAction(tr("&Freeze List"), this); + freezeRecentAct->setCheckable(true); + exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcuts(QKeySequence::Quit); exitAct->setStatusTip(tr("Exit the application")); @@ -880,6 +1075,21 @@ void main_window::CreateConnects() { connect(bootElfAct, &QAction::triggered, this, &main_window::BootElf); connect(bootGameAct, &QAction::triggered, this, &main_window::BootGame); + connect(clearRecentAct, &QAction::triggered, [this](){ + if (freezeRecentAct->isChecked()) { return; } + m_rg_names.clear(); + m_rg_paths.clear(); + for (auto act : m_recentGameActs) + { + m_bootRecentMenu->removeAction(act); + } + m_recentGameActs.clear(); + guiSettings->SetValue(GUI::rg_paths, QStringList()); + guiSettings->SetValue(GUI::rg_names, QStringList()); + }); + connect(freezeRecentAct, &QAction::triggered, [=](bool checked) { + guiSettings->SetValue(GUI::rg_freeze, checked); + }); connect(bootInstallPkgAct, &QAction::triggered, this, &main_window::InstallPkg); connect(bootInstallPupAct, &QAction::triggered, this, &main_window::InstallPup); connect(exitAct, &QAction::triggered, this, &QWidget::close); @@ -1002,6 +1212,13 @@ void main_window::CreateMenus() QMenu *bootMenu = menuBar()->addMenu(tr("&Boot")); bootMenu->addAction(bootElfAct); bootMenu->addAction(bootGameAct); + + m_bootRecentMenu = bootMenu->addMenu(tr("Boot Recent")); + m_bootRecentMenu->setToolTipsVisible(true); + m_bootRecentMenu->addAction(clearRecentAct); + m_bootRecentMenu->addAction(freezeRecentAct); + m_bootRecentMenu->addSeparator(); + bootMenu->addSeparator(); bootMenu->addAction(bootInstallPkgAct); bootMenu->addAction(bootInstallPupAct); @@ -1115,6 +1332,7 @@ void main_window::CreateDockWindows() } }); connect(gameListFrame, &game_list_frame::RequestIconPathSet, this, &main_window::SetAppIconFromPath); + connect(gameListFrame, &game_list_frame::RequestAddRecentGame, this, &main_window::AddRecentAction); } void main_window::ConfigureGuiFromSettings(bool configureAll) @@ -1135,6 +1353,28 @@ void main_window::ConfigureGuiFromSettings(bool configureAll) restoreState(guiSettings->GetValue(GUI::mw_windowState).toByteArray()); + freezeRecentAct->setChecked(guiSettings->GetValue(GUI::rg_freeze).toBool()); + m_rg_names = guiSettings->GetValue(GUI::rg_names).toStringList(); + m_rg_paths = guiSettings->GetValue(GUI::rg_paths).toStringList(); + + // Fill the recent games menu + for (uint i = 0; i < m_rg_paths.count(); i++) + { + // create new action + QAction* act = CreateRecentAction(m_rg_paths[i], m_rg_names[i], i + 1); + + // add action to menu + if (act) + { + m_recentGameActs.append(act); + m_bootRecentMenu->addAction(act); + } + else + { + i--; // list count is now an entry shorter so we have to repeat the same index in order to load all other entries + } + } + showLogAct->setChecked(guiSettings->GetValue(GUI::mw_logger).toBool()); showGameListAct->setChecked(guiSettings->GetValue(GUI::mw_gamelist).toBool()); showDebuggerAct->setChecked(guiSettings->GetValue(GUI::mw_debugger).toBool()); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 295ad8f633..de47278f0f 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -89,10 +89,21 @@ private: void EnableMenus(bool enabled); void keyPressEvent(QKeyEvent *keyEvent); + QAction* CreateRecentAction(const QString& path, const QString& name, const uint& sc_idx); + void BootRecentAction(const QAction* act); + void AddRecentAction(const QString path, QString name); + + QStringList m_rg_names; + QStringList m_rg_paths; + QMenu* m_bootRecentMenu; + QList m_recentGameActs; + QActionGroup* iconSizeActGroup; QAction *bootElfAct; QAction *bootGameAct; + QAction *clearRecentAct; + QAction *freezeRecentAct; QAction *bootInstallPkgAct; QAction *bootInstallPupAct; QAction *sysPauseAct;