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) {
r.handle_greedy(v);
// validate
String errors;
// For efficiency, this needs to be parallel to LocaleCategory's values.
String sublocales[10] = {
_("menu"),
_("help"),
_("tool"),
_("tooltip"),
_("label"),
_("button"),
_("title"),
_("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 += translations[LOCALE_CAT_MENU ].validate(_("menu"), v.sublocales[_("menu") ]);
errors += translations[LOCALE_CAT_HELP ].validate(_("help"), v.sublocales[_("help") ]);
errors += translations[LOCALE_CAT_TOOL ].validate(_("tool"), v.sublocales[_("tool") ]);
errors += translations[LOCALE_CAT_TOOLTIP].validate(_("tooltip"), v.sublocales[_("tooltip")]);
errors += translations[LOCALE_CAT_LABEL ].validate(_("label"), v.sublocales[_("label") ]);
errors += translations[LOCALE_CAT_BUTTON ].validate(_("button"), v.sublocales[_("button") ]);
errors += translations[LOCALE_CAT_TITLE ].validate(_("title"), v.sublocales[_("title") ]);
errors += translations[LOCALE_CAT_ACTION ].validate(_("action"), v.sublocales[_("action") ]);
errors += translations[LOCALE_CAT_ERROR ].validate(_("error"), v.sublocales[_("error") ]);
errors += translations[LOCALE_CAT_TYPE ].validate(_("type"), v.sublocales[_("type") ]);
// errors?
if (!errors.empty()) {
if (ver != app_version) {
......@@ -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;
// 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);
if (it == translations.end()) {
if (!kc.second.optional) {
......@@ -258,8 +251,8 @@ String SubLocale::validate(const String& name, const SubLocaleValidator& v) cons
}
// 2. keys in this but not in v
FOR_EACH_CONST(kv, translations) {
map<String,KeyValidator>::const_iterator it = v.keys.find(kv.first);
if (it == v.keys.end() && !kv.second.empty()) {
map<String,KeyValidator>::const_iterator it = v->keys.find(kv.first);
if (it == v->keys.end() && !kv.second.empty()) {
// allow extra keys with empty values as a kind of documentation
// for example in the help stirngs:
// help:
......
......@@ -16,7 +16,7 @@
DECLARE_POINTER_TYPE(Locale);
DECLARE_POINTER_TYPE(SubLocale);
class SubLocaleValidator;
DECLARE_POINTER_TYPE(SubLocaleValidator);
// ----------------------------------------------------------------------------- : Locale class
......@@ -31,7 +31,7 @@ class SubLocale : public IntrusivePtrBase<SubLocale> {
String tr(const String& key, const String& def);
/// 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();
};
......
......@@ -16,12 +16,13 @@ WordListWord::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
REFLECT(name);
REFLECT(line_below);
REFLECT(is_prefix);
REFLECT(words);
REFLECT(script);
} else {
REFLECT_NAMELESS(name);
}
......
......@@ -11,6 +11,7 @@
#include <util/prec.hpp>
#include <util/reflect.hpp>
#include <script/scriptable.hpp>
DECLARE_POINTER_TYPE(WordListWord);
DECLARE_POINTER_TYPE(WordList);
......@@ -27,6 +28,7 @@ class WordListWord : public IntrusivePtrBase<WordListWord> {
bool line_below; ///< Line below in the list?
bool is_prefix; ///< Is this a prefix before other words?
vector<WordListWordP> words; ///< Sublist
OptionalScript script; ///< Generate words using a script
inline bool isGroup() const { return !words.empty(); }
......
......@@ -25,6 +25,7 @@ DataEditor::DataEditor(Window* parent, int id, long style)
: CardViewer(parent, id, style)
, current_viewer(nullptr)
, current_editor(nullptr)
, hovered_viewer(nullptr)
{
// Create a caret
SetCaret(new wxCaret(this,1,1));
......@@ -134,6 +135,7 @@ void DataEditor::onInit() {
createTabIndex();
current_viewer = nullptr;
current_editor = nullptr;
hovered_viewer = nullptr;
}
// ----------------------------------------------------------------------------- : Search / replace
......@@ -213,24 +215,44 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
current_editor->onMotion(pos, ev);
}
if (!HasCapture()) {
// change cursor and set status text
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
// find editor under mouse
ValueViewer* new_hovered_viewer = nullptr;
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
if (v->containsPoint(pos) && v->getField()->editable) {
wxCursor c = e->cursor(pos);
if (c.Ok()) SetCursor(c);
else SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(v->getField()->description);
return;
new_hovered_viewer = v.get();
break;
}
}
// no field under cursor
SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(wxEmptyString);
if (hovered_viewer && hovered_viewer != new_hovered_viewer) {
ValueEditor* e = hovered_viewer->getEditor();
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) {
// 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) );
if (frame) frame->SetStatusText(wxEmptyString);
}
......
......@@ -86,6 +86,7 @@ class DataEditor : public CardViewer {
// --------------------------------------------------- : Data
ValueViewer* current_viewer; ///< The currently selected 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
private:
......
......@@ -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 line below
if (lineBelow(item)) {
if (lineBelow(item) && item != itemCount()) {
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);
}
......
......@@ -47,6 +47,7 @@ class ValueEditor {
virtual bool onLeftDClick (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 void onMouseLeave (const RealPoint& pos, wxMouseEvent& ev) {}
virtual bool onMouseWheel (const RealPoint& pos, wxMouseEvent& ev) { return false; }
/// Key events
......
This diff is collapsed.
......@@ -43,7 +43,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
virtual bool onLeftDClick(const RealPoint& pos, wxMouseEvent&);
virtual bool onRightDown (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 wxMenu* getMenu(int type) const;
......@@ -89,6 +90,8 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
virtual wxCursor cursor(const RealPoint& pos) const;
virtual void determineSize(bool force_fit = false);
virtual bool containsPoint(const RealPoint& p) const;
virtual RealRect boundingBox() const;
virtual void onShow(bool);
virtual void draw(RotatedDC&);
......@@ -162,14 +165,19 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
friend class DropDownWordList;
DropDownWordListP drop_down;
bool dropDownShown();
bool dropDownShown() const;
mutable WordListPos* hovered_words;
/// Find all word lists in the current value
void findWordLists();
/// 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
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
WordListPosP findWordList(size_t index) const;
/// Show a word list drop down menu, if wl
......
......@@ -54,6 +54,12 @@ SCRIPT_FUNCTION(reverse) {
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
SCRIPT_FUNCTION(substring) {
SCRIPT_PARAM(String, input);
......@@ -156,6 +162,11 @@ SCRIPT_RULE_1(tag_remove, String, tag) {
SCRIPT_RETURN(remove_tag(input, tag));
}
SCRIPT_FUNCTION(remove_tags) {
SCRIPT_PARAM(String, input);
SCRIPT_RETURN(untag_no_escape(input));
}
// ----------------------------------------------------------------------------- : Collection stuff
/// compare script values for equallity
......@@ -593,6 +604,7 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("to lower"), script_to_lower);
ctx.setVariable(_("to title"), script_to_title);
ctx.setVariable(_("reverse"), script_reverse);
ctx.setVariable(_("trim"), script_trim);
ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("format"), script_format);
......@@ -602,6 +614,7 @@ void init_script_basic_functions(Context& ctx) {
// tagged string
ctx.setVariable(_("tag contents"), script_tag_contents);
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 remove rule"), script_tag_remove_rule);
// collection
......
......@@ -327,22 +327,22 @@ enum Precedence
};
/// Parse an expression
/** @param input Read tokens from the input
* @param scrip Add resulting instructions to the script
* @param minPrec Minimum precedence level for operators
/** @param input Read tokens from the input
* @param scrip Add resulting instructions to the script
* @param min_prec Minimum precedence level for operators
* 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.
/** @param input Read tokens from the input
* @param script Add resulting instructions to the script
* @param minPrec Minimum precedence level for operators
* @param closeWith Add this instruction at the end
* @param closeWithData Data for the instruction at the end
/** @param input Read tokens from the input
* @param script Add resulting instructions to the script
* @param min_prec Minimum precedence level for operators
* @param close_with Add this 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
*/
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) {
......@@ -542,7 +542,9 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
}
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
added -= script.getInstructions().size();
// read any operators after an expression
// EBNF: expr = expr | expr oper expr
// without left recursion: expr = expr (oper expr)*
......@@ -565,10 +567,8 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
} else if (minPrec <= PREC_SET && token==_(":=")) {
// We made a mistake, the part before the := should be a variable name,
// not an expression. Remove that instruction.
// TODO: There is a bug here:
// (if x then a else b) := c will use the 'b' as variable name
Instruction instr = script.getInstructions().back();
if (instr.instr != I_GET_VAR) {
Instruction& instr = script.getInstructions().back();
if (added == 1 && instr.instr != I_GET_VAR) {
input.add_error(_("Can only assign to variables"));
}
script.getInstructions().pop_back();
......@@ -610,7 +610,18 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
input.expected(_("name"));
}
} 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, _("]"));
} else if (minPrec <= PREC_FUN && token==_("(")) {
// function call, read arguments
......
......@@ -30,6 +30,7 @@ void ActionStack::add(Action* action, bool allow_merge) {
action->perform(false); // TODO: delete action if perform throws
tellListeners(*action, false);
// clear redo list
if (!redo_actions.empty()) allow_merge = false; // don't merge after undo
FOR_EACH(a, redo_actions) delete a;
redo_actions.clear();
// try to merge?
......
......@@ -286,3 +286,15 @@ void RotatedDC::SetClippingRegion(const RealRect& rect) {
void RotatedDC::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 {
// Fill the dc with the color of the current brush
void Fill();
// --------------------------------------------------- : Forwarded properties
// --------------------------------------------------- : Properties
/// Sets the pen for the dc, does not scale the line width
void SetPen(const wxPen&);
......@@ -193,6 +193,11 @@ class RotatedDC : public Rotation {
void SetClippingRegion(const RealRect& rect);
void DestroyClippingRegion();
// --------------------------------------------------- : Other
/// Get the current contents of the given ractangle, for later restoring
Bitmap GetBackground(const RealRect& r);
inline wxDC& getDC() { return dc; }
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