From 96a870552eb56f4401c49e1e008ababd7f2dc7d7 Mon Sep 17 00:00:00 2001 From: garakmon Date: Wed, 8 Apr 2020 00:42:38 -0400 Subject: [PATCH] monitor files for changes - add fileWatcher to project that monitors changes to most files (except map and layout json files) - add config setting whether to monitor files for changes (monitor_files) --- include/config.h | 4 ++++ include/project.h | 2 ++ src/config.cpp | 16 ++++++++++++++++ src/mainwindow.cpp | 21 ++++++++++++++++++++ src/project.cpp | 48 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/include/config.h b/include/config.h index a289391a..d5e5e41c 100644 --- a/include/config.h +++ b/include/config.h @@ -41,6 +41,7 @@ public: this->metatilesZoom = 30; this->showPlayerView = false; this->showCursorTile = true; + this->monitorFiles = true; this->regionMapDimensions = QSize(32, 20); this->theme = "default"; } @@ -53,6 +54,7 @@ public: void setMetatilesZoom(int zoom); void setShowPlayerView(bool enabled); void setShowCursorTile(bool enabled); + void setMonitorFiles(bool monitor); void setRegionMapDimensions(int width, int height); void setTheme(QString theme); QString getRecentProject(); @@ -64,6 +66,7 @@ public: int getMetatilesZoom(); bool getShowPlayerView(); bool getShowCursorTile(); + bool getMonitorFiles(); QSize getRegionMapDimensions(); QString getTheme(); protected: @@ -87,6 +90,7 @@ private: int metatilesZoom; bool showPlayerView; bool showCursorTile; + bool monitorFiles; QSize regionMapDimensions; QString theme; }; diff --git a/include/project.h b/include/project.h index 431d94df..521c54ab 100644 --- a/include/project.h +++ b/include/project.h @@ -15,6 +15,7 @@ #include #include #include +#include static QString NONE_MAP_CONSTANT = "MAP_NONE"; static QString NONE_MAP_NAME = "None"; @@ -61,6 +62,7 @@ public: QMap metatileBehaviorMapInverse; QMap facingDirections; ParseUtil parser; + QFileSystemWatcher fileWatcher; void set_root(QString); diff --git a/src/config.cpp b/src/config.cpp index e0678ad3..f96c3f2a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -157,6 +157,12 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { if (!ok) { logWarn(QString("Invalid config value for show_cursor_tile: '%1'. Must be 0 or 1.").arg(value)); } + } else if (key == "monitor_files") { + bool ok; + this->showCursorTile = value.toInt(&ok); + if (!ok) { + logWarn(QString("Invalid config value for monitor_files: '%1'. Must be 0 or 1.").arg(value)); + } } else if (key == "region_map_dimensions") { bool ok1, ok2; QStringList dims = value.split("x"); @@ -190,6 +196,7 @@ QMap PorymapConfig::getKeyValueMap() { map.insert("metatiles_zoom", QString("%1").arg(this->metatilesZoom)); map.insert("show_player_view", this->showPlayerView ? "1" : "0"); map.insert("show_cursor_tile", this->showCursorTile ? "1" : "0"); + map.insert("monitor_files", this->monitorFiles ? "1" : "0"); map.insert("region_map_dimensions", QString("%1x%2").arg(this->regionMapDimensions.width()) .arg(this->regionMapDimensions.height())); map.insert("theme", this->theme); @@ -233,6 +240,11 @@ void PorymapConfig::setPrettyCursors(bool enabled) { this->save(); } +void PorymapConfig::setMonitorFiles(bool monitor) { + this->monitorFiles = monitor; + this->save(); +} + void PorymapConfig::setGeometry(QByteArray windowGeometry_, QByteArray windowState_, QByteArray mapSplitterState_, QByteArray eventsSlpitterState_, QByteArray mainSplitterState_) { this->windowGeometry = windowGeometry_; @@ -315,6 +327,10 @@ bool PorymapConfig::getShowCursorTile() { return this->showCursorTile; } +bool PorymapConfig::getMonitorFiles() { + return this->monitorFiles; +} + QSize PorymapConfig::getRegionMapDimensions() { return this->regionMapDimensions; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d304ada9..40dc2671 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -301,6 +301,27 @@ bool MainWindow::openProject(QString dir) { if (!already_open) { editor->closeProject(); editor->project = new Project; + QObject::connect(&editor->project->fileWatcher, &QFileSystemWatcher::fileChanged, [this](QString changed){ + QMessageBox notice(this); + notice.setText("File Changed"); + notice.setInformativeText(QString("The file %1 has changed on disk. Would you like to reload the project?") + .arg(changed.remove(editor->project->root + "/"))); + notice.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + notice.setIcon(QMessageBox::Question); + + QCheckBox showAgainCheck("Do not ask again."); + notice.setCheckBox(&showAgainCheck); + + int choice = notice.exec(); + if (choice == QMessageBox::Yes) { + on_action_Reload_Project_triggered(); + } else if (choice == QMessageBox::No) { + if (showAgainCheck.isChecked()) { + porymapConfig.setMonitorFiles(false); + this->editor->project->fileWatcher.blockSignals(true); + } + } + }); editor->project->set_root(dir); success = loadDataStructures() && populateMapList() diff --git a/src/project.cpp b/src/project.cpp index 533221b7..b29ac6be 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -514,6 +514,7 @@ bool Project::readMapLayouts() { mapLayoutsTable.clear(); QString layoutsFilepath = QString("%1/data/layouts/layouts.json").arg(root); + fileWatcher.addPath(layoutsFilepath); QJsonDocument layoutsDoc; if (!parser.tryParseJsonFile(&layoutsDoc, layoutsFilepath)) { logError(QString("Failed to read map layouts from %1").arg(layoutsFilepath)); @@ -1738,6 +1739,7 @@ bool Project::readWildMonData() { } QString wildMonJsonFilepath = QString("%1/src/data/wild_encounters.json").arg(root); + fileWatcher.addPath(wildMonJsonFilepath); QJsonDocument wildMonsJsonDoc; if (!parser.tryParseJsonFile(&wildMonsJsonDoc, wildMonJsonFilepath)) { logError(QString("Failed to read wild encounters from %1").arg(wildMonJsonFilepath)); @@ -1806,6 +1808,7 @@ bool Project::readMapGroups() { mapGroups->clear(); QString mapGroupsFilepath = QString("%1/data/maps/map_groups.json").arg(root); + fileWatcher.addPath(mapGroupsFilepath); QJsonDocument mapGroupsDoc; if (!parser.tryParseJsonFile(&mapGroupsDoc, mapGroupsFilepath)) { logError(QString("Failed to read map groups from %1").arg(mapGroupsFilepath)); @@ -1975,7 +1978,9 @@ QMap Project::getTilesetLabels() { bool Project::readTilesetProperties() { QStringList definePrefixes; definePrefixes << "NUM_"; - QMap defines = parser.readCDefines("include/fieldmap.h", definePrefixes); + QString filename = "include/fieldmap.h"; + fileWatcher.addPath(root + "/" + filename); + QMap defines = parser.readCDefines(filename, definePrefixes); auto it = defines.find("NUM_TILES_IN_PRIMARY"); if (it != defines.end()) { @@ -2034,6 +2039,7 @@ bool Project::readRegionMapSections() { QStringList prefixes = (QStringList() << "MAPSEC_"); QString filename = "include/constants/region_map_sections.h"; + fileWatcher.addPath(root + "/" + filename); this->mapSectionNameToValue = parser.readCDefines(filename, prefixes); if (this->mapSectionNameToValue.isEmpty()) { logError(QString("Failed to read region map sections from %1.").arg(filename)); @@ -2050,6 +2056,7 @@ bool Project::readHealLocations() { dataQualifiers.clear(); flyableMaps.clear(); QString filename = "src/data/heal_locations.h"; + fileWatcher.addPath(root + "/" + filename); QString text = parser.readTextFile(root + "/" + filename); text.replace(QRegularExpression("//.*?(\r\n?|\n)|/\\*.*?\\*/", QRegularExpression::DotMatchesEverythingOption), ""); @@ -2097,6 +2104,7 @@ bool Project::readItemNames() { itemNames->clear(); QStringList prefixes = (QStringList() << "ITEM_"); QString filename = "include/constants/items.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, itemNames); if (itemNames->isEmpty()) { logError(QString("Failed to read item constants from %1").arg(filename)); @@ -2109,6 +2117,7 @@ bool Project::readFlagNames() { flagNames->clear(); QStringList prefixes = (QStringList() << "FLAG_"); QString filename = "include/constants/flags.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, flagNames); if (flagNames->isEmpty()) { logError(QString("Failed to read flag constants from %1").arg(filename)); @@ -2121,6 +2130,7 @@ bool Project::readVarNames() { varNames->clear(); QStringList prefixes = (QStringList() << "VAR_"); QString filename = "include/constants/vars.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, varNames); if (varNames->isEmpty()) { logError(QString("Failed to read var constants from %1").arg(filename)); @@ -2133,6 +2143,7 @@ bool Project::readMovementTypes() { movementTypes->clear(); QStringList prefixes = (QStringList() << "MOVEMENT_TYPE_"); QString filename = "include/constants/event_object_movement.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, movementTypes); if (movementTypes->isEmpty()) { logError(QString("Failed to read movement type constants from %1").arg(filename)); @@ -2143,6 +2154,7 @@ bool Project::readMovementTypes() { bool Project::readInitialFacingDirections() { QString filename = "src/event_object_movement.c"; + fileWatcher.addPath(root + "/" + filename); facingDirections = parser.readNamedIndexCArray(filename, "gInitialMovementTypeFacingDirections"); if (facingDirections.isEmpty()) { logError(QString("Failed to read initial movement type facing directions from %1").arg(filename)); @@ -2155,6 +2167,7 @@ bool Project::readMapTypes() { mapTypes->clear(); QStringList prefixes = (QStringList() << "MAP_TYPE_"); QString filename = "include/constants/map_types.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, mapTypes); if (mapTypes->isEmpty()) { logError(QString("Failed to read map type constants from %1").arg(filename)); @@ -2167,6 +2180,7 @@ bool Project::readMapBattleScenes() { mapBattleScenes->clear(); QStringList prefixes = (QStringList() << "MAP_BATTLE_SCENE_"); QString filename = "include/constants/map_types.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted("include/constants/map_types.h", prefixes, mapBattleScenes); if (mapBattleScenes->isEmpty()) { logError(QString("Failed to read map battle scene constants from %1").arg(filename)); @@ -2179,6 +2193,7 @@ bool Project::readWeatherNames() { weatherNames->clear(); QStringList prefixes = (QStringList() << "\\bWEATHER_"); QString filename = "include/constants/weather.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, weatherNames); if (weatherNames->isEmpty()) { logError(QString("Failed to read weather constants from %1").arg(filename)); @@ -2191,6 +2206,7 @@ bool Project::readCoordEventWeatherNames() { coordEventWeatherNames->clear(); QStringList prefixes = (QStringList() << "COORD_EVENT_WEATHER_"); QString filename = "include/constants/weather.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, coordEventWeatherNames); if (coordEventWeatherNames->isEmpty()) { logError(QString("Failed to read coord event weather constants from %1").arg(filename)); @@ -2203,6 +2219,7 @@ bool Project::readSecretBaseIds() { secretBaseIds->clear(); QStringList prefixes = (QStringList() << "SECRET_BASE_[A-Za-z0-9_]*_[0-9]+"); QString filename = "include/constants/secret_bases.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, secretBaseIds); if (secretBaseIds->isEmpty()) { logError(QString("Failed to read secret base id constants from %1").arg(filename)); @@ -2215,6 +2232,7 @@ bool Project::readBgEventFacingDirections() { bgEventFacingDirections->clear(); QStringList prefixes = (QStringList() << "BG_EVENT_PLAYER_FACING_"); QString filename = "include/constants/event_bg.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, bgEventFacingDirections); if (bgEventFacingDirections->isEmpty()) { logError(QString("Failed to read bg event facing direction constants from %1").arg(filename)); @@ -2227,6 +2245,7 @@ bool Project::readTrainerTypes() { trainerTypes->clear(); QStringList prefixes = (QStringList() << "TRAINER_TYPE_"); QString filename = "include/constants/trainer_types.h"; + fileWatcher.addPath(root + "/" + filename); parser.readCDefinesSorted(filename, prefixes, trainerTypes); if (trainerTypes->isEmpty()) { logError(QString("Failed to read trainer type constants from %1").arg(filename)); @@ -2241,6 +2260,7 @@ bool Project::readMetatileBehaviors() { QStringList prefixes = (QStringList() << "MB_"); QString filename = "include/constants/metatile_behaviors.h"; + fileWatcher.addPath(root + "/" + filename); this->metatileBehaviorMap = parser.readCDefines(filename, prefixes); if (this->metatileBehaviorMap.isEmpty()) { logError(QString("Failed to read metatile behaviors from %1.").arg(filename)); @@ -2256,7 +2276,9 @@ bool Project::readMetatileBehaviors() { QStringList Project::getSongNames() { QStringList songDefinePrefixes; songDefinePrefixes << "SE_" << "MUS_"; - QMap songDefines = parser.readCDefines("include/constants/songs.h", songDefinePrefixes); + QString filename = "include/constants/songs.h"; + fileWatcher.addPath(root + "/" + filename); + QMap songDefines = parser.readCDefines(filename, songDefinePrefixes); QStringList names = songDefines.keys(); this->defaultSong = names.value(0, "MUS_DUMMY"); @@ -2267,7 +2289,9 @@ QMap Project::getEventObjGfxConstants() { QStringList eventObjGfxPrefixes; eventObjGfxPrefixes << "OBJ_EVENT_GFX_"; - QMap constants = parser.readCDefines("include/constants/event_objects.h", eventObjGfxPrefixes); + QString filename = "include/constants/event_objects.h"; + fileWatcher.addPath(root + "/" + filename); + QMap constants = parser.readCDefines(filename, eventObjGfxPrefixes); return constants; } @@ -2275,7 +2299,9 @@ QMap Project::getEventObjGfxConstants() { bool Project::readMiscellaneousConstants() { miscConstants.clear(); if (projectConfig.getEncounterJsonActive()) { - QMap pokemonDefines = parser.readCDefines("include/constants/pokemon.h", QStringList() << "MIN_" << "MAX_"); + QString filename = "include/constants/pokemon.h"; + fileWatcher.addPath(root + "/" + filename); + QMap pokemonDefines = parser.readCDefines(filename, QStringList() << "MIN_" << "MAX_"); miscConstants.insert("max_level_define", pokemonDefines.value("MAX_LEVEL") > pokemonDefines.value("MIN_LEVEL") ? pokemonDefines.value("MAX_LEVEL") : 100); miscConstants.insert("min_level_define", pokemonDefines.value("MIN_LEVEL") < pokemonDefines.value("MAX_LEVEL") ? pokemonDefines.value("MIN_LEVEL") : 1); } @@ -2322,6 +2348,11 @@ void Project::loadEventPixmaps(QList objects) { QMap constants = getEventObjGfxConstants(); + fileWatcher.addPaths(QStringList() << root + "/" + "src/data/object_events/object_event_graphics_info_pointers.h" + << root + "/" + "src/data/object_events/object_event_graphics_info.h" + << root + "/" + "src/data/object_events/object_event_pic_tables.h" + << root + "/" + "src/data/object_events/object_event_graphics.h"); + QMap pointerHash = parser.readNamedIndexCArray("src/data/object_events/object_event_graphics_info_pointers.h", "gObjectEventGraphicsInfoPointers"); for (Event *object : objects) { @@ -2383,10 +2414,13 @@ void Project::loadEventPixmaps(QList objects) { bool Project::readSpeciesIconPaths() { speciesToIconPath.clear(); - QString filename = "src/pokemon_icon.c"; - QMap monIconNames = parser.readNamedIndexCArray(filename, "gMonIconTable"); + QString srcfilename = "src/pokemon_icon.c"; + QString incfilename = "src/data/graphics/pokemon.h"; + fileWatcher.addPath(root + "/" + srcfilename); + fileWatcher.addPath(root + "/" + incfilename); + QMap monIconNames = parser.readNamedIndexCArray(srcfilename, "gMonIconTable"); for (QString species : monIconNames.keys()) { - QString path = parser.readCIncbin("src/data/graphics/pokemon.h", monIconNames.value(species)); + QString path = parser.readCIncbin(incfilename, monIconNames.value(species)); speciesToIconPath.insert(species, root + "/" + path.replace("4bpp", "png")); } return true;