Add item to duplicate map/layouts from list

This commit is contained in:
GriffinR 2024-11-22 15:17:25 -05:00
parent d0101d807e
commit 4671321690
14 changed files with 304 additions and 359 deletions

View File

@ -35,6 +35,7 @@ class Map : public QObject
Q_OBJECT
public:
explicit Map(QObject *parent = nullptr);
explicit Map(const Map &other, QObject *parent = nullptr);
~Map();
public:
@ -125,7 +126,7 @@ private:
bool m_scriptsLoaded = false;
QMap<Event::Group, QList<Event *>> m_events;
QList<Event *> m_ownedEvents; // for memory management
QSet<Event *> m_ownedEvents; // for memory management
QList<int> m_metatileLayerOrder;
QList<float> m_metatileLayerOpacity;

View File

@ -53,17 +53,17 @@ public:
QString battleScene() const { return m_battleScene; }
signals:
void songChanged(QString, QString);
void locationChanged(QString, QString);
void requiresFlashChanged(bool, bool);
void weatherChanged(QString, QString);
void typeChanged(QString, QString);
void showsLocationNameChanged(bool, bool);
void allowsRunningChanged(bool, bool);
void allowsBikingChanged(bool, bool);
void allowsEscapingChanged(bool, bool);
void floorNumberChanged(int, int);
void battleSceneChanged(QString, QString);
void songChanged(QString);
void locationChanged(QString);
void requiresFlashChanged(bool);
void weatherChanged(QString);
void typeChanged(QString);
void showsLocationNameChanged(bool);
void allowsRunningChanged(bool);
void allowsBikingChanged(bool);
void allowsEscapingChanged(bool);
void floorNumberChanged(int);
void battleSceneChanged(QString);
void modified();
private:

View File

@ -20,6 +20,7 @@ class Layout : public QObject {
Q_OBJECT
public:
Layout() {}
Layout(const Layout &other);
static QString layoutNameFromMapName(const QString &mapName);
static QString layoutConstantFromName(QString mapName);

View File

@ -132,7 +132,8 @@ public:
bool readMapGroups();
void addNewMap(Map* newMap, const QString &groupName);
void addNewMapGroup(const QString &groupName);
void addNewLayout(Layout* newLayout);
Map *createNewMap(const Project::NewMapSettings &mapSettings, const Map* toDuplicate = nullptr);
Layout *createNewLayout(const Layout::Settings &layoutSettings, const Layout* toDuplicate = nullptr);
NewMapSettings getNewMapSettings() const;
Layout::Settings getNewLayoutSettings() const;
bool isIdentifierUnique(const QString &identifier) const;
@ -159,7 +160,6 @@ public:
bool loadMapData(Map*);
bool readMapLayouts();
Layout *loadLayout(QString layoutId);
Layout *createNewLayout(const Layout::Settings &layoutSettings, const Layout* toDuplicate = nullptr);
bool loadLayout(Layout *);
bool loadMapLayout(Map*);
bool loadLayoutTilesets(Layout *);
@ -277,9 +277,9 @@ signals:
void fileChanged(QString filepath);
void mapSectionIdNamesChanged(const QStringList &idNames);
void mapLoaded(Map *map);
void mapAdded(Map *newMap, const QString &groupName);
void mapCreated(Map *newMap, const QString &groupName);
void mapGroupAdded(const QString &groupName);
void layoutAdded(Layout *newLayout);
void layoutCreated(Layout *newLayout);
};
#endif // PROJECT_H

View File

@ -29,29 +29,38 @@ public:
void clear();
void setHeader(MapHeader *header);
void setHeaderData(const MapHeader &header);
MapHeader headerData() const;
void setSong(const QString &song);
void setLocation(const QString &location);
void setRequiresFlash(bool requiresFlash);
void setWeather(const QString &weather);
void setType(const QString &type);
void setBattleScene(const QString &battleScene);
void setShowsLocationName(bool showsLocationName);
void setAllowsRunning(bool allowsRunning);
void setAllowsBiking(bool allowsBiking);
void setAllowsEscaping(bool allowsEscaping);
void setFloorNumber(int floorNumber);
QString song() const;
QString location() const;
bool requiresFlash() const;
QString weather() const;
QString type() const;
QString battleScene() const;
bool showsLocationName() const;
bool allowsRunning() const;
bool allowsBiking() const;
bool allowsEscaping() const;
int floorNumber() const;
void setLocations(QStringList locations);
void setLocationDisabled(bool disabled);
bool isLocationDisabled() const { return m_locationDisabled; }
private:
Ui::MapHeaderForm *ui;
QPointer<MapHeader> m_header = nullptr;
bool m_locationDisabled = false;
void updateUi();
void updateSong();
void updateLocation();
void updateRequiresFlash();
void updateWeather();
void updateType();
void updateBattleScene();
void updateShowsLocationName();
void updateAllowsRunning();
void updateAllowsBiking();
void updateAllowsEscaping();
void updateFloorNumber();
void onSongUpdated(const QString &song);
void onLocationChanged(const QString &location);

View File

@ -25,9 +25,6 @@ public:
virtual void accept() override;
signals:
void applied(const QString &newMapName);
private:
Ui::NewMapDialog *ui;
Project *project;
@ -45,10 +42,9 @@ private:
bool validateGroup(bool allowEmpty = false);
bool validateLayoutID(bool allowEmpty = false);
void refresh();
void setUI(const Project::NewMapSettings &settings);
void saveSettings();
void useLayoutSettings(const Layout *mapLayout);
void setLayout(const Layout *mapLayout);
private slots:
void dialogButtonClicked(QAbstractButton *button);

View File

@ -13,7 +13,6 @@
Map::Map(QObject *parent) : QObject(parent)
{
m_scriptsLoaded = false;
m_editHistory = new QUndoStack(this);
resetEvents();
@ -21,6 +20,33 @@ Map::Map(QObject *parent) : QObject(parent)
connect(m_header, &MapHeader::modified, this, &Map::modified);
}
Map::Map(const Map &other, QObject *parent) : Map(parent) {
m_name = other.m_name;
m_constantName = other.m_constantName;
m_layoutId = other.m_layoutId;
m_sharedEventsMap = other.m_sharedEventsMap;
m_sharedScriptsMap = other.m_sharedScriptsMap;
m_customAttributes = other.m_customAttributes;
*m_header = *other.m_header;
m_layout = other.m_layout;
m_isPersistedToFile = false;
m_metatileLayerOrder = other.m_metatileLayerOrder;
m_metatileLayerOpacity = other.m_metatileLayerOpacity;
// Copy events
for (auto i = other.m_events.constBegin(); i != other.m_events.constEnd(); i++) {
QList<Event*> newEvents;
for (const auto &event : i.value()) {
auto newEvent = event->duplicate();
m_ownedEvents.insert(newEvent);
newEvents.append(newEvent);
}
m_events[i.key()] = newEvents;
}
// Duplicating the map connections is probably not desirable, so we skip them.
}
Map::~Map() {
qDeleteAll(m_ownedEvents);
m_ownedEvents.clear();
@ -201,7 +227,7 @@ void Map::removeEvent(Event *event) {
void Map::addEvent(Event *event) {
event->setMap(this);
m_events[event->getEventGroup()].append(event);
if (!m_ownedEvents.contains(event)) m_ownedEvents.append(event);
if (!m_ownedEvents.contains(event)) m_ownedEvents.insert(event);
}
int Map::getIndexOfEvent(Event *event) const {

View File

@ -35,98 +35,87 @@ MapHeader &MapHeader::operator=(const MapHeader &other) {
void MapHeader::setSong(const QString &song) {
if (m_song == song)
return;
auto before = m_song;
m_song = song;
emit songChanged(before, m_song);
emit songChanged(m_song);
emit modified();
}
void MapHeader::setLocation(const QString &location) {
if (m_location == location)
return;
auto before = m_location;
m_location = location;
emit locationChanged(before, m_location);
emit locationChanged(m_location);
emit modified();
}
void MapHeader::setRequiresFlash(bool requiresFlash) {
if (m_requiresFlash == requiresFlash)
return;
auto before = m_requiresFlash;
m_requiresFlash = requiresFlash;
emit requiresFlashChanged(before, m_requiresFlash);
emit requiresFlashChanged(m_requiresFlash);
emit modified();
}
void MapHeader::setWeather(const QString &weather) {
if (m_weather == weather)
return;
auto before = m_weather;
m_weather = weather;
emit weatherChanged(before, m_weather);
emit weatherChanged(m_weather);
emit modified();
}
void MapHeader::setType(const QString &type) {
if (m_type == type)
return;
auto before = m_type;
m_type = type;
emit typeChanged(before, m_type);
emit typeChanged(m_type);
emit modified();
}
void MapHeader::setShowsLocationName(bool showsLocationName) {
if (m_showsLocationName == showsLocationName)
return;
auto before = m_showsLocationName;
m_showsLocationName = showsLocationName;
emit showsLocationNameChanged(before, m_showsLocationName);
emit showsLocationNameChanged(m_showsLocationName);
emit modified();
}
void MapHeader::setAllowsRunning(bool allowsRunning) {
if (m_allowsRunning == allowsRunning)
return;
auto before = m_allowsRunning;
m_allowsRunning = allowsRunning;
emit allowsRunningChanged(before, m_allowsRunning);
emit allowsRunningChanged(m_allowsRunning);
emit modified();
}
void MapHeader::setAllowsBiking(bool allowsBiking) {
if (m_allowsBiking == allowsBiking)
return;
auto before = m_allowsBiking;
m_allowsBiking = allowsBiking;
emit allowsBikingChanged(before, m_allowsBiking);
emit allowsBikingChanged(m_allowsBiking);
emit modified();
}
void MapHeader::setAllowsEscaping(bool allowsEscaping) {
if (m_allowsEscaping == allowsEscaping)
return;
auto before = m_allowsEscaping;
m_allowsEscaping = allowsEscaping;
emit allowsEscapingChanged(before, m_allowsEscaping);
emit allowsEscapingChanged(m_allowsEscaping);
emit modified();
}
void MapHeader::setFloorNumber(int floorNumber) {
if (m_floorNumber == floorNumber)
return;
auto before = m_floorNumber;
m_floorNumber = floorNumber;
emit floorNumberChanged(before, m_floorNumber);
emit floorNumberChanged(m_floorNumber);
emit modified();
}
void MapHeader::setBattleScene(const QString &battleScene) {
if (m_battleScene == battleScene)
return;
auto before = m_battleScene;
m_battleScene = battleScene;
emit battleSceneChanged(before, m_battleScene);
emit battleSceneChanged(m_battleScene);
emit modified();
}

View File

@ -5,7 +5,9 @@
#include "scripting.h"
#include "imageproviders.h"
Layout::Layout(const Layout &other) : Layout() {
copyFrom(&other);
}
Layout *Layout::copy() const {
Layout *layout = new Layout;

View File

@ -621,9 +621,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::mapCreated, this, &MainWindow::onNewMapCreated);
connect(project, &Project::layoutCreated, this, &MainWindow::onNewLayoutCreated);
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);
@ -1210,6 +1210,10 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) {
if (itemType == "map_name") {
// Right-clicking on a map.
openItemAction = menu.addAction("Open Map");
connect(menu.addAction("Duplicate Map"), &QAction::triggered, [this, itemName] {
auto dialog = new NewMapDialog(this->editor->project, this->editor->project->getMap(itemName), this);
dialog->open();
});
//menu.addSeparator();
//connect(menu.addAction("Delete Map"), &QAction::triggered, [this, index] { deleteMapListItem(index); }); // TODO: No support for deleting maps
} else if (itemType == "map_group") {
@ -1227,6 +1231,14 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) {
} else if (itemType == "map_layout") {
// Right-clicking on a map layout
openItemAction = menu.addAction("Open Layout");
connect(menu.addAction("Duplicate Layout"), &QAction::triggered, [this, itemName] {
auto layout = this->editor->project->loadLayout(itemName);
if (layout) {
auto dialog = new NewLayoutDialog(this->editor->project, layout, this);
connect(dialog, &NewLayoutDialog::applied, this, &MainWindow::userSetLayout);
dialog->open();
}
});
addToFolderAction = menu.addAction("Add New Map with Layout");
//menu.addSeparator();
//deleteFolderAction = menu.addAction("Delete Layout"); // TODO: No support for deleting layouts
@ -1236,7 +1248,6 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) {
// All folders only contain maps, so adding an item to any folder is adding a new map.
connect(addToFolderAction, &QAction::triggered, [this, itemName] {
auto dialog = new NewMapDialog(this->editor->project, ui->mapListContainer->currentIndex(), itemName, this);
connect(dialog, &NewMapDialog::applied, this, &MainWindow::userSetMap);
dialog->open();
});
}
@ -1371,6 +1382,8 @@ void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
editor->project->saveHealLocations(newMap);
editor->save();
}
userSetMap(newMap->name());
}
// Called any time a new layout is created (including as a byproduct of creating a new map)
@ -1395,7 +1408,6 @@ void MainWindow::onNewMapGroupCreated(const QString &groupName) {
void MainWindow::openNewMapDialog() {
auto dialog = new NewMapDialog(this->editor->project, this);
connect(dialog, &NewMapDialog::applied, this, &MainWindow::userSetMap);
dialog->open();
}

View File

@ -363,8 +363,59 @@ bool Project::loadMapData(Map* map) {
return true;
}
Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* toDuplicate) {
Map *map = toDuplicate ? new Map(*toDuplicate) : new Map;
map->setName(settings.name);
map->setConstantName(settings.id);
map->setHeader(settings.header);
map->setNeedsHealLocation(settings.canFlyTo);
Layout *layout = this->mapLayouts.value(settings.layout.id);
if (layout) {
// Layout already exists
map->setNeedsLayoutDir(false); // TODO: Remove this member?
} else {
layout = createNewLayout(settings.layout);
}
if (!layout) {
delete map;
return nullptr;
}
map->setLayout(layout);
// Make sure we keep the order of the map names the same as in the map group order.
int mapNamePos;
if (this->groupNames.contains(settings.group)) {
mapNamePos = 0;
for (const auto &name : this->groupNames) {
mapNamePos += this->groupNameToMapNames[name].length();
if (name == settings.group)
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(settings.group);
mapNamePos = this->mapNames.length();
}
this->mapNames.insert(mapNamePos, map->name());
this->groupNameToMapNames[settings.group].append(map->name());
this->mapConstantsToMapNames.insert(map->constantName(), map->name());
this->mapNamesToMapConstants.insert(map->name(), map->constantName());
map->setIsPersistedToFile(false);
emit mapCreated(map, settings.group);
return map;
}
Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout *toDuplicate) {
Layout *layout = new Layout;
if (this->layoutIds.contains(settings.id))
return nullptr;
Layout *layout = toDuplicate ? new Layout(*toDuplicate) : new Layout();
layout->id = settings.id;
layout->name = settings.name;
layout->width = settings.width;
@ -374,13 +425,6 @@ Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout
layout->tileset_primary_label = settings.primaryTilesetLabel;
layout->tileset_secondary_label = settings.secondaryTilesetLabel;
if (toDuplicate) {
// If we're duplicating an existing layout we'll copy over the blockdata.
// Otherwise addNewLayout will fill our new layout using the default settings.
layout->blockdata = toDuplicate->blockdata;
layout->border = toDuplicate->border;
}
const QString basePath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders);
layout->border_path = QString("%1%2/border.bin").arg(basePath, layout->name);
layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layout->name);
@ -393,9 +437,22 @@ Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout
return nullptr;
}
addNewLayout(layout);
this->mapLayouts.insert(layout->id, layout);
this->layoutIds.append(layout->id);
if (layout->blockdata.isEmpty()) {
// Fill layout using default fill settings
setNewLayoutBlockdata(layout);
}
if (layout->border.isEmpty()) {
// Fill border using default fill settings
setNewLayoutBorder(layout);
}
saveLayout(layout); // TODO: Ideally we shouldn't automatically save new layouts
emit layoutCreated(layout);
return layout;
}
@ -1886,60 +1943,6 @@ bool Project::readMapGroups() {
return true;
}
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->groupNameToMapNames[groupName].append(newMap->name());
this->mapConstantsToMapNames.insert(newMap->constantName(), newMap->name());
this->mapNamesToMapConstants.insert(newMap->name(), newMap->constantName());
newMap->setIsPersistedToFile(false);
// 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());
}
emit mapAdded(newMap, groupName);
}
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;
@ -2005,12 +2008,11 @@ Layout::Settings Project::getNewLayoutSettings() const {
return settings;
}
// When we ask the user to provide a new identifier for something (like a map/layout name or ID)
// When we ask the user to provide a new identifier for something (like a map name or MAPSEC id)
// we use this to make sure that it doesn't collide with any known identifiers first.
// Porymap knows of many more identifiers than this, but for simplicity we only check the lists that users can add to via Porymap.
// In general this only matters to Porymap if the identifier will be added to the group it collides with,
// but name collisions are likely undesirable in the project.
// TODO: Use elsewhere
bool Project::isIdentifierUnique(const QString &identifier) const {
if (this->mapNames.contains(identifier))
return false;

View File

@ -92,46 +92,47 @@ void MapHeaderForm::setHeader(MapHeader *header) {
if (m_header) {
m_header->disconnect(this);
}
m_header = header;
if (m_header) {
// If the MapHeader is changed externally (for example, with the scripting API) update the UI accordingly
connect(m_header, &MapHeader::songChanged, this, &MapHeaderForm::updateSong);
connect(m_header, &MapHeader::locationChanged, this, &MapHeaderForm::updateLocation);
connect(m_header, &MapHeader::requiresFlashChanged, this, &MapHeaderForm::updateRequiresFlash);
connect(m_header, &MapHeader::weatherChanged, this, &MapHeaderForm::updateWeather);
connect(m_header, &MapHeader::typeChanged, this, &MapHeaderForm::updateType);
connect(m_header, &MapHeader::battleSceneChanged, this, &MapHeaderForm::updateBattleScene);
connect(m_header, &MapHeader::showsLocationNameChanged, this, &MapHeaderForm::updateShowsLocationName);
connect(m_header, &MapHeader::allowsRunningChanged, this, &MapHeaderForm::updateAllowsRunning);
connect(m_header, &MapHeader::allowsBikingChanged, this, &MapHeaderForm::updateAllowsBiking);
connect(m_header, &MapHeader::allowsEscapingChanged, this, &MapHeaderForm::updateAllowsEscaping);
connect(m_header, &MapHeader::floorNumberChanged, this, &MapHeaderForm::updateFloorNumber);
if (!m_header) {
clear();
return;
}
// If the MapHeader is changed externally (for example, with the scripting API) update the UI accordingly
connect(m_header, &MapHeader::songChanged, this, &MapHeaderForm::setSong);
connect(m_header, &MapHeader::locationChanged, this, &MapHeaderForm::setLocation);
connect(m_header, &MapHeader::requiresFlashChanged, this, &MapHeaderForm::setRequiresFlash);
connect(m_header, &MapHeader::weatherChanged, this, &MapHeaderForm::setWeather);
connect(m_header, &MapHeader::typeChanged, this, &MapHeaderForm::setType);
connect(m_header, &MapHeader::battleSceneChanged, this, &MapHeaderForm::setBattleScene);
connect(m_header, &MapHeader::showsLocationNameChanged, this, &MapHeaderForm::setShowsLocationName);
connect(m_header, &MapHeader::allowsRunningChanged, this, &MapHeaderForm::setAllowsRunning);
connect(m_header, &MapHeader::allowsBikingChanged, this, &MapHeaderForm::setAllowsBiking);
connect(m_header, &MapHeader::allowsEscapingChanged, this, &MapHeaderForm::setAllowsEscaping);
connect(m_header, &MapHeader::floorNumberChanged, this, &MapHeaderForm::setFloorNumber);
// Immediately update the UI to reflect the assigned MapHeader
updateUi();
setHeaderData(*m_header);
}
void MapHeaderForm::clear() {
m_header = nullptr;
updateUi();
setHeaderData(MapHeader());
}
void MapHeaderForm::updateUi() {
updateSong();
updateLocation();
updateRequiresFlash();
updateWeather();
updateType();
updateBattleScene();
updateShowsLocationName();
updateAllowsRunning();
updateAllowsBiking();
updateAllowsEscaping();
updateFloorNumber();
void MapHeaderForm::setHeaderData(const MapHeader &header) {
setSong(header.song());
setLocation(header.location());
setRequiresFlash(header.requiresFlash());
setWeather(header.weather());
setType(header.type());
setBattleScene(header.battleScene());
setShowsLocationName(header.showsLocationName());
setAllowsRunning(header.allowsRunning());
setAllowsBiking(header.allowsBiking());
setAllowsEscaping(header.allowsEscaping());
setFloorNumber(header.floorNumber());
}
MapHeader MapHeaderForm::headerData() const {
@ -140,132 +141,55 @@ MapHeader MapHeaderForm::headerData() const {
// Build header from UI
MapHeader header;
header.setSong(ui->comboBox_Song->currentText());
header.setLocation(ui->comboBox_Location->currentText());
header.setRequiresFlash(ui->checkBox_RequiresFlash->isChecked());
header.setWeather(ui->comboBox_Weather->currentText());
header.setType(ui->comboBox_Type->currentText());
header.setBattleScene(ui->comboBox_BattleScene->currentText());
header.setShowsLocationName(ui->checkBox_ShowLocationName->isChecked());
header.setAllowsRunning(ui->checkBox_AllowRunning->isChecked());
header.setAllowsBiking(ui->checkBox_AllowBiking->isChecked());
header.setAllowsEscaping(ui->checkBox_AllowEscaping->isChecked());
header.setFloorNumber(ui->spinBox_FloorNumber->value());
header.setSong(song());
header.setLocation(location());
header.setRequiresFlash(requiresFlash());
header.setWeather(weather());
header.setType(type());
header.setBattleScene(battleScene());
header.setShowsLocationName(showsLocationName());
header.setAllowsRunning(allowsRunning());
header.setAllowsBiking(allowsBiking());
header.setAllowsEscaping(allowsEscaping());
header.setFloorNumber(floorNumber());
return header;
}
void MapHeaderForm::setLocationDisabled(bool disabled) {
m_locationDisabled = disabled;
ui->label_Location->setDisabled(m_locationDisabled);
ui->comboBox_Location->setDisabled(m_locationDisabled);
}
// Set data in UI
void MapHeaderForm::setSong(const QString &song) { ui->comboBox_Song->setCurrentText(song); }
void MapHeaderForm::setLocation(const QString &location) { ui->comboBox_Location->setCurrentText(location); }
void MapHeaderForm::setRequiresFlash(bool requiresFlash) { ui->checkBox_RequiresFlash->setChecked(requiresFlash); }
void MapHeaderForm::setWeather(const QString &weather) { ui->comboBox_Weather->setCurrentText(weather); }
void MapHeaderForm::setType(const QString &type) { ui->comboBox_Type->setCurrentText(type); }
void MapHeaderForm::setBattleScene(const QString &battleScene) { ui->comboBox_BattleScene->setCurrentText(battleScene); }
void MapHeaderForm::setShowsLocationName(bool showsLocationName) { ui->checkBox_ShowLocationName->setChecked(showsLocationName); }
void MapHeaderForm::setAllowsRunning(bool allowsRunning) { ui->checkBox_AllowRunning->setChecked(allowsRunning); }
void MapHeaderForm::setAllowsBiking(bool allowsBiking) { ui->checkBox_AllowBiking->setChecked(allowsBiking); }
void MapHeaderForm::setAllowsEscaping(bool allowsEscaping) { ui->checkBox_AllowEscaping->setChecked(allowsEscaping); }
void MapHeaderForm::setFloorNumber(int floorNumber) { ui->spinBox_FloorNumber->setValue(floorNumber); }
void MapHeaderForm::updateSong() {
const QSignalBlocker b(ui->comboBox_Song);
ui->comboBox_Song->setCurrentText(m_header ? m_header->song() : QString());
}
// Read data from UI
QString MapHeaderForm::song() const { return ui->comboBox_Song->currentText(); }
QString MapHeaderForm::location() const { return ui->comboBox_Location->currentText(); }
bool MapHeaderForm::requiresFlash() const { return ui->checkBox_RequiresFlash->isChecked(); }
QString MapHeaderForm::weather() const { return ui->comboBox_Weather->currentText(); }
QString MapHeaderForm::type() const { return ui->comboBox_Type->currentText(); }
QString MapHeaderForm::battleScene() const { return ui->comboBox_BattleScene->currentText(); }
bool MapHeaderForm::showsLocationName() const { return ui->checkBox_ShowLocationName->isChecked(); }
bool MapHeaderForm::allowsRunning() const { return ui->checkBox_AllowRunning->isChecked(); }
bool MapHeaderForm::allowsBiking() const { return ui->checkBox_AllowBiking->isChecked(); }
bool MapHeaderForm::allowsEscaping() const { return ui->checkBox_AllowEscaping->isChecked(); }
int MapHeaderForm::floorNumber() const { return ui->spinBox_FloorNumber->value(); }
void MapHeaderForm::updateLocation() {
const QSignalBlocker b(ui->comboBox_Location);
ui->comboBox_Location->setCurrentText(m_header ? m_header->location() : QString());
}
void MapHeaderForm::updateRequiresFlash() {
const QSignalBlocker b(ui->checkBox_RequiresFlash);
ui->checkBox_RequiresFlash->setChecked(m_header ? m_header->requiresFlash() : false);
}
void MapHeaderForm::updateWeather() {
const QSignalBlocker b(ui->comboBox_Weather);
ui->comboBox_Weather->setCurrentText(m_header ? m_header->weather() : QString());
}
void MapHeaderForm::updateType() {
const QSignalBlocker b(ui->comboBox_Type);
ui->comboBox_Type->setCurrentText(m_header ? m_header->type() : QString());
}
void MapHeaderForm::updateBattleScene() {
const QSignalBlocker b(ui->comboBox_BattleScene);
ui->comboBox_BattleScene->setCurrentText(m_header ? m_header->battleScene() : QString());
}
void MapHeaderForm::updateShowsLocationName() {
const QSignalBlocker b(ui->checkBox_ShowLocationName);
ui->checkBox_ShowLocationName->setChecked(m_header ? m_header->showsLocationName() : false);
}
void MapHeaderForm::updateAllowsRunning() {
const QSignalBlocker b(ui->checkBox_AllowRunning);
ui->checkBox_AllowRunning->setChecked(m_header ? m_header->allowsRunning() : false);
}
void MapHeaderForm::updateAllowsBiking() {
const QSignalBlocker b(ui->checkBox_AllowBiking);
ui->checkBox_AllowBiking->setChecked(m_header ? m_header->allowsBiking() : false);
}
void MapHeaderForm::updateAllowsEscaping() {
const QSignalBlocker b(ui->checkBox_AllowEscaping);
ui->checkBox_AllowEscaping->setChecked(m_header ? m_header->allowsEscaping() : false);
}
void MapHeaderForm::updateFloorNumber() {
const QSignalBlocker b(ui->spinBox_FloorNumber);
ui->spinBox_FloorNumber->setValue(m_header ? m_header->floorNumber() : 0);
}
void MapHeaderForm::onSongUpdated(const QString &song)
{
if (m_header) m_header->setSong(song);
}
void MapHeaderForm::onLocationChanged(const QString &location)
{
if (m_header) m_header->setLocation(location);
}
void MapHeaderForm::onWeatherChanged(const QString &weather)
{
if (m_header) m_header->setWeather(weather);
}
void MapHeaderForm::onTypeChanged(const QString &type)
{
if (m_header) m_header->setType(type);
}
void MapHeaderForm::onBattleSceneChanged(const QString &battleScene)
{
if (m_header) m_header->setBattleScene(battleScene);
}
void MapHeaderForm::onRequiresFlashChanged(int selected)
{
if (m_header) m_header->setRequiresFlash(selected == Qt::Checked);
}
void MapHeaderForm::onShowLocationNameChanged(int selected)
{
if (m_header) m_header->setShowsLocationName(selected == Qt::Checked);
}
void MapHeaderForm::onAllowRunningChanged(int selected)
{
if (m_header) m_header->setAllowsRunning(selected == Qt::Checked);
}
void MapHeaderForm::onAllowBikingChanged(int selected)
{
if (m_header) m_header->setAllowsBiking(selected == Qt::Checked);
}
void MapHeaderForm::onAllowEscapingChanged(int selected)
{
if (m_header) m_header->setAllowsEscaping(selected == Qt::Checked);
}
void MapHeaderForm::onFloorNumberChanged(int offset)
{
if (m_header) m_header->setFloorNumber(offset);
}
// Send changes in UI to tracked MapHeader (if there is one)
void MapHeaderForm::onSongUpdated(const QString &song) { if (m_header) m_header->setSong(song); }
void MapHeaderForm::onLocationChanged(const QString &location) { if (m_header) m_header->setLocation(location); }
void MapHeaderForm::onWeatherChanged(const QString &weather) { if (m_header) m_header->setWeather(weather); }
void MapHeaderForm::onTypeChanged(const QString &type) { if (m_header) m_header->setType(type); }
void MapHeaderForm::onBattleSceneChanged(const QString &battleScene) { if (m_header) m_header->setBattleScene(battleScene); }
void MapHeaderForm::onRequiresFlashChanged(int selected) { if (m_header) m_header->setRequiresFlash(selected == Qt::Checked); }
void MapHeaderForm::onShowLocationNameChanged(int selected) { if (m_header) m_header->setShowsLocationName(selected == Qt::Checked); }
void MapHeaderForm::onAllowRunningChanged(int selected) { if (m_header) m_header->setAllowsRunning(selected == Qt::Checked); }
void MapHeaderForm::onAllowBikingChanged(int selected) { if (m_header) m_header->setAllowsBiking(selected == Qt::Checked); }
void MapHeaderForm::onAllowEscapingChanged(int selected) { if (m_header) m_header->setAllowsEscaping(selected == Qt::Checked); }
void MapHeaderForm::onFloorNumberChanged(int offset) { if (m_header) m_header->setFloorNumber(offset); }

View File

@ -47,15 +47,26 @@ NewLayoutDialog::NewLayoutDialog(Project *project, QWidget *parent) :
adjustSize();
}
// Creating new layout from AdvanceMap import
// TODO: Re-use for a "Duplicate Layout" option
NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layout, QWidget *parent) :
// Creating new layout from an existing layout (e.g. via AdvanceMap import, or duplicating from map list).
NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layoutToCopy, QWidget *parent) :
NewLayoutDialog(project, parent)
{
if (layout) {
this->importedLayout = layout->copy();
refresh();
if (!layoutToCopy)
return;
this->importedLayout = layoutToCopy->copy();
if (!this->importedLayout->name.isEmpty()) {
// If the layout we're duplicating has a name and ID we'll initialize the name/ID fields
// using that name and add a suffix to make it unique.
// Layouts imported with AdvanceMap won't have a name/ID.
int i = 2;
do {
settings.name = QString("%1_%2").arg(this->importedLayout->name).arg(i);
settings.id = QString("%1_%2").arg(this->importedLayout->id).arg(i);
i++;
} while (!this->project->isIdentifierUnique(settings.name) || !this->project->isIdentifierUnique(settings.id));
}
refresh();
}
NewLayoutDialog::~NewLayoutDialog()
@ -162,6 +173,8 @@ void NewLayoutDialog::accept() {
}
ui->label_GenericError->setVisible(false);
// TODO: See if we can get away with emitting this from Project so that we don't need to connect
// to this signal every time we create the dialog.
emit applied(layout->id);
QDialog::accept();
}

View File

@ -50,7 +50,6 @@ NewMapDialog::NewMapDialog(Project *project, QWidget *parent) :
// Create a collapsible section that has all the map header data.
this->headerForm = new MapHeaderForm();
this->headerForm->init(project);
this->headerForm->setHeader(&settings.header);
auto sectionLayout = new QVBoxLayout();
sectionLayout->addWidget(this->headerForm);
@ -61,47 +60,36 @@ NewMapDialog::NewMapDialog(Project *project, QWidget *parent) :
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewMapDialog::dialogButtonClicked);
refresh();
setUI(settings);
adjustSize(); // TODO: Save geometry?
}
// Adding new map to existing map list folder.
// Adding new map to existing map list folder. Initialize settings accordingly.
// Even if we initialize settings like this we'll allow users to change them afterwards,
// because nothing is expecting them to stay at these values.
NewMapDialog::NewMapDialog(Project *project, int mapListTab, const QString &mapListItem, QWidget *parent) :
NewMapDialog(project, parent)
{
switch (mapListTab)
{
case MapListTab::Groups:
settings.group = mapListItem;
ui->label_Group->setDisabled(true);
ui->comboBox_Group->setDisabled(true);
ui->comboBox_Group->setTextItem(settings.group);
ui->comboBox_Group->setTextItem(mapListItem);
break;
case MapListTab::Areas:
settings.header.setLocation(mapListItem);
this->headerForm->setLocationDisabled(true);
// Header UI is kept in sync automatically by MapHeaderForm
this->headerForm->setLocation(mapListItem);
break;
case MapListTab::Layouts:
settings.layout.id = mapListItem;
ui->label_LayoutID->setDisabled(true);
ui->comboBox_LayoutID->setDisabled(true);
ui->comboBox_LayoutID->setTextItem(settings.layout.id);
ui->comboBox_LayoutID->setTextItem(mapListItem);
break;
}
}
// TODO: Use for a "Duplicate Map" option
NewMapDialog::NewMapDialog(Project *project, const Map *mapToCopy, QWidget *parent) :
NewMapDialog(project, parent)
{
/*
if (this->importedMap)
delete this->importedMap;
this->importedMap = new Map(mapToCopy);
useLayoutSettings(this->importedMap->layout());
*/
if (!mapToCopy)
return;
// TODO
}
NewMapDialog::~NewMapDialog()
@ -111,35 +99,38 @@ NewMapDialog::~NewMapDialog()
delete ui;
}
// Sync UI with settings. If any UI elements are disabled (because their settings are being enforced)
// then we don't update them using the settings here.
void NewMapDialog::refresh() {
void NewMapDialog::setUI(const Project::NewMapSettings &settings) {
ui->lineEdit_Name->setText(settings.name);
ui->lineEdit_MapID->setText(settings.id);
ui->comboBox_Group->setTextItem(settings.group);
ui->checkBox_CanFlyTo->setChecked(settings.canFlyTo);
ui->comboBox_LayoutID->setTextItem(settings.layout.id);
ui->newLayoutForm->setSettings(settings.layout);
// Header UI is kept in sync automatically by MapHeaderForm
if (this->importedMap && this->importedMap->layout()) {
// When importing a layout these settings shouldn't be changed.
ui->newLayoutForm->setSettings(this->importedMap->layout()->settings());
} else {
ui->newLayoutForm->setSettings(settings.layout);
}
ui->checkBox_CanFlyTo->setChecked(settings.canFlyTo);
this->headerForm->setHeaderData(settings.header);
}
void NewMapDialog::saveSettings() {
settings.name = ui->lineEdit_Name->text();
settings.id = ui->lineEdit_MapID->text();
settings.group = ui->comboBox_Group->currentText();
settings.canFlyTo = ui->checkBox_CanFlyTo->isChecked();
settings.layout = ui->newLayoutForm->settings();
settings.layout.id = ui->comboBox_LayoutID->currentText();
// We don't provide full control for naming new layouts here (just via the ID).
// If a user wants to explicitly name a layout they can create it individually before creating the map.
settings.layout.name = Layout::layoutNameFromMapName(settings.name);
settings.layout.name = Layout::layoutNameFromMapName(settings.name); // TODO: Verify uniqueness
settings.canFlyTo = ui->checkBox_CanFlyTo->isChecked();
settings.header = this->headerForm->headerData();
porymapConfig.newMapHeaderSectionExpanded = this->headerSection->isExpanded();
}
void NewMapDialog::useLayoutSettings(const Layout *layout) {
void NewMapDialog::setLayout(const Layout *layout) {
if (layout) {
ui->comboBox_LayoutID->setTextItem(layout->id);
ui->newLayoutForm->setSettings(layout->settings());
ui->newLayoutForm->setDisabled(true);
} else {
@ -152,10 +143,10 @@ bool NewMapDialog::validateMapID(bool allowEmpty) {
const QString expectedPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_prefix);
QString errorText;
if (id.isEmpty()) {
if (id.isEmpty() || id == expectedPrefix) {
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);
errorText = QString("%1 must start with '%2'.").arg(ui->label_MapID->text()).arg(expectedPrefix);
} else if (!this->project->isIdentifierUnique(id)) {
errorText = QString("%1 '%2' is not unique.").arg(ui->label_MapID->text()).arg(id);
}
@ -223,8 +214,14 @@ bool NewMapDialog::validateLayoutID(bool allowEmpty) {
QString errorText;
if (layoutId.isEmpty()) {
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_LayoutID->text());
} else if (!this->project->layoutIds.contains(layoutId) && !this->project->isIdentifierUnique(layoutId)) {
errorText = QString("%1 must either be the ID for an existing layout, or a unique identifier for a new layout.").arg(ui->label_LayoutID->text());
} else if (!this->project->isIdentifierUnique(layoutId)) {
// Layout name is already in use by something. If we're duplicating a map this isn't allowed.
if (this->importedMap) {
errorText = QString("%1 is not unique.").arg(ui->label_LayoutID->text());
// If we're not duplicating a map this is ok as long as it's the name of an existing layout.
} else if (!this->project->layoutIds.contains(layoutId)) {
errorText = QString("%1 must either be the ID for an existing layout, or a unique identifier for a new layout.").arg(ui->label_LayoutID->text());
}
}
bool isValid = errorText.isEmpty();
@ -236,7 +233,7 @@ bool NewMapDialog::validateLayoutID(bool allowEmpty) {
void NewMapDialog::on_comboBox_LayoutID_currentTextChanged(const QString &text) {
validateLayoutID(true);
useLayoutSettings(this->project->mapLayouts.value(text));
setLayout(this->project->mapLayouts.value(text));
}
void NewMapDialog::dialogButtonClicked(QAbstractButton *button) {
@ -244,16 +241,7 @@ void NewMapDialog::dialogButtonClicked(QAbstractButton *button) {
if (role == QDialogButtonBox::RejectRole){
reject();
} else if (role == QDialogButtonBox::ResetRole) {
auto newSettings = this->project->getNewMapSettings();
// If the location setting is disabled we need to enforce that setting on the new header.
if (this->headerForm->isLocationDisabled())
newSettings.header.setLocation(settings.header.location());
settings = newSettings;
this->headerForm->setHeader(&settings.header); // TODO: Unnecessary?
refresh();
setUI(this->project->getNewMapSettings());
} else if (role == QDialogButtonBox::AcceptRole) {
accept();
}
@ -273,30 +261,12 @@ void NewMapDialog::accept() {
// Update settings from UI
saveSettings();
Map *newMap = new Map;
newMap->setName(settings.name);
newMap->setConstantName(settings.id);
newMap->setHeader(settings.header);
newMap->setNeedsHealLocation(settings.canFlyTo);
Layout *layout = this->project->mapLayouts.value(settings.layout.id);
if (layout) {
// Layout already exists
newMap->setNeedsLayoutDir(false); // TODO: Remove this member?
} else {
layout = this->project->createNewLayout(settings.layout);
}
if (!layout) {
ui->label_GenericError->setText(QString("Failed to create layout for map. See %1 for details.").arg(getLogPath()));
Map *map = this->project->createNewMap(settings, this->importedMap);
if (!map) {
ui->label_GenericError->setText(QString("Failed to create map. See %1 for details.").arg(getLogPath()));
ui->label_GenericError->setVisible(true);
delete newMap;
return;
}
ui->label_GenericError->setVisible(false);
newMap->setLayout(layout);
this->project->addNewMap(newMap, settings.group);
emit applied(newMap->name());
QDialog::accept();
}