Automatically add new map groups

This commit is contained in:
GriffinR 2024-11-14 16:01:54 -05:00
parent 8bb0100540
commit 724f42019c
13 changed files with 331 additions and 298 deletions

View File

@ -25,7 +25,7 @@
<x>0</x>
<y>0</y>
<width>427</width>
<height>520</height>
<height>522</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_3">
@ -68,7 +68,7 @@
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QComboBox" name="comboBox"/>
</item>
<item row="12" column="0" colspan="2">
@ -142,7 +142,7 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="5" column="1">
<widget class="QLabel" name="label_GroupError">
<property name="visible">
<bool>false</bool>
@ -175,7 +175,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Layout ID</string>
@ -197,15 +197,11 @@
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QPushButton" name="pushButton_Accept">
<property name="text">
<string>Accept</string>
</property>
</widget>
</item>
</layout>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::Reset</set>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -70,7 +70,7 @@ public:
this->showTilesetEditorLayerGrid = true;
this->monitorFiles = true;
this->tilesetCheckerboardFill = true;
this->newMapHeaderSectionExpanded = true;
this->newMapHeaderSectionExpanded = false;
this->theme = "default";
this->wildMonChartTheme = "";
this->textEditorOpenFolder = "";

View File

@ -188,7 +188,9 @@ private slots:
void onOpenConnectedMap(MapConnection*);
void onTilesetsSaved(QString, QString);
void openNewMapDialog();
void onNewMapCreated();
void onNewMapCreated(Map *newMap, const QString &groupName);
void onNewMapGroupCreated(const QString &groupName);
void onNewLayoutCreated(Layout *layout);
void onMapLoaded(Map *map);
void importMapFromAdvanceMap1_92();
void onMapRulerStatusChanged(const QString &);

View File

@ -32,20 +32,18 @@ public:
public:
QString root;
QStringList groupNames;
QMap<QString, int> mapGroups;
QList<QStringList> groupedMapNames;
QStringList mapNames;
QStringList groupNames;
QMap<QString, QStringList> groupNameToMapNames;
QList<HealLocation> healLocations;
QMap<QString, int> healLocationNameToValue;
QMap<QString, QString> mapConstantsToMapNames;
QMap<QString, QString> mapNamesToMapConstants;
QMap<QString, QString> mapNameToLayoutId;
QMap<QString, QString> mapNameToMapSectionName;
QStringList mapLayoutsTable;
QStringList mapLayoutsTableMaster;
QString layoutsLabel;
QMap<QString, QString> layoutIdsToNames;
QStringList layoutIds;
QStringList layoutIdsMaster;
QMap<QString, Layout*> mapLayouts;
QMap<QString, Layout*> mapLayoutsMaster;
QMap<QString, EventGraphics*> eventGraphicsMap;
@ -122,7 +120,9 @@ public:
void deleteFile(QString path);
bool readMapGroups();
Map* addNewMapToGroup(Map*, int, bool, bool);
void addNewMap(Map* newMap, const QString &groupName);
void addNewMapGroup(const QString &groupName);
void addNewLayout(Layout* newLayout);
QString getNewMapName();
QString getProjectTitle();
@ -214,8 +214,8 @@ public:
QString getScriptDefaultString(bool usePoryScript, QString mapName) const;
QStringList getEventScriptsFilePaths() const;
QString getDefaultPrimaryTilesetLabel();
QString getDefaultSecondaryTilesetLabel();
QString getDefaultPrimaryTilesetLabel() const;
QString getDefaultSecondaryTilesetLabel() const;
void updateTilesetMetatileLabels(Tileset *tileset);
QString buildMetatileLabelsText(const QMap<QString, uint16_t> defines);
QString findMetatileLabelsTileset(QString label);
@ -265,6 +265,9 @@ signals:
void fileChanged(QString filepath);
void mapSectionIdNamesChanged(const QStringList &idNames);
void mapLoaded(Map *map);
void mapAdded(Map *newMap, const QString &groupName);
void mapGroupAdded(const QString &groupName);
void layoutAdded(Layout *newLayout);
};
#endif // PROJECT_H

View File

@ -13,9 +13,8 @@
class Project;
enum MapListUserRoles {
GroupRole = Qt::UserRole + 1, // Used to hold the map group number.
TypeRole, // Used to differentiate between the different layers of the map list tree view.
TypeRole2, // Used for various extra data needed.
NameRole = Qt::UserRole, // Holds the name of the item in the list
TypeRole, // Used to differentiate between the different layers of the map list tree view.
};
@ -92,7 +91,7 @@ public:
public:
void setMap(QString mapName) { this->openMap = mapName; }
QStandardItem *createGroupItem(QString groupName, int groupIndex, QStandardItem *fromItem = nullptr);
QStandardItem *createGroupItem(QString groupName, QStandardItem *fromItem = nullptr);
QStandardItem *createMapItem(QString mapName, QStandardItem *fromItem = nullptr);
QStandardItem *insertGroupItem(QString groupName);
@ -138,10 +137,10 @@ public:
void setMap(QString mapName) { this->openMap = mapName; }
QStandardItem *createAreaItem(QString areaName);
QStandardItem *createMapItem(QString mapName, int areaIndex, int mapIndex);
QStandardItem *createMapItem(QString mapName);
QStandardItem *insertAreaItem(QString areaName);
QStandardItem *insertMapItem(QString mapName, QString areaName, int groupIndex);
QStandardItem *insertMapItem(QString mapName, QString areaName);
virtual QStandardItem *getItem(const QModelIndex &index) const override;
virtual QModelIndex indexOf(QString mapName) const override;

View File

