mirror of
https://github.com/huderlem/porymap.git
synced 2026-08-22 08:36:06 -05:00
Merge pull request #721 from GriffinRichards/global-defines
Add global define settings
This commit is contained in:
89
forms/newdefinedialog.ui
Normal file
89
forms/newdefinedialog.ui
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NewDefineDialog</class>
|
||||
<widget class="QDialog" name="NewDefineDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>252</width>
|
||||
<height>124</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="form">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_Name">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_Name">
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_NameError">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(255, 0, 0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit_Value"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_Value">
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1582,19 +1582,21 @@
|
||||
<height>499</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<widget class="QToolButton" name="button_HelpFiles">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
<layout class="QGridLayout" name="gridLayout_10" rowstretch="1,2,8">
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/help.ico</normaloff>:/icons/help.ico</iconset>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QWidget" name="widget_ProjectPaths" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="spacing">
|
||||
@@ -1626,7 +1628,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>544</width>
|
||||
<height>437</height>
|
||||
<height>338</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="layout_ProjectPaths">
|
||||
@@ -1646,6 +1648,34 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="button_AddGlobalConstantsFile">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Add additional C files containing #defines or enums. These will be used to resolve unknown symbols during project launch.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Global Constants File...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolButton" name="button_HelpFiles">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/help.ico</normaloff>:/icons/help.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<layout class="QGridLayout" name="gridLayout_GlobalConstantsFiles"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
@@ -1671,8 +1701,8 @@
|
||||
<height>499</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_11">
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolButton" name="button_HelpIdentifiers">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
@@ -1683,7 +1713,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QWidget" name="widget_Identifiers" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_20">
|
||||
<property name="spacing">
|
||||
@@ -1715,7 +1745,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>544</width>
|
||||
<height>437</height>
|
||||
<height>421</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="layout_Identifiers">
|
||||
@@ -1735,6 +1765,36 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="button_AddGlobalConstant">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Add an additional #define name and expression. This may be used to evaluate other #defines during project launch.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Global Constant...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/add.ico</normaloff>:/icons/add.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<layout class="QGridLayout" name="gridLayout_GlobalConstants"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
||||
@@ -345,6 +345,8 @@ public:
|
||||
this->unusedTileCovered = 0x0000;
|
||||
this->unusedTileSplit = 0x0000;
|
||||
this->maxEventsPerGroup = 255;
|
||||
this->globalConstantsFilepaths.clear();
|
||||
this->globalConstants.clear();
|
||||
this->identifiers.clear();
|
||||
this->readKeys.clear();
|
||||
}
|
||||
@@ -417,6 +419,8 @@ public:
|
||||
QMargins playerViewDistance;
|
||||
QList<uint32_t> warpBehaviors;
|
||||
int maxEventsPerGroup;
|
||||
QStringList globalConstantsFilepaths;
|
||||
QMap<QString,QString> globalConstants;
|
||||
|
||||
protected:
|
||||
virtual QString getConfigFilepath() override;
|
||||
|
||||
@@ -43,7 +43,7 @@ class ParseUtil
|
||||
{
|
||||
public:
|
||||
ParseUtil();
|
||||
void set_root(const QString &dir);
|
||||
void setRoot(const QString &dir) { this->root = dir; }
|
||||
static QString readTextFile(const QString &path, QString *error = nullptr);
|
||||
bool cacheFile(const QString &path, QString *error = nullptr);
|
||||
void clearFileCache() { this->fileCache.clear(); }
|
||||
@@ -55,9 +55,13 @@ public:
|
||||
QString readCIncbin(const QString &text, const QString &label);
|
||||
QMap<QString, QString> readCIncbinMulti(const QString &filepath);
|
||||
QStringList readCIncbinArray(const QString &filename, const QString &label);
|
||||
QMap<QString, int> readCDefinesByRegex(const QString &filename, const QSet<QString> ®exList, QString *error = nullptr);
|
||||
QMap<QString, int> readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error = nullptr);
|
||||
QHash<QString, int> readCDefinesByRegex(const QString &filename, const QSet<QString> ®exList, QString *error = nullptr);
|
||||
QHash<QString, int> readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error = nullptr);
|
||||
QStringList readCDefineNames(const QString &filename, const QSet<QString> ®exList, QString *error = nullptr);
|
||||
void loadGlobalCDefinesFromFile(const QString &filename, QString *error = nullptr);
|
||||
void loadGlobalCDefines(const QMap<QString,QString> &defines);
|
||||
void loadGlobalCDefines(const QHash<QString,QString> &defines);
|
||||
void resetCDefines();
|
||||
OrderedMap<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString>& = {});
|
||||
QList<QStringList> getLabelMacros(const QList<QStringList>&, const QString&);
|
||||
QStringList getLabelValues(const QList<QStringList>&, const QString&);
|
||||
@@ -90,8 +94,20 @@ private:
|
||||
QString curDefine;
|
||||
QHash<QString, QString> fileCache;
|
||||
QHash<QString, QStringList> errorMap;
|
||||
int evaluateDefine(const QString&, const QString &, QMap<QString, int>*, QMap<QString, QString>*);
|
||||
QList<Token> tokenizeExpression(QString, QMap<QString, int>*, QMap<QString, QString>*);
|
||||
|
||||
// The maps of define names to values/expressions that are available while parsing C defines.
|
||||
// As the parser reads and evaluates more defines it will update these maps accordingly.
|
||||
QHash<QString, int> knownDefineValues;
|
||||
QHash<QString, QString> knownDefineExpressions;
|
||||
|
||||
// Maps of special define names to values/expressions that take precedence over defines encountered while parsing.
|
||||
// Some (like 'TRUE'/'FALSE') are always present in these maps, others may be specified by the user with 'loadGlobalCDefines' / 'loadGlobalCDefinesFromFile'.
|
||||
QHash<QString, int> globalDefineValues;
|
||||
QHash<QString, QString> globalDefineExpressions;
|
||||
|
||||
int evaluateDefine(const QString &identifier, bool *ok = nullptr);
|
||||
int evaluateExpression(const QString &expression);
|
||||
QList<Token> tokenizeExpression(QString expression);
|
||||
QList<Token> generatePostfix(const QList<Token> &tokens);
|
||||
int evaluatePostfix(const QList<Token> &postfix);
|
||||
void recordError(const QString &message);
|
||||
@@ -100,11 +116,11 @@ private:
|
||||
QString createErrorMessage(const QString &message, const QString &expression);
|
||||
|
||||
struct ParsedDefines {
|
||||
QMap<QString,QString> expressions; // Map of all define names encountered to their expressions
|
||||
QHash<QString,QString> expressions; // Map of all define names encountered to their expressions
|
||||
QStringList filteredNames; // List of define names that matched the search text, in the order that they were encountered
|
||||
};
|
||||
ParsedDefines readCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
|
||||
QMap<QString, int> evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
|
||||
QHash<QString, int> evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error);
|
||||
bool defineNameMatchesFilter(const QString &name, const QSet<QString> &filterList) const;
|
||||
bool defineNameMatchesFilter(const QString &name, const QSet<QRegularExpression> &filterList) const;
|
||||
QString loadTextFile(const QString &path, QString *error = nullptr);
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
int maxEncounterRate;
|
||||
bool wildEncountersLoaded;
|
||||
|
||||
void set_root(QString);
|
||||
void setRoot(const QString&);
|
||||
|
||||
void clearMaps();
|
||||
void clearTilesetCache();
|
||||
@@ -203,6 +203,7 @@ public:
|
||||
bool readEventGraphics();
|
||||
bool readFieldmapProperties();
|
||||
bool readFieldmapMasks();
|
||||
bool readGlobalConstants();
|
||||
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
|
||||
|
||||
QPixmap getEventPixmap(const QString &gfxName, const QString &movementName);
|
||||
|
||||
32
include/ui/newdefinedialog.h
Normal file
32
include/ui/newdefinedialog.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef NEWDEFINEDIALOG_H
|
||||
#define NEWDEFINEDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QAbstractButton>
|
||||
|
||||
namespace Ui {
|
||||
class NewDefineDialog;
|
||||
}
|
||||
|
||||
class NewDefineDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewDefineDialog(QWidget *parent = nullptr);
|
||||
~NewDefineDialog();
|
||||
|
||||
virtual void accept() override;
|
||||
|
||||
signals:
|
||||
void createdDefine(const QString &name, const QString &expression);
|
||||
|
||||
private:
|
||||
Ui::NewDefineDialog *ui;
|
||||
|
||||
bool validateName(bool allowEmpty = false);
|
||||
void onNameChanged(const QString &name);
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
};
|
||||
|
||||
#endif // NEWDEFINEDIALOG_H
|
||||
@@ -67,6 +67,12 @@ private:
|
||||
void setWarpBehaviorsList(QStringList list);
|
||||
void openFilesHelp();
|
||||
void openIdentifiersHelp();
|
||||
void addNewGlobalConstantsFilepath();
|
||||
void addGlobalConstantsFilepath(const QString &filepath);
|
||||
QStringList getGlobalConstantsFilepaths();
|
||||
void addNewGlobalConstant();
|
||||
void addGlobalConstant(const QString &name, const QString &expression);
|
||||
QMap<QString,QString> getGlobalConstants();
|
||||
|
||||
private slots:
|
||||
void dialogButtonClicked(QAbstractButton *button);
|
||||
|
||||
@@ -104,6 +104,7 @@ SOURCES += src/core/advancemapparser.cpp \
|
||||
src/ui/metatileselector.cpp \
|
||||
src/ui/movablerect.cpp \
|
||||
src/ui/movementpermissionsselector.cpp \
|
||||
src/ui/newdefinedialog.cpp \
|
||||
src/ui/neweventtoolbutton.cpp \
|
||||
src/ui/newlayoutdialog.cpp \
|
||||
src/ui/newlayoutform.cpp \
|
||||
@@ -217,6 +218,7 @@ HEADERS += include/core/advancemapparser.h \
|
||||
include/ui/metatileselector.h \
|
||||
include/ui/movablerect.h \
|
||||
include/ui/movementpermissionsselector.h \
|
||||
include/ui/newdefinedialog.h \
|
||||
include/ui/neweventtoolbutton.h \
|
||||
include/ui/newlayoutdialog.h \
|
||||
include/ui/newlayoutform.h \
|
||||
@@ -272,6 +274,7 @@ FORMS += forms/mainwindow.ui \
|
||||
forms/loadingscreen.ui \
|
||||
forms/mapheaderform.ui \
|
||||
forms/maplisttoolbar.ui \
|
||||
forms/newdefinedialog.ui \
|
||||
forms/newlayoutdialog.ui \
|
||||
forms/newlayoutform.ui \
|
||||
forms/newlocationdialog.ui \
|
||||
|
||||
@@ -234,7 +234,7 @@ void KeyValueConfigBase::load() {
|
||||
continue;
|
||||
}
|
||||
|
||||
this->parseConfigKeyValue(match.captured("key").trimmed().toLower(), match.captured("value").trimmed());
|
||||
this->parseConfigKeyValue(match.captured("key").trimmed(), match.captured("value").trimmed());
|
||||
}
|
||||
this->setUnreadKeys();
|
||||
|
||||
@@ -840,6 +840,10 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
} else {
|
||||
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
|
||||
}
|
||||
} else if (key.startsWith("global_constant/")) {
|
||||
this->globalConstants.insert(key.mid(QStringLiteral("global_constant/").length()), value);
|
||||
} else if (key == "global_constants_filepaths") {
|
||||
this->globalConstantsFilepaths = value.split(",", Qt::SkipEmptyParts);
|
||||
} else if (key == "prefabs_filepath") {
|
||||
this->prefabFilepath = value;
|
||||
} else if (key == "prefabs_import_prompted") {
|
||||
@@ -863,7 +867,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
|
||||
} else if (key == "event_icon_path_heal") {
|
||||
this->eventIconPaths[Event::Group::Heal] = value;
|
||||
} else if (key.startsWith("pokemon_icon_path/")) {
|
||||
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()).toUpper(), value);
|
||||
this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()), value);
|
||||
} else if (key == "collision_sheet_path") {
|
||||
this->collisionSheetPath = value;
|
||||
} else if (key == "collision_sheet_width") {
|
||||
@@ -970,12 +974,16 @@ QMap<QString, QString> ProjectConfig::getKeyValueMap() {
|
||||
map.insert("event_icon_path_coord", this->eventIconPaths[Event::Group::Coord]);
|
||||
map.insert("event_icon_path_bg", this->eventIconPaths[Event::Group::Bg]);
|
||||
map.insert("event_icon_path_heal", this->eventIconPaths[Event::Group::Heal]);
|
||||
for (auto i = this->pokemonIconPaths.cbegin(), end = this->pokemonIconPaths.cend(); i != end; i++){
|
||||
const QString path = i.value();
|
||||
if (!path.isEmpty()) map.insert("pokemon_icon_path/" + i.key(), path);
|
||||
for (auto it = this->pokemonIconPaths.constBegin(); it != this->pokemonIconPaths.constEnd(); it++) {
|
||||
const QString path = it.value();
|
||||
if (!path.isEmpty()) map.insert("pokemon_icon_path/" + it.key(), path);
|
||||
}
|
||||
for (auto i = this->identifiers.cbegin(), end = this->identifiers.cend(); i != end; i++) {
|
||||
map.insert("ident/"+defaultIdentifiers.value(i.key()).first, i.value());
|
||||
for (auto it = this->globalConstants.constBegin(); it != this->globalConstants.constEnd(); it++) {
|
||||
map.insert("global_constant/" + it.key(), it.value());
|
||||
}
|
||||
map.insert("global_constants_filepaths", this->globalConstantsFilepaths.join(","));
|
||||
for (auto it = this->identifiers.constBegin(); it != this->identifiers.constEnd(); it++) {
|
||||
map.insert("ident/"+defaultIdentifiers.value(it.key()).first, it.value());
|
||||
}
|
||||
map.insert("collision_sheet_path", this->collisionSheetPath);
|
||||
map.insert("collision_sheet_width", QString::number(this->collisionSheetSize.width()));
|
||||
|
||||
@@ -16,26 +16,8 @@ const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|l
|
||||
const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)");
|
||||
const QRegularExpression ParseUtil::re_poryRawSection("\\b(raw)\\s*`(?<raw_script>[^`]*)");
|
||||
|
||||
static const QMap<QString, int> globalDefineValues = {
|
||||
{"FALSE", 0},
|
||||
{"TRUE", 1},
|
||||
{"SCHAR_MIN", SCHAR_MIN},
|
||||
{"SCHAR_MAX", SCHAR_MAX},
|
||||
{"CHAR_MIN", CHAR_MIN},
|
||||
{"CHAR_MAX", CHAR_MAX},
|
||||
{"UCHAR_MAX", UCHAR_MAX},
|
||||
{"SHRT_MIN", SHRT_MIN},
|
||||
{"SHRT_MAX", SHRT_MAX},
|
||||
{"USHRT_MAX", USHRT_MAX},
|
||||
{"INT_MIN", INT_MIN},
|
||||
{"INT_MAX", INT_MAX},
|
||||
{"UINT_MAX", UINT_MAX},
|
||||
};
|
||||
|
||||
ParseUtil::ParseUtil() { }
|
||||
|
||||
void ParseUtil::set_root(const QString &dir) {
|
||||
this->root = dir;
|
||||
ParseUtil::ParseUtil() {
|
||||
resetCDefines();
|
||||
}
|
||||
|
||||
QString ParseUtil::pathWithRoot(const QString &path) {
|
||||
@@ -146,28 +128,48 @@ QList<QStringList> ParseUtil::parseAsm(const QString &filename) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// 'identifier' is the name of the #define to evaluate, e.g. 'FOO' in '#define FOO (BAR+1)'
|
||||
// 'expression' is the text of the #define to evaluate, e.g. '(BAR+1)' in '#define FOO (BAR+1)'
|
||||
// 'knownValues' is a pointer to a map of identifier->values for defines that have already been evaluated.
|
||||
// 'unevaluatedExpressions' is a pointer to a map of identifier->expressions for defines that have not been evaluated. If this map contains any
|
||||
// identifiers found in 'expression' then this function will be called recursively to evaluate that define first.
|
||||
// This function will maintain the passed maps appropriately as new #defines are evaluated.
|
||||
int ParseUtil::evaluateDefine(const QString &identifier, const QString &expression, QMap<QString, int> *knownValues, QMap<QString, QString> *unevaluatedExpressions) {
|
||||
if (unevaluatedExpressions->contains(identifier))
|
||||
unevaluatedExpressions->remove(identifier);
|
||||
// Try to evaluate the given #define/enum 'identifier' name using the information the parser has.
|
||||
// If it recognizes the name as an identifier it's aware of (either from having parsed it or having been told about
|
||||
// it using 'loadGlobalCDefines') it will evaluate it if necessary then return the resulting value and set 'ok' to true.
|
||||
// Evaluated identifiers are cached, and will only be re-evaluated if the parser encounters a new expression for that identifier.
|
||||
// If it doesn't recognize it, 'ok' will be set to false and it will return 0.
|
||||
int ParseUtil::evaluateDefine(const QString &identifier, bool *ok) {
|
||||
if (ok) *ok = true;
|
||||
|
||||
if (knownValues->contains(identifier))
|
||||
return knownValues->value(identifier);
|
||||
// Global defines take precedence
|
||||
if (this->globalDefineExpressions.contains(identifier)) {
|
||||
int value = evaluateExpression(this->globalDefineExpressions.take(identifier));
|
||||
this->globalDefineValues.insert(identifier, value);
|
||||
return value;
|
||||
}
|
||||
auto it = this->globalDefineValues.constFind(identifier);
|
||||
if (it != this->globalDefineValues.constEnd()) {
|
||||
return it.value();
|
||||
}
|
||||
|
||||
QList<Token> tokens = tokenizeExpression(expression, knownValues, unevaluatedExpressions);
|
||||
QList<Token> postfixExpression = generatePostfix(tokens);
|
||||
int value = evaluatePostfix(postfixExpression);
|
||||
// Check known expressions before checking known values.
|
||||
// If an identifier is redefined then we'll receive a new expression for it, and we want to make sure we re-evaluate it.
|
||||
if (this->knownDefineExpressions.contains(identifier)) {
|
||||
int value = evaluateExpression(this->knownDefineExpressions.take(identifier));
|
||||
this->knownDefineValues.insert(identifier, value);
|
||||
return value;
|
||||
}
|
||||
it = this->knownDefineValues.constFind(identifier);
|
||||
if (it != this->knownDefineValues.constEnd()) {
|
||||
return it.value();
|
||||
}
|
||||
|
||||
knownValues->insert(identifier, value);
|
||||
return value;
|
||||
if (ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int> *knownValues, QMap<QString, QString> *unevaluatedExpressions) {
|
||||
int ParseUtil::evaluateExpression(const QString &expression) {
|
||||
QList<Token> tokens = tokenizeExpression(expression);
|
||||
QList<Token> postfixExpression = generatePostfix(tokens);
|
||||
return evaluatePostfix(postfixExpression);
|
||||
}
|
||||
|
||||
QList<Token> ParseUtil::tokenizeExpression(QString expression) {
|
||||
QList<Token> tokens;
|
||||
|
||||
static const QStringList tokenTypes = {"hex", "decimal", "identifier", "operator", "leftparen", "rightparen"};
|
||||
@@ -184,14 +186,14 @@ QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int
|
||||
QString token = match.captured(tokenType);
|
||||
if (!token.isEmpty()) {
|
||||
if (tokenType == "identifier") {
|
||||
if (unevaluatedExpressions->contains(token)) {
|
||||
// This expression depends on a define we know of but haven't evaluated. Evaluate it now
|
||||
evaluateDefine(token, unevaluatedExpressions->value(token), knownValues, unevaluatedExpressions);
|
||||
}
|
||||
if (knownValues->contains(token)) {
|
||||
bool ok;
|
||||
int tokenValue = evaluateDefine(token, &ok);
|
||||
if (ok) {
|
||||
// Any errors encountered when this identifier was evaluated should be recorded for this expression as well.
|
||||
recordErrors(this->errorMap.value(token));
|
||||
QString actualToken = QString("%1").arg(knownValues->value(token));
|
||||
|
||||
// Replace token with evaluated expression
|
||||
QString actualToken = QString::number(tokenValue);
|
||||
expression = expression.replace(0, token.length(), actualToken);
|
||||
token = actualToken;
|
||||
tokenType = "decimal";
|
||||
@@ -484,23 +486,27 @@ ParseUtil::ParsedDefines ParseUtil::readCDefines(const QString &filename, const
|
||||
result.filteredNames.append(name);
|
||||
}
|
||||
}
|
||||
// QHash::insert(const QHash<K, V> &other) was introduced in 5.15.
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
this->knownDefineExpressions.insert(result.expressions);
|
||||
#else
|
||||
for (auto it = result.expressions.constBegin(); it != result.expressions.constEnd(); it++) {
|
||||
this->knownDefineExpressions.insert(it.key(), it.value());
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read all the define names and their expressions in the specified file, then evaluate the ones matching the search text (and any they depend on).
|
||||
QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
|
||||
QHash<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QSet<QString> &filterList, bool useRegex, QString *error) {
|
||||
ParsedDefines defines = readCDefines(filename, filterList, useRegex, error);
|
||||
|
||||
// Evaluate defines
|
||||
QMap<QString, int> filteredValues;
|
||||
QMap<QString, int> allValues = globalDefineValues;
|
||||
QHash<QString, int> filteredValues;
|
||||
this->errorMap.clear();
|
||||
while (!defines.filteredNames.isEmpty()) {
|
||||
const QString name = defines.filteredNames.takeFirst();
|
||||
const QString expression = defines.expressions.take(name);
|
||||
if (expression == " ") continue;
|
||||
this->curDefine = name;
|
||||
filteredValues.insert(name, evaluateDefine(name, expression, &allValues, &defines.expressions));
|
||||
this->curDefine = defines.filteredNames.takeFirst();
|
||||
filteredValues.insert(this->curDefine, evaluateDefine(this->curDefine));
|
||||
logRecordedErrors(); // Only log errors for defines that Porymap is looking for
|
||||
}
|
||||
|
||||
@@ -508,12 +514,12 @@ QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QS
|
||||
}
|
||||
|
||||
// Find and evaluate a specific set of defines with known names.
|
||||
QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error) {
|
||||
QHash<QString, int> ParseUtil::readCDefinesByName(const QString &filename, const QSet<QString> &names, QString *error) {
|
||||
return evaluateCDefines(filename, names, false, error);
|
||||
}
|
||||
|
||||
// Find and evaluate an unknown list of defines with a known name pattern.
|
||||
QMap<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QSet<QString> ®exList, QString *error) {
|
||||
QHash<QString, int> ParseUtil::readCDefinesByRegex(const QString &filename, const QSet<QString> ®exList, QString *error) {
|
||||
return evaluateCDefines(filename, regexList, true, error);
|
||||
}
|
||||
|
||||
@@ -524,6 +530,50 @@ QStringList ParseUtil::readCDefineNames(const QString &filename, const QSet<QStr
|
||||
return readCDefines(filename, regexList, true, error).filteredNames;
|
||||
}
|
||||
|
||||
// Find any defines in the specified file and save their expressions.
|
||||
// If any of these defines are encountered later by other define parsing functions then they'll be recognized and evaluated.
|
||||
void ParseUtil::loadGlobalCDefinesFromFile(const QString &filename, QString *error) {
|
||||
loadGlobalCDefines(readCDefines(filename, {}, false, error).expressions);
|
||||
}
|
||||
|
||||
void ParseUtil::loadGlobalCDefines(const QHash<QString,QString> &defines) {
|
||||
// QHash::insert(const QHash<K, V> &other) was introduced in 5.15.
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
this->globalDefineExpressions.insert(defines);
|
||||
#else
|
||||
for (auto it = defines.constBegin(); it != defines.constEnd(); it++) {
|
||||
this->globalDefineExpressions.insert(it.key(), it.value());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ParseUtil::loadGlobalCDefines(const QMap<QString,QString> &defines) {
|
||||
for (auto it = defines.constBegin(); it != defines.constEnd(); it++)
|
||||
this->globalDefineExpressions.insert(it.key(), it.value());
|
||||
}
|
||||
|
||||
void ParseUtil::resetCDefines() {
|
||||
static const QHash<QString, int> defaultDefineValues = {
|
||||
{"FALSE", 0},
|
||||
{"TRUE", 1},
|
||||
{"SCHAR_MIN", SCHAR_MIN},
|
||||
{"SCHAR_MAX", SCHAR_MAX},
|
||||
{"CHAR_MIN", CHAR_MIN},
|
||||
{"CHAR_MAX", CHAR_MAX},
|
||||
{"UCHAR_MAX", UCHAR_MAX},
|
||||
{"SHRT_MIN", SHRT_MIN},
|
||||
{"SHRT_MAX", SHRT_MAX},
|
||||
{"USHRT_MAX", USHRT_MAX},
|
||||
{"INT_MIN", INT_MIN},
|
||||
{"INT_MAX", INT_MAX},
|
||||
{"UINT_MAX", UINT_MAX},
|
||||
};
|
||||
this->globalDefineValues = defaultDefineValues;
|
||||
this->globalDefineExpressions.clear();
|
||||
this->knownDefineValues.clear();
|
||||
this->knownDefineExpressions.clear();
|
||||
}
|
||||
|
||||
QStringList ParseUtil::readCArray(const QString &filename, const QString &label) {
|
||||
QStringList list;
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
|
||||
|
||||
// Create the project
|
||||
auto project = new Project(editor);
|
||||
project->set_root(dir);
|
||||
project->setRoot(dir);
|
||||
connect(project, &Project::fileChanged, this, &MainWindow::showFileWatcherWarning);
|
||||
connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
|
||||
connect(project, &Project::mapCreated, this, &MainWindow::onNewMapCreated);
|
||||
@@ -1709,6 +1709,13 @@ void MainWindow::duplicate() {
|
||||
void MainWindow::copy() {
|
||||
auto focused = QApplication::focusWidget();
|
||||
if (focused) {
|
||||
// Allow copying text from selectable QLabels.
|
||||
auto label = dynamic_cast<QLabel*>(focused);
|
||||
if (label && !label->selectedText().isEmpty()) {
|
||||
setClipboardData(label->selectedText());
|
||||
return;
|
||||
}
|
||||
|
||||
QString objectName = focused->objectName();
|
||||
if (objectName == "graphicsView_currentMetatileSelection") {
|
||||
// copy the current metatile selection as json data
|
||||
|
||||
@@ -48,10 +48,10 @@ Project::~Project()
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
void Project::set_root(QString dir) {
|
||||
void Project::setRoot(const QString &dir) {
|
||||
this->root = dir;
|
||||
FileDialog::setDirectory(dir);
|
||||
this->parser.set_root(dir);
|
||||
this->parser.setRoot(dir);
|
||||
}
|
||||
|
||||
// Before attempting the initial project load we should check for a few notable files.
|
||||
@@ -78,7 +78,8 @@ bool Project::sanityCheck() {
|
||||
bool Project::load() {
|
||||
resetFileCache();
|
||||
this->disabledSettingsNames.clear();
|
||||
bool success = readMapLayouts()
|
||||
bool success = readGlobalConstants()
|
||||
&& readMapLayouts()
|
||||
&& readRegionMapSections()
|
||||
&& readItemNames()
|
||||
&& readFlagNames()
|
||||
@@ -1485,7 +1486,7 @@ bool Project::readTilesetMetatileLabels() {
|
||||
fileWatcher.addPath(root + "/" + metatileLabelsFilename);
|
||||
|
||||
const QSet<QString> regexList = {QString("\\b%1").arg(projectConfig.getIdentifier(ProjectIdentifier::define_metatile_label_prefix))};
|
||||
const QMap<QString, int> defines = parser.readCDefinesByRegex(metatileLabelsFilename, regexList);
|
||||
const auto defines = parser.readCDefinesByRegex(metatileLabelsFilename, regexList);
|
||||
for (auto i = defines.constBegin(); i != defines.constEnd(); i++) {
|
||||
QString label = i.key();
|
||||
uint32_t metatileId = i.value();
|
||||
@@ -2120,16 +2121,15 @@ bool Project::readFieldmapProperties() {
|
||||
|
||||
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_fieldmap);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
const QMap<QString, int> defines = parser.readCDefinesByName(filename, {
|
||||
numTilesPrimaryName,
|
||||
numTilesTotalName,
|
||||
numMetatilesPrimaryName,
|
||||
numPalsPrimaryName,
|
||||
numPalsTotalName,
|
||||
maxMapSizeName,
|
||||
numTilesPerMetatileName,
|
||||
mapOffsetWidthName,
|
||||
mapOffsetHeightName,
|
||||
const auto defines = parser.readCDefinesByName(filename, { numTilesPrimaryName,
|
||||
numTilesTotalName,
|
||||
numMetatilesPrimaryName,
|
||||
numPalsPrimaryName,
|
||||
numPalsTotalName,
|
||||
maxMapSizeName,
|
||||
numTilesPerMetatileName,
|
||||
mapOffsetWidthName,
|
||||
mapOffsetHeightName,
|
||||
});
|
||||
|
||||
auto loadDefine = [defines](const QString name, int * dest, int min, int max) {
|
||||
@@ -2223,16 +2223,15 @@ bool Project::readFieldmapMasks() {
|
||||
const QString elevationMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_elevation);
|
||||
const QString behaviorMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_behavior);
|
||||
const QString layerTypeMaskName = projectConfig.getIdentifier(ProjectIdentifier::define_mask_layer);
|
||||
const QSet<QString> searchNames = {
|
||||
metatileIdMaskName,
|
||||
collisionMaskName,
|
||||
elevationMaskName,
|
||||
behaviorMaskName,
|
||||
layerTypeMaskName,
|
||||
};
|
||||
|
||||
const QString globalFieldmap = projectConfig.getFilePath(ProjectFilePath::global_fieldmap);
|
||||
fileWatcher.addPath(root + "/" + globalFieldmap);
|
||||
QMap<QString, int> defines = parser.readCDefinesByName(globalFieldmap, searchNames);
|
||||
const auto defines = parser.readCDefinesByName(globalFieldmap, { metatileIdMaskName,
|
||||
collisionMaskName,
|
||||
elevationMaskName,
|
||||
behaviorMaskName,
|
||||
layerTypeMaskName,
|
||||
});
|
||||
|
||||
// These mask values are accessible via the settings editor for users who don't have these defines.
|
||||
// If users do have the defines we disable them in the settings editor and direct them to their project files.
|
||||
@@ -2243,8 +2242,8 @@ bool Project::readFieldmapMasks() {
|
||||
|
||||
// Read Block masks
|
||||
auto readBlockMask = [defines](const QString name, uint16_t *value) {
|
||||
auto it = defines.find(name);
|
||||
if (it == defines.end())
|
||||
auto it = defines.constFind(name);
|
||||
if (it == defines.constEnd())
|
||||
return false;
|
||||
*value = static_cast<uint16_t>(it.value());
|
||||
if (*value != it.value()){
|
||||
@@ -2318,7 +2317,7 @@ bool Project::readFieldmapMasks() {
|
||||
// Read #defines for encounter and terrain types to populate in the Tileset Editor dropdowns (if necessary)
|
||||
QString error;
|
||||
if (projectConfig.metatileEncounterTypeMask) {
|
||||
QMap<QString, int> defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_encounter_types)}, &error);
|
||||
const auto defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_encounter_types)}, &error);
|
||||
if (!error.isEmpty()) {
|
||||
logWarn(QString("Failed to read encounter type constants from '%1': %2").arg(globalFieldmap).arg(error));
|
||||
error = QString();
|
||||
@@ -2329,7 +2328,7 @@ bool Project::readFieldmapMasks() {
|
||||
}
|
||||
}
|
||||
if (projectConfig.metatileTerrainTypeMask) {
|
||||
QMap<QString, int> defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_terrain_types)}, &error);
|
||||
const auto defines = parser.readCDefinesByRegex(globalFieldmap, {projectConfig.getIdentifier(ProjectIdentifier::regex_terrain_types)}, &error);
|
||||
if (!error.isEmpty()) {
|
||||
logWarn(QString("Failed to read terrain type constants from '%1': %2").arg(globalFieldmap).arg(error));
|
||||
error = QString();
|
||||
@@ -2677,7 +2676,7 @@ bool Project::readMetatileBehaviors() {
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_metatile_behaviors);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
QString error;
|
||||
QMap<QString, int> defines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_behaviors)}, &error);
|
||||
const auto defines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_behaviors)}, &error);
|
||||
if (defines.isEmpty() && projectConfig.metatileBehaviorMask) {
|
||||
// Not having any metatile behavior names is ok (their values will be displayed instead)
|
||||
// but if the user's metatiles can have nonzero values then warn them, as they likely want names.
|
||||
@@ -2714,9 +2713,14 @@ bool Project::readObjEventGfxConstants() {
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_events);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
QString error;
|
||||
this->gfxDefines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_obj_event_gfx)}, &error);
|
||||
const auto defines = parser.readCDefinesByRegex(filename, {projectConfig.getIdentifier(ProjectIdentifier::regex_obj_event_gfx)}, &error);
|
||||
if (!error.isEmpty())
|
||||
logWarn(QString("Failed to read object event graphics constants from '%1': %2").arg(filename).arg(error));
|
||||
|
||||
this->gfxDefines.clear();
|
||||
for (auto it = defines.constBegin(); it != defines.constEnd(); it++)
|
||||
this->gfxDefines.insert(it.key(), it.value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2724,7 +2728,7 @@ bool Project::readMiscellaneousConstants() {
|
||||
const QString filename = projectConfig.getFilePath(ProjectFilePath::constants_global);
|
||||
const QString maxObjectEventsName = projectConfig.getIdentifier(ProjectIdentifier::define_obj_event_count);
|
||||
fileWatcher.addPath(root + "/" + filename);
|
||||
QMap<QString, int> defines = parser.readCDefinesByName(filename, {maxObjectEventsName});
|
||||
const auto defines = parser.readCDefinesByName(filename, {maxObjectEventsName});
|
||||
|
||||
this->maxObjectEvents = 64; // Default value
|
||||
auto it = defines.find(maxObjectEventsName);
|
||||
@@ -2747,6 +2751,19 @@ bool Project::readMiscellaneousConstants() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Project::readGlobalConstants() {
|
||||
this->parser.resetCDefines();
|
||||
for (const auto &path : projectConfig.globalConstantsFilepaths) {
|
||||
QString error;
|
||||
this->parser.loadGlobalCDefinesFromFile(path, &error);
|
||||
if (!error.isEmpty()) {
|
||||
logWarn(QString("Failed to read global constants file '%1': %2").arg(path).arg(error));
|
||||
}
|
||||
}
|
||||
this->parser.loadGlobalCDefines(projectConfig.globalConstants);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Project::readEventScriptLabels() {
|
||||
this->globalScriptLabels.clear();
|
||||
|
||||
@@ -2944,9 +2961,7 @@ QPixmap Project::getEventPixmap(const QString &gfxName, int frame, bool hFlip) {
|
||||
// Invalid gfx constant. If this is a number, try to use that instead.
|
||||
bool ok;
|
||||
int gfxNum = ParseUtil::gameStringToInt(gfxName, &ok);
|
||||
if (ok && gfxNum < this->gfxDefines.count()) {
|
||||
gfx = this->eventGraphicsMap.value(this->gfxDefines.key(gfxNum, "NULL"), nullptr);
|
||||
}
|
||||
if (ok) gfx = this->eventGraphicsMap.value(this->gfxDefines.key(gfxNum, "NULL"), nullptr);
|
||||
}
|
||||
if (gfx && !gfx->loaded) {
|
||||
// This is the first request for this event's sprite. We'll attempt to load it now.
|
||||
|
||||
60
src/ui/newdefinedialog.cpp
Normal file
60
src/ui/newdefinedialog.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "newdefinedialog.h"
|
||||
#include "ui_newdefinedialog.h"
|
||||
#include "validator.h"
|
||||
|
||||
const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }";
|
||||
|
||||
NewDefineDialog::NewDefineDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::NewDefineDialog)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->lineEdit_Name->setValidator(new IdentifierValidator(this));
|
||||
|
||||
connect(ui->lineEdit_Name, &QLineEdit::textChanged, this, &NewDefineDialog::onNameChanged);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewDefineDialog::dialogButtonClicked);
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
NewDefineDialog::~NewDefineDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NewDefineDialog::onNameChanged(const QString &) {
|
||||
validateName(true);
|
||||
}
|
||||
|
||||
bool NewDefineDialog::validateName(bool allowEmpty) {
|
||||
const QString name = ui->lineEdit_Name->text();
|
||||
|
||||
QString errorText;
|
||||
if (name.isEmpty() && !allowEmpty) {
|
||||
errorText = QString("%1 cannot be empty.").arg(ui->label_Name->text());
|
||||
}
|
||||
|
||||
bool isValid = errorText.isEmpty();
|
||||
ui->label_NameError->setText(errorText);
|
||||
ui->label_NameError->setVisible(!isValid);
|
||||
ui->lineEdit_Name->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : "");
|
||||
return isValid;
|
||||
}
|
||||
|
||||
void NewDefineDialog::dialogButtonClicked(QAbstractButton *button) {
|
||||
auto role = ui->buttonBox->buttonRole(button);
|
||||
if (role == QDialogButtonBox::RejectRole){
|
||||
reject();
|
||||
} else if (role == QDialogButtonBox::AcceptRole) {
|
||||
accept();
|
||||
}
|
||||
}
|
||||
|
||||
void NewDefineDialog::accept() {
|
||||
if (!validateName())
|
||||
return;
|
||||
emit createdDefine(ui->lineEdit_Name->text(), ui->lineEdit_Value->text());
|
||||
QDialog::accept();
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "noscrollcombobox.h"
|
||||
#include "prefab.h"
|
||||
#include "filedialog.h"
|
||||
#include "newdefinedialog.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
@@ -54,6 +55,9 @@ void ProjectSettingsEditor::connectSignals() {
|
||||
connect(ui->button_AddWarpBehavior, &QAbstractButton::clicked, [this](bool) { this->updateWarpBehaviorsList(true); });
|
||||
connect(ui->button_RemoveWarpBehavior, &QAbstractButton::clicked, [this](bool) { this->updateWarpBehaviorsList(false); });
|
||||
|
||||
connect(ui->button_AddGlobalConstantsFile, &QAbstractButton::clicked, this, &ProjectSettingsEditor::addNewGlobalConstantsFilepath);
|
||||
connect(ui->button_AddGlobalConstant, &QAbstractButton::clicked, this, &ProjectSettingsEditor::addNewGlobalConstant);
|
||||
|
||||
// Connect file selection buttons
|
||||
connect(ui->button_ChoosePrefabs, &QAbstractButton::clicked, [this](bool) { this->choosePrefabsFile(); });
|
||||
connect(ui->button_CollisionGraphics, &QAbstractButton::clicked, [this](bool) { this->chooseImageFile(ui->lineEdit_CollisionGraphics); });
|
||||
@@ -501,6 +505,12 @@ void ProjectSettingsEditor::refresh() {
|
||||
lineEdit->setText(projectConfig.getCustomFilePath(lineEdit->objectName()));
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
lineEdit->setText(projectConfig.getCustomIdentifier(lineEdit->objectName()));
|
||||
for (const auto &path : projectConfig.globalConstantsFilepaths) {
|
||||
addGlobalConstantsFilepath(path);
|
||||
}
|
||||
for (auto it = projectConfig.globalConstants.constBegin(); it != projectConfig.globalConstants.constEnd(); it++) {
|
||||
addGlobalConstant(it.key(), it.value());
|
||||
}
|
||||
|
||||
// Set warp behaviors
|
||||
QStringList behaviorNames;
|
||||
@@ -578,6 +588,10 @@ void ProjectSettingsEditor::save() {
|
||||
for (auto lineEdit : ui->scrollAreaContents_Identifiers->findChildren<QLineEdit*>())
|
||||
projectConfig.setIdentifier(lineEdit->objectName(), lineEdit->text());
|
||||
|
||||
// Save global constants
|
||||
projectConfig.globalConstantsFilepaths = getGlobalConstantsFilepaths();
|
||||
projectConfig.globalConstants = getGlobalConstants();
|
||||
|
||||
// Save warp behaviors
|
||||
projectConfig.warpBehaviors.clear();
|
||||
const QStringList behaviorNames = this->getWarpBehaviorsList();
|
||||
@@ -624,6 +638,97 @@ void ProjectSettingsEditor::chooseFile(QLineEdit * filepathEdit, const QString &
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::addNewGlobalConstantsFilepath() {
|
||||
QString filepath = stripProjectDir(FileDialog::getOpenFileName(this, "Choose Global Constants File"));
|
||||
if (filepath.isEmpty() || getGlobalConstantsFilepaths().contains(filepath))
|
||||
return;
|
||||
|
||||
addGlobalConstantsFilepath(filepath);
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::addGlobalConstantsFilepath(const QString &filepath) {
|
||||
auto filepathLabel = new QLabel(filepath, this);
|
||||
filepathLabel->setFrameStyle(QFrame::Panel | QFrame::Raised);
|
||||
filepathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
int newRow = ui->gridLayout_GlobalConstantsFiles->rowCount();
|
||||
ui->gridLayout_GlobalConstantsFiles->addWidget(filepathLabel, newRow, 0);
|
||||
|
||||
auto deleteButton = new QToolButton();
|
||||
deleteButton->setIcon(QIcon(":/icons/delete.ico"));
|
||||
connect(deleteButton, &QAbstractButton::clicked, [this, filepathLabel, deleteButton](bool) {
|
||||
ui->gridLayout_GlobalConstantsFiles->removeWidget(filepathLabel);
|
||||
ui->gridLayout_GlobalConstantsFiles->removeWidget(deleteButton);
|
||||
delete filepathLabel;
|
||||
delete deleteButton;
|
||||
this->hasUnsavedChanges = true;
|
||||
});
|
||||
ui->gridLayout_GlobalConstantsFiles->addWidget(deleteButton, newRow, 1);
|
||||
}
|
||||
|
||||
QStringList ProjectSettingsEditor::getGlobalConstantsFilepaths() {
|
||||
QStringList paths;
|
||||
for (int row = 1; row < ui->gridLayout_GlobalConstantsFiles->rowCount(); row++) {
|
||||
auto item = ui->gridLayout_GlobalConstantsFiles->itemAtPosition(row, 0);
|
||||
if (!item) continue;
|
||||
auto pathLabel = dynamic_cast<QLabel*>(item->widget());
|
||||
if (!pathLabel) continue;
|
||||
paths.append(pathLabel->text());
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::addNewGlobalConstant() {
|
||||
auto dialog = new NewDefineDialog(this);
|
||||
connect(dialog, &NewDefineDialog::createdDefine, [this](const QString &name, const QString &expression) {
|
||||
if (!getGlobalConstants().contains(name)) {
|
||||
addGlobalConstant(name, expression);
|
||||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
});
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void ProjectSettingsEditor::addGlobalConstant(const QString &name, const QString &expression) {
|
||||
auto nameLabel = new QLabel(name, this);
|
||||
nameLabel->setFrameStyle(QFrame::Panel | QFrame::Raised);
|
||||
nameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
|
||||
auto expressionLineEdit = new QLineEdit(expression, this);
|
||||
|
||||
int newRow = ui->gridLayout_GlobalConstants->rowCount();
|
||||
ui->gridLayout_GlobalConstants->addWidget(nameLabel, newRow, 0);
|
||||
ui->gridLayout_GlobalConstants->addWidget(expressionLineEdit, newRow, 1);
|
||||
|
||||
auto deleteButton = new QToolButton();
|
||||
deleteButton->setIcon(QIcon(":/icons/delete.ico"));
|
||||
connect(deleteButton, &QAbstractButton::clicked, [this, nameLabel, expressionLineEdit, deleteButton](bool) {
|
||||
ui->gridLayout_GlobalConstants->removeWidget(nameLabel);
|
||||
ui->gridLayout_GlobalConstants->removeWidget(expressionLineEdit);
|
||||
ui->gridLayout_GlobalConstants->removeWidget(deleteButton);
|
||||
delete nameLabel;
|
||||
delete expressionLineEdit;
|
||||
delete deleteButton;
|
||||
this->hasUnsavedChanges = true;
|
||||
});
|
||||
ui->gridLayout_GlobalConstants->addWidget(deleteButton, newRow, 2);
|
||||
}
|
||||
|
||||
QMap<QString,QString> ProjectSettingsEditor::getGlobalConstants() {
|
||||
QMap<QString,QString> constants;
|
||||
for (int row = 1; row < ui->gridLayout_GlobalConstants->rowCount(); row++) {
|
||||
auto nameItem = ui->gridLayout_GlobalConstants->itemAtPosition(row, 0);
|
||||
auto expressionItem = ui->gridLayout_GlobalConstants->itemAtPosition(row, 1);
|
||||
if (!nameItem || !expressionItem) continue;
|
||||
auto nameLabel = dynamic_cast<QLabel*>(nameItem->widget());
|
||||
auto expressionLineEdit = dynamic_cast<QLineEdit*>(expressionItem->widget());
|
||||
if (!nameLabel || !expressionLineEdit) continue;
|
||||
constants.insert(nameLabel->text(), expressionLineEdit->text());
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
// Display relative path if this file is in the project folder
|
||||
QString ProjectSettingsEditor::stripProjectDir(QString s) {
|
||||
if (s.startsWith(this->baseDir))
|
||||
|
||||
Reference in New Issue
Block a user