diff --git a/forms/regionmapeditor.ui b/forms/regionmapeditor.ui
index 517e6e0c..ec845dc3 100644
--- a/forms/regionmapeditor.ui
+++ b/forms/regionmapeditor.ui
@@ -1124,8 +1124,6 @@
Edit
-
-
@@ -1150,22 +1148,6 @@
Ctrl+S
-
-
- Undo
-
-
- Ctrl+Z
-
-
-
-
- Redo
-
-
- Ctrl+Y
-
-
Resize
diff --git a/include/core/regionmap.h b/include/core/regionmap.h
index 3c85d847..58dc6e35 100644
--- a/include/core/regionmap.h
+++ b/include/core/regionmap.h
@@ -19,32 +19,6 @@ using std::shared_ptr;
class Project;
-enum RegionMapEditorBox {
- BackgroundImage = 1,
- CityMapImage = 2,
-};
-
-class RegionMapHistoryItem {
-public:
- int which;
- int mapWidth = 0;
- int mapHeight = 0;
- QVector tiles;
- QString cityMap;
- RegionMapHistoryItem(int which, QVector tiles, QString cityMap) {
- this->which = which;
- this->tiles = tiles;
- this->cityMap = cityMap;
- }
- RegionMapHistoryItem(int which, QVector tiles, int width, int height) {
- this->which = which;
- this->tiles = tiles;
- this->mapWidth = width;
- this->mapHeight = height;
- }
- ~RegionMapHistoryItem() {}
-};
-
struct LayoutSquare
{
QString map_section;
@@ -69,9 +43,9 @@ public:
RegionMap() = delete;
RegionMap(Project *);
- Project *project = nullptr;
+ ~RegionMap() {}
- History history; // TODO
+ Project *project = nullptr;
bool loadMapData(poryjson::Json);
bool loadTilemap(poryjson::Json);
@@ -121,6 +95,9 @@ public:
QByteArray getTilemap();
void setTilemap(QByteArray newTilemap);
+ QList getLayout(QString layer);
+ void setLayout(QString layer, QList layout);
+
QStringList getLayers() { return this->layout_layers; }
void setLayer(QString layer) { this->current_layer = layer; }
QString getLayer() { return this->current_layer; }
@@ -146,6 +123,12 @@ public:
QString fullPath(QString local);
+ void commit(QUndoCommand *command);
+ QUndoStack editHistory;
+
+ void undo();
+ void redo();
+
private:
// TODO: defaults needed?
tsl::ordered_map *region_map_entries = nullptr;
diff --git a/include/ui/regionmapeditor.h b/include/ui/regionmapeditor.h
index 32c74adc..e3e53a75 100644
--- a/include/ui/regionmapeditor.h
+++ b/include/ui/regionmapeditor.h
@@ -45,9 +45,6 @@ public:
void onRegionMapEntriesSelectedTileChanged(QString) {};
void onRegionMapEntryDragged(int, int);
- void undo();
- void redo();
-
void resize(int width, int height);
QObjectList shortcutableObjects() const;
@@ -61,7 +58,7 @@ private:
poryjson::Json rmConfigJson;
- History history;
+ QUndoGroup history;
int currIndex = 0;
unsigned selectedCityTile;
@@ -124,8 +121,6 @@ private:
private slots:
void on_action_RegionMap_Save_triggered();
- void on_action_RegionMap_Undo_triggered();
- void on_action_RegionMap_Redo_triggered();
void on_action_RegionMap_Resize_triggered();
void on_action_RegionMap_ClearImage_triggered();
void on_action_RegionMap_ClearLayout_triggered();
diff --git a/include/ui/tilemaptileselector.h b/include/ui/tilemaptileselector.h
index c7e45b47..9c2ba1af 100644
--- a/include/ui/tilemaptileselector.h
+++ b/include/ui/tilemaptileselector.h
@@ -123,7 +123,9 @@ public:
this->tileset = QImage(tilesetFilepath);
this->format = format;
bool err;
- this->palette = PaletteUtil::parse(palFilepath, &err);
+ if (!palFilepath.isEmpty()) {
+ this->palette = PaletteUtil::parse(palFilepath, &err);
+ }
this->setPixmap(QPixmap::fromImage(this->tileset));
this->numTilesWide = this->tileset.width() / 8;
this->selectedTile = 0x00;
diff --git a/porymap.pro b/porymap.pro
index 2a86653e..89808d2c 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -12,7 +12,7 @@ TARGET = porymap
TEMPLATE = app
RC_ICONS = resources/icons/porymap-icon-2.ico
ICON = resources/icons/porymap.icns
-QMAKE_CXXFLAGS += -std=c++11 -Wall
+QMAKE_CXXFLAGS += -std=c++17 -Wall
QMAKE_TARGET_BUNDLE_PREFIX = com.pret
SOURCES += src/core/block.cpp \
@@ -33,6 +33,7 @@ SOURCES += src/core/block.cpp \
src/core/wildmoninfo.cpp \
src/core/editcommands.cpp \
src/lib/orderedjson.cpp \
+ src/core/regionmapeditcommands.cpp \
src/mainwindow_scriptapi.cpp \
src/ui/aboutporymap.cpp \
src/ui/draggablepixmapitem.cpp \
@@ -106,6 +107,7 @@ HEADERS += include/core/block.h \
include/core/regionmap.h \
include/core/wildmoninfo.h \
include/core/editcommands.h \
+ include/core/regionmapeditcommands.h \
include/lib/orderedmap.h \
include/lib/orderedjson.h \
include/ui/aboutporymap.h \
diff --git a/src/core/regionmap.cpp b/src/core/regionmap.cpp
index f4b43933..b0d17c65 100644
--- a/src/core/regionmap.cpp
+++ b/src/core/regionmap.cpp
@@ -4,6 +4,7 @@
#include "project.h"
#include "log.h"
#include "config.h"
+#include "regionmapeditcommands.h"
#include
#include
@@ -163,7 +164,7 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) {
layout.append(square);
}
}
- this->layouts["main"] = layout;
+ setLayout("main", layout);
break;
}
case LayoutFormat::CArray:
@@ -226,7 +227,7 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) {
}
y++;
}
- this->layouts[layerName] = layout;
+ setLayout(layerName, layout);
}
} else {
@@ -241,13 +242,23 @@ bool RegionMap::loadLayout(poryjson::Json layoutJson) {
return !errored;
}
-void RegionMap::save() {
- logInfo("Saving region map data.");
+void RegionMap::commit(QUndoCommand *command) {
+ editHistory.push(command);
+}
+void RegionMap::undo() {
+ //
+ editHistory.undo();
+}
+
+void RegionMap::redo() {
+ //
+ editHistory.redo();
+}
+
+void RegionMap::save() {
saveTilemap();
saveLayout();
-
- // TODO
}
void RegionMap::saveTilemap() {
@@ -320,18 +331,19 @@ void RegionMap::saveOptions(int id, QString sec, QString name, int x, int y) {
}
void RegionMap::resetSquare(int index) {
- // TODO
-
+ this->layouts[this->current_layer][index].map_section = "MAPSEC_NONE";
+ this->layouts[this->current_layer][index].has_map = false;
}
void RegionMap::clearLayout() {
- // TODO
-
+ for (int i = 0; i < this->layout_width * this->layout_height; i++) {
+ resetSquare(i);
+ }
}
void RegionMap::clearImage() {
- // TODO
-
+ QByteArray zeros(this->tilemapSize(), 0);
+ this->setTilemap(zeros);
}
void RegionMap::replaceSectionId(unsigned oldId, unsigned newId) {
@@ -404,8 +416,17 @@ void RegionMap::setTilemap(QByteArray newTilemap) {
}
}
+QList RegionMap::getLayout(QString layer) {
+ return this->layouts[layer];
+}
+
+void RegionMap::setLayout(QString layer, QList layout) {
+ this->layouts[layer] = layout;
+}
+
QVector RegionMap::getTiles() {
QVector tileIds;
+ // unused? remove when redo history is fully transitioned
// TODO: change this to use TilemapTile instead of uint8_t
return tileIds;
@@ -423,7 +444,7 @@ int RegionMap::get_tilemap_index(int x, int y) {
// Layout coords to layout index.
int RegionMap::get_layout_index(int x, int y) {
- return x + y * this->tilemap_width;
+ return x + y * this->layout_width;
}
unsigned RegionMap::getTileId(int index) {
@@ -526,7 +547,7 @@ MapSectionEntry RegionMap::getEntry(QString section) {
}
QString RegionMap::palPath() {
- return this->project->root + "/" + this->palette_path;
+ return this->palette_path.isEmpty() ? QString() : this->project->root + "/" + this->palette_path;
}
QString RegionMap::pngPath() {
diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp
index e7eb46d2..bcde453e 100644
--- a/src/ui/regionmapeditor.cpp
+++ b/src/ui/regionmapeditor.cpp
@@ -1,6 +1,7 @@
#include "regionmapeditor.h"
#include "ui_regionmapeditor.h"
#include "regionmappropertiesdialog.h"
+#include "regionmapeditcommands.h"
#include "imageexport.h"
#include "shortcut.h"
#include "config.h"
@@ -36,6 +37,11 @@ RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project_) :
RegionMapEditor::~RegionMapEditor()
{
delete ui;
+ // deletion must be done in this order else crashes
+ auto stacks = this->history.stacks();
+ for (auto *stack : stacks) {
+ this->history.removeStack(stack);
+ }
for (auto p : this->region_maps) {
delete p.second;
}
@@ -64,6 +70,21 @@ void RegionMapEditor::initShortcuts() {
shortcut_RM_Options_delete->setObjectName("shortcut_RM_Options_delete");
shortcut_RM_Options_delete->setWhatsThis("Map Layout: Delete Square");
+ QAction *undoAction = this->history.createUndoAction(this, tr("&Undo"));
+ undoAction->setObjectName("action_RegionMap_Undo");
+ undoAction->setShortcut(QKeySequence("Ctrl+Z"));
+
+ QAction *redoAction = this->history.createRedoAction(this, tr("&Redo"));
+ redoAction->setObjectName("action_RegionMap_Redo");
+ redoAction->setShortcuts({QKeySequence("Ctrl+Y"), QKeySequence("Ctrl+Shift+Z")});
+
+ ui->menuEdit->addAction(undoAction);
+ ui->menuEdit->addAction(redoAction);
+
+ connect(&(this->history), &QUndoGroup::indexChanged, [this](int) {
+ on_tabWidget_Region_Map_currentChanged(this->ui->tabWidget_Region_Map->currentIndex());
+ });
+
shortcutsConfig.load();
shortcutsConfig.setDefaultShortcuts(shortcutableObjects());
applyUserShortcuts();
@@ -409,6 +430,8 @@ bool RegionMapEditor::load() {
newMap->loadMapData(o);
region_maps[alias] = newMap;
+
+ this->history.addStack(&(newMap->editHistory));
}
// add to ui
@@ -420,6 +443,7 @@ bool RegionMapEditor::load() {
if (!region_maps.empty()) {
this->region_map = region_maps.begin()->second;
this->currIndex = this->region_map->firstLayoutIndex();
+ this->region_map->editHistory.setActive();
displayRegionMap();
}
@@ -477,11 +501,14 @@ void RegionMapEditor::on_comboBox_regionSelector_textActivated(const QString &re
//
if (this->region_maps.contains(region)) {
this->region_map = region_maps.at(region);
+ this->region_map->editHistory.setActive();
this->currIndex = this->region_map->firstLayoutIndex();
// TODO: make the above into a function that takes an alias string? in case there is more to it
// TODO: anything else needed here?
displayRegionMap();
+
+ //this->editGroup.setActiveStack(&(this->region_map->editHistory));
}
}
@@ -510,15 +537,6 @@ void RegionMapEditor::displayRegionMapImage() {
this->ui->graphicsView_Region_Map_BkgImg->setScene(this->scene_region_map_image);
on_verticalSlider_Zoom_Map_Image_valueChanged(this->ui->verticalSlider_Zoom_Map_Image->value());
-
- // if (regionMapFirstDraw) {
- // on_verticalSlider_Zoom_Map_Image_valueChanged(this->ui->verticalSlider_Zoom_Map_Image->value());
- // RegionMapHistoryItem *commit = new RegionMapHistoryItem(
- // RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->tilemapWidth(), this->region_map->height()
- // );
- // history.push(commit);
- // regionMapFirstDraw = false;
- // }
}
void RegionMapEditor::displayRegionMapLayout() {
@@ -670,6 +688,8 @@ void RegionMapEditor::displayRegionMapTileSelector() {
this->ui->graphicsView_RegionMap_Tiles->setScene(this->scene_region_map_tiles);
on_verticalSlider_Zoom_Image_Tiles_valueChanged(this->ui->verticalSlider_Zoom_Image_Tiles->value());
+ this->ui->frame_tileOptions->setEnabled(this->region_map->tilemapFormat() != TilemapFormat::Plain);
+
this->mapsquare_selector_item->select(this->selectedImageTile);
}
@@ -842,21 +862,9 @@ void RegionMapEditor::mouseEvent_region_map(QGraphicsSceneMouseEvent *event, Reg
item->select(event);
//} else if (event->buttons() & Qt::MiddleButton) {// TODO
} else {
- if (event->type() == QEvent::GraphicsSceneMouseRelease) {
- //< TODO: history
- // RegionMapHistoryItem *current = history.current();
- // bool addToHistory = !(current && current->tiles == this->region_map->getTiles());
- // if (addToHistory) {
- // RegionMapHistoryItem *commit = new RegionMapHistoryItem(
- // RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->width(), this->region_map->height()
- // );
- // history.push(commit);
- // }
- } else {
- item->paint(event);
- this->region_map_layout_item->draw();
- this->hasUnsavedChanges = true;
- }
+ item->paint(event);
+ this->region_map_layout_item->draw();
+ this->hasUnsavedChanges = true;
}
}
@@ -894,15 +902,20 @@ void RegionMapEditor::mouseEvent_city_map(QGraphicsSceneMouseEvent *event, CityM
void RegionMapEditor::on_tabWidget_Region_Map_currentChanged(int index) {
this->ui->stackedWidget_RM_Options->setCurrentIndex(index);
+ if (!region_map) return;
switch (index)
{
case 0:
this->ui->verticalSlider_Zoom_Image_Tiles->setVisible(true);
- this->region_map_item->draw();
+ if (this->region_map_item)
+ this->region_map_item->draw();
break;
case 1:
this->ui->verticalSlider_Zoom_Image_Tiles->setVisible(false);
- this->region_map_layout_item->draw();
+ if (this->region_map_layout_item) {
+ this->region_map_layout_item->draw();
+ updateRegionMapLayoutOptions(this->currIndex);
+ }
break;
case 2:
this->ui->verticalSlider_Zoom_Image_Tiles->setVisible(false);
@@ -912,9 +925,17 @@ void RegionMapEditor::on_tabWidget_Region_Map_currentChanged(int index) {
}
void RegionMapEditor::on_comboBox_RM_ConnectedMap_textActivated(const QString &mapsec) {
+ QString layer = this->region_map->getLayer();
+ QList oldLayout = this->region_map->getLayout(layer);
this->region_map->setSquareMapSection(this->currIndex, mapsec);
- onRegionMapLayoutSelectedTileChanged(this->currIndex);// re-draw layout image
+ QList newLayout = this->region_map->getLayout(layer);
+
+ EditLayout *command = new EditLayout(this->region_map, layer, this->currIndex, oldLayout, newLayout);
+ this->region_map->commit(command);
+
this->hasUnsavedChanges = true;// TODO: sometimes this is called for unknown reasons
+
+ onRegionMapLayoutSelectedTileChanged(this->currIndex);// re-draw layout image
}
void RegionMapEditor::on_comboBox_RM_Entry_MapSection_textActivated(const QString &text) {
@@ -988,10 +1009,21 @@ void RegionMapEditor::on_lineEdit_RM_MapName_textEdited(const QString &text) {
}
void RegionMapEditor::on_pushButton_RM_Options_delete_clicked() {
- this->region_map->resetSquare(this->region_map_layout_item->selectedTile);
- updateRegionMapLayoutOptions(this->region_map_layout_item->selectedTile);
+ // TODO: crashing
+ int index = this->region_map->tilemapToLayoutIndex(this->currIndex);
+ QList oldLayout = this->region_map->getLayout(this->region_map->getLayer());
+ //this->region_map->resetSquare(this->region_map_layout_item->selectedTile);
+ this->region_map->resetSquare(index);
+ QList newLayout = this->region_map->getLayout(this->region_map->getLayer());
+ EditLayout *commit = new EditLayout(this->region_map, this->region_map->getLayer(), this->currIndex, oldLayout, newLayout);
+ commit->setText("Reset Layout Square");
+ this->region_map->editHistory.push(commit);
+ //updateRegionMapLayoutOptions(this->region_map_layout_item->selectedTile);
+ updateRegionMapLayoutOptions(this->currIndex);
this->region_map_layout_item->draw();
- this->region_map_layout_item->select(this->region_map_layout_item->selectedTile);
+ //this->region_map_layout_item->select(this->region_map_layout_item->selectedTile);
+ this->region_map_layout_item->select(this->currIndex);
+ // ^ this line necessary?
this->hasUnsavedChanges = true;
}
@@ -1082,63 +1114,6 @@ void RegionMapEditor::on_action_RegionMap_Resize_triggered() {
return;
}
-void RegionMapEditor::on_action_RegionMap_Undo_triggered() {
- //< TODO: edit history
- undo();
- this->hasUnsavedChanges = true;
-}
-
-void RegionMapEditor::undo() {
- //< TODO: edit history
- RegionMapHistoryItem *commit = history.back();
- if (!commit) return;
-
- switch (commit->which)
- {
- case RegionMapEditorBox::BackgroundImage:
- //if (commit->mapWidth != this->region_map->width() || commit->mapHeight != this->region_map->height())
- // this->resize(commit->mapWidth, commit->mapHeight);
- this->region_map->setTiles(commit->tiles);
- this->region_map_item->draw();
- this->region_map_layout_item->draw();
- this->region_map_entries_item->draw();
- break;
- case RegionMapEditorBox::CityMapImage:
- //< if (commit->cityMap == this->city_map_item->file)
- //< this->city_map_item->setTiles(commit->tiles);
- //< this->city_map_item->draw();
- break;
- }
-}
-
-void RegionMapEditor::on_action_RegionMap_Redo_triggered() {
- //< TODO: edit history
- redo();
- this->hasUnsavedChanges = true;
-}
-
-void RegionMapEditor::redo() {
- //< TODO: edit history
- RegionMapHistoryItem *commit = history.next();
- if (!commit) return;
-
- switch (commit->which)
- {
- case RegionMapEditorBox::BackgroundImage:
- //if (commit->mapWidth != this->region_map->width() || commit->mapHeight != this->region_map->height())
- // this->resize(commit->mapWidth, commit->mapHeight);
- this->region_map->setTiles(commit->tiles);
- this->region_map_item->draw();
- this->region_map_layout_item->draw();
- this->region_map_entries_item->draw();
- break;
- case RegionMapEditorBox::CityMapImage:
- //< this->city_map_item->setTiles(commit->tiles);
- //< this->city_map_item->draw();
- break;
- }
-}
-
void RegionMapEditor::resize(int w, int h) {
this->region_map->resize(w, h);
this->currIndex = this->region_map->padLeft() * w + this->region_map->padTop();
@@ -1189,11 +1164,13 @@ void RegionMapEditor::on_action_Swap_triggered() {
}
void RegionMapEditor::on_action_RegionMap_ClearImage_triggered() {
+ QByteArray oldTilemap = this->region_map->getTilemap();
this->region_map->clearImage();
- RegionMapHistoryItem *commit = new RegionMapHistoryItem(
- RegionMapEditorBox::BackgroundImage, this->region_map->getTiles(), this->region_map->tilemapWidth(), this->region_map->tilemapHeight()
- );
- history.push(commit);
+ QByteArray newTilemap = this->region_map->getTilemap();
+
+ EditTilemap *commit = new EditTilemap(this->region_map, oldTilemap, newTilemap, -1);
+ commit->setText("Clear Tilemap");
+ this->region_map->editHistory.push(commit);
displayRegionMapImage();
displayRegionMapLayout();
@@ -1209,7 +1186,12 @@ void RegionMapEditor::on_action_RegionMap_ClearLayout_triggered() {
);
if (result == QMessageBox::Yes) {
+ QList oldLayout = this->region_map->getLayout(this->region_map->getLayer());
this->region_map->clearLayout();
+ QList newLayout = this->region_map->getLayout(this->region_map->getLayer());
+ EditLayout *commit = new EditLayout(this->region_map, this->region_map->getLayer(), -1, oldLayout, newLayout);
+ commit->setText("Clear Layout");
+ this->region_map->editHistory.push(commit);
displayRegionMapLayout();
} else {
return;
diff --git a/src/ui/regionmappixmapitem.cpp b/src/ui/regionmappixmapitem.cpp
index 75a9eb3d..ba1cdcc9 100644
--- a/src/ui/regionmappixmapitem.cpp
+++ b/src/ui/regionmappixmapitem.cpp
@@ -1,4 +1,7 @@
#include "regionmappixmapitem.h"
+#include "regionmapeditcommands.h"
+
+static unsigned actionId_ = 0;
void RegionMapPixmapItem::draw() {
if (!region_map) return;
@@ -20,17 +23,25 @@ void RegionMapPixmapItem::draw() {
void RegionMapPixmapItem::paint(QGraphicsSceneMouseEvent *event) {
if (region_map) {
- QPointF pos = event->pos();
- int x = static_cast(pos.x()) / 8;
- int y = static_cast(pos.y()) / 8;
- int index = x + y * region_map->tilemapWidth();
- this->region_map->setTileData(index,
- this->tile_selector->selectedTile,
- this->tile_selector->tile_hFlip,
- this->tile_selector->tile_vFlip,
- this->tile_selector->tile_palette
- );
- draw();
+ if (event->type() == QEvent::GraphicsSceneMouseRelease) {
+ actionId_++;
+ } else {
+ QPointF pos = event->pos();
+ int x = static_cast(pos.x()) / 8;
+ int y = static_cast(pos.y()) / 8;
+ int index = x + y * region_map->tilemapWidth();
+ QByteArray oldTilemap = this->region_map->getTilemap();
+ this->region_map->setTileData(index,
+ this->tile_selector->selectedTile,
+ this->tile_selector->tile_hFlip,
+ this->tile_selector->tile_vFlip,
+ this->tile_selector->tile_palette
+ );
+ QByteArray newTilemap = this->region_map->getTilemap();
+ EditTilemap *command = new EditTilemap(this->region_map, oldTilemap, newTilemap, actionId_);
+ this->region_map->commit(command);
+ draw();
+ }
}
}
diff --git a/src/ui/tilemaptileselector.cpp b/src/ui/tilemaptileselector.cpp
index b7759c4c..48587893 100644
--- a/src/ui/tilemaptileselector.cpp
+++ b/src/ui/tilemaptileselector.cpp
@@ -20,6 +20,7 @@ void TilemapTileSelector::select(unsigned tileId) {
QPoint coords = this->getTileIdCoords(tileId);
SelectablePixmapItem::select(coords.x(), coords.y(), 0, 0);
this->selectedTile = tileId;
+ this->drawSelection();
emit selectedTileChanged(tileId);
}
@@ -50,11 +51,7 @@ QImage TilemapTileSelector::tileImg(shared_ptr tile) {
switch(this->format) {
case TilemapFormat::Plain:
- {
- // TODO: even allow palettes for Plain tiles?
- // 1 x palette x any colors
break;
- }
case TilemapFormat::BPP_4:
{
// before Qt 6, the color table is a QVector which is deprecated now, and this method does not exits
@@ -67,10 +64,11 @@ QImage TilemapTileSelector::tileImg(shared_ptr tile) {
}
case TilemapFormat::BPP_8:
{
+ // TODO:
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- tilesetImage.setColorTable(this->palette.toVector());
+ //tilesetImage.setColorTable(this->palette.toVector());
#else
- tilesetImage.setColorTable(this->palette);
+ //tilesetImage.setColorTable(this->palette);
#endif
break;
}