Added the ability to undo actions that affect the whole map,

...such as resizing and reverting.
This commit is contained in:
Kristoffer Erlandsson 2004-05-13 21:25:04 +00:00
parent 05ea9267eb
commit 75122b8ea3
6 changed files with 298 additions and 114 deletions

View file

@ -159,6 +159,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
editor/editor_palettes.cpp \
editor/editor_main.cpp \
editor/editor_dialogs.cpp \
editor/editor_undo.cpp \
actions.cpp \
ai.cpp \
ai_attack.cpp \
@ -217,6 +218,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
editor/map_manip.hpp \
editor/editor_palettes.hpp \
editor/editor_dialogs.hpp \
editor/editor_undo.hpp \
actions.hpp \
ai.hpp \
ai_attack.hpp \

View file

@ -302,6 +302,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
editor/editor_palettes.cpp \
editor/editor_main.cpp \
editor/editor_dialogs.cpp \
editor/editor_undo.cpp \
actions.cpp \
ai.cpp \
ai_attack.cpp \
@ -360,6 +361,7 @@ wesnoth_editor_SOURCES = editor/editor.cpp \
editor/map_manip.hpp \
editor/editor_palettes.hpp \
editor/editor_dialogs.hpp \
editor/editor_undo.hpp \
actions.hpp \
ai.hpp \
ai_attack.hpp \

View file