@ -20,6 +20,7 @@ public:
void initUi(Project *project);
struct Settings {
QString id; // TODO: Support in UI (toggleable line edit)
int width;
int height;
int borderWidth;

View File

@ -20,31 +20,30 @@ class NewMapDialog : public QDialog
public:
explicit NewMapDialog(QWidget *parent = nullptr, Project *project = nullptr);
~NewMapDialog();
Map *map;
int group;
bool existingLayout;
bool importedMap;
QString layoutId;
void init();
void init(int tabIndex, QString data);
void init(Layout *);
static void setDefaultSettings(Project *project);
void accept() override;
static void setDefaultSettings(const Project *project);
signals:
void applied();
void applied(const QString &newMapName);
private:
Ui::NewMapDialog *ui;
Project *project;
CollapsibleSection *headerSection;
MapHeaderForm *headerForm;
Layout *importedLayout = nullptr;
bool validateMapGroup();
bool validateID();
bool validateName();
// Each of these validation functions will allow empty names up until `OK` is selected,
// because clearing the text during editing is common and we don't want to flash errors for this.
bool validateID(bool allowEmpty = false);
bool validateName(bool allowEmpty = false);
bool validateGroup(bool allowEmpty = false);
void saveSettings();
void useLayout(QString layoutId);
bool isExistingLayout() const;
void useLayoutSettings(Layout *mapLayout);
struct Settings {
@ -56,11 +55,11 @@ private:
static struct Settings settings;
private slots:
//void on_checkBox_UseExistingLayout_stateChanged(int state); //TODO
//void on_comboBox_Layout_currentTextChanged(const QString &text);
void on_pushButton_Accept_clicked();
//void on_comboBox_Layout_currentTextChanged(const QString &text);//TODO
void dialogButtonClicked(QAbstractButton *button);
void on_lineEdit_Name_textChanged(const QString &);
void on_lineEdit_MapID_textChanged(const QString &);
void on_comboBox_Group_currentTextChanged(const QString &text);
};
#endif // NEWMAPDIALOG_H

View File

@ -392,7 +392,8 @@ OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) {
cloneJson["x"] = this->getX();
cloneJson["y"] = this->getY();
cloneJson["target_local_id"] = this->getTargetID();
cloneJson["target_map"] = project->mapNamesToMapConstants.value(this->getTargetMap());
const QString mapName = this->getTargetMap();
cloneJson["target_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
this->addCustomValuesTo(&cloneJson);
return cloneJson;
@ -407,7 +408,7 @@ bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) {
// Log a warning if "target_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["target_map"]);
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Target Map constant '%1' is invalid.").arg(mapConstant));
logWarn(QString("Unknown Target Map constant '%1'.").arg(mapConstant));
this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);
@ -496,7 +497,8 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) {
warpJson["x"] = this->getX();
warpJson["y"] = this->getY();
warpJson["elevation"] = this->getElevation();
warpJson["dest_map"] = project->mapNamesToMapConstants.value(this->getDestinationMap());
const QString mapName = this->getDestinationMap();
warpJson["dest_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
warpJson["dest_warp_id"] = this->getDestinationWarpID();
this->addCustomValuesTo(&warpJson);
@ -513,7 +515,7 @@ bool WarpEvent::loadFromJson(QJsonObject json, Project *project) {
// Log a warning if "dest_map" isn't a known map ID, but don't overwrite user data.
const QString mapConstant = ParseUtil::jsonToQString(json["dest_map"]);
if (!project->mapConstantsToMapNames.contains(mapConstant))
logWarn(QString("Destination Map constant '%1' is invalid.").arg(mapConstant));
logWarn(QString("Unknown Destination Map constant '%1'.").arg(mapConstant));
this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);

View File

@ -617,6 +617,9 @@ bool MainWindow::openProject(QString dir, bool initial) {
project->set_root(dir);
connect(project, &Project::fileChanged, this, &MainWindow::showFileWatcherWarning);
connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
connect(project, &Project::mapAdded, this, &MainWindow::onNewMapCreated);
connect(project, &Project::mapGroupAdded, this, &MainWindow::onNewMapGroupCreated);
connect(project, &Project::layoutAdded, this, &MainWindow::onNewLayoutCreated);
connect(project, &Project::mapSectionIdNamesChanged, this->mapHeaderForm, &MapHeaderForm::setLocations);
this->editor->setProject(project);
@ -702,7 +705,7 @@ bool MainWindow::setInitialMap() {
// User recently had a map open that still exists.
if (setMap(recent))
return true;
} else if (editor->project->mapLayoutsTable.contains(recent)) {
} else if (editor->project->layoutIds.contains(recent)) {
// User recently had a layout open that still exists.
if (setLayout(recent))
return true;
@ -713,7 +716,7 @@ bool MainWindow::setInitialMap() {
if (name != recent && setMap(name))
return true;
}
for (const auto &id : editor->project->mapLayoutsTable) {
for (const auto &id : editor->project->layoutIds) {
if (id != recent && setLayout(id))
return true;
}
@ -932,13 +935,13 @@ bool MainWindow::userSetLayout(QString layoutId) {
bool MainWindow::setLayout(QString layoutId) {
if (this->editor->map)
logInfo("Switching to a layout-only editing mode. Disabling map-related edits.");
logInfo("Switching to layout-only editing mode. Disabling map-related edits.");
unsetMap();
// Prefer logging the name of the layout as displayed in the map list.
const QString layoutName = this->editor->project ? this->editor->project->layoutIdsToNames.value(layoutId, layoutId) : layoutId;
logInfo(QString("Setting layout to '%1'").arg(layoutName));
const Layout* layout = this->editor->project ? this->editor->project->mapLayouts.value(layoutId) : nullptr;
logInfo(QString("Setting layout to '%1'").arg(layout ? layout->name : layoutId));
if (!this->editor->setLayout(layoutId)) {
return false;
@ -1069,7 +1072,7 @@ bool MainWindow::setProjectUI() {
const QSignalBlocker b_LayoutSelector(ui->comboBox_LayoutSelector);
ui->comboBox_LayoutSelector->clear();
ui->comboBox_LayoutSelector->addItems(project->mapLayoutsTable);
ui->comboBox_LayoutSelector->addItems(project->layoutIds);
const QSignalBlocker b_DiveMap(ui->comboBox_DiveMap);
ui->comboBox_DiveMap->clear();
@ -1189,7 +1192,7 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) {
auto sourceModel = static_cast<MapListModel*>(model->sourceModel());
QStandardItem *selectedItem = sourceModel->itemFromIndex(index);
const QString itemType = selectedItem->data(MapListUserRoles::TypeRole).toString();
const QString itemName = selectedItem->data(Qt::UserRole).toString();
const QString itemName = selectedItem->data(MapListUserRoles::NameRole).toString();
QMenu menu(this);
QAction* addToFolderAction = nullptr;
@ -1278,7 +1281,7 @@ void MainWindow::mapListAddGroup() {
if (dialog.exec() == QDialog::Accepted) {
QString newFieldName = newNameEdit->text();
if (newFieldName.isEmpty()) return;
this->mapGroupModel->insertGroupItem(newFieldName);
this->editor->project->addNewMapGroup(newFieldName);
}
}
@ -1307,7 +1310,7 @@ void MainWindow::mapListAddLayout() {
});
NoScrollComboBox *useExistingCombo = new NoScrollComboBox(&dialog);
useExistingCombo->addItems(this->editor->project->mapLayoutsTable);
useExistingCombo->addItems(this->editor->project->layoutIds);
useExistingCombo->setEnabled(false);
QCheckBox *useExistingCheck = new QCheckBox(&dialog);
@ -1358,13 +1361,13 @@ void MainWindow::mapListAddLayout() {
errorMessage = "Name cannot be empty";
}
// unique layout name & id
else if (this->editor->project->mapLayoutsTable.contains(newId->text())
/*else if (this->editor->project->layoutIds.contains(newId->text())
|| this->editor->project->layoutIdsToNames.find(tryLayoutName) != this->editor->project->layoutIdsToNames.end()) {
errorMessage = "Layout Name / ID is not unique";
}
}*/ // TODO: Re-implement
// from id is existing value
else if (useExistingCheck->isChecked()) {
if (!this->editor->project->mapLayoutsTable.contains(useExistingCombo->currentText())) {
if (!this->editor->project->layoutIds.contains(useExistingCombo->currentText())) {
errorMessage = "Existing layout ID is not valid";
}
}
@ -1395,7 +1398,6 @@ void MainWindow::mapListAddLayout() {
layoutSettings.tileset_secondary_label = secondaryCombo->currentText();
}
Layout *newLayout = this->editor->project->createNewLayout(layoutSettings);
this->layoutTreeModel->insertLayoutItem(newLayout->id);
setLayout(newLayout->id);
}
}
@ -1407,10 +1409,9 @@ void MainWindow::mapListAddArea() {
connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix);
QLineEdit *newNameEdit = new QLineEdit(&dialog);
QLineEdit *newNameDisplay = new QLineEdit(&dialog);
auto newNameEdit = new QLineEdit(&dialog);
auto newNameDisplay = new QLabel(&dialog);
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);
@ -1450,55 +1451,52 @@ void MainWindow::mapListAddArea() {
}
}
void MainWindow::onNewMapCreated() {
QString newMapName = this->newMapDialog->map->name();
int newMapGroup = this->newMapDialog->group;
Map *newMap = this->newMapDialog->map;
bool existingLayout = this->newMapDialog->existingLayout;
bool importedMap = this->newMapDialog->importedMap;
newMap = editor->project->addNewMapToGroup(newMap, newMapGroup, existingLayout, importedMap);
logInfo(QString("Created a new map named %1.").arg(newMapName));
void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
logInfo(QString("Created a new map named %1.").arg(newMap->name()));
// TODO: Creating a new map shouldn't be automatically saved
editor->project->saveMap(newMap);
editor->project->saveAllDataStructures();
// Add new Map / Layout to the mapList models
this->mapGroupModel->insertMapItem(newMapName, editor->project->groupNames[newMapGroup]);
this->mapAreaModel->insertMapItem(newMapName, newMap->header()->location(), newMapGroup);
this->layoutTreeModel->insertMapItem(newMapName, newMap->layout()->id);
// Add new map to the map lists
this->mapGroupModel->insertMapItem(newMap->name(), groupName);
this->mapAreaModel->insertMapItem(newMap->name(), newMap->header()->location());
this->layoutTreeModel->insertMapItem(newMap->name(), newMap->layout()->id);
// Refresh any combo box that displays map names and persists between maps
// (other combo boxes like for warp destinations are repopulated when the map changes).
int mapIndex = this->editor->project->mapNames.indexOf(newMapName);
int mapIndex = this->editor->project->mapNames.indexOf(newMap->name());
if (mapIndex >= 0) {
const QSignalBlocker b_DiveMap(ui->comboBox_DiveMap);
const QSignalBlocker b_EmergeMap(ui->comboBox_EmergeMap);
ui->comboBox_DiveMap->insertItem(mapIndex, newMapName);
ui->comboBox_EmergeMap->insertItem(mapIndex, newMapName);
ui->comboBox_DiveMap->insertItem(mapIndex, newMap->name());
ui->comboBox_EmergeMap->insertItem(mapIndex, newMap->name());
}
// Refresh layout combo box (if a new one was created)
if (!existingLayout) {
int layoutIndex = this->editor->project->mapLayoutsTable.indexOf(newMap->layout()->id);
if (layoutIndex >= 0) {
const QSignalBlocker b_Layouts(ui->comboBox_LayoutSelector);
ui->comboBox_LayoutSelector->insertItem(layoutIndex, newMap->layout()->id);
}
}
setMap(newMapName);
if (newMap->needsHealLocation()) {
addNewEvent(Event::Type::HealLocation);
editor->project->saveHealLocations(newMap);
editor->save();
}
}
disconnect(this->newMapDialog, &NewMapDialog::applied, this, &MainWindow::onNewMapCreated);
delete newMap;
void MainWindow::onNewLayoutCreated(Layout *layout) {
logInfo(QString("Created a new layout named %1.").arg(layout->name));
// Refresh layout combo box
int layoutIndex = this->editor->project->layoutIds.indexOf(layout->id);
if (layoutIndex >= 0) {
const QSignalBlocker b(ui->comboBox_LayoutSelector);
ui->comboBox_LayoutSelector->insertItem(layoutIndex, layout->id);
}
// Add new layout to the Layouts map list view
this->layoutTreeModel->insertLayoutItem(layout->id);
}
void MainWindow::onNewMapGroupCreated(const QString &groupName) {
// Add new map group to the Groups map list view
this->mapGroupModel->insertGroupItem(groupName);
}
void MainWindow::openNewMapDialog() {
@ -1508,7 +1506,7 @@ void MainWindow::openNewMapDialog() {
}
if (!this->newMapDialog) {
this->newMapDialog = new NewMapDialog(this, this->editor->project);
connect(this->newMapDialog, &NewMapDialog::applied, this, &MainWindow::onNewMapCreated);
connect(this->newMapDialog, &NewMapDialog::applied, this, &MainWindow::userSetMap);
}
openSubWindow(this->newMapDialog);
@ -1701,9 +1699,10 @@ void MainWindow::openMapListItem(const QModelIndex &index) {
if (!index.isValid())
return;
QVariant data = index.data(Qt::UserRole);
QVariant data = index.data(MapListUserRoles::NameRole);
if (data.isNull())
return;
const QString name = data.toString();
// Normally when a new map/layout is opened the search filters are cleared and the lists will scroll to display that map/layout in the list.
// We don't want to do this when the user interacts with a list directly, so we temporarily prevent changes to the search filter.
@ -1712,9 +1711,9 @@ void MainWindow::openMapListItem(const QModelIndex &index) {
QString type = index.data(MapListUserRoles::TypeRole).toString();
if (type == "map_name") {
userSetMap(data.toString());
userSetMap(name);
} else if (type == "map_layout") {
userSetLayout(data.toString());
userSetLayout(name);
}
if (toolbar) toolbar->setFilterLocked(false);

View File

@ -368,6 +368,7 @@ bool Project::loadMapData(Map* map) {
return true;
}
// TODO: Refactor, we're duplicating logic between here, the new map dialog, and addNewLayout
Layout *Project::createNewLayout(Layout::SimpleSettings &layoutSettings) {
QString basePath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders);
Layout *layout;
@ -409,15 +410,16 @@ Layout *Project::createNewLayout(Layout::SimpleSettings &layoutSettings) {
return nullptr;
}
mapLayouts.insert(layout->id, layout);
mapLayoutsMaster.insert(layout->id, layout->copy());
mapLayoutsTable.append(layout->id);
mapLayoutsTableMaster.append(layout->id);
layoutIdsToNames.insert(layout->id, layout->name);
// TODO: Redundancy here, some of this is already handled in saveLayout > updateLayout
this->mapLayouts.insert(layout->id, layout);
this->mapLayoutsMaster.insert(layout->id, layout->copy());
this->layoutIds.append(layout->id);
this->layoutIdsMaster.append(layout->id);
saveLayout(layout);
this->loadLayout(layout);
loadLayout(layout);
emit layoutAdded(layout);
return layout;
}
@ -471,12 +473,12 @@ bool Project::loadMapLayout(Map* map) {
}
void Project::clearMapLayouts() {
qDeleteAll(mapLayouts);
mapLayouts.clear();
qDeleteAll(mapLayoutsMaster);
mapLayoutsMaster.clear();
mapLayoutsTable.clear();
layoutIdsToNames.clear();
qDeleteAll(this->mapLayouts);
this->mapLayouts.clear();
qDeleteAll(this->mapLayoutsMaster);
this->mapLayoutsMaster.clear();
this->layoutIds.clear();
this->layoutIdsMaster.clear();
}
bool Project::readMapLayouts() {
@ -498,9 +500,9 @@ bool Project::readMapLayouts() {
return false;
}
layoutsLabel = ParseUtil::jsonToQString(layoutsObj["layouts_table_label"]);
if (layoutsLabel.isNull()) {
layoutsLabel = "gMapLayouts";
this->layoutsLabel = ParseUtil::jsonToQString(layoutsObj["layouts_table_label"]);
if (this->layoutsLabel.isEmpty()) {
this->layoutsLabel = "gMapLayouts";
logWarn(QString("'layouts_table_label' value is missing from %1. Defaulting to %2")
.arg(layoutsFilepath)
.arg(layoutsLabel));
@ -599,11 +601,11 @@ bool Project::readMapLayouts() {
delete layout;
return false;
}
mapLayouts.insert(layout->id, layout);
mapLayoutsMaster.insert(layout->id, layout->copy());
mapLayoutsTable.append(layout->id);
mapLayoutsTableMaster.append(layout->id);
layoutIdsToNames.insert(layout->id, layout->name);
this->mapLayouts.insert(layout->id, layout);
this->mapLayoutsMaster.insert(layout->id, layout->copy());
this->layoutIds.append(layout->id);
this->layoutIdsMaster.append(layout->id);
}
return true;
@ -618,11 +620,11 @@ void Project::saveMapLayouts() {
}
OrderedJson::object layoutsObj;
layoutsObj["layouts_table_label"] = layoutsLabel;
layoutsObj["layouts_table_label"] = this->layoutsLabel;
OrderedJson::array layoutsArr;
for (QString layoutId : mapLayoutsTableMaster) {
Layout *layout = mapLayoutsMaster.value(layoutId);
for (const QString &layoutId : this->layoutIdsMaster) {
Layout *layout = this->mapLayoutsMaster.value(layoutId);
OrderedJson::object layoutObj;
layoutObj["id"] = layout->id;
layoutObj["name"] = layout->name;
@ -669,14 +671,12 @@ void Project::saveMapGroups() {
}
mapGroupsObj["group_order"] = groupNamesArr;
int groupNum = 0;
for (QStringList mapNames : groupedMapNames) {
for (const auto &groupName : this->groupNames) {
OrderedJson::array groupArr;
for (QString mapName : mapNames) {
for (const auto &mapName : this->groupNameToMapNames.value(groupName)) {
groupArr.push_back(mapName);
}
mapGroupsObj[this->groupNames.at(groupNum)] = groupArr;
groupNum++;
mapGroupsObj[groupName] = groupArr;
}
ignoreWatchedFileTemporarily(mapGroupsFilepath);
@ -1390,15 +1390,15 @@ void Project::saveLayout(Layout *layout) {
}
void Project::updateLayout(Layout *layout) {
if (!mapLayoutsTableMaster.contains(layout->id)) {
mapLayoutsTableMaster.append(layout->id);
if (!this->layoutIdsMaster.contains(layout->id)) {
this->layoutIdsMaster.append(layout->id);
}
if (mapLayoutsMaster.contains(layout->id)) {
mapLayoutsMaster[layout->id]->copyFrom(layout);
if (this->mapLayoutsMaster.contains(layout->id)) {
this->mapLayoutsMaster[layout->id]->copyFrom(layout);
}
else {
mapLayoutsMaster.insert(layout->id, layout->copy());
this->mapLayoutsMaster.insert(layout->id, layout->copy());
}
}
@ -1821,10 +1821,9 @@ bool Project::readWildMonData() {
bool Project::readMapGroups() {
this->mapConstantsToMapNames.clear();
this->mapNamesToMapConstants.clear();
this->mapGroups.clear();
this->groupNames.clear();
this->groupedMapNames.clear();
this->mapNames.clear();
this->groupNames.clear();
this->groupNameToMapNames.clear();
this->initTopLevelMapFields();
@ -1845,7 +1844,6 @@ bool Project::readMapGroups() {
for (int groupIndex = 0; groupIndex < mapGroupOrder.size(); groupIndex++) {
const QString groupName = ParseUtil::jsonToQString(mapGroupOrder.at(groupIndex));
const QJsonArray mapNamesJson = mapGroupsObj.value(groupName).toArray();
this->groupedMapNames.append(QStringList());
this->groupNames.append(groupName);
// Process the names in this map group
@ -1885,8 +1883,8 @@ bool Project::readMapGroups() {
// Success, save the constants to the project
this->mapNames.append(mapName);
this->groupedMapNames[groupIndex].append(mapName);
this->mapGroups.insert(mapName, groupIndex);
this->groupNameToMapNames[groupName].append(mapName);
// TODO: These are not well-kept in sync (and that's probably a bad design indication. Maybe Maps should have a not-fully-loaded state, but have all their map.json data cached)
this->mapConstantsToMapNames.insert(mapConstant, mapName);
this->mapNamesToMapConstants.insert(mapName, mapConstant);
// TODO: Either verify that these are known IDs, or make sure nothing breaks when they're unknown.
@ -1913,34 +1911,69 @@ bool Project::readMapGroups() {
return true;
}
Map* Project::addNewMapToGroup(Map *newMap, int groupNum, bool existingLayout, bool importedMap) {
int mapNamePos = 0;
for (int i = 0; i <= groupNum; i++)
mapNamePos += this->groupedMapNames.value(i).length();
void Project::addNewMap(Map *newMap, const QString &groupName) {
if (!newMap)
return;
// Make sure we keep the order of the map names the same as in the map group order.
int mapNamePos;
if (this->groupNames.contains(groupName)) {
mapNamePos = 0;
for (const auto &name : this->groupNames) {
mapNamePos += this->groupNameToMapNames[name].length();
if (name == groupName)
break;
}
} else {
// Adding map to a map group that doesn't exist yet.
// Create the group, and we already know the map will be last in the list.
addNewMapGroup(groupName);
mapNamePos = this->mapNames.length();
}
this->mapNames.insert(mapNamePos, newMap->name());
this->mapGroups.insert(newMap->name(), groupNum);
this->groupedMapNames[groupNum].append(newMap->name());
this->groupNameToMapNames[groupName].append(newMap->name());
this->mapConstantsToMapNames.insert(newMap->constantName(), newMap->name());
this->mapNamesToMapConstants.insert(newMap->name(), newMap->constantName());
newMap->setIsPersistedToFile(false);
if (!existingLayout) {
this->mapLayouts.insert(newMap->layoutId(), newMap->layout());
this->mapLayoutsTable.append(newMap->layoutId());
this->layoutIdsToNames.insert(newMap->layout()->id, newMap->layout()->name);
if (!importedMap) {
setNewLayoutBlockdata(newMap->layout());
}
if (newMap->layout()->border.isEmpty()) {
setNewLayoutBorder(newMap->layout());
}
// If we don't recognize the layout ID (i.e., it's also new) we'll add that too.
if (!this->layoutIds.contains(newMap->layout()->id)) {
addNewLayout(newMap->layout());
}
loadLayoutTilesets(newMap->layout());
emit mapAdded(newMap, groupName);
}
return newMap;
void Project::addNewLayout(Layout* newLayout) {
if (!newLayout || this->layoutIds.contains(newLayout->id))
return;
this->mapLayouts.insert(newLayout->id, newLayout);
this->layoutIds.append(newLayout->id);
if (newLayout->blockdata.isEmpty()) {
// Fill layout using default fill settings
setNewLayoutBlockdata(newLayout);
}
if (newLayout->border.isEmpty()) {
// Fill border using default fill settings
setNewLayoutBorder(newLayout);
}
emit layoutAdded(newLayout);
}
void Project::addNewMapGroup(const QString &groupName) {
if (this->groupNames.contains(groupName))
return;
this->groupNames.append(groupName);
this->groupNameToMapNames.insert(groupName, QStringList());
this->hasUnsavedDataChanges = true;
emit mapGroupAdded(groupName);
}
QString Project::getNewMapName() {
@ -1949,7 +1982,7 @@ QString Project::getNewMapName() {
QString newMapName;
do {
newMapName = QString("NewMap%1").arg(++i);
} while (mapNames.contains(newMapName));
} while (this->mapNames.contains(newMapName));
return newMapName;
}
@ -1966,7 +1999,7 @@ Project::DataQualifiers Project::getDataQualifiers(QString text, QString label)
return qualifiers;
}
QString Project::getDefaultPrimaryTilesetLabel() {
QString Project::getDefaultPrimaryTilesetLabel() const {
QString defaultLabel = projectConfig.defaultPrimaryTileset;
if (!this->primaryTilesetLabels.contains(defaultLabel)) {
QString firstLabel = this->primaryTilesetLabels.first();
@ -1976,7 +2009,7 @@ QString Project::getDefaultPrimaryTilesetLabel() {
return defaultLabel;
}
QString Project::getDefaultSecondaryTilesetLabel() {
QString Project::getDefaultSecondaryTilesetLabel() const {
QString defaultLabel = projectConfig.defaultSecondaryTileset;
if (!this->secondaryTilesetLabels.contains(defaultLabel)) {
QString firstLabel = this->secondaryTilesetLabels.first();

View File

@ -66,7 +66,7 @@ QWidget *GroupNameDelegate::createEditor(QWidget *parent, const QStyleOptionView
}
void GroupNameDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
QString groupName = index.data(Qt::UserRole).toString();
QString groupName = index.data(MapListUserRoles::NameRole).toString();
QLineEdit *le = static_cast<QLineEdit *>(editor);
le->setText(groupName);
}
@ -74,7 +74,7 @@ void GroupNameDelegate::setEditorData(QWidget *editor, const QModelIndex &index)
void GroupNameDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QLineEdit *le = static_cast<QLineEdit *>(editor);
QString groupName = le->text();
model->setData(index, groupName, Qt::UserRole);
model->setData(index, groupName, MapListUserRoles::NameRole);
}
void GroupNameDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const {
@ -112,7 +112,7 @@ QMimeData *MapGroupModel::mimeData(const QModelIndexList &indexes) const {
// if dropping a selection containing a group(s) and map(s), clear all selection but first group.
for (const QModelIndex &index : indexes) {
if (index.isValid() && data(index, MapListUserRoles::TypeRole).toString() == "map_group") {
QString groupName = data(index, Qt::UserRole).toString();
QString groupName = data(index, MapListUserRoles::NameRole).toString();
stream << groupName;
mimeData->setData("application/porymap.mapgroupmodel.group", encodedData);
mimeData->setData("application/porymap.mapgroupmodel.source.row", QByteArray::number(index.row()));
@ -122,7 +122,7 @@ QMimeData *MapGroupModel::mimeData(const QModelIndexList &indexes) const {
for (const QModelIndex &index : indexes) {
if (index.isValid()) {
QString mapName = data(index, Qt::UserRole).toString();
QString mapName = data(index, MapListUserRoles::NameRole).toString();
stream << mapName;
}
}
@ -168,12 +168,12 @@ bool MapGroupModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
QStringList mapsToMove;
for (int i = 0; i < this->rowCount(originIndex); ++i ) {
children << this->index( i, 0, originIndex);
mapsToMove << this->index( i, 0 , originIndex).data(Qt::UserRole).toString();
mapsToMove << this->index( i, 0 , originIndex).data(MapListUserRoles::NameRole).toString();
}
QModelIndex groupIndex = index(row, 0, parentIndex);
QStandardItem *groupItem = this->itemFromIndex(groupIndex);
createGroupItem(groupName, row, groupItem);
createGroupItem(groupName, groupItem);
for (QString mapName : mapsToMove) {
QStandardItem *mapItem = createMapItem(mapName);
@ -224,43 +224,38 @@ bool MapGroupModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
void MapGroupModel::updateProject() {
if (!this->project) return;
QStringList groupNames;
QMap<QString, int> mapGroups;
QList<QStringList> groupedMapNames;
// Temporary objects in case of failure, so we won't modify the project unless it succeeds.
QStringList mapNames;
QStringList groupNames;
QMap<QString, QStringList> groupNameToMapNames;
for (int g = 0; g < this->root->rowCount(); g++) {
QStandardItem *groupItem = this->item(g);
QString groupName = groupItem->data(Qt::UserRole).toString();
const QStandardItem *groupItem = this->item(g);
QString groupName = groupItem->data(MapListUserRoles::NameRole).toString();
groupNames.append(groupName);
mapGroups[groupName] = g;
QStringList mapsInGroup;
for (int m = 0; m < groupItem->rowCount(); m++) {
QStandardItem *mapItem = groupItem->child(m);
const QStandardItem *mapItem = groupItem->child(m);
if (!mapItem) {
logError("An error occured while trying to apply updates to map group structure.");
return;
}
QString mapName = mapItem->data(Qt::UserRole).toString();
mapsInGroup.append(mapName);
QString mapName = mapItem->data(MapListUserRoles::NameRole).toString();
groupNameToMapNames[groupName].append(mapName);
mapNames.append(mapName);
}
groupedMapNames.append(mapsInGroup);
}
this->project->groupNames = groupNames;
this->project->mapGroups = mapGroups;
this->project->groupedMapNames = groupedMapNames;
this->project->mapNames = mapNames;
this->project->groupNames = groupNames;
this->project->groupNameToMapNames = groupNameToMapNames;
this->project->hasUnsavedDataChanges = true;
}
QStandardItem *MapGroupModel::createGroupItem(QString groupName, int groupIndex, QStandardItem *group) {
QStandardItem *MapGroupModel::createGroupItem(QString groupName, QStandardItem *group) {
if (!group) group = new QStandardItem;
group->setText(groupName);
group->setData(groupName, Qt::UserRole);
group->setData(groupName, MapListUserRoles::NameRole);
group->setData("map_group", MapListUserRoles::TypeRole);
group->setData(groupIndex, MapListUserRoles::GroupRole);
group->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable);
this->groupItems.insert(groupName, group);
return group;
@ -268,7 +263,7 @@ QStandardItem *MapGroupModel::createGroupItem(QString groupName, int groupIndex,
QStandardItem *MapGroupModel::createMapItem(QString mapName, QStandardItem *map) {
if (!map) map = new QStandardItem;
map->setData(mapName, Qt::UserRole);
map->setData(mapName, MapListUserRoles::NameRole);
map->setData("map_name", MapListUserRoles::TypeRole);
map->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
this->mapItems[mapName] = map;
@ -276,9 +271,8 @@ QStandardItem *MapGroupModel::createMapItem(QString mapName, QStandardItem *map)
}
QStandardItem *MapGroupModel::insertGroupItem(QString groupName) {
QStandardItem *group = createGroupItem(groupName, this->groupItems.size());
QStandardItem *group = createGroupItem(groupName);
this->root->appendRow(group);
this->updateProject();
return group;
}
@ -300,15 +294,13 @@ QStandardItem *MapGroupModel::insertMapItem(QString mapName, QString groupName)
void MapGroupModel::initialize() {
this->groupItems.clear();
this->mapItems.clear();
for (int i = 0; i < this->project->groupNames.length(); i++) {
QString group_name = this->project->groupNames.value(i);
QStandardItem *group = createGroupItem(group_name, i);
for (const auto &groupName : this->project->groupNames) {
QStandardItem *group = createGroupItem(groupName);
root->appendRow(group);
QStringList names = this->project->groupedMapNames.value(i);
for (int j = 0; j < names.length(); j++) {
QString map_name = names.value(j);
QStandardItem *map = createMapItem(map_name);
group->appendRow(map);
for (const auto &mapName : this->project->groupNameToMapNames.value(groupName)) {
group->appendRow(createMapItem(mapName));
}
}
}
@ -361,7 +353,7 @@ QVariant MapGroupModel::data(const QModelIndex &index, int role) const {
}
return mapFolderIcon;
} else if (type == "map_name") {
QString mapName = item->data(Qt::UserRole).toString();
QString mapName = item->data(MapListUserRoles::NameRole).toString();
if (mapName == this->openMap) {
return mapOpenedIcon;
}
@ -381,10 +373,10 @@ QVariant MapGroupModel::data(const QModelIndex &index, int role) const {
QString type = item->data(MapListUserRoles::TypeRole).toString();
if (type == "map_name") {
return QString("[%1.%2] ").arg(this->getItem(index)->row()).arg(row, 2, 10, QLatin1Char('0')) + item->data(Qt::UserRole).toString();
return QString("[%1.%2] ").arg(this->getItem(index)->row()).arg(row, 2, 10, QLatin1Char('0')) + item->data(MapListUserRoles::NameRole).toString();
}
else if (type == "map_group") {
return item->data(Qt::UserRole).toString();
return item->data(MapListUserRoles::NameRole).toString();
}
}
@ -392,8 +384,9 @@ QVariant MapGroupModel::data(const QModelIndex &index, int role) const {
}
bool MapGroupModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (role == Qt::UserRole && data(index, MapListUserRoles::TypeRole).toString() == "map_group") {
if (role == MapListUserRoles::NameRole && data(index, MapListUserRoles::TypeRole).toString() == "map_group") {
// verify uniqueness of new group name
// TODO: Check that the name is a valid symbol name (i.e. only word characters, not starting with a number)
if (this->project->groupNames.contains(value.toString())) {
return false;
}
@ -417,18 +410,18 @@ QStandardItem *MapAreaModel::createAreaItem(QString mapsecName) {
QStandardItem *area = new QStandardItem;
area->setText(mapsecName);
area->setEditable(false);
area->setData(mapsecName, Qt::UserRole);
area->setData(mapsecName, MapListUserRoles::NameRole);
area->setData("map_section", MapListUserRoles::TypeRole);
// group->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
this->areaItems.insert(mapsecName, area);
return area;
}
QStandardItem *MapAreaModel::createMapItem(QString mapName, int, int) {
QStandardItem *MapAreaModel::createMapItem(QString mapName) {
QStandardItem *map = new QStandardItem;
map->setText(mapName);
map->setEditable(false);
map->setData(mapName, Qt::UserRole);
map->setData(mapName, MapListUserRoles::NameRole);
map->setData("map_name", MapListUserRoles::TypeRole);
// map->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
this->mapItems.insert(mapName, map);
@ -443,19 +436,18 @@ QStandardItem *MapAreaModel::insertAreaItem(QString areaName) {
return item;
}
QStandardItem *MapAreaModel::insertMapItem(QString mapName, QString areaName, int groupIndex) {
QStandardItem *MapAreaModel::insertMapItem(QString mapName, QString areaName) {
QStandardItem *area = this->areaItems[areaName];
if (!area) {
return nullptr;
}
int mapIndex = area->rowCount();
QStandardItem *map = createMapItem(mapName, groupIndex, mapIndex);
QStandardItem *map = createMapItem(mapName);
area->appendRow(map);
return map;
}
void MapAreaModel::removeItem(QStandardItem *item) {
this->project->removeMapsec(item->data(Qt::UserRole).toString());
this->project->removeMapsec(item->data(MapListUserRoles::NameRole).toString());
this->removeRow(item->row());
}
@ -467,17 +459,12 @@ void MapAreaModel::initialize() {
this->root->appendRow(createAreaItem(idName));
}
for (int i = 0; i < this->project->groupNames.length(); i++) {
QStringList names = this->project->groupedMapNames.value(i);
for (int j = 0; j < names.length(); j++) {
QString mapName = names.value(j);
QStandardItem *map = createMapItem(mapName, i, j);
QString mapsecName = this->project->mapNameToMapSectionName.value(mapName);
if (this->areaItems.contains(mapsecName)) {
this->areaItems[mapsecName]->appendRow(map);
}
}
for (const auto &mapName : this->project->mapNames) {
const QString mapsecName = this->project->mapNameToMapSectionName.value(mapName);
if (this->areaItems.contains(mapsecName))
this->areaItems[mapsecName]->appendRow(createMapItem(mapName));
}
this->sort(0, Qt::AscendingOrder);
}
@ -529,7 +516,7 @@ QVariant MapAreaModel::data(const QModelIndex &index, int role) const {
}
return folderIcon;
} else if (type == "map_name") {
QString mapName = item->data(Qt::UserRole).toString();
QString mapName = item->data(MapListUserRoles::NameRole).toString();
if (mapName == this->openMap) {
return mapOpenedIcon;
}
@ -549,7 +536,7 @@ QVariant MapAreaModel::data(const QModelIndex &index, int role) const {
QString type = item->data(MapListUserRoles::TypeRole).toString();
if (type == "map_section") {
return item->data(Qt::UserRole).toString();
return item->data(MapListUserRoles::NameRole).toString();
}
}
@ -567,9 +554,9 @@ LayoutTreeModel::LayoutTreeModel(Project *project, QObject *parent) : MapListMod
QStandardItem *LayoutTreeModel::createLayoutItem(QString layoutId) {
QStandardItem *layout = new QStandardItem;
layout->setText(this->project->layoutIdsToNames[layoutId]);
layout->setText(this->project->mapLayouts[layoutId]->name);
layout->setEditable(false);
layout->setData(layoutId, Qt::UserRole);
layout->setData(layoutId, MapListUserRoles::NameRole);
layout->setData("map_layout", MapListUserRoles::TypeRole);
// // group->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
this->layoutItems.insert(layoutId, layout);
@ -580,7 +567,7 @@ QStandardItem *LayoutTreeModel::createMapItem(QString mapName) {
QStandardItem *map = new QStandardItem;
map->setText(mapName);
map->setEditable(false);
map->setData(mapName, Qt::UserRole);
map->setData(mapName, MapListUserRoles::NameRole);
map->setData("map_name", MapListUserRoles::TypeRole);
map->setFlags(Qt::NoItemFlags | Qt::ItemNeverHasChildren);
this->mapItems.insert(mapName, map);
@ -619,19 +606,17 @@ void LayoutTreeModel::removeItem(QStandardItem *) {
void LayoutTreeModel::initialize() {
this->layoutItems.clear();
this->mapItems.clear();
for (int i = 0; i < this->project->mapLayoutsTable.length(); i++) {
QString layoutId = project->mapLayoutsTable.value(i);
QStandardItem *layoutItem = createLayoutItem(layoutId);
this->root->appendRow(layoutItem);
for (const auto &layoutId : this->project->layoutIds) {
this->root->appendRow(createLayoutItem(layoutId));
}
for (auto mapList : this->project->groupedMapNames) {
for (auto mapName : mapList) {
QString layoutId = project->mapNameToLayoutId.value(mapName);
QStandardItem *map = createMapItem(mapName);
this->layoutItems[layoutId]->appendRow(map);
}
for (const auto &mapName : this->project->mapNames) {
QString layoutId = project->mapNameToLayoutId.value(mapName);
if (this->layoutItems.contains(layoutId))
this->layoutItems[layoutId]->appendRow(createMapItem(mapName));
}
this->sort(0, Qt::AscendingOrder);
}
@ -667,7 +652,7 @@ QVariant LayoutTreeModel::data(const QModelIndex &index, int role) const {
QString type = item->data(MapListUserRoles::TypeRole).toString();
if (type == "map_layout") {
QString layoutId = item->data(Qt::UserRole).toString();
QString layoutId = item->data(MapListUserRoles::NameRole).toString();
if (layoutId == this->openLayout) {
return mapOpenedIcon;
}

View File

@ -2,6 +2,8 @@
#include "ui_newlayoutform.h"
#include "project.h"
const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }";
NewLayoutForm::NewLayoutForm(QWidget *parent)
: QWidget(parent)
, ui(new Ui::NewLayoutForm)
@ -10,7 +12,7 @@ NewLayoutForm::NewLayoutForm(QWidget *parent)
// TODO: Read from project?
ui->spinBox_BorderWidth->setMaximum(MAX_BORDER_WIDTH);
ui->spinBox_BorderHeight->setMaximum(MAX_BORDER_HEIGHT);
ui->spinBox_BorderHeight->setMaximum(MAX_BORDER_HEIGHT);
connect(ui->spinBox_MapWidth, QOverload<int>::of(&QSpinBox::valueChanged), [=](int){validateMapDimensions();});
connect(ui->spinBox_MapHeight, QOverload<int>::of(&QSpinBox::valueChanged), [=](int){validateMapDimensions();});
@ -119,5 +121,7 @@ bool NewLayoutForm::validateTilesets() {
bool isValid = errorText.isEmpty();
ui->label_TilesetsError->setText(errorText);
ui->label_TilesetsError->setVisible(!isValid);
ui->comboBox_PrimaryTileset->lineEdit()->setStyleSheet(!primaryErrorText.isEmpty() ? lineEdit_ErrorStylesheet : "");
ui->comboBox_SecondaryTileset->lineEdit()->setStyleSheet(!secondaryErrorText.isEmpty() ? lineEdit_ErrorStylesheet : "");
return isValid;
}

View File

@ -20,8 +20,6 @@ NewMapDialog::NewMapDialog(QWidget *parent, Project *project) :
setModal(true);
ui->setupUi(this);
this->project = project;
this->existingLayout = false; // TODO: Replace, we can determine this from the Layout ID combo box
this->importedMap = false;
ui->newLayoutForm->initUi(project);
@ -32,6 +30,7 @@ NewMapDialog::NewMapDialog(QWidget *parent, Project *project) :
auto validator = new QRegularExpressionValidator(re, this);
ui->lineEdit_Name->setValidator(validator);
ui->lineEdit_MapID->setValidator(validator);
ui->comboBox_Group->setValidator(validator);
// Create a collapsible section that has all the map header data.
this->headerForm = new MapHeaderForm();
@ -43,11 +42,14 @@ NewMapDialog::NewMapDialog(QWidget *parent, Project *project) :
this->headerSection->setContentLayout(sectionLayout);
ui->layout_HeaderData->addWidget(this->headerSection);
ui->layout_HeaderData->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding));
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewMapDialog::dialogButtonClicked);
}
NewMapDialog::~NewMapDialog()
{
saveSettings();
delete this->importedLayout;
delete ui;
}
@ -73,7 +75,7 @@ void NewMapDialog::init(int tabIndex, QString fieldName) {
this->headerForm->setLocationsDisabled(true);
break;
case MapListTab::Layouts:
useLayout(fieldName);
useLayoutSettings(project->mapLayouts.value(fieldName));
break;
}
init();
@ -81,24 +83,23 @@ void NewMapDialog::init(int tabIndex, QString fieldName) {
// Creating new map from AdvanceMap import
// TODO: Re-use for a "Duplicate Map/Layout" option?
void NewMapDialog::init(Layout *layout) {
this->importedMap = true;
useLayoutSettings(layout);
void NewMapDialog::init(Layout *layoutToCopy) {
if (this->importedLayout)
delete this->importedLayout;
// TODO: These are probably leaking
this->map = new Map();
this->map->setLayout(new Layout());
this->map->layout()->blockdata = layout->blockdata;
this->importedLayout = new Layout();
this->importedLayout->blockdata = layoutToCopy->blockdata;
if (!layoutToCopy->border.isEmpty())
this->importedLayout->border = layoutToCopy->border;
if (!layout->border.isEmpty()) {
this->map->layout()->border = layout->border;
}
useLayoutSettings(this->importedLayout);
init();
}
void NewMapDialog::setDefaultSettings(Project *project) {
void NewMapDialog::setDefaultSettings(const Project *project) {
settings.group = project->groupNames.at(0);
settings.canFlyTo = false;
// TODO: Layout id
settings.layout.width = project->getDefaultMapDimension();
settings.layout.height = project->getDefaultMapDimension();
settings.layout.borderWidth = DEFAULT_BORDER_WIDTH;
@ -128,6 +129,7 @@ void NewMapDialog::saveSettings() {
void NewMapDialog::useLayoutSettings(Layout *layout) {
if (!layout) return;
settings.layout.id = layout->id;
settings.layout.width = layout->width;
settings.layout.height = layout->height;
settings.layout.borderWidth = layout->border_width;
@ -139,39 +141,24 @@ void NewMapDialog::useLayoutSettings(Layout *layout) {
ui->newLayoutForm->setDisabled(true);
}
void NewMapDialog::useLayout(QString layoutId) {
this->existingLayout = true;
this->layoutId = layoutId;
useLayoutSettings(project->mapLayouts.value(this->layoutId));
// Return true if the "layout ID" field is specifying a layout that already exists.
bool NewMapDialog::isExistingLayout() const {
return this->project->mapLayouts.contains(settings.layout.id);
}
// TODO: Create the map group if it doesn't exist
bool NewMapDialog::validateMapGroup() {
this->group = project->groupNames.indexOf(ui->comboBox_Group->currentText());
QString errorText;
if (this->group < 0) {
errorText = QString("The specified map group '%1' does not exist.")
.arg(ui->comboBox_Group->currentText());
}
bool isValid = errorText.isEmpty();
ui->label_GroupError->setText(errorText);
ui->label_GroupError->setVisible(!isValid);
return isValid;
}
bool NewMapDialog::validateID() {
bool NewMapDialog::validateID(bool allowEmpty) {
QString id = ui->lineEdit_MapID->text();
const QString expectedPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
QString errorText;
QString expectedPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
if (!id.startsWith(expectedPrefix)) {
errorText = QString("The specified ID name '%1' must start with '%2'.").arg(id).arg(expectedPrefix);
if (id.isEmpty()) {
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_MapID->text());
} else if (!id.startsWith(expectedPrefix)) {
errorText = QString("%1 '%2' must start with '%3'.").arg(ui->label_MapID->text()).arg(id).arg(expectedPrefix);
} else {
for (auto i = project->mapNamesToMapConstants.constBegin(), end = project->mapNamesToMapConstants.constEnd(); i != end; i++) {
for (auto i = this->project->mapNamesToMapConstants.constBegin(), end = this->project->mapNamesToMapConstants.constEnd(); i != end; i++) {
if (id == i.value()) {
errorText = QString("The specified ID name '%1' is already in use.").arg(id);
errorText = QString("%1 '%2' is already in use.").arg(ui->label_MapID->text()).arg(id);
break;
}
}
@ -185,15 +172,17 @@ bool NewMapDialog::validateID() {
}
void NewMapDialog::on_lineEdit_MapID_textChanged(const QString &) {
validateID();
validateID(true);
}
bool NewMapDialog::validateName() {
bool NewMapDialog::validateName(bool allowEmpty) {
QString name = ui->lineEdit_Name->text();
QString errorText;
if (project->mapNames.contains(name)) {
errorText = QString("The specified map name '%1' is already in use.").arg(name);
if (name.isEmpty()) {
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_Name->text());
} else if (project->mapNames.contains(name)) {
errorText = QString("%1 '%2' is already in use.").arg(ui->label_Name->text()).arg(name);
}
bool isValid = errorText.isEmpty();
@ -204,31 +193,53 @@ bool NewMapDialog::validateName() {
}
void NewMapDialog::on_lineEdit_Name_textChanged(const QString &text) {
validateName();
validateName(true);
ui->lineEdit_MapID->setText(Map::mapConstantFromName(text));
}
void NewMapDialog::on_pushButton_Accept_clicked() {
bool NewMapDialog::validateGroup(bool allowEmpty) {
QString groupName = ui->comboBox_Group->currentText();
QString errorText;
if (groupName.isEmpty()) {
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_Group->text());
}
bool isValid = errorText.isEmpty();
ui->label_GroupError->setText(errorText);
ui->label_GroupError->setVisible(!isValid);
ui->comboBox_Group->lineEdit()->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : "");
return isValid;
}
void NewMapDialog::on_comboBox_Group_currentTextChanged(const QString &) {
validateGroup(true);
}
void NewMapDialog::dialogButtonClicked(QAbstractButton *button) {
auto role = ui->buttonBox->buttonRole(button);
if (role == QDialogButtonBox::RejectRole){
reject();
} else if (role == QDialogButtonBox::ResetRole) {
setDefaultSettings(this->project); // TODO: Don't allow this to change locked settings
init();
} else if (role == QDialogButtonBox::AcceptRole) {
accept();
}
}
void NewMapDialog::accept() {
saveSettings();
// Make sure to call each validation function so that all errors are shown at once.
bool success = true;
if (!ui->newLayoutForm->validate()) success = false;
if (!validateMapGroup()) success = false;
if (!validateID()) success = false;
if (!validateName()) success = false;
if (!validateGroup()) success = false;
if (!success)
return;
// We check if the map name is empty separately from validateName, because validateName is also used during editing.
// It's likely that users will clear the name text box while editing, and we don't want to flash errors at them for this.
if (ui->lineEdit_Name->text().isEmpty()) {
ui->label_NameError->setText("The specified map name cannot be empty.");
ui->label_NameError->setVisible(true);
ui->lineEdit_Name->setStyleSheet(lineEdit_ErrorStylesheet);
return;
}
Map *newMap = new Map;
newMap->setName(ui->lineEdit_Name->text());
newMap->setConstantName(ui->lineEdit_MapID->text());
@ -236,8 +247,9 @@ void NewMapDialog::on_pushButton_Accept_clicked() {
newMap->setNeedsHealLocation(settings.canFlyTo);
Layout *layout;
if (this->existingLayout) {
layout = this->project->mapLayouts.value(this->layoutId);
const bool existingLayout = isExistingLayout();
if (existingLayout) {
layout = this->project->mapLayouts.value(settings.layout.id);
newMap->setNeedsLayoutDir(false);
} else {
layout = new Layout;
@ -258,17 +270,15 @@ void NewMapDialog::on_pushButton_Accept_clicked() {
layout->border_path = QString("%1%2/border.bin").arg(basePath, newMap->name());
layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, newMap->name());
}
if (this->importedMap) {
layout->blockdata = map->layout()->blockdata;
if (!map->layout()->border.isEmpty())
layout->border = map->layout()->border;
if (this->importedLayout) { // TODO: This seems at odds with existingLayout. Would it be possible to override an existing layout?
// Copy layout data from imported layout
layout->blockdata = this->importedLayout->blockdata;
if (!this->importedLayout->border.isEmpty())
layout->border = this->importedLayout->border;
}
newMap->setLayout(layout);
if (this->existingLayout) {
project->loadMapLayout(newMap);
}
map = newMap;
emit applied();
this->close();
this->project->addNewMap(newMap, settings.group);
emit applied(newMap->name());
QDialog::accept();
}