diff --git a/include/core/map.h b/include/core/map.h index b223536d..3df0ed88 100644 --- a/include/core/map.h +++ b/include/core/map.h @@ -45,7 +45,8 @@ public: void setConstantName(const QString &constantName) { m_constantName = constantName; } QString constantName() const { return m_constantName; } - static QString mapConstantFromName(QString mapName, bool includePrefix = true); + static QString mapConstantFromName(const QString &name); + QString expectedConstantName() const { return Map::mapConstantFromName(m_name); } void setLayout(Layout *layout); Layout* layout() const { return m_layout; } diff --git a/include/core/maplayout.h b/include/core/maplayout.h index da58c4b7..f8fb1b1c 100644 --- a/include/core/maplayout.h +++ b/include/core/maplayout.h @@ -20,9 +20,7 @@ public: Layout() {} Layout(const Layout &other); - static QString layoutConstantFromName(QString mapName); - static QString defaultSuffix(); - + static QString layoutConstantFromName(const QString &name); bool loaded = false; diff --git a/include/core/utility.h b/include/core/utility.h new file mode 100644 index 00000000..b1dbe8bc --- /dev/null +++ b/include/core/utility.h @@ -0,0 +1,13 @@ +#pragma once +#ifndef UTILITY_H +#define UTILITY_H + +#include + +namespace Util { + void numericalModeSort(QStringList &list); + int roundUp(int numToRound, int multiple); + QString toDefineCase(QString input); +} + +#endif // UTILITY_H diff --git a/include/project.h b/include/project.h index f3a1093b..5019a147 100644 --- a/include/project.h +++ b/include/project.h @@ -248,8 +248,6 @@ public: static QString getEmptyMapsecName(); static QString getMapGroupPrefix(); - static void numericalModeSort(QStringList &list); - private: QMap mapSectionDisplayNames; QMap modifiedFileTimestamps; diff --git a/porymap.pro b/porymap.pro index 97265940..2675e700 100644 --- a/porymap.pro +++ b/porymap.pro @@ -51,6 +51,7 @@ SOURCES += src/core/advancemapparser.cpp \ src/core/parseutil.cpp \ src/core/tile.cpp \ src/core/tileset.cpp \ + src/core/utility.cpp \ src/core/validator.cpp \ src/core/regionmap.cpp \ src/core/wildmoninfo.cpp \ @@ -162,6 +163,7 @@ HEADERS += include/core/advancemapparser.h \ include/core/parseutil.h \ include/core/tile.h \ include/core/tileset.h \ + include/core/utility.h \ include/core/validator.h \ include/core/regionmap.h \ include/core/wildmoninfo.h \ diff --git a/src/core/map.cpp b/src/core/map.cpp index d2ab8ef6..a77744b3 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -2,7 +2,7 @@ #include "map.h" #include "imageproviders.h" #include "scripting.h" - +#include "utility.h" #include "editcommands.h" #include @@ -56,14 +56,9 @@ void Map::setLayout(Layout *layout) { } } -QString Map::mapConstantFromName(QString mapName, bool includePrefix) { - // Transform map names of the form 'GraniteCave_B1F` into map constants like 'MAP_GRANITE_CAVE_B1F'. - static const QRegularExpression caseChange("([a-z])([A-Z])"); - QString nameWithUnderscores = mapName.replace(caseChange, "\\1_\\2"); - const QString prefix = includePrefix ? projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix) : ""; - QString withMapAndUppercase = prefix + nameWithUnderscores.toUpper(); - static const QRegularExpression underscores("_+"); - return withMapAndUppercase.replace(underscores, "_"); +// We don't enforce this for existing maps, but for creating new maps we need to formulaically generate a new MAP_NAME ID. +QString Map::mapConstantFromName(const QString &name) { + return projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix) + Util::toDefineCase(name); } int Map::getWidth() const { diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp index 2b52a80f..54f630f7 100644 --- a/src/core/maplayout.cpp +++ b/src/core/maplayout.cpp @@ -4,6 +4,7 @@ #include "scripting.h" #include "imageproviders.h" +#include "utility.h" Layout::Layout(const Layout &other) : Layout() { copyFrom(&other); @@ -32,13 +33,9 @@ void Layout::copyFrom(const Layout *other) { this->border = other->border; } -QString Layout::layoutConstantFromName(QString mapName) { - // Transform map names of the form 'GraniteCave_B1F` into layout constants like 'LAYOUT_GRANITE_CAVE_B1F'. - static const QRegularExpression caseChange("([a-z])([A-Z])"); - QString nameWithUnderscores = mapName.replace(caseChange, "\\1_\\2"); - QString withMapAndUppercase = "LAYOUT_" + nameWithUnderscores.toUpper(); - static const QRegularExpression underscores("_+"); - return withMapAndUppercase.replace(underscores, "_"); +QString Layout::layoutConstantFromName(const QString &name) { + // TODO: Expose "LAYOUT_" to config + return "LAYOUT_" + Util::toDefineCase(name); } Layout::Settings Layout::settings() const { diff --git a/src/core/utility.cpp b/src/core/utility.cpp new file mode 100644 index 00000000..60f0089d --- /dev/null +++ b/src/core/utility.cpp @@ -0,0 +1,40 @@ +#include "utility.h" + +#include +#include + +// Sometimes we want to sort names alphabetically to make them easier to find in large combo box lists. +// QStringList::sort (as of writing) can only sort numbers in lexical order, which has an undesirable +// effect (e.g. MAPSEC_ROUTE_10 comes after MAPSEC_ROUTE_1, rather than MAPSEC_ROUTE_9). +// We can use QCollator to sort these lists with better handling for numbers. +void Util::numericalModeSort(QStringList &list) { + static QCollator collator; + collator.setNumericMode(true); + std::sort(list.begin(), list.end(), collator); +} + +int Util::roundUp(int numToRound, int multiple) { + if (multiple <= 0) + return numToRound; + + int remainder = abs(numToRound) % multiple; + if (remainder == 0) + return numToRound; + + if (numToRound < 0) + return -(abs(numToRound) - remainder); + else + return numToRound + multiple - remainder; +} + +// Ex: input 'GraniteCave_B1F' returns 'GRANITE_CAVE_B1F'. +QString Util::toDefineCase(QString input) { + static const QRegularExpression re_CaseChange("([a-z])([A-Z])"); + input.replace(re_CaseChange, "\\1_\\2"); + + // Remove sequential underscores + static const QRegularExpression re_Underscores("_+"); + input.replace(re_Underscores, "_"); + + return input.toUpper(); +} diff --git a/src/project.cpp b/src/project.cpp index 38b1810b..5e839c8e 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -10,6 +10,7 @@ #include "filedialog.h" #include "validator.h" #include "orderedjson.h" +#include "utility.h" #include #include @@ -349,7 +350,7 @@ Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* t map->setNeedsHealLocation(settings.canFlyTo); // Generate a unique MAP constant. - map->setConstantName(toUniqueIdentifier(Map::mapConstantFromName(map->name()))); + map->setConstantName(toUniqueIdentifier(map->expectedConstantName())); Layout *layout = this->mapLayouts.value(settings.layout.id); if (!layout) { @@ -2074,8 +2075,8 @@ bool Project::readTilesetLabels() { } } - numericalModeSort(this->primaryTilesetLabels); - numericalModeSort(this->secondaryTilesetLabels); + Util::numericalModeSort(this->primaryTilesetLabels); + Util::numericalModeSort(this->secondaryTilesetLabels); bool success = true; if (this->secondaryTilesetLabels.isEmpty()) { @@ -2348,7 +2349,7 @@ bool Project::readRegionMapSections() { if (!this->mapSectionIdNames.contains(defaultName)) { this->mapSectionIdNames.append(defaultName); } - numericalModeSort(this->mapSectionIdNames); + Util::numericalModeSort(this->mapSectionIdNames); return true; } @@ -2372,7 +2373,7 @@ void Project::addNewMapsec(const QString &idName) { } this->mapSectionIdNames.append(idName); - numericalModeSort(this->mapSectionIdNames); + Util::numericalModeSort(this->mapSectionIdNames); this->hasUnsavedDataChanges = true; @@ -2596,7 +2597,7 @@ bool Project::readSongNames() { // Song names don't have a very useful order (esp. if we include SE_* values), so sort them alphabetically. // The default song should be the first in the list, not the first alphabetically, so save that before sorting. this->defaultSong = this->songNames.value(0, "0"); - numericalModeSort(this->songNames); + Util::numericalModeSort(this->songNames); return true; } @@ -3058,14 +3059,3 @@ bool Project::hasUnsavedChanges() { } return false; } - -// TODO: This belongs in a more general utility file, once we have one. -// Sometimes we want to sort names alphabetically to make them easier to find in large combo box lists. -// QStringList::sort (as of writing) can only sort numbers in lexical order, which has an undesirable -// effect (e.g. MAPSEC_ROUTE_10 comes after MAPSEC_ROUTE_1, rather than MAPSEC_ROUTE_9). -// We can use QCollator to sort these lists with better handling for numbers. -void Project::numericalModeSort(QStringList &list) { - QCollator collator; - collator.setNumericMode(true); - std::sort(list.begin(), list.end(), collator); -} diff --git a/src/ui/movablerect.cpp b/src/ui/movablerect.cpp index ba323185..fde7f820 100644 --- a/src/ui/movablerect.cpp +++ b/src/ui/movablerect.cpp @@ -3,6 +3,7 @@ #include #include "movablerect.h" +#include "utility.h" MovableRect::MovableRect(bool *enabled, int width, int height, QRgb color) : QGraphicsRectItem(0, 0, width, height) @@ -22,10 +23,6 @@ void MovableRect::updateLocation(int x, int y) { ************************************************************************ ******************************************************************************/ -int roundUp(int numToRound, int multiple) { - return (numToRound + multiple - 1) & -multiple; -} - ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color) : QObject(parent), MovableRect(enabled, width * 16, height * 16, color) @@ -117,8 +114,8 @@ void ResizableRect::mousePressEvent(QGraphicsSceneMouseEvent *event) { } void ResizableRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - int dx = roundUp(event->scenePos().x() - this->clickedPos.x(), 16); - int dy = roundUp(event->scenePos().y() - this->clickedPos.y(), 16); + int dx = Util::roundUp(event->scenePos().x() - this->clickedPos.x(), 16); + int dy = Util::roundUp(event->scenePos().y() - this->clickedPos.y(), 16); QRect resizedRect = this->clickedRect; diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index 1250e8dd..c536fd07 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -3,6 +3,7 @@ #include "noscrollcombobox.h" #include "prefab.h" #include "filedialog.h" +#include "utility.h" #include #include @@ -293,7 +294,7 @@ QStringList ProjectSettingsEditor::getWarpBehaviorsList() { void ProjectSettingsEditor::setWarpBehaviorsList(QStringList list) { list.removeDuplicates(); - Project::numericalModeSort(list); + Util::numericalModeSort(list); ui->textEdit_WarpBehaviors->setText(list.join("\n")); } diff --git a/src/ui/resizelayoutpopup.cpp b/src/ui/resizelayoutpopup.cpp index 45559b70..20a378b9 100644 --- a/src/ui/resizelayoutpopup.cpp +++ b/src/ui/resizelayoutpopup.cpp @@ -2,12 +2,10 @@ #include "editor.h" #include "movablerect.h" #include "config.h" +#include "utility.h" #include "ui_resizelayoutpopup.h" -// TODO: put this in a util file or something -extern int roundUp(int, int); - CheckeredBgScene::CheckeredBgScene(QObject *parent) : QGraphicsScene(parent) { } void CheckeredBgScene::drawBackground(QPainter *painter, const QRectF &rect) { @@ -62,7 +60,7 @@ void BoundedPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem QVariant BoundedPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionChange && scene()) { QPointF newPos = value.toPointF(); - return QPointF(roundUp(newPos.x(), 16), roundUp(newPos.y(), 16)); + return QPointF(Util::roundUp(newPos.x(), 16), Util::roundUp(newPos.y(), 16)); } else return QGraphicsItem::itemChange(change, value); diff --git a/src/ui/wildmonchart.cpp b/src/ui/wildmonchart.cpp index 69b7b17e..521706d6 100644 --- a/src/ui/wildmonchart.cpp +++ b/src/ui/wildmonchart.cpp @@ -2,6 +2,7 @@ #include "wildmonchart.h" #include "ui_wildmonchart.h" #include "config.h" +#include "utility.h" static const QString baseWindowTitle = QString("Wild Pokémon Summary Charts"); @@ -367,13 +368,7 @@ QChart* WildMonChart::createLevelDistributionChart() { series->attachAxis(axisY); // We round the y-axis max up to a multiple of 5. - auto roundUp = [](int num, int multiple) { - auto remainder = num % multiple; - if (remainder == 0) - return num; - return num + multiple - remainder; - }; - axisY->setMax(roundUp(qCeil(axisY->max()), 5)); + axisY->setMax(Util::roundUp(qCeil(axisY->max()), 5)); return chart; }