Commit 71fd64f9 authored by twanvl's avatar twanvl

Added support for scripts to determine word lists;

Added 'trim' and 'remove_tags' script functions;
Simplified safety improvements of locale checker;
Added 'is_targeted' function to magic game to replace the contains(..) calls
parent 74641b82
This diff is collapsed.
...@@ -210,26 +210,16 @@ void Locale::validate(Version ver) { ...@@ -210,26 +210,16 @@ void Locale::validate(Version ver) {
r.handle_greedy(v); r.handle_greedy(v);
// validate // validate
String errors; String errors;
// For efficiency, this needs to be parallel to LocaleCategory's values. errors += translations[LOCALE_CAT_MENU ].validate(_("menu"), v.sublocales[_("menu") ]);
String sublocales[10] = { errors += translations[LOCALE_CAT_HELP ].validate(_("help"), v.sublocales[_("help") ]);
_("menu"), errors += translations[LOCALE_CAT_TOOL ].validate(_("tool"), v.sublocales[_("tool") ]);
_("help"), errors += translations[LOCALE_CAT_TOOLTIP].validate(_("tooltip"), v.sublocales[_("tooltip")]);
_("tool"), errors += translations[LOCALE_CAT_LABEL ].validate(_("label"), v.sublocales[_("label") ]);
_("tooltip"), errors += translations[LOCALE_CAT_BUTTON ].validate(_("button"), v.sublocales[_("button") ]);
_("label"), errors += translations[LOCALE_CAT_TITLE ].validate(_("title"), v.sublocales[_("title") ]);
_("button"), errors += translations[LOCALE_CAT_ACTION ].validate(_("action"), v.sublocales[_("action") ]);
_("title"), errors += translations[LOCALE_CAT_ERROR ].validate(_("error"), v.sublocales[_("error") ]);
_("type"), errors += translations[LOCALE_CAT_TYPE ].validate(_("type"), v.sublocales[_("type") ]);
_("action"),
_("error")
};
for (String * current = sublocales; current < sublocales + 10; ++current) {
if (v.sublocales[*current])
errors += translations[current - sublocales].validate(*current, *v.sublocales[*current]);
else
errors += _("\nError validating local file: expected keys file missing \"") + *current + _("\" section.");
}
// errors? // errors?
if (!errors.empty()) { if (!errors.empty()) {
if (ver != app_version) { if (ver != app_version) {
...@@ -242,10 +232,13 @@ void Locale::validate(Version ver) { ...@@ -242,10 +232,13 @@ void Locale::validate(Version ver) {
} }
} }
String SubLocale::validate(const String& name, const SubLocaleValidator& v) const { String SubLocale::validate(const String& name, const SubLocaleValidatorP& v) const {
if (!v) {
return _("\nInternal error validating local file: expected keys file missing for \"") + name + _("\" section.");
}
String errors; String errors;
// 1. keys in v but not in this, check arg count // 1. keys in v but not in this, check arg count
FOR_EACH_CONST(kc, v.keys) { FOR_EACH_CONST(kc, v->keys) {
map<String,String>::const_iterator it = translations.find(kc.first); map<String,String>::const_iterator it = translations.find(kc.first);
if (it == translations.end()) { if (it == translations.end()) {
if (!kc.second.optional) { if (!kc.second.optional) {
...@@ -258,8 +251,8 @@ String SubLocale::validate(const String& name, const SubLocaleValidator& v) cons ...@@ -258,8 +251,8 @@ String SubLocale::validate(const String& name, const SubLocaleValidator& v) cons
} }
// 2. keys in this but not in v // 2. keys in this but not in v
FOR_EACH_CONST(kv, translations) { FOR_EACH_CONST(kv, translations) {
map<String,KeyValidator>::const_iterator it = v.keys.find(kv.first); map<String,KeyValidator>::const_iterator it = v->keys.find(kv.first);
if (it == v.keys.end() && !kv.second.empty()) { if (it == v->keys.end() && !kv.second.empty()) {
// allow extra keys with empty values as a kind of documentation // allow extra keys with empty values as a kind of documentation
// for example in the help stirngs: // for example in the help stirngs:
// help: // help:
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
DECLARE_POINTER_TYPE(Locale); DECLARE_POINTER_TYPE(Locale);
DECLARE_POINTER_TYPE(SubLocale); DECLARE_POINTER_TYPE(SubLocale);
class SubLocaleValidator; DECLARE_POINTER_TYPE(SubLocaleValidator);
// ----------------------------------------------------------------------------- : Locale class // ----------------------------------------------------------------------------- : Locale class
...@@ -31,7 +31,7 @@ class SubLocale : public IntrusivePtrBase<SubLocale> { ...@@ -31,7 +31,7 @@ class SubLocale : public IntrusivePtrBase<SubLocale> {
String tr(const String& key, const String& def); String tr(const String& key, const String& def);
/// Is this a valid sublocale? Returns errors /// Is this a valid sublocale? Returns errors
String validate(const String& name, const SubLocaleValidator&) const; String validate(const String& name, const SubLocaleValidatorP&) const;
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
......
...@@ -16,12 +16,13 @@ WordListWord::WordListWord() ...@@ -16,12 +16,13 @@ WordListWord::WordListWord()
{} {}
IMPLEMENT_REFLECTION_NO_SCRIPT(WordListWord) { IMPLEMENT_REFLECTION_NO_SCRIPT(WordListWord) {
if (line_below || is_prefix || isGroup() || (tag.reading() && tag.isComplex())) { if (line_below || is_prefix || isGroup() || script || (tag.reading() && tag.isComplex())) {
// complex value // complex value
REFLECT(name); REFLECT(name);
REFLECT(line_below); REFLECT(line_below);
REFLECT(is_prefix); REFLECT(is_prefix);
REFLECT(words); REFLECT(words);
REFLECT(script);
} else { } else {
REFLECT_NAMELESS(name); REFLECT_NAMELESS(name);
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <util/reflect.hpp> #include <util/reflect.hpp>
#include <script/scriptable.hpp>
DECLARE_POINTER_TYPE(WordListWord); DECLARE_POINTER_TYPE(WordListWord);
DECLARE_POINTER_TYPE(WordList); DECLARE_POINTER_TYPE(WordList);
...@@ -27,6 +28,7 @@ class WordListWord : public IntrusivePtrBase<WordListWord> { ...@@ -27,6 +28,7 @@ class WordListWord : public IntrusivePtrBase<WordListWord> {
bool line_below; ///< Line below in the list? bool line_below; ///< Line below in the list?
bool is_prefix; ///< Is this a prefix before other words? bool is_prefix; ///< Is this a prefix before other words?
vector<WordListWordP> words; ///< Sublist vector<WordListWordP> words; ///< Sublist
OptionalScript script; ///< Generate words using a script
inline bool isGroup() const { return !words.empty(); } inline bool isGroup() const { return !words.empty(); }
......
...@@ -25,6 +25,7 @@ DataEditor::DataEditor(Window* parent, int id, long style) ...@@ -25,6 +25,7 @@ DataEditor::DataEditor(Window* parent, int id, long style)
: CardViewer(parent, id, style) : CardViewer(parent, id, style)
, current_viewer(nullptr) , current_viewer(nullptr)
, current_editor(nullptr) , current_editor(nullptr)
, hovered_viewer(nullptr)
{ {
// Create a caret // Create a caret
SetCaret(new wxCaret(this,1,1)); SetCaret(new wxCaret(this,1,1));
...@@ -134,6 +135,7 @@ void DataEditor::onInit() { ...@@ -134,6 +135,7 @@ void DataEditor::onInit() {
createTabIndex(); createTabIndex();
current_viewer = nullptr; current_viewer = nullptr;
current_editor = nullptr; current_editor = nullptr;
hovered_viewer = nullptr;
} }
// ----------------------------------------------------------------------------- : Search / replace // ----------------------------------------------------------------------------- : Search / replace
...@@ -213,24 +215,44 @@ void DataEditor::onMotion(wxMouseEvent& ev) { ...@@ -213,24 +215,44 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
current_editor->onMotion(pos, ev); current_editor->onMotion(pos, ev);
} }
if (!HasCapture()) { if (!HasCapture()) {
// change cursor and set status text // find editor under mouse
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) ); ValueViewer* new_hovered_viewer = nullptr;
FOR_EACH_EDITOR_REVERSE { // find high z index fields first FOR_EACH_EDITOR_REVERSE { // find high z index fields first
if (v->containsPoint(pos) && v->getField()->editable) { if (v->containsPoint(pos) && v->getField()->editable) {
wxCursor c = e->cursor(pos); new_hovered_viewer = v.get();
if (c.Ok()) SetCursor(c); break;
else SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(v->getField()->description);
return;
} }
} }
// no field under cursor if (hovered_viewer && hovered_viewer != new_hovered_viewer) {
SetCursor(wxCURSOR_ARROW); ValueEditor* e = hovered_viewer->getEditor();
if (frame) frame->SetStatusText(wxEmptyString); if (e) e->onMouseLeave(pos, ev);
}
hovered_viewer = new_hovered_viewer;
// change cursor and set status text
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
if (hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor();
wxCursor c;
if (e) c = e->cursor(pos);
if (c.Ok()) SetCursor(c);
else SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(hovered_viewer->getField()->description);
} else {
// no field under cursor
SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(wxEmptyString);
}
} }
} }
void DataEditor::onMouseLeave(wxMouseEvent& ev) { void DataEditor::onMouseLeave(wxMouseEvent& ev) {
// on mouse leave for editor
if (hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor();
if (e) e->onMouseLeave(mousePoint(ev), ev);
hovered_viewer = nullptr;
}
// clear status text
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) ); wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
if (frame) frame->SetStatusText(wxEmptyString); if (frame) frame->SetStatusText(wxEmptyString);
} }
......
...@@ -86,6 +86,7 @@ class DataEditor : public CardViewer { ...@@ -86,6 +86,7 @@ class DataEditor : public CardViewer {
// --------------------------------------------------- : Data // --------------------------------------------------- : Data
ValueViewer* current_viewer; ///< The currently selected viewer ValueViewer* current_viewer; ///< The currently selected viewer
ValueEditor* current_editor; ///< The currently selected editor, corresponding to the viewer ValueEditor* current_editor; ///< The currently selected editor, corresponding to the viewer
ValueViewer* hovered_viewer; ///< The editor under the mouse cursor
vector<ValueViewer*> by_tab_index; ///< The editable viewers, sorted by tab index vector<ValueViewer*> by_tab_index; ///< The editable viewers, sorted by tab index
private: private:
......
...@@ -291,7 +291,7 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) { ...@@ -291,7 +291,7 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
draw_menu_arrow(this, dc, RealRect(marginW, y, item_size.width, item_size.height), item == selected_item); draw_menu_arrow(this, dc, RealRect(marginW, y, item_size.width, item_size.height), item == selected_item);
} }
// draw line below // draw line below
if (lineBelow(item)) { if (lineBelow(item) && item != itemCount()) {
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
dc.DrawLine(marginW, y + (int)item_size.height, marginW + (int)item_size.width, y + (int)item_size.height); dc.DrawLine(marginW, y + (int)item_size.height, marginW + (int)item_size.width, y + (int)item_size.height);
} }
......
...@@ -47,6 +47,7 @@ class ValueEditor { ...@@ -47,6 +47,7 @@ class ValueEditor {
virtual bool onLeftDClick (const RealPoint& pos, wxMouseEvent& ev) { return false; } virtual bool onLeftDClick (const RealPoint& pos, wxMouseEvent& ev) { return false; }
virtual bool onRightDown (const RealPoint& pos, wxMouseEvent& ev) { return false; } virtual bool onRightDown (const RealPoint& pos, wxMouseEvent& ev) { return false; }
virtual bool onMotion (const RealPoint& pos, wxMouseEvent& ev) { return false; } virtual bool onMotion (const RealPoint& pos, wxMouseEvent& ev) { return false; }
virtual void onMouseLeave (const RealPoint& pos, wxMouseEvent& ev) {}
virtual bool onMouseWheel (const RealPoint& pos, wxMouseEvent& ev) { return false; } virtual bool onMouseWheel (const RealPoint& pos, wxMouseEvent& ev) { return false; }
/// Key events /// Key events
......
This diff is collapsed.
...@@ -43,7 +43,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -43,7 +43,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
virtual bool onLeftDClick(const RealPoint& pos, wxMouseEvent&); virtual bool onLeftDClick(const RealPoint& pos, wxMouseEvent&);
virtual bool onRightDown (const RealPoint& pos, wxMouseEvent&); virtual bool onRightDown (const RealPoint& pos, wxMouseEvent&);
virtual bool onMotion (const RealPoint& pos, wxMouseEvent&); virtual bool onMotion (const RealPoint& pos, wxMouseEvent&);
virtual bool onMouseWheel(const RealPoint& pos, wxMouseEvent& ev); virtual void onMouseLeave(const RealPoint& pos, wxMouseEvent&);
virtual bool onMouseWheel(const RealPoint& pos, wxMouseEvent&);
virtual bool onContextMenu(IconMenu& m, wxContextMenuEvent&); virtual bool onContextMenu(IconMenu& m, wxContextMenuEvent&);
virtual wxMenu* getMenu(int type) const; virtual wxMenu* getMenu(int type) const;
...@@ -89,6 +90,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -89,6 +90,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
virtual wxCursor cursor(const RealPoint& pos) const; virtual wxCursor cursor(const RealPoint& pos) const;
virtual void determineSize(bool force_fit = false); virtual void determineSize(bool force_fit = false);
virtual bool containsPoint(const RealPoint& p) const;
virtual RealRect boundingBox() const;
virtual void onShow(bool); virtual void onShow(bool);
virtual void draw(RotatedDC&); virtual void draw(RotatedDC&);
...@@ -162,14 +165,19 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -162,14 +165,19 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
friend class DropDownWordList; friend class DropDownWordList;
DropDownWordListP drop_down; DropDownWordListP drop_down;
bool dropDownShown(); bool dropDownShown() const;
mutable WordListPos* hovered_words;
/// Find all word lists in the current value /// Find all word lists in the current value
void findWordLists(); void findWordLists();
/// Draw word list indicators /// Draw word list indicators
void drawWordListIndicators(RotatedDC& dc); void drawWordListIndicators(RotatedDC& dc, bool redrawing = false);
/// Re-draw word list indicators
void redrawWordListIndicators();
/// Find a WordListPos under the mouse cursor (if any), pos is in internal coordinates /// Find a WordListPos under the mouse cursor (if any), pos is in internal coordinates
WordListPosP findWordList(const RealPoint& pos) const; WordListPosP findWordList(const RealPoint& pos) const;
/// Find a WordListPos rectangle under the mouse cursor (if any), pos is in internal coordinates
WordListPosP findWordListBody(const RealPoint& pos) const;
/// Find a WordListPos for a index position /// Find a WordListPos for a index position
WordListPosP findWordList(size_t index) const; WordListPosP findWordList(size_t index) const;
/// Show a word list drop down menu, if wl /// Show a word list drop down menu, if wl
......
...@@ -54,6 +54,12 @@ SCRIPT_FUNCTION(reverse) { ...@@ -54,6 +54,12 @@ SCRIPT_FUNCTION(reverse) {
SCRIPT_RETURN(input); SCRIPT_RETURN(input);
} }
// remove leading and trailing whitespace from a string
SCRIPT_FUNCTION(trim) {
SCRIPT_PARAM(String, input);
SCRIPT_RETURN(trim(input));
}
// extract a substring // extract a substring
SCRIPT_FUNCTION(substring) { SCRIPT_FUNCTION(substring) {
SCRIPT_PARAM(String, input); SCRIPT_PARAM(String, input);
...@@ -156,6 +162,11 @@ SCRIPT_RULE_1(tag_remove, String, tag) { ...@@ -156,6 +162,11 @@ SCRIPT_RULE_1(tag_remove, String, tag) {
SCRIPT_RETURN(remove_tag(input, tag)); SCRIPT_RETURN(remove_tag(input, tag));
} }
SCRIPT_FUNCTION(remove_tags) {
SCRIPT_PARAM(String, input);
SCRIPT_RETURN(untag_no_escape(input));
}
// ----------------------------------------------------------------------------- : Collection stuff // ----------------------------------------------------------------------------- : Collection stuff
/// compare script values for equallity /// compare script values for equallity
...@@ -593,6 +604,7 @@ void init_script_basic_functions(Context& ctx) { ...@@ -593,6 +604,7 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("to lower"), script_to_lower); ctx.setVariable(_("to lower"), script_to_lower);
ctx.setVariable(_("to title"), script_to_title); ctx.setVariable(_("to title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse); ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("trim"), script_trim);
ctx.setVariable(_("substring"), script_substring); ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains); ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format); ctx.setVariable(_("format"), script_format);
...@@ -602,6 +614,7 @@ void init_script_basic_functions(Context& ctx) { ...@@ -602,6 +614,7 @@ void init_script_basic_functions(Context& ctx) {
// tagged string // tagged string
ctx.setVariable(_("tag contents"), script_tag_contents); ctx.setVariable(_("tag contents"), script_tag_contents);
ctx.setVariable(_("remove tag"), script_tag_remove); ctx.setVariable(_("remove tag"), script_tag_remove);
ctx.setVariable(_("remove tags"), script_remove_tags);
ctx.setVariable(_("tag contents rule"), script_tag_contents_rule); ctx.setVariable(_("tag contents rule"), script_tag_contents_rule);
ctx.setVariable(_("tag remove rule"), script_tag_remove_rule); ctx.setVariable(_("tag remove rule"), script_tag_remove_rule);
// collection // collection
......
...@@ -327,22 +327,22 @@ enum Precedence ...@@ -327,22 +327,22 @@ enum Precedence
}; };
/// Parse an expression /// Parse an expression
/** @param input Read tokens from the input /** @param input Read tokens from the input
* @param scrip Add resulting instructions to the script * @param scrip Add resulting instructions to the script
* @param minPrec Minimum precedence level for operators * @param min_prec Minimum precedence level for operators
* NOTE: The net stack effect of an expression should be +1 * NOTE: The net stack effect of an expression should be +1
*/ */
void parseExpr(TokenIterator& input, Script& script, Precedence minPrec); void parseExpr(TokenIterator& input, Script& script, Precedence min_prec);
/// Parse an expression, possibly with operators applied. Optionally adds an instruction at the end. /// Parse an expression, possibly with operators applied. Optionally adds an instruction at the end.
/** @param input Read tokens from the input /** @param input Read tokens from the input
* @param script Add resulting instructions to the script * @param script Add resulting instructions to the script
* @param minPrec Minimum precedence level for operators * @param min_prec Minimum precedence level for operators
* @param closeWith Add this instruction at the end * @param close_with Add this instruction at the end
* @param closeWithData Data for the instruction at the end * @param close_with_data Data for the instruction at the end
* NOTE: The net stack effect of an expression should be +1 * NOTE: The net stack effect of an expression should be +1
*/ */
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith = I_NOP, int closeWithData = 0); void parseOper(TokenIterator& input, Script& script, Precedence min_prec, InstructionType close_with = I_NOP, int close_with_data = 0);
ScriptP parse(const String& s, bool string_mode, vector<ScriptParseError>& errors_out) { ScriptP parse(const String& s, bool string_mode, vector<ScriptParseError>& errors_out) {
...@@ -542,7 +542,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) { ...@@ -542,7 +542,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
} }
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) { void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith, int closeWithData) {
size_t added = script.getInstructions().size(); // number of instructions added
parseExpr(input, script, minPrec); // first argument parseExpr(input, script, minPrec); // first argument
added -= script.getInstructions().size();
// read any operators after an expression // read any operators after an expression
// EBNF: expr = expr | expr oper expr // EBNF: expr = expr | expr oper expr
// without left recursion: expr = expr (oper expr)* // without left recursion: expr = expr (oper expr)*
...@@ -565,10 +567,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -565,10 +567,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
} else if (minPrec <= PREC_SET && token==_(":=")) { } else if (minPrec <= PREC_SET && token==_(":=")) {
// We made a mistake, the part before the := should be a variable name, // We made a mistake, the part before the := should be a variable name,
// not an expression. Remove that instruction. // not an expression. Remove that instruction.
// TODO: There is a bug here: Instruction& instr = script.getInstructions().back();
// (if x then a else b) := c will use the 'b' as variable name if (added == 1 && instr.instr != I_GET_VAR) {
Instruction instr = script.getInstructions().back();
if (instr.instr != I_GET_VAR) {
input.add_error(_("Can only assign to variables")); input.add_error(_("Can only assign to variables"));
} }
script.getInstructions().pop_back(); script.getInstructions().pop_back();
...@@ -610,7 +610,18 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -610,7 +610,18 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
input.expected(_("name")); input.expected(_("name"));
} }
} else if (minPrec <= PREC_FUN && token==_("[")) { // get member by expr } else if (minPrec <= PREC_FUN && token==_("[")) { // get member by expr
parseOper(input, script, PREC_ALL, I_BINARY, I_MEMBER); size_t before = script.getInstructions().size();
parseOper(input, script, PREC_ALL);
if (script.getInstructions().size() == before + 1 && script.getInstructions().back().instr == I_PUSH_CONST) {
// optimize:
// PUSH_CONST x
// MEMBER
// becomes
// MEMBER_CONST x
script.getInstructions().back().instr = I_MEMBER_C;
} else {
script.addInstruction(I_BINARY, I_MEMBER);
}
expectToken(input, _("]")); expectToken(input, _("]"));
} else if (minPrec <= PREC_FUN && token==_("(")) { } else if (minPrec <= PREC_FUN && token==_("(")) {
// function call, read arguments // function call, read arguments
......
...@@ -30,6 +30,7 @@ void ActionStack::add(Action* action, bool allow_merge) { ...@@ -30,6 +30,7 @@ void ActionStack::add(Action* action, bool allow_merge) {
action->perform(false); // TODO: delete action if perform throws action->perform(false); // TODO: delete action if perform throws
tellListeners(*action, false); tellListeners(*action, false);
// clear redo list // clear redo list
if (!redo_actions.empty()) allow_merge = false; // don't merge after undo
FOR_EACH(a, redo_actions) delete a; FOR_EACH(a, redo_actions) delete a;
redo_actions.clear(); redo_actions.clear();
// try to merge? // try to merge?
......
...@@ -286,3 +286,15 @@ void RotatedDC::SetClippingRegion(const RealRect& rect) { ...@@ -286,3 +286,15 @@ void RotatedDC::SetClippingRegion(const RealRect& rect) {
void RotatedDC::DestroyClippingRegion() { void RotatedDC::DestroyClippingRegion() {
dc.DestroyClippingRegion(); dc.DestroyClippingRegion();
} }
// ----------------------------------------------------------------------------- : Other
Bitmap RotatedDC::GetBackground(const RealRect& r) {
wxRect wr = trNoNeg(r);
Bitmap background(wr.width, wr.height);
wxMemoryDC mdc;
mdc.SelectObject(background);
mdc.Blit(0, 0, wr.width, wr.height, &dc, wr.x, wr.y);
mdc.SelectObject(wxNullBitmap);
return background;
}
...@@ -172,7 +172,7 @@ class RotatedDC : public Rotation { ...@@ -172,7 +172,7 @@ class RotatedDC : public Rotation {
// Fill the dc with the color of the current brush // Fill the dc with the color of the current brush
void Fill(); void Fill();
// --------------------------------------------------- : Forwarded properties // --------------------------------------------------- : Properties
/// Sets the pen for the dc, does not scale the line width /// Sets the pen for the dc, does not scale the line width
void SetPen(const wxPen&); void SetPen(const wxPen&);
...@@ -193,6 +193,11 @@ class RotatedDC : public Rotation { ...@@ -193,6 +193,11 @@ class RotatedDC : public Rotation {
void SetClippingRegion(const RealRect& rect); void SetClippingRegion(const RealRect& rect);
void DestroyClippingRegion(); void DestroyClippingRegion();
// --------------------------------------------------- : Other
/// Get the current contents of the given ractangle, for later restoring
Bitmap GetBackground(const RealRect& r);
inline wxDC& getDC() { return dc; } inline wxDC& getDC() { return dc; }
private: private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment