summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/game_list.cpp48
-rw-r--r--src/citra_qt/game_list.h4
2 files changed, 51 insertions, 1 deletions
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index 222c82b1c..db6f920ff 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -39,6 +39,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} {
39 39
40 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 40 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
41 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 41 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
42 connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
42 43
43 // We must register all custom types with the Qt Automoc system so that we are able to use it 44 // We must register all custom types with the Qt Automoc system so that we are able to use it
44 // with signals/slots. In this case, QList falls under the umbrells of custom types. 45 // with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -103,6 +104,12 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
103 item_model->removeRows(0, item_model->rowCount()); 104 item_model->removeRows(0, item_model->rowCount());
104 105
105 emit ShouldCancelWorker(); 106 emit ShouldCancelWorker();
107
108 auto watch_dirs = watcher.directories();
109 if (!watch_dirs.isEmpty()) {
110 watcher.removePaths(watch_dirs);
111 }
112 UpdateWatcherList(dir_path.toStdString(), deep_scan ? 256 : 0);
106 GameListWorker* worker = new GameListWorker(dir_path, deep_scan); 113 GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
107 114
108 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); 115 connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
@@ -140,6 +147,45 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
140 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); 147 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
141} 148}
142 149
150void GameList::RefreshGameDirectory() {
151 if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
152 LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
153 PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
154 }
155}
156
157/**
158 * Adds the game list folder to the QFileSystemWatcher to check for updates.
159 *
160 * The file watcher will fire off an update to the game list when a change is detected in the game
161 * list folder.
162 *
163 * Notice: This method is run on the UI thread because QFileSystemWatcher is not thread safe and
164 * this function is fast enough to not stall the UI thread. If performance is an issue, it should
165 * be moved to another thread and properly locked to prevent concurrency issues.
166 *
167 * @param dir folder to check for changes in
168 * @param recursion 0 if recursion is disabled. Any positive number passed to this will add each
169 * directory recursively to the watcher and will update the file list if any of the folders
170 * change. The number determines how deep the recursion should traverse.
171 */
172void GameList::UpdateWatcherList(const std::string& dir, unsigned int recursion) {
173 const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
174 const std::string& virtual_name) -> bool {
175 std::string physical_name = directory + DIR_SEP + virtual_name;
176
177 if (FileUtil::IsDirectory(physical_name)) {
178 UpdateWatcherList(physical_name, recursion - 1);
179 }
180 return true;
181 };
182
183 watcher.addPath(QString::fromStdString(dir));
184 if (recursion > 0) {
185 FileUtil::ForeachDirectoryEntry(nullptr, dir, callback);
186 }
187}
188
143void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { 189void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
144 const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, 190 const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory,
145 const std::string& virtual_name) -> bool { 191 const std::string& virtual_name) -> bool {
@@ -182,6 +228,6 @@ void GameListWorker::run() {
182} 228}
183 229
184void GameListWorker::Cancel() { 230void GameListWorker::Cancel() {
185 disconnect(this, nullptr, nullptr, nullptr); 231 this->disconnect();
186 stop_processing = true; 232 stop_processing = true;
187} 233}
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h
index e6b7eea0b..b141fa3a5 100644
--- a/src/citra_qt/game_list.h
+++ b/src/citra_qt/game_list.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <QFileSystemWatcher>
7#include <QModelIndex> 8#include <QModelIndex>
8#include <QSettings> 9#include <QSettings>
9#include <QStandardItem> 10#include <QStandardItem>
@@ -46,8 +47,11 @@ private:
46 void DonePopulating(); 47 void DonePopulating();
47 48
48 void PopupContextMenu(const QPoint& menu_location); 49 void PopupContextMenu(const QPoint& menu_location);
50 void UpdateWatcherList(const std::string& path, unsigned int recursion);
51 void RefreshGameDirectory();
49 52
50 QTreeView* tree_view = nullptr; 53 QTreeView* tree_view = nullptr;
51 QStandardItemModel* item_model = nullptr; 54 QStandardItemModel* item_model = nullptr;
52 GameListWorker* current_worker = nullptr; 55 GameListWorker* current_worker = nullptr;
56 QFileSystemWatcher watcher;
53}; 57};