@ -45,7 +45,6 @@
#include <cmath>
namespace {
const unsigned int undo_limit = 100;
// Milliseconds to sleep in every iteration of the main loop.
const unsigned int sdl_delay = 20;
const std::string prefs_filename = get_dir(get_user_data_dir() + "/editor")
@ -261,7 +260,7 @@ void map_editor::edit_flood_fill() {
action.add_terrain((*it).second, palette_.selected_terrain(),
(*it).first);
}
add_undo_action(action);
save_undo_action(action);
invalidate_all_and_adjacent(to_invalidate);
}
@ -277,6 +276,7 @@ void map_editor::edit_new_map() {
const std::string map = new_map_dialog(gui_, palette_.selected_terrain(),
changed_since_save(), game_config_);
if (map != "") {
clear_undo_actions();
throw new_map_exception(map);
}
}
@ -287,6 +287,7 @@ void map_editor::edit_load_map() {
const std::string new_map = load_map(fn);
if (new_map != "") {
if (!changed_since_save() || confirm_modification_disposal(gui_)) {
clear_undo_actions();
throw new_map_exception(new_map, fn);
}
}
@ -302,7 +303,7 @@ void map_editor::edit_fill_selection() {
map_.set_terrain(*it, palette_.selected_terrain());
}
}
add_undo_action(undo_action);
save_undo_action(undo_action);
invalidate_all_and_adjacent(selected_hexes_);
}
@ -339,15 +340,17 @@ void map_editor::edit_paste() {
undo_action.set_selection(filled, selected_hexes_);
invalidate_all_and_adjacent(filled);
selected_hexes_ = filled;
add_undo_action(undo_action, false);
save_undo_action(undo_action, false);
}
void map_editor::edit_revert() {
const std::string new_map = load_map(filename_);
if (new_map != "") {
if (!changed_since_save() || confirm_modification_disposal(gui_)) {
throw new_map_exception(new_map, filename_);
}
map_undo_action action;
action.set_type(map_undo_action::WHOLE_MAP);
action.set_map_data(map_.write(), new_map);
save_undo_action(action, false);
throw new_map_exception(new_map, filename_);
}
}
@ -358,6 +361,10 @@ void map_editor::edit_resize() {
const std::string resized_map =
resize_map(map_, new_size.first, new_size.second, palette_.selected_terrain());
if (resized_map != "") {
map_undo_action action;
action.set_type(map_undo_action::WHOLE_MAP);
action.set_map_data(map_.write(), resized_map);
save_undo_action(action, false);
throw new_map_exception(resized_map, filename_);
}
}
@ -449,65 +456,59 @@ void map_editor::toggle_grid() {
gui_.invalidate_all();
}
void map_editor::add_undo_action(map_undo_action &action, const bool keep_selection) {
void map_editor::save_undo_action(map_undo_action &action, const bool keep_selection) {
if (keep_selection) {
action.set_selection(selected_hexes_, selected_hexes_);
}
undo_stack_.push_back(action);
if(undo_stack_.size() > undo_limit) {
undo_stack_.pop_front();
}
// Adding an undo action means that an operations was performed,
// which in turns means that no further redo may be performed.
redo_stack_.clear();
add_undo_action(action);
num_operations_since_save_++;
}
void map_editor::undo() {
if(!undo_stack_.empty()) {
if(exist_undo_actions()) {
--num_operations_since_save_;
map_undo_action action = undo_stack_.back();
selected_hexes_ = action.undo_selection();
highlight_selected_hexes(true);
std::vector<gamemap::location> to_invalidate;
for(std::map<gamemap::location,gamemap::TERRAIN>::const_iterator it =
action.undo_terrains().begin();
it != action.undo_terrains().end(); ++it) {
map_.set_terrain(it->first, it->second);
to_invalidate.push_back(it->first);
map_undo_action action = pop_undo_action();
if (action.undo_type() == map_undo_action::REGULAR) {
selected_hexes_ = action.undo_selection();
highlight_selected_hexes(true);
std::vector<gamemap::location> to_invalidate;
for(std::map<gamemap::location,gamemap::TERRAIN>::const_iterator it =
action.undo_terrains().begin();
it != action.undo_terrains().end(); ++it) {
map_.set_terrain(it->first, it->second);
to_invalidate.push_back(it->first);
}
std::copy(selected_hexes_.begin(), selected_hexes_.end(),
std::back_inserter(to_invalidate));
invalidate_all_and_adjacent(to_invalidate);
}
undo_stack_.pop_back();
redo_stack_.push_back(action);
if(redo_stack_.size() > undo_limit) {
redo_stack_.pop_front();
else if (action.undo_type() == map_undo_action::WHOLE_MAP) {
throw new_map_exception(action.old_map_data(), filename_);
}
std::copy(selected_hexes_.begin(), selected_hexes_.end(),
std::back_inserter(to_invalidate));
invalidate_all_and_adjacent(to_invalidate);
}
}
void map_editor::redo() {
if(!redo_stack_.empty()) {
if(exist_redo_actions()) {
++num_operations_since_save_;
map_undo_action action = redo_stack_.back();
map_undo_action action = pop_redo_action();
selected_hexes_ = action.redo_selection();
highlight_selected_hexes(true);
std::vector<gamemap::location> to_invalidate;
for(std::map<gamemap::location,gamemap::TERRAIN>::const_iterator it =
action.redo_terrains().begin();
it != action.redo_terrains().end(); ++it) {
map_.set_terrain(it->first, it->second);
to_invalidate.push_back(it->first);
if (action.undo_type() == map_undo_action::REGULAR) {
highlight_selected_hexes(true);
std::vector<gamemap::location> to_invalidate;
for(std::map<gamemap::location,gamemap::TERRAIN>::const_iterator it =
action.redo_terrains().begin();
it != action.redo_terrains().end(); ++it) {
map_.set_terrain(it->first, it->second);
to_invalidate.push_back(it->first);
}
std::copy(selected_hexes_.begin(), selected_hexes_.end(),
std::back_inserter(to_invalidate));
invalidate_all_and_adjacent(to_invalidate);
}
redo_stack_.pop_back();
undo_stack_.push_back(action);
if (undo_stack_.size() > undo_limit) {
undo_stack_.pop_front();
else if (action.undo_type() == map_undo_action::WHOLE_MAP) {
throw new_map_exception(action.new_map_data(), filename_);
}
std::copy(selected_hexes_.begin(), selected_hexes_.end(),
std::back_inserter(to_invalidate));
invalidate_all_and_adjacent(to_invalidate);
}
}
@ -544,7 +545,7 @@ void map_editor::set_starting_position(const int player, const gamemap::location
map_.set_terrain(selected_hex_, gamemap::KEEP);
// This operation is currently not undoable, so we need to make sure
// that save is always asked for after it is performed.
num_operations_since_save_ = undo_limit + 1;
num_operations_since_save_++;
map_.set_starting_position(player, loc);
invalidate_adjacent(loc);
}
@ -633,7 +634,6 @@ void map_editor::left_button_down(const int mousex, const int mousey) {
else {
std::vector<gamemap::location> locs =
get_tiles(map_, hex, brush_.selected_brush_size());
redo_stack_.clear();
map_undo_action action;
std::vector<gamemap::location> to_invalidate;
for(std::vector<gamemap::location>::const_iterator it = locs.begin();
@ -645,7 +645,7 @@ void map_editor::left_button_down(const int mousex, const int mousey) {
}
}
if (!to_invalidate.empty()) {
add_undo_action(action);
save_undo_action(action);
invalidate_all_and_adjacent(to_invalidate);
}
}
@ -698,14 +698,14 @@ void map_editor::perform_selection_move() {
invalidate_all_and_adjacent(selected_hexes_);
selected_hexes_ = new_selection;
invalidate_all_and_adjacent(selected_hexes_);
add_undo_action(undo_action, false);
save_undo_action(undo_action, false);
}
void map_editor::draw_terrain(const gamemap::TERRAIN terrain,
const gamemap::location hex) {
const gamemap::TERRAIN current_terrain = map_[hex.x][hex.y];
map_undo_action undo_action(current_terrain, terrain, hex);
add_undo_action(undo_action);
save_undo_action(undo_action);
map_.set_terrain(hex, terrain);
invalidate_adjacent(hex);
}

View file

@ -14,6 +14,7 @@
#include "editor_palettes.hpp"
#include "editor_layout.hpp"
#include "editor_undo.hpp"
#include "../display.hpp"
#include "../events.hpp"
@ -27,57 +28,6 @@
namespace map_editor {
/// A saved action that may be undone.
class map_undo_action
{
public:
map_undo_action() {
}
map_undo_action(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc){
add_terrain(old_tr, new_tr, lc);
}
const std::map<gamemap::location,gamemap::TERRAIN>& undo_terrains() const {
return old_terrain_;
}
const std::map<gamemap::location,gamemap::TERRAIN>& redo_terrains() const {
return new_terrain_;
}
const std::set<gamemap::location> undo_selection() const {
return old_selection_;
}
const std::set<gamemap::location> redo_selection() const {
return new_selection_;
}
void add_terrain(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc) {
old_terrain_[lc] = old_tr;
new_terrain_[lc] = new_tr;
}
void set_selection(const std::set<gamemap::location> &new_selection,
const std::set<gamemap::location> &old_selection) {
new_selection_ = new_selection;
old_selection_ = old_selection;
}
private:
std::map<gamemap::location,gamemap::TERRAIN> old_terrain_;
std::map<gamemap::location,gamemap::TERRAIN> new_terrain_;
std::set<gamemap::location> new_selection_;
std::set<gamemap::location> old_selection_;
};
typedef std::deque<map_undo_action> map_undo_list;
/// A map editor. Receives SDL events and can execute hotkey commands.
class map_editor : public events::handler,
public hotkey::command_executor {
@ -226,14 +176,6 @@ private:
/// may have changed the starting positions.
void recalculate_starting_pos_labels();
/// Add an undo action to the undo stack. Resize the stack if it
/// gets larger than the maximum size. Add an operation to the
/// number done since save. If keep_selection is true, it indicates
/// that the selection has not changed and the currently selected
/// terrain should be kept if this action is redone/undone. Also
/// clear the redo stack.
void add_undo_action(map_undo_action &action, const bool keep_selection=true);
/// Update the selection and highlightning of the hexes the mouse
/// currently is over.
void update_mouse_over_hexes(const gamemap::location mouse_over_hex);
@ -256,6 +198,13 @@ private:
/// highlighted.
void highlight_selected_hexes(const bool clear_old=true);
/// Save an action so that it may be undone. Add an operation to the
/// number done since save. If keep_selection is true, it indicates
/// that the selection has not changed and the currently selected
/// terrain should be kept if this action is redone/undone.
void save_undo_action(map_undo_action &action,
const bool keep_selection=true);
/// An item in the clipboard. Consists of the copied terrain and an
/// offset. When pasting stuff, the offset is used to calculate
/// where to put the pasted hex when calculating from the one
@ -273,8 +222,6 @@ private:
display &gui_;
gamemap &map_;
map_undo_list undo_stack_;
map_undo_list redo_stack_;
std::string filename_;
ABORT_MODE abort_;
// Keep track of the number of operations performed since the last

131
src/editor/editor_undo.cpp Normal file
View file

@ -0,0 +1,131 @@
/*
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "editor_undo.hpp"
namespace {
const unsigned int undo_limit = 100;
map_editor::map_undo_list undo_stack;
map_editor::map_undo_list redo_stack;
}
namespace map_editor {
map_undo_action::map_undo_action(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc){
add_terrain(old_tr, new_tr, lc);
undo_type_ = REGULAR;
}
map_undo_action::map_undo_action() {
undo_type_ = REGULAR;
}
const std::map<gamemap::location,gamemap::TERRAIN>& map_undo_action::undo_terrains() const {
return old_terrain_;
}
const std::map<gamemap::location,gamemap::TERRAIN>& map_undo_action::redo_terrains() const {
return new_terrain_;
}
const std::set<gamemap::location> map_undo_action::undo_selection() const {
return old_selection_;
}
const std::set<gamemap::location> map_undo_action::redo_selection() const {
return new_selection_;
}
void map_undo_action::add_terrain(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc) {
old_terrain_[lc] = old_tr;
new_terrain_[lc] = new_tr;
}
void map_undo_action::set_selection(const std::set<gamemap::location> &new_selection,
const std::set<gamemap::location> &old_selection) {
new_selection_ = new_selection;
old_selection_ = old_selection;
}
std::string map_undo_action::old_map_data() const {
return old_map_data_;
}
std::string map_undo_action::new_map_data() const {
return new_map_data_;
}
void map_undo_action::set_map_data(const std::string &old_data,
const std::string &new_data) {
old_map_data_ = old_data;
new_map_data_ = new_data;
}
void map_undo_action::set_type(const UNDO_TYPE new_type) {
undo_type_ = new_type;
}
map_undo_action::UNDO_TYPE map_undo_action::undo_type() const {
return undo_type_;
}
void add_undo_action(map_undo_action &action) {
undo_stack.push_back(action);
if (undo_stack.size() > undo_limit) {
undo_stack.pop_front();
}
// Adding an undo action means that an operations was performed,
// which in turns means that no further redo may be performed.
redo_stack.clear();
}
bool exist_undo_actions() {
return !undo_stack.empty();
}
bool exist_redo_actions() {
return !redo_stack.empty();
}
map_undo_action pop_undo_action() {
map_undo_action action = undo_stack.back();
undo_stack.pop_back();
redo_stack.push_back(action);
if (redo_stack.size() > undo_limit) {
redo_stack.pop_front();
}
return action;
}
map_undo_action pop_redo_action() {
map_undo_action action = redo_stack.back();
redo_stack.pop_back();
undo_stack.push_back(action);
if (undo_stack.size() > undo_limit) {
undo_stack.pop_front();
}
return action;
}
void clear_undo_actions() {
undo_stack.clear();
redo_stack.clear();
}
}

102
src/editor/editor_undo.hpp Normal file
View file

@ -0,0 +1,102 @@
/*
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
// This module is used to manage actions that may be undone in the map
// editor.
#ifndef EDITOR_UNDO_H_INCLUDED
#define EDITOR_UNDO_H_INCLUDED
#include "../map.hpp"
#include <queue>
#include <vector>
#include <set>
namespace map_editor {
/// A saved action that may be undone.
class map_undo_action {
public:
enum UNDO_TYPE { REGULAR, WHOLE_MAP };
map_undo_action();
map_undo_action(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc);
const std::map<gamemap::location,gamemap::TERRAIN>& undo_terrains() const;
const std::map<gamemap::location,gamemap::TERRAIN>& redo_terrains() const;
const std::set<gamemap::location> undo_selection() const;
const std::set<gamemap::location> redo_selection() const;
void add_terrain(const gamemap::TERRAIN& old_tr,
const gamemap::TERRAIN& new_tr,
const gamemap::location& lc);
void set_selection(const std::set<gamemap::location> &new_selection,
const std::set<gamemap::location> &old_selection);
void set_map_data(const std::string &old_data,
const std::string &new_data);
std::string new_map_data() const;
std::string old_map_data() const;
void set_type(const UNDO_TYPE new_type);
UNDO_TYPE undo_type() const;
private:
std::map<gamemap::location,gamemap::TERRAIN> old_terrain_;
std::map<gamemap::location,gamemap::TERRAIN> new_terrain_;
std::set<gamemap::location> new_selection_;
std::set<gamemap::location> old_selection_;
std::string new_map_data_;
std::string old_map_data_;
UNDO_TYPE undo_type_;
};
typedef std::deque<map_undo_action> map_undo_list;
/// Add an undo action to the undo stack. Resize the stack if it gets
/// larger than the maximum size. Add an operation to the number done
/// since save. If keep_selection is true, it indicates that the
/// selection has not changed and the currently selected terrain should
/// be kept if this action is redone/undone. Also clear the redo stack.
void add_undo_action(map_undo_action &action);
/// Return true if there exist any undo actions in the undo stack.
bool exist_undo_actions();
/// Return true if there exist any redo actions in the redo stack.
bool exist_redo_actions();
/// Remove, store in the redo stack and return the last undo action
/// stored.
map_undo_action pop_undo_action();
/// Remove, store in the undo stack and return the last redo action
/// stored.
map_undo_action pop_redo_action();
/// Clear all stored information about performed actions.
void clear_undo_actions();
}
#endif // EDITOR_UNDO_H_INCLUDED