Fix new name regexes, some assumptions about MAPSEC_NONE, memory leak

This commit is contained in:
GriffinR 2024-10-17 14:01:27 -04:00
parent 728355d202
commit 10aa6f6c3f
5 changed files with 73 additions and 49 deletions

View file

@ -132,7 +132,6 @@ public:
QString getProjectTitle(); QString getProjectTitle();
QString readMapLayoutId(QString map_name); QString readMapLayoutId(QString map_name);
QString readMapLayoutName(QString mapName);
QString readMapLocation(QString map_name); QString readMapLocation(QString map_name);
bool readWildMonData(); bool readWildMonData();
@ -243,6 +242,7 @@ public:
static bool mapDimensionsValid(int width, int height); static bool mapDimensionsValid(int width, int height);
bool calculateDefaultMapSize(); bool calculateDefaultMapSize();
static int getMaxObjectEvents(); static int getMaxObjectEvents();
static QString getEmptyMapsecName();
private: private:
void updateLayout(Layout *); void updateLayout(Layout *);

View file

@ -1370,9 +1370,13 @@ void MainWindow::mapListAddGroup() {
QLineEdit *newNameEdit = new QLineEdit(&dialog); QLineEdit *newNameEdit = new QLineEdit(&dialog);
newNameEdit->setClearButtonEnabled(true); newNameEdit->setClearButtonEnabled(true);
static const QRegularExpression re_validChars("[_A-Za-z0-9]*$"); static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(re_validChars); newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit));
newNameEdit->setValidator(validator);
connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){
if (!this->editor->project->groupNames.contains(newNameEdit->text()))
dialog.accept();
});
QFormLayout form(&dialog); QFormLayout form(&dialog);
@ -1397,10 +1401,10 @@ void MainWindow::mapListAddLayout() {
QLineEdit *newNameEdit = new QLineEdit(&dialog); QLineEdit *newNameEdit = new QLineEdit(&dialog);
newNameEdit->setClearButtonEnabled(true); newNameEdit->setClearButtonEnabled(true);
static const QRegularExpression re_validChars("[_A-Za-z0-9]*$"); static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(re_validChars); newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit));
newNameEdit->setValidator(validator);
// TODO: Support arbitrary LAYOUT_ ID names (Note from GriffinR: This is already handled in an unopened PR)
QLabel *newId = new QLabel("LAYOUT_", &dialog); QLabel *newId = new QLabel("LAYOUT_", &dialog);
connect(newNameEdit, &QLineEdit::textChanged, [&](QString text){ connect(newNameEdit, &QLineEdit::textChanged, [&](QString text){
newId->setText(Layout::layoutConstantFromName(text.remove("_Layout"))); newId->setText(Layout::layoutConstantFromName(text.remove("_Layout")));
@ -1499,7 +1503,7 @@ void MainWindow::mapListAddLayout() {
layoutSettings.tileset_secondary_label = secondaryCombo->currentText(); layoutSettings.tileset_secondary_label = secondaryCombo->currentText();
} }
Layout *newLayout = this->editor->project->createNewLayout(layoutSettings); Layout *newLayout = this->editor->project->createNewLayout(layoutSettings);
QStandardItem *item = this->layoutTreeModel->insertLayoutItem(newLayout->id); this->layoutTreeModel->insertLayoutItem(newLayout->id);
setLayout(newLayout->id); setLayout(newLayout->id);
} }
} }
@ -1511,13 +1515,18 @@ void MainWindow::mapListAddArea() {
QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix);
QLineEdit *newNameEdit = new QLineEdit(&dialog); QLineEdit *newNameEdit = new QLineEdit(&dialog);
newNameEdit->setText(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)); QLineEdit *newNameDisplay = new QLineEdit(&dialog);
newNameEdit->setClearButtonEnabled(false); newNameDisplay->setText(prefix);
newNameDisplay->setEnabled(false);
connect(newNameEdit, &QLineEdit::textEdited, [newNameDisplay, prefix] (const QString &text) {
// As the user types a name, update the label to show the name with the prefix.
newNameDisplay->setText(prefix + text);
});
QRegularExpression re_validChars(QString("%1[_A-Za-z0-9]+$").arg(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))); static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(re_validChars); newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit));
newNameEdit->setValidator(validator);
connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){
if (!this->editor->project->mapSectionNameToValue.contains(newNameEdit->text())) if (!this->editor->project->mapSectionNameToValue.contains(newNameEdit->text()))
@ -1527,12 +1536,12 @@ void MainWindow::mapListAddArea() {
QFormLayout form(&dialog); QFormLayout form(&dialog);
form.addRow("New Map Section Name", newNameEdit); form.addRow("New Map Section Name", newNameEdit);
form.addRow("Constant Name", newNameDisplay);
form.addRow(&newItemButtonBox); form.addRow(&newItemButtonBox);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
QString newFieldName = newNameEdit->text(); if (newNameEdit->text().isEmpty()) return;
if (newFieldName.isEmpty()) return; this->mapAreaModel->insertAreaItem(newNameDisplay->text());
this->mapAreaModel->insertAreaItem(newFieldName);
} }
} }
@ -1540,13 +1549,13 @@ void MainWindow::mapListAddItem() {
if (!this->editor || !this->editor->project) return; if (!this->editor || !this->editor->project) return;
switch (this->ui->mapListContainer->currentIndex()) { switch (this->ui->mapListContainer->currentIndex()) {
case 0: case MapListTab::Groups:
this->mapListAddGroup(); this->mapListAddGroup();
break; break;
case 1: case MapListTab::Areas:
this->mapListAddArea(); this->mapListAddArea();
break; break;
case 2: case MapListTab::Layouts:
this->mapListAddLayout(); this->mapListAddLayout();
break; break;
} }
@ -1596,14 +1605,14 @@ void MainWindow::mapListRemoveItem() {
if (!this->editor || !this->editor->project) return; if (!this->editor || !this->editor->project) return;
switch (this->ui->mapListContainer->currentIndex()) { switch (this->ui->mapListContainer->currentIndex()) {
case 0: case MapListTab::Groups:
this->mapListRemoveGroup(); this->mapListRemoveGroup();
break; break;
case 1: case MapListTab::Areas:
// Disabled // Disabled
// this->mapListRemoveArea(); // this->mapListRemoveArea();
break; break;
case 2: case MapListTab::Layouts:
// Disabled // Disabled
// this->mapListRemoveLayout(); // this->mapListRemoveLayout();
break; break;
@ -2913,10 +2922,8 @@ void MainWindow::onTilesetsSaved(QString primaryTilesetLabel, QString secondaryT
} else { } else {
this->editor->project->getTileset(secondaryTilesetLabel, true); this->editor->project->getTileset(secondaryTilesetLabel, true);
} }
if (updated) { if (updated)
this->editor->layout->clearBorderCache();
redrawMapScene(); redrawMapScene();
}
} }
void MainWindow::onMapRulerStatusChanged(const QString &status) { void MainWindow::onMapRulerStatusChanged(const QString &status) {
@ -3232,6 +3239,7 @@ void MainWindow::do_CollapseAll() {
} }
} }
// TODO: Save this state in porymapConfig
void MainWindow::do_HideShow() { void MainWindow::do_HideShow() {
switch (ui->mapListContainer->currentIndex()) { switch (ui->mapListContainer->currentIndex()) {
case MapListTab::Groups: case MapListTab::Groups:
@ -3265,6 +3273,7 @@ void MainWindow::on_toolButton_CollapseAll_Groups_clicked() {
} }
} }
// TODO: Save this state in porymapConfig
void MainWindow::on_toolButton_EnableDisable_EditGroups_clicked() { void MainWindow::on_toolButton_EnableDisable_EditGroups_clicked() {
this->ui->mapList->clearSelection(); this->ui->mapList->clearSelection();
if (this->ui->toolButton_EnableDisable_EditGroups->isChecked()) { if (this->ui->toolButton_EnableDisable_EditGroups->isChecked()) {
@ -3607,6 +3616,7 @@ bool MainWindow::closeProject() {
return true; return true;
// Check loaded maps for unsaved changes // Check loaded maps for unsaved changes
// TODO: This needs to check for unsaved changes in layouts too.
bool unsavedChanges = false; bool unsavedChanges = false;
for (auto map : editor->project->mapCache.values()) { for (auto map : editor->project->mapCache.values()) {
if (map && map->hasUnsavedChanges()) { if (map && map->hasUnsavedChanges()) {

View file

@ -412,10 +412,6 @@ QString Project::readMapLayoutId(QString map_name) {
return ParseUtil::jsonToQString(mapObj["layout"]); return ParseUtil::jsonToQString(mapObj["layout"]);
} }
QString Project::readMapLayoutName(QString mapName) {
return this->layoutIdsToNames[readMapLayoutId(mapName)];
}
QString Project::readMapLocation(QString map_name) { QString Project::readMapLocation(QString map_name) {
if (mapCache.contains(map_name)) { if (mapCache.contains(map_name)) {
return mapCache.value(map_name)->location; return mapCache.value(map_name)->location;
@ -537,6 +533,8 @@ bool Project::loadMapLayout(Map* map) {
void Project::clearMapLayouts() { void Project::clearMapLayouts() {
qDeleteAll(mapLayouts); qDeleteAll(mapLayouts);
mapLayouts.clear(); mapLayouts.clear();
qDeleteAll(mapLayoutsMaster);
mapLayoutsMaster.clear();
mapLayoutsTable.clear(); mapLayoutsTable.clear();
layoutIdsToNames.clear(); layoutIdsToNames.clear();
} }
@ -763,7 +761,7 @@ void Project::saveMapSections() {
longestLength += 1; longestLength += 1;
// mapSectionValueToName // TODO: Maybe print as an enum now that we can?
for (int value : this->mapSectionValueToName.keys()) { for (int value : this->mapSectionValueToName.keys()) {
QString line = QString("#define %1 0x%2\n") QString line = QString("#define %1 0x%2\n")
.arg(this->mapSectionValueToName[value], -1 * longestLength) .arg(this->mapSectionValueToName[value], -1 * longestLength)
@ -771,6 +769,8 @@ void Project::saveMapSections() {
text += line; text += line;
} }
// TODO: We should maybe consider another way to update MAPSEC values in this file, in case we break anything by relocating it to the bottom of the file.
// (or alternatively keep separate strings for text before/after the MAPSEC values)
text += "\n" + this->extraFileText[projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections)] + "\n"; text += "\n" + this->extraFileText[projectConfig.getFilePath(ProjectFilePath::constants_region_map_sections)] + "\n";
text += QString("#endif // GUARD_REGIONMAPSEC_H\n"); text += QString("#endif // GUARD_REGIONMAPSEC_H\n");
@ -2307,10 +2307,12 @@ bool Project::readRegionMapSections() {
continue; continue;
} }
// include guards // include guards
// TODO: Assuming guard name is the same across projects (it isn't)
else if (currentLine.contains("GUARD_REGIONMAPSEC_H")) { else if (currentLine.contains("GUARD_REGIONMAPSEC_H")) {
continue; continue;
} }
// defines captured (not considering comments) // defines captured
// TODO: Regex to consider comments/extra space
else if (currentLine.contains("#define " + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))) { else if (currentLine.contains("#define " + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix))) {
continue; continue;
} }
@ -2324,18 +2326,28 @@ bool Project::readRegionMapSections() {
return true; return true;
} }
int Project::appendMapsec(QString name) { QString Project::getEmptyMapsecName() {
// This function assumes a valid and unique name. return projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix) + projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty);
// Will return the new index. }
const QString emptyMapsecName = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)
+ projectConfig.getIdentifier(ProjectIdentifier::define_map_section_empty);
int noneBefore = this->mapSectionNameToValue[emptyMapsecName]; // This function assumes a valid and unique name.
this->mapSectionNameToValue[name] = noneBefore; // Will return the new index.
this->mapSectionValueToName[noneBefore] = name; // TODO: We're not currently tracking map/layout agonstic changes like this as unsaved, so there's no warning if you close the project after doing this.
this->mapSectionNameToValue[emptyMapsecName] = noneBefore + 1; int Project::appendMapsec(QString name) {
this->mapSectionValueToName[noneBefore + 1] = emptyMapsecName; const QString emptyMapsecName = getEmptyMapsecName();
return noneBefore; int newMapsecValue = mapSectionValueToName.isEmpty() ? 0 : mapSectionValueToName.lastKey();
// If the user has the 'empty' MAPSEC value defined last in the list we'll shift it so that it stays last in the list.
if (this->mapSectionNameToValue.contains(emptyMapsecName) && this->mapSectionNameToValue.value(emptyMapsecName) == newMapsecValue) {
this->mapSectionNameToValue.insert(emptyMapsecName, newMapsecValue + 1);
this->mapSectionValueToName.insert(newMapsecValue + 1, emptyMapsecName);
}
// TODO: Update 'define_map_section_count'?
this->mapSectionNameToValue[name] = newMapsecValue;
this->mapSectionValueToName[newMapsecValue] = name;
return newMapsecValue;
} }
// Read the constants to preserve any "unused" heal locations when writing the file later // Read the constants to preserve any "unused" heal locations when writing the file later

View file

@ -154,9 +154,6 @@ void ScriptUtility::setMainTab(int index) {
// Can't select tab if it's disabled // Can't select tab if it's disabled
if (!window->ui->mainTabBar->isTabEnabled(index)) if (!window->ui->mainTabBar->isTabEnabled(index))
return; return;
// don't change tab when not editing a map
if (!window->editor || !window->editor->map)
return;
window->on_mainTabBar_tabBarClicked(index); window->on_mainTabBar_tabBarClicked(index);
} }

View file

@ -19,10 +19,9 @@ void MapTree::removeSelected() {
QWidget *GroupNameDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { QWidget *GroupNameDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const {
QLineEdit *editor = new QLineEdit(parent); QLineEdit *editor = new QLineEdit(parent);
static const QRegularExpression expression("[A-Za-z0-9_]+"); static const QRegularExpression expression("[A-Za-z_]+[\\w]*");
editor->setPlaceholderText("gMapGroup_"); editor->setPlaceholderText("gMapGroup_");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression, parent); editor->setValidator(new QRegularExpressionValidator(expression, parent));
editor->setValidator(validator);
editor->setFrame(false); editor->setFrame(false);
return editor; return editor;
} }
@ -401,7 +400,12 @@ QStandardItem *MapAreaModel::insertAreaItem(QString areaName) {
int newAreaIndex = this->project->appendMapsec(areaName); int newAreaIndex = this->project->appendMapsec(areaName);
QStandardItem *item = createAreaItem(areaName, newAreaIndex); QStandardItem *item = createAreaItem(areaName, newAreaIndex);
this->root->insertRow(newAreaIndex, item); this->root->insertRow(newAreaIndex, item);
this->areaItems["MAPSEC_NONE"]->setData(newAreaIndex + 1, MapListUserRoles::GroupRole);
// MAPSEC_NONE may have shifted to accomodate the new item, update it in the list.
const QString emptyMapsecName = Project::getEmptyMapsecName();
if (this->areaItems.contains(emptyMapsecName))
this->areaItems[emptyMapsecName]->setData(this->project->mapSectionNameToValue.value(emptyMapsecName), MapListUserRoles::GroupRole);
return item; return item;
} }
@ -427,6 +431,7 @@ void MapAreaModel::initialize() {
this->mapItems.clear(); this->mapItems.clear();
this->setSortRole(MapListUserRoles::GroupRole); this->setSortRole(MapListUserRoles::GroupRole);
// TODO: Ignore 'define_map_section_count' and/or 'define_map_section_empty'?
for (int i : this->project->mapSectionNameToValue) { for (int i : this->project->mapSectionNameToValue) {
QString mapsecName = project->mapSectionValueToName.value(i); QString mapsecName = project->mapSectionValueToName.value(i);
QStandardItem *areaItem = createAreaItem(mapsecName, i); QStandardItem *areaItem = createAreaItem(mapsecName, i);