diff options
| author | 2018-01-11 19:21:20 -0700 | |
|---|---|---|
| committer | 2018-01-12 19:11:03 -0700 | |
| commit | ebf9a784a9f7f4148a669dbb39e7cd50df779a14 (patch) | |
| tree | d585685a1c0a34b903af1d086d62560bf56bb29f /src/citra_qt/game_list.cpp | |
| parent | config: Default CPU core to Unicorn. (diff) | |
| download | yuzu-ebf9a784a9f7f4148a669dbb39e7cd50df779a14.tar.gz yuzu-ebf9a784a9f7f4148a669dbb39e7cd50df779a14.tar.xz yuzu-ebf9a784a9f7f4148a669dbb39e7cd50df779a14.zip | |
Massive removal of unused modules
Diffstat (limited to 'src/citra_qt/game_list.cpp')
| -rw-r--r-- | src/citra_qt/game_list.cpp | 422 |
1 files changed, 0 insertions, 422 deletions
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp deleted file mode 100644 index a8e3541cd..000000000 --- a/src/citra_qt/game_list.cpp +++ /dev/null | |||
| @@ -1,422 +0,0 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QApplication> | ||
| 6 | #include <QFileInfo> | ||
| 7 | #include <QHeaderView> | ||
| 8 | #include <QKeyEvent> | ||
| 9 | #include <QMenu> | ||
| 10 | #include <QThreadPool> | ||
| 11 | #include "common/common_paths.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #include "core/loader/loader.h" | ||
| 15 | #include "game_list.h" | ||
| 16 | #include "game_list_p.h" | ||
| 17 | #include "ui_settings.h" | ||
| 18 | |||
| 19 | GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) { | ||
| 20 | this->gamelist = gamelist; | ||
| 21 | edit_filter_text_old = ""; | ||
| 22 | } | ||
| 23 | |||
| 24 | // EventFilter in order to process systemkeys while editing the searchfield | ||
| 25 | bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { | ||
| 26 | // If it isn't a KeyRelease event then continue with standard event processing | ||
| 27 | if (event->type() != QEvent::KeyRelease) | ||
| 28 | return QObject::eventFilter(obj, event); | ||
| 29 | |||
| 30 | QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); | ||
| 31 | int rowCount = gamelist->tree_view->model()->rowCount(); | ||
| 32 | QString edit_filter_text = gamelist->search_field->edit_filter->text().toLower(); | ||
| 33 | |||
| 34 | // If the searchfield's text hasn't changed special function keys get checked | ||
| 35 | // If no function key changes the searchfield's text the filter doesn't need to get reloaded | ||
| 36 | if (edit_filter_text == edit_filter_text_old) { | ||
| 37 | switch (keyEvent->key()) { | ||
| 38 | // Escape: Resets the searchfield | ||
| 39 | case Qt::Key_Escape: { | ||
| 40 | if (edit_filter_text_old.isEmpty()) { | ||
| 41 | return QObject::eventFilter(obj, event); | ||
| 42 | } else { | ||
| 43 | gamelist->search_field->edit_filter->clear(); | ||
| 44 | edit_filter_text = ""; | ||
| 45 | } | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | // Return and Enter | ||
| 49 | // If the enter key gets pressed first checks how many and which entry is visible | ||
| 50 | // If there is only one result launch this game | ||
| 51 | case Qt::Key_Return: | ||
| 52 | case Qt::Key_Enter: { | ||
| 53 | QStandardItemModel* item_model = new QStandardItemModel(gamelist->tree_view); | ||
| 54 | QModelIndex root_index = item_model->invisibleRootItem()->index(); | ||
| 55 | QStandardItem* child_file; | ||
| 56 | QString file_path; | ||
| 57 | int resultCount = 0; | ||
| 58 | for (int i = 0; i < rowCount; ++i) { | ||
| 59 | if (!gamelist->tree_view->isRowHidden(i, root_index)) { | ||
| 60 | ++resultCount; | ||
| 61 | child_file = gamelist->item_model->item(i, 0); | ||
| 62 | file_path = child_file->data(GameListItemPath::FullPathRole).toString(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | if (resultCount == 1) { | ||
| 66 | // To avoid loading error dialog loops while confirming them using enter | ||
| 67 | // Also users usually want to run a diffrent game after closing one | ||
| 68 | gamelist->search_field->edit_filter->setText(""); | ||
| 69 | edit_filter_text = ""; | ||
| 70 | emit gamelist->GameChosen(file_path); | ||
| 71 | } else { | ||
| 72 | return QObject::eventFilter(obj, event); | ||
| 73 | } | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | default: | ||
| 77 | return QObject::eventFilter(obj, event); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | edit_filter_text_old = edit_filter_text; | ||
| 81 | return QObject::eventFilter(obj, event); | ||
| 82 | } | ||
| 83 | |||
| 84 | void GameList::SearchField::setFilterResult(int visible, int total) { | ||
| 85 | QString result_of_text = tr("of"); | ||
| 86 | QString result_text; | ||
| 87 | if (total == 1) { | ||
| 88 | result_text = tr("result"); | ||
| 89 | } else { | ||
| 90 | result_text = tr("results"); | ||
| 91 | } | ||
| 92 | label_filter_result->setText( | ||
| 93 | QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text)); | ||
| 94 | } | ||
| 95 | |||
| 96 | void GameList::SearchField::clear() { | ||
| 97 | edit_filter->setText(""); | ||
| 98 | } | ||
| 99 | |||
| 100 | void GameList::SearchField::setFocus() { | ||
| 101 | if (edit_filter->isVisible()) { | ||
| 102 | edit_filter->setFocus(); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} { | ||
| 107 | KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); | ||
| 108 | layout_filter = new QHBoxLayout; | ||
| 109 | layout_filter->setMargin(8); | ||
| 110 | label_filter = new QLabel; | ||
| 111 | label_filter->setText(tr("Filter:")); | ||
| 112 | edit_filter = new QLineEdit; | ||
| 113 | edit_filter->setText(""); | ||
| 114 | edit_filter->setPlaceholderText(tr("Enter pattern to filter")); | ||
| 115 | edit_filter->installEventFilter(keyReleaseEater); | ||
| 116 | edit_filter->setClearButtonEnabled(true); | ||
| 117 | connect(edit_filter, SIGNAL(textChanged(const QString&)), parent, | ||
| 118 | SLOT(onTextChanged(const QString&))); | ||
| 119 | label_filter_result = new QLabel; | ||
| 120 | button_filter_close = new QToolButton(this); | ||
| 121 | button_filter_close->setText("X"); | ||
| 122 | button_filter_close->setCursor(Qt::ArrowCursor); | ||
| 123 | button_filter_close->setStyleSheet("QToolButton{ border: none; padding: 0px; color: " | ||
| 124 | "#000000; font-weight: bold; background: #F0F0F0; }" | ||
| 125 | "QToolButton:hover{ border: none; padding: 0px; color: " | ||
| 126 | "#EEEEEE; font-weight: bold; background: #E81123}"); | ||
| 127 | connect(button_filter_close, SIGNAL(clicked()), parent, SLOT(onFilterCloseClicked())); | ||
| 128 | layout_filter->setSpacing(10); | ||
| 129 | layout_filter->addWidget(label_filter); | ||
| 130 | layout_filter->addWidget(edit_filter); | ||
| 131 | layout_filter->addWidget(label_filter_result); | ||
| 132 | layout_filter->addWidget(button_filter_close); | ||
| 133 | setLayout(layout_filter); | ||
| 134 | } | ||
| 135 | |||
| 136 | /** | ||
| 137 | * Checks if all words separated by spaces are contained in another string | ||
| 138 | * This offers a word order insensitive search function | ||
| 139 | * | ||
| 140 | * @param String that gets checked if it contains all words of the userinput string | ||
| 141 | * @param String containing all words getting checked | ||
| 142 | * @return true if the haystack contains all words of userinput | ||
| 143 | */ | ||
| 144 | bool GameList::containsAllWords(QString haystack, QString userinput) { | ||
| 145 | QStringList userinput_split = userinput.split(" ", QString::SplitBehavior::SkipEmptyParts); | ||
| 146 | return std::all_of(userinput_split.begin(), userinput_split.end(), | ||
| 147 | [haystack](QString s) { return haystack.contains(s); }); | ||
| 148 | } | ||
| 149 | |||
| 150 | // Event in order to filter the gamelist after editing the searchfield | ||
| 151 | void GameList::onTextChanged(const QString& newText) { | ||
| 152 | int rowCount = tree_view->model()->rowCount(); | ||
| 153 | QString edit_filter_text = newText.toLower(); | ||
| 154 | |||
| 155 | QModelIndex root_index = item_model->invisibleRootItem()->index(); | ||
| 156 | |||
| 157 | // If the searchfield is empty every item is visible | ||
| 158 | // Otherwise the filter gets applied | ||
| 159 | if (edit_filter_text.isEmpty()) { | ||
| 160 | for (int i = 0; i < rowCount; ++i) { | ||
| 161 | tree_view->setRowHidden(i, root_index, false); | ||
| 162 | } | ||
| 163 | search_field->setFilterResult(rowCount, rowCount); | ||
| 164 | } else { | ||
| 165 | QStandardItem* child_file; | ||
| 166 | QString file_path, file_name, file_title, file_programmid; | ||
| 167 | int result_count = 0; | ||
| 168 | for (int i = 0; i < rowCount; ++i) { | ||
| 169 | child_file = item_model->item(i, 0); | ||
| 170 | file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); | ||
| 171 | file_name = file_path.mid(file_path.lastIndexOf("/") + 1); | ||
| 172 | file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); | ||
| 173 | file_programmid = | ||
| 174 | child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); | ||
| 175 | |||
| 176 | // Only items which filename in combination with its title contains all words | ||
| 177 | // that are in the searchfiel will be visible in the gamelist | ||
| 178 | // The search is case insensitive because of toLower() | ||
| 179 | // I decided not to use Qt::CaseInsensitive in containsAllWords to prevent | ||
| 180 | // multiple conversions of edit_filter_text for each game in the gamelist | ||
| 181 | if (containsAllWords(file_name.append(" ").append(file_title), edit_filter_text) || | ||
| 182 | (file_programmid.count() == 16 && edit_filter_text.contains(file_programmid))) { | ||
| 183 | tree_view->setRowHidden(i, root_index, false); | ||
| 184 | ++result_count; | ||
| 185 | } else { | ||
| 186 | tree_view->setRowHidden(i, root_index, true); | ||
| 187 | } | ||
| 188 | search_field->setFilterResult(result_count, rowCount); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | void GameList::onFilterCloseClicked() { | ||
| 194 | main_window->filterBarSetChecked(false); | ||
| 195 | } | ||
| 196 | |||
| 197 | GameList::GameList(GMainWindow* parent) : QWidget{parent} { | ||
| 198 | watcher = new QFileSystemWatcher(this); | ||
| 199 | connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); | ||
| 200 | |||
| 201 | this->main_window = parent; | ||
| 202 | layout = new QVBoxLayout; | ||
| 203 | tree_view = new QTreeView; | ||
| 204 | search_field = new SearchField(this); | ||
| 205 | item_model = new QStandardItemModel(tree_view); | ||
| 206 | tree_view->setModel(item_model); | ||
| 207 | |||
| 208 | tree_view->setAlternatingRowColors(true); | ||
| 209 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 210 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 211 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 212 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 213 | tree_view->setSortingEnabled(true); | ||
| 214 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 215 | tree_view->setUniformRowHeights(true); | ||
| 216 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 217 | |||
| 218 | item_model->insertColumns(0, COLUMN_COUNT); | ||
| 219 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | ||
| 220 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | ||
| 221 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | ||
| 222 | |||
| 223 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | ||
| 224 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | ||
| 225 | |||
| 226 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 227 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 228 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 229 | |||
| 230 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 231 | layout->setSpacing(0); | ||
| 232 | layout->addWidget(tree_view); | ||
| 233 | layout->addWidget(search_field); | ||
| 234 | setLayout(layout); | ||
| 235 | } | ||
| 236 | |||
| 237 | GameList::~GameList() { | ||
| 238 | emit ShouldCancelWorker(); | ||
| 239 | } | ||
| 240 | |||
| 241 | void GameList::setFilterFocus() { | ||
| 242 | if (tree_view->model()->rowCount() > 0) { | ||
| 243 | search_field->setFocus(); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | void GameList::setFilterVisible(bool visibility) { | ||
| 248 | search_field->setVisible(visibility); | ||
| 249 | } | ||
| 250 | |||
| 251 | void GameList::clearFilter() { | ||
| 252 | search_field->clear(); | ||
| 253 | } | ||
| 254 | |||
| 255 | void GameList::AddEntry(const QList<QStandardItem*>& entry_items) { | ||
| 256 | item_model->invisibleRootItem()->appendRow(entry_items); | ||
| 257 | } | ||
| 258 | |||
| 259 | void GameList::ValidateEntry(const QModelIndex& item) { | ||
| 260 | // We don't care about the individual QStandardItem that was selected, but its row. | ||
| 261 | int row = item_model->itemFromIndex(item)->row(); | ||
| 262 | QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); | ||
| 263 | QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); | ||
| 264 | |||
| 265 | if (file_path.isEmpty()) | ||
| 266 | return; | ||
| 267 | std::string std_file_path(file_path.toStdString()); | ||
| 268 | if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path)) | ||
| 269 | return; | ||
| 270 | // Users usually want to run a diffrent game after closing one | ||
| 271 | search_field->clear(); | ||
| 272 | emit GameChosen(file_path); | ||
| 273 | } | ||
| 274 | |||
| 275 | void GameList::DonePopulating(QStringList watch_list) { | ||
| 276 | // Clear out the old directories to watch for changes and add the new ones | ||
| 277 | auto watch_dirs = watcher->directories(); | ||
| 278 | if (!watch_dirs.isEmpty()) { | ||
| 279 | watcher->removePaths(watch_dirs); | ||
| 280 | } | ||
| 281 | // Workaround: Add the watch paths in chunks to allow the gui to refresh | ||
| 282 | // This prevents the UI from stalling when a large number of watch paths are added | ||
| 283 | // Also artificially caps the watcher to a certain number of directories | ||
| 284 | constexpr int LIMIT_WATCH_DIRECTORIES = 5000; | ||
| 285 | constexpr int SLICE_SIZE = 25; | ||
| 286 | int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); | ||
| 287 | for (int i = 0; i < len; i += SLICE_SIZE) { | ||
| 288 | watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); | ||
| 289 | QCoreApplication::processEvents(); | ||
| 290 | } | ||
| 291 | tree_view->setEnabled(true); | ||
| 292 | int rowCount = tree_view->model()->rowCount(); | ||
| 293 | search_field->setFilterResult(rowCount, rowCount); | ||
| 294 | if (rowCount > 0) { | ||
| 295 | search_field->setFocus(); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | void GameList::PopupContextMenu(const QPoint& menu_location) { | ||
| 300 | QModelIndex item = tree_view->indexAt(menu_location); | ||
| 301 | if (!item.isValid()) | ||
| 302 | return; | ||
| 303 | |||
| 304 | int row = item_model->itemFromIndex(item)->row(); | ||
| 305 | QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); | ||
| 306 | u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); | ||
| 307 | |||
| 308 | QMenu context_menu; | ||
| 309 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | ||
| 310 | open_save_location->setEnabled(program_id != 0); | ||
| 311 | connect(open_save_location, &QAction::triggered, | ||
| 312 | [&]() { emit OpenSaveFolderRequested(program_id); }); | ||
| 313 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | ||
| 314 | } | ||
| 315 | |||
| 316 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | ||
| 317 | if (!FileUtil::Exists(dir_path.toStdString()) || | ||
| 318 | !FileUtil::IsDirectory(dir_path.toStdString())) { | ||
| 319 | LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data()); | ||
| 320 | search_field->setFilterResult(0, 0); | ||
| 321 | return; | ||
| 322 | } | ||
| 323 | |||
| 324 | tree_view->setEnabled(false); | ||
| 325 | // Delete any rows that might already exist if we're repopulating | ||
| 326 | item_model->removeRows(0, item_model->rowCount()); | ||
| 327 | |||
| 328 | emit ShouldCancelWorker(); | ||
| 329 | |||
| 330 | GameListWorker* worker = new GameListWorker(dir_path, deep_scan); | ||
| 331 | |||
| 332 | connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection); | ||
| 333 | connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating, | ||
| 334 | Qt::QueuedConnection); | ||
| 335 | // Use DirectConnection here because worker->Cancel() is thread-safe and we want it to cancel | ||
| 336 | // without delay. | ||
| 337 | connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel, | ||
| 338 | Qt::DirectConnection); | ||
| 339 | |||
| 340 | QThreadPool::globalInstance()->start(worker); | ||
| 341 | current_worker = std::move(worker); | ||
| 342 | } | ||
| 343 | |||
| 344 | void GameList::SaveInterfaceLayout() { | ||
| 345 | UISettings::values.gamelist_header_state = tree_view->header()->saveState(); | ||
| 346 | } | ||
| 347 | |||
| 348 | void GameList::LoadInterfaceLayout() { | ||
| 349 | auto header = tree_view->header(); | ||
| 350 | if (!header->restoreState(UISettings::values.gamelist_header_state)) { | ||
| 351 | // We are using the name column to display icons and titles | ||
| 352 | // so make it as large as possible as default. | ||
| 353 | header->resizeSection(COLUMN_NAME, header->width()); | ||
| 354 | } | ||
| 355 | |||
| 356 | item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); | ||
| 357 | } | ||
| 358 | |||
| 359 | const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf", | ||
| 360 | "cci", "cxi", "app"}; | ||
| 361 | |||
| 362 | static bool HasSupportedFileExtension(const std::string& file_name) { | ||
| 363 | QFileInfo file = QFileInfo(file_name.c_str()); | ||
| 364 | return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); | ||
| 365 | } | ||
| 366 | |||
| 367 | void GameList::RefreshGameDirectory() { | ||
| 368 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | ||
| 369 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | ||
| 370 | search_field->clear(); | ||
| 371 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { | ||
| 376 | const auto callback = [this, recursion](unsigned* num_entries_out, const std::string& directory, | ||
| 377 | const std::string& virtual_name) -> bool { | ||
| 378 | std::string physical_name = directory + DIR_SEP + virtual_name; | ||
| 379 | |||
| 380 | if (stop_processing) | ||
| 381 | return false; // Breaks the callback loop. | ||
| 382 | |||
| 383 | bool is_dir = FileUtil::IsDirectory(physical_name); | ||
| 384 | if (!is_dir && HasSupportedFileExtension(physical_name)) { | ||
| 385 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); | ||
| 386 | if (!loader) | ||
| 387 | return true; | ||
| 388 | |||
| 389 | std::vector<u8> smdh; | ||
| 390 | loader->ReadIcon(smdh); | ||
| 391 | |||
| 392 | u64 program_id = 0; | ||
| 393 | loader->ReadProgramId(program_id); | ||
| 394 | |||
| 395 | emit EntryReady({ | ||
| 396 | new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), | ||
| 397 | new GameListItem( | ||
| 398 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||
| 399 | new GameListItemSize(FileUtil::GetSize(physical_name)), | ||
| 400 | }); | ||
| 401 | } else if (is_dir && recursion > 0) { | ||
| 402 | watch_list.append(QString::fromStdString(physical_name)); | ||
| 403 | AddFstEntriesToGameList(physical_name, recursion - 1); | ||
| 404 | } | ||
| 405 | |||
| 406 | return true; | ||
| 407 | }; | ||
| 408 | |||
| 409 | FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); | ||
| 410 | } | ||
| 411 | |||
| 412 | void GameListWorker::run() { | ||
| 413 | stop_processing = false; | ||
| 414 | watch_list.append(dir_path); | ||
| 415 | AddFstEntriesToGameList(dir_path.toStdString(), deep_scan ? 256 : 0); | ||
| 416 | emit Finished(watch_list); | ||
| 417 | } | ||
| 418 | |||
| 419 | void GameListWorker::Cancel() { | ||
| 420 | this->disconnect(); | ||
| 421 | stop_processing = true; | ||
| 422 | } | ||