Initial framework for scenario editor undo/redo
In addition to the framework, this enables undo/redo of one action - adding a new town
This commit is contained in:
@@ -703,6 +703,7 @@ void aTextInsert::undo() {
|
||||
auto result = contents.erase(del_start, del_end);
|
||||
in.setText(contents);
|
||||
in.selectionPoint = in.insertionPoint = result - contents.begin();
|
||||
done = false;
|
||||
}
|
||||
|
||||
void aTextInsert::redo() {
|
||||
@@ -710,6 +711,7 @@ void aTextInsert::redo() {
|
||||
contents.insert(at, text);
|
||||
in.setText(contents);
|
||||
in.selectionPoint = in.insertionPoint = at + text.length();
|
||||
done = true;
|
||||
}
|
||||
|
||||
void aTextInsert::append(char c) {
|
||||
@@ -725,6 +727,7 @@ void aTextDelete::undo() {
|
||||
contents.insert(start, text);
|
||||
in.setText(contents);
|
||||
in.selectionPoint = in.insertionPoint = start + ip;
|
||||
done = false;
|
||||
}
|
||||
|
||||
void aTextDelete::redo() {
|
||||
@@ -734,6 +737,7 @@ void aTextDelete::redo() {
|
||||
auto result = contents.erase(del_start, del_end);
|
||||
in.setText(contents);
|
||||
in.selectionPoint = in.insertionPoint = result - contents.begin();
|
||||
done = true;
|
||||
}
|
||||
|
||||
void aTextDelete::append_front(char c) {
|
||||
|
@@ -38,6 +38,7 @@ short current_terrain_type = 0;
|
||||
short safety = 0;
|
||||
location spot_hit,last_spot_hit(-1,-1),mouse_spot(-1,-1);
|
||||
short copied_spec = -1;
|
||||
cUndoList undo_list;
|
||||
|
||||
cTown::cItem store_place_item;
|
||||
|
||||
@@ -228,8 +229,7 @@ static bool handle_lb_action(location the_point) {
|
||||
case LB_LOAD_SCEN:
|
||||
file_to_load = nav_get_scenario();
|
||||
if(!file_to_load.empty() && load_scenario(file_to_load, scenario)) {
|
||||
cur_town = scenario.last_town_edited;
|
||||
town = scenario.towns[cur_town];
|
||||
set_current_town(scenario.last_town_edited);
|
||||
cur_out = scenario.last_out_edited;
|
||||
current_terrain = scenario.outdoors[cur_out.x][cur_out.y];
|
||||
overall_mode = MODE_MAIN_SCREEN;
|
||||
@@ -252,7 +252,7 @@ static bool handle_lb_action(location the_point) {
|
||||
showError("You have reached the limit of 200 towns you can have in one scenario.");
|
||||
return true;
|
||||
}
|
||||
if(new_town(scenario.towns.size()))
|
||||
if(new_town())
|
||||
set_up_main_screen();
|
||||
break;
|
||||
case LB_EDIT_TEXT:
|
||||
@@ -302,6 +302,7 @@ static bool handle_lb_action(location the_point) {
|
||||
set_up_main_screen();
|
||||
}
|
||||
mouse_button_held = false;
|
||||
update_edit_menu();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
|
||||
#include "scen.global.hpp"
|
||||
#include "undo.hpp"
|
||||
|
||||
void init_screen_locs();
|
||||
void handle_action(location the_point,sf::Event event);
|
||||
@@ -45,3 +46,12 @@ void place_edit_special(location loc);
|
||||
void set_special(location spot_hit);
|
||||
bool save_check(std::string which_dlog);
|
||||
|
||||
/// Represents the action of adding a new town to the end of the list
|
||||
class aNewTown : public cAction {
|
||||
class cTown* theTown;
|
||||
public:
|
||||
aNewTown(class cTown* t);
|
||||
void undo() override;
|
||||
void redo() override;
|
||||
~aNewTown();
|
||||
};
|
||||
|
@@ -13,12 +13,11 @@
|
||||
#include <iostream>
|
||||
#include "fileio.hpp"
|
||||
#include "scen.actions.hpp"
|
||||
#include "scen.townout.hpp"
|
||||
|
||||
//extern bool ae_loading, startup_loaded, All_Done, party_in_memory, finished_init;
|
||||
extern cScenario scenario;
|
||||
extern cTown* town;
|
||||
extern cOutdoors* current_terrain;
|
||||
extern short cur_town;
|
||||
extern location cur_out;
|
||||
extern bool change_made, ae_loading;
|
||||
|
||||
@@ -52,8 +51,7 @@ void set_up_apple_events(int, char*[]) {
|
||||
std::copy(msg.get(), msg.get() + len, std::inserter(fileName, fileName.begin()));
|
||||
|
||||
if(load_scenario(fileName, scenario)) {
|
||||
cur_town = scenario.last_town_edited;
|
||||
town = scenario.towns[cur_town];
|
||||
set_current_town(scenario.last_town_edited);
|
||||
cur_out = scenario.last_out_edited;
|
||||
current_terrain = scenario.outdoors[cur_out.x][cur_out.y];
|
||||
change_made = false;
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "map_parse.hpp"
|
||||
#include "winutil.hpp"
|
||||
#include "choicedlog.hpp"
|
||||
#include "undo.hpp"
|
||||
|
||||
extern cScenario scenario;
|
||||
|
||||
@@ -995,6 +996,9 @@ void save_scenario(bool rename) {
|
||||
if(toFile.empty()) return;
|
||||
}
|
||||
|
||||
extern cUndoList undo_list;
|
||||
undo_list.save();
|
||||
|
||||
if(scenario.is_legacy && cChoiceDlog("save-legacy-scen", {"update", "cancel"}).show() == "update")
|
||||
scenario.is_legacy = false;
|
||||
|
||||
|
@@ -208,6 +208,7 @@ void Handle_Update() {
|
||||
|
||||
extern fs::path progDir;
|
||||
void handle_menu_choice(eMenu item_hit) {
|
||||
extern cUndoList undo_list;
|
||||
bool isEdit = false, isHelp = false;
|
||||
std::string helpDlog;
|
||||
fs::path file_to_load;
|
||||
@@ -232,6 +233,8 @@ void handle_menu_choice(eMenu item_hit) {
|
||||
set_up_main_screen();
|
||||
} else if(!file_to_load.empty())
|
||||
set_up_start_screen(); // Failed to load file, dump to start
|
||||
undo_list.clear();
|
||||
update_edit_menu();
|
||||
break;
|
||||
case eMenu::FILE_SAVE:
|
||||
save_scenario();
|
||||
@@ -244,6 +247,8 @@ void handle_menu_choice(eMenu item_hit) {
|
||||
overall_mode = MODE_MAIN_SCREEN;
|
||||
set_up_main_screen();
|
||||
}
|
||||
undo_list.clear();
|
||||
update_edit_menu();
|
||||
break;
|
||||
case eMenu::FILE_CLOSE:
|
||||
if(!save_check("save-before-close"))
|
||||
@@ -253,6 +258,8 @@ void handle_menu_choice(eMenu item_hit) {
|
||||
pal_sbar->hide();
|
||||
shut_down_menus(0);
|
||||
set_up_start_screen();
|
||||
undo_list.clear();
|
||||
update_edit_menu();
|
||||
break;
|
||||
case eMenu::QUIT: // quit
|
||||
if(!save_check("save-before-quit"))
|
||||
@@ -292,9 +299,8 @@ void handle_menu_choice(eMenu item_hit) {
|
||||
showError("You have reached the limit of 200 towns you can have in one scenario.");
|
||||
return;
|
||||
}
|
||||
if(new_town(scenario.towns.size()))
|
||||
if(new_town())
|
||||
set_up_main_screen();
|
||||
change_made = true;
|
||||
break;
|
||||
case eMenu::OUT_RESIZE:
|
||||
if(resize_outdoors())
|
||||
@@ -523,7 +529,15 @@ void handle_menu_choice(eMenu item_hit) {
|
||||
if(isEdit) {
|
||||
if(!cDialog::sendInput(editKey)) {
|
||||
// Handle non-dialog edit operations here.
|
||||
// switch(editKey.k) {}
|
||||
switch(editKey.k) {
|
||||
case key_undo: undo_list.undo(); break;
|
||||
case key_redo: undo_list.redo(); break;
|
||||
default: break;
|
||||
}
|
||||
update_edit_menu();
|
||||
if(overall_mode == MODE_MAIN_SCREEN)
|
||||
set_up_main_screen();
|
||||
redraw_screen();
|
||||
}
|
||||
}
|
||||
if(isHelp)
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
void init_menubar();
|
||||
void shut_down_menus(short mode);
|
||||
void update_edit_menu();
|
||||
|
||||
enum class eMenu {
|
||||
NONE, ABOUT, QUIT, FRILL, UNFRILL,
|
||||
|
@@ -10,12 +10,14 @@
|
||||
#include "scenario.hpp" // Include before Cocoa because the Cocoa header defines things that cause compilation errors in here
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include "winutil.hpp"
|
||||
#include "undo.hpp"
|
||||
|
||||
using MenuHandle = NSMenu*;
|
||||
MenuHandle menu_bar_handle;
|
||||
MenuHandle file_menu, edit_menu, app_menu, scen_menu, town_menu, out_menu, help_menu;
|
||||
|
||||
extern cScenario scenario;
|
||||
extern cUndoList undo_list;
|
||||
|
||||
@interface MenuHandler : NSObject
|
||||
-(void) menuChoice:(id) sender;
|
||||
@@ -141,6 +143,27 @@ void shut_down_menus(short mode) {
|
||||
}
|
||||
}
|
||||
|
||||
void update_edit_menu() {
|
||||
NSMenuItem* mi_undo = [edit_menu itemAtIndex: 0];
|
||||
if(undo_list.noUndo()) {
|
||||
[mi_undo setTitle: @"Can't Undo"];
|
||||
[mi_undo setEnabled: NO];
|
||||
} else {
|
||||
std::string undo_name = "Undo " + undo_list.undoName();
|
||||
[mi_undo setTitle: [NSString stringWithCString: undo_name.c_str() encoding: NSASCIIStringEncoding]];
|
||||
[mi_undo setEnabled: YES];
|
||||
}
|
||||
NSMenuItem* mi_redo = [edit_menu itemAtIndex: 1];
|
||||
if(undo_list.noRedo()) {
|
||||
[mi_redo setTitle: @"Can't Redo"];
|
||||
[mi_redo setEnabled: NO];
|
||||
} else {
|
||||
std::string redo_name = "Redo " + undo_list.redoName();
|
||||
[mi_redo setTitle: [NSString stringWithCString: redo_name.c_str() encoding: NSASCIIStringEncoding]];
|
||||
[mi_redo setEnabled: YES];
|
||||
}
|
||||
}
|
||||
|
||||
@implementation MenuHandler
|
||||
-(void) menuChoice:(id) sender {
|
||||
handle_menu_choice(eMenu([[sender representedObject] intValue]));
|
||||
|
@@ -190,6 +190,10 @@ void shut_down_menus(short mode) {
|
||||
DrawMenuBar(mainPtr.getSystemHandle());
|
||||
}
|
||||
|
||||
void update_edit_menu() {
|
||||
// TODO: Set the undo/redo menuitem title according to the current action to be undone/redone
|
||||
}
|
||||
|
||||
#include "cursors.hpp"
|
||||
|
||||
LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
@@ -212,17 +216,15 @@ LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lPara
|
||||
|
||||
#include "fileio.hpp"
|
||||
#include "scen.actions.hpp"
|
||||
#include "scen.townout.hpp"
|
||||
|
||||
extern short cur_town;
|
||||
extern location cur_out;
|
||||
extern cTown* town;
|
||||
extern cOutdoors* current_terrain;
|
||||
extern bool change_made, ae_loading;
|
||||
void set_up_apple_events(int argc, char* argv[]) {
|
||||
if(argc > 1) {
|
||||
if(load_scenario(argv[1], scenario)) {
|
||||
cur_town = scenario.last_town_edited;
|
||||
town = scenario.towns[cur_town];
|
||||
set_current_town(scenario.last_town_edited);
|
||||
cur_out = scenario.last_out_edited;
|
||||
current_terrain = scenario.outdoors[cur_out.x][cur_out.y];
|
||||
change_made = false;
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "scenario.hpp"
|
||||
#include "town.hpp"
|
||||
#include "graphtool.hpp"
|
||||
#include "scen.actions.hpp"
|
||||
#include "scen.graphics.hpp"
|
||||
#include "scen.townout.hpp"
|
||||
#include "scen.keydlgs.hpp"
|
||||
@@ -31,6 +32,7 @@ extern ter_num_t template_terrain[64][64];
|
||||
extern cScenario scenario;
|
||||
extern cOutdoors* current_terrain;
|
||||
extern location cur_out;
|
||||
extern cUndoList undo_list;
|
||||
|
||||
const char *day_str_1[] = {"Unused","Day creature appears","Day creature disappears",
|
||||
"Unused","Unused","Unused","Unused","Unused","Unused"};
|
||||
@@ -1283,9 +1285,37 @@ location pick_out(location default_loc,cScenario& scenario) {
|
||||
return default_loc;
|
||||
}
|
||||
|
||||
bool new_town(short which_town) {
|
||||
void set_current_town(int to) {
|
||||
if(to < 0 || to >= scenario.towns.size()) return;
|
||||
cur_town = to;
|
||||
town = scenario.towns[cur_town];
|
||||
scenario.last_town_edited = cur_town;
|
||||
}
|
||||
|
||||
aNewTown::aNewTown(cTown* t)
|
||||
: cAction("add town")
|
||||
, theTown(t)
|
||||
{}
|
||||
|
||||
void aNewTown::undo() {
|
||||
scenario.towns.resize(scenario.towns.size() - 1);
|
||||
done = false;
|
||||
set_current_town(scenario.towns.size() - 1);
|
||||
}
|
||||
|
||||
void aNewTown::redo() {
|
||||
scenario.towns.push_back(theTown);
|
||||
done = true;
|
||||
set_current_town(scenario.towns.size() - 1);
|
||||
}
|
||||
|
||||
aNewTown::~aNewTown() {
|
||||
if(!done) delete theTown;
|
||||
}
|
||||
|
||||
bool new_town() {
|
||||
cChoiceDlog new_dlg("new-town", {"okay", "cancel"});
|
||||
new_dlg->getControl("num").setTextToNum(which_town);
|
||||
new_dlg->getControl("num").setTextToNum(scenario.towns.size());
|
||||
if(new_dlg.show() == "cancel") return false;
|
||||
|
||||
std::string size = dynamic_cast<cLedGroup&>(new_dlg->getControl("size")).getSelected();
|
||||
@@ -1295,9 +1325,7 @@ bool new_town(short which_town) {
|
||||
else if(size == "med") scenario.towns.push_back(new cTown(scenario, AREA_MEDIUM));
|
||||
else if(size == "sm") scenario.towns.push_back(new cTown(scenario, AREA_SMALL));
|
||||
|
||||
cur_town = which_town;
|
||||
town = scenario.towns[cur_town];
|
||||
scenario.last_town_edited = cur_town;
|
||||
set_current_town(scenario.towns.size() - 1);
|
||||
town->name = new_dlg->getControl("name").getText().substr(0,30);
|
||||
|
||||
for(short i = 0; i < town->max_dim; i++)
|
||||
@@ -1314,6 +1342,7 @@ bool new_town(short which_town) {
|
||||
}
|
||||
}
|
||||
|
||||
undo_list.add(action_ptr(new aNewTown(scenario.towns.back())));
|
||||
change_made = true;
|
||||
|
||||
return true;
|
||||
|
@@ -16,9 +16,10 @@ short edit_talk_node(short which_node);
|
||||
location pick_out(location default_loc,cScenario& scenario);
|
||||
cTown* pick_import_town();
|
||||
cOutdoors* pick_import_out();
|
||||
bool new_town(short which_town);
|
||||
bool new_town();
|
||||
bool resize_outdoors();
|
||||
void edit_placed_item(short which_i);
|
||||
|
||||
void delete_last_town();
|
||||
void edit_town_wand();
|
||||
void set_current_town(int to);
|
||||
|
@@ -28,11 +28,22 @@ void cUndoList::redo(){
|
||||
(*cur)->redo();
|
||||
}
|
||||
|
||||
bool cUndoList::noUndo() {
|
||||
std::string cUndoList::undoName() const {
|
||||
if(noUndo()) return "";
|
||||
return (*cur)->getActionName();
|
||||
}
|
||||
|
||||
std::string cUndoList::redoName() const {
|
||||
if(noRedo()) return "";
|
||||
auto next = std::next(cur);
|
||||
return (*next)->getActionName();
|
||||
}
|
||||
|
||||
bool cUndoList::noUndo() const {
|
||||
return cur == theList.end();
|
||||
}
|
||||
|
||||
bool cUndoList::noRedo() {
|
||||
bool cUndoList::noRedo() const {
|
||||
return cur == theList.begin();
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BOE_UNDO_HPP
|
||||
#define BOE_UNDO_HPP
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -16,7 +19,7 @@
|
||||
class cAction {
|
||||
std::string actname;
|
||||
protected:
|
||||
bool done = false;
|
||||
bool done = true;
|
||||
public:
|
||||
cAction(std::string name) : actname(name) {}
|
||||
virtual void undo() = 0; ///< Undoes this action if it has not already been undone
|
||||
@@ -39,18 +42,14 @@ public:
|
||||
void save(); ///< Sets the last saved action to the current action
|
||||
void revert(); ///< Undoes all actions back to (but excluding) the last saved action
|
||||
void clear(); ///< Clears the list
|
||||
bool noUndo(); ///< Check whether there's an action to undo
|
||||
bool noRedo(); ///< Check whether there's an action to redo
|
||||
bool noUndo() const; ///< Check whether there's an action to undo
|
||||
bool noRedo() const; ///< Check whether there's an action to redo
|
||||
std::string undoName() const; ///< Get the action name of the next action to undo
|
||||
std::string redoName() const; ///< Get the action name of the next action to redo
|
||||
void add(action_ptr what);
|
||||
static size_t maxUndoSize;
|
||||
};
|
||||
|
||||
// As a special convention, I will prefix action classes with 'a' instead of 'c'
|
||||
/*
|
||||
class aEditMonster : public cAction {
|
||||
cMonster oldMonst, newMonst;
|
||||
};
|
||||
|
||||
class aEditItem : public cAction {
|
||||
cItem oldItem, newItem;
|
||||
};*/
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user