mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
more script functions and text editor improvements
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@100 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+5
-4
@@ -12,7 +12,8 @@
|
||||
#include <data/card.hpp>
|
||||
#include <data/keyword.hpp>
|
||||
#include <data/field.hpp>
|
||||
#include <data/field/text.hpp> // for 0.2.7 fix
|
||||
#include <data/field/text.hpp> // for 0.2.7 fix
|
||||
#include <util/tagged_string.hpp> // for 0.2.7 fix
|
||||
#include <script/value.hpp>
|
||||
#include <script/script_manager.hpp>
|
||||
#include <wx/sstream.h>
|
||||
@@ -62,9 +63,9 @@ String Set::typeName() const { return _("set"); }
|
||||
void fix_value_207(const ValueP& value) {
|
||||
if (TextValue* v = dynamic_cast<TextValue*>(value.get())) {
|
||||
// text value -> fix it
|
||||
// v->value.assign( // don't change defaultness
|
||||
// fix_old_tags(v->value); // remove tags
|
||||
// );
|
||||
v->value.assignDontChangeDefault( // don't change defaultness
|
||||
fix_old_tags(v->value()) // remove tags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -213,6 +213,11 @@ void TextValueEditor::onMenu(wxCommandEvent& ev) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Other overrides
|
||||
|
||||
void TextValueEditor::draw(RotatedDC& dc) {
|
||||
TextValueViewer::draw(dc);
|
||||
v.drawSelection(dc, style(), selection_start, selection_end);
|
||||
}
|
||||
|
||||
wxCursor rotated_ibeam;
|
||||
|
||||
wxCursor TextValueEditor::cursor() const {
|
||||
@@ -237,6 +242,8 @@ void TextValueEditor::onAction(const ValueAction& action, bool undone) {
|
||||
TYPE_CASE(action, TextValueAction) {
|
||||
selection_start = action.selection_start;
|
||||
selection_end = action.selection_end;
|
||||
fixSelection();
|
||||
if (isCurrent()) showCaret();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +478,7 @@ void TextValueEditor::moveSelection(size_t new_end, bool also_move_start, Moveme
|
||||
|
||||
void TextValueEditor::moveSelectionNoRedraw(size_t new_end, bool also_move_start, Movement dir) {
|
||||
selection_end = new_end;
|
||||
if (also_move_start) selection_start = selection_end;
|
||||
if (also_move_start) selection_start = selection_end;
|
||||
fixSelection(dir);
|
||||
}
|
||||
|
||||
@@ -479,7 +486,7 @@ void TextValueEditor::fixSelection(Movement dir) {
|
||||
const String& val = value().value();
|
||||
// value may have become smaller because of undo/redo
|
||||
// make sure the selection stays inside the text
|
||||
size_t size;
|
||||
size_t size = val.size();
|
||||
selection_end = min(size, selection_end);
|
||||
selection_start = min(size, selection_start);
|
||||
// start and end must be on the same side of separators
|
||||
@@ -513,6 +520,8 @@ void TextValueEditor::fixSelection(Movement dir) {
|
||||
atompos = val.find(_("<atom"), atompos + 1);
|
||||
}
|
||||
// start and end must not be inside or between tags
|
||||
selection_start = v.firstVisibleChar(selection_start, dir == MOVE_LEFT ? -1 : +1);
|
||||
selection_end = v.firstVisibleChar(selection_end, dir == MOVE_LEFT ? -1 : +1);
|
||||
// TODO
|
||||
}
|
||||
|
||||
@@ -521,7 +530,7 @@ size_t TextValueEditor::prevCharBoundry(size_t pos) const {
|
||||
return max(0, (int)pos - 1);
|
||||
}
|
||||
size_t TextValueEditor::nextCharBoundry(size_t pos) const {
|
||||
return max(value().value().size(), pos + 1);
|
||||
return min(value().value().size(), pos + 1);
|
||||
}
|
||||
size_t TextValueEditor::prevWordBoundry(size_t pos) const {
|
||||
const String& val = value().value();
|
||||
|
||||
@@ -79,6 +79,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
|
||||
virtual wxCursor cursor() const;
|
||||
virtual void determineSize();
|
||||
virtual void onShow(bool);
|
||||
virtual void draw(RotatedDC&);
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
private:
|
||||
|
||||
@@ -82,7 +82,7 @@ void TextViewer::draw(RotatedDC& dc, const String& text, const TextStyle& style,
|
||||
void TextViewer::drawSelection(RotatedDC& dc, const TextStyle& style, size_t sel_start, size_t sel_end) {
|
||||
Rotater r(dc, style.getRotation());
|
||||
if (sel_start == sel_end) return;
|
||||
if (sel_end < sel_start) swap(sel_start, sel_end);
|
||||
if (sel_end < sel_start) swap(sel_start, sel_end);
|
||||
dc.SetBrush(*wxBLACK_BRUSH);
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetLogicalFunction(wxINVERT);
|
||||
@@ -96,7 +96,7 @@ void TextViewer::Line::drawSelection(RotatedDC& dc, size_t sel_start, size_t sel
|
||||
if (!visible(dc)) return;
|
||||
if (sel_start < end() && sel_end > start) {
|
||||
double x1 = positions[sel_start];
|
||||
double x2 = positions[max(end(), sel_end)];
|
||||
double x2 = positions[min(end(), sel_end)];
|
||||
dc.DrawRectangle(RealRect(x1, top, x2 - x1, line_height));
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,7 @@ const TextViewer::Line& TextViewer::findLine(size_t index) const {
|
||||
}
|
||||
|
||||
size_t TextViewer::moveLine(size_t index, int delta) const {
|
||||
if (lines.empty()) return index;
|
||||
const Line* line1 = &findLine(index);
|
||||
const Line* line2 = line1 + delta;
|
||||
if (line2 >= &lines.front() && line2 <= &lines.back()) {
|
||||
@@ -151,6 +152,7 @@ size_t TextViewer::indexAt(const RealPoint& pos) const {
|
||||
}
|
||||
|
||||
RealRect TextViewer::charRect(size_t index) const {
|
||||
if (lines.empty()) return RealRect(0,0,0,0);
|
||||
const Line& l = findLine(index);
|
||||
size_t pos = index - l.start;
|
||||
if (pos >= l.positions.size()) {
|
||||
@@ -160,9 +162,32 @@ RealRect TextViewer::charRect(size_t index) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool TextViewer::isVisible(size_t index) const {
|
||||
if (lines.empty()) return false;
|
||||
const Line& l = findLine(index);
|
||||
size_t pos = index - l.start;
|
||||
if (pos >= l.positions.size()) {
|
||||
return false;
|
||||
} else if (pos + 1 == l.positions.size()) {
|
||||
return true; // last char of the line
|
||||
} else {
|
||||
return l.positions[pos + 1] - l.positions[pos] > 0.0001;
|
||||
}
|
||||
}
|
||||
size_t TextViewer::firstVisibleChar(size_t index, int delta) const {
|
||||
if (lines.empty()) return index;
|
||||
const Line& l = findLine(index);
|
||||
int pos = (int)(index - l.start);
|
||||
while (pos + delta > 0 && (size_t)pos + delta + 1 < l.positions.size()) {
|
||||
if (l.positions[pos + 1] - l.positions[pos] > 0.0001) break;
|
||||
pos += delta;
|
||||
}
|
||||
return pos + l.start;
|
||||
}
|
||||
|
||||
double TextViewer::heightOfLastLine() const {
|
||||
if (lines.empty()) return 0;
|
||||
else return lines.back().line_height;
|
||||
return lines.back().line_height;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Elements
|
||||
|
||||
@@ -56,8 +56,6 @@ class TextViewer {
|
||||
|
||||
// --------------------------------------------------- : Positions
|
||||
|
||||
/// Find the character index that is before/after the given index, and which has a nonzero width
|
||||
// size_t moveChar(size_t index, int delta) const;
|
||||
/// Find the character index that is on a line above/below index
|
||||
/** If this would move outisde the text, returns the input index */
|
||||
size_t moveLine(size_t index, int delta) const;
|
||||
@@ -78,6 +76,10 @@ class TextViewer {
|
||||
|
||||
/// Return the rectangle around a single character
|
||||
RealRect charRect(size_t index) const;
|
||||
/// Is the character at the given index visible?
|
||||
bool isVisible(size_t index) const;
|
||||
/// Find the first character index that is at/before/after the given index, and which has a nonzero width
|
||||
size_t firstVisibleChar(size_t index, int delta) const;
|
||||
|
||||
/// Return the height of the last line
|
||||
double heightOfLastLine() const;
|
||||
|
||||
+109
-31
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <script/value.hpp>
|
||||
#include <script/context.hpp>
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <wx/regex.h>
|
||||
|
||||
DECLARE_TYPEOF_COLLECTION(UInt);
|
||||
@@ -56,6 +57,7 @@ class ScriptReplaceRule : public ScriptValue {
|
||||
ret += inside;
|
||||
input = next_input;
|
||||
}
|
||||
ret += input;
|
||||
SCRIPT_RETURN(ret);
|
||||
} else {
|
||||
// dumb replacing
|
||||
@@ -79,7 +81,7 @@ SCRIPT_FUNCTION(replace_rule) {
|
||||
throw ScriptError(_("Error while compiling regular expression: '")+match+_("'"));
|
||||
}
|
||||
// replace
|
||||
ScriptValueP replace = ctx.getVariable(_("replace"));
|
||||
SCRIPT_PARAM(ScriptValueP, replace);
|
||||
if (replace->type() == SCRIPT_FUNCTION) {
|
||||
ret->replacement_function = replace;
|
||||
} else {
|
||||
@@ -234,25 +236,57 @@ String spec_sort(const String& spec, const String& input) {
|
||||
}
|
||||
|
||||
|
||||
class ScriptSortRule : public ScriptValue {
|
||||
public:
|
||||
ScriptSortRule(const String& order) : order(order) {}
|
||||
|
||||
virtual ScriptType type() const { return SCRIPT_FUNCTION; }
|
||||
virtual String typeName() const { return _("sort_rule"); }
|
||||
virtual ScriptValueP eval(Context& ctx) const {
|
||||
SCRIPT_PARAM(String, input);
|
||||
SCRIPT_RETURN(spec_sort(order, input));
|
||||
}
|
||||
|
||||
private:
|
||||
String order;
|
||||
};
|
||||
// Utility for defining a script rule with a single parameter
|
||||
#define SCRIPT_RULE_1(funname, type1, name1) \
|
||||
class ScriptRule_##funname: public ScriptValue { \
|
||||
public: \
|
||||
inline ScriptRule_##funname(const type1& name1) : name1(name1) {} \
|
||||
virtual ScriptType type() const { return SCRIPT_FUNCTION; } \
|
||||
virtual String typeName() const { return _(#funname)_("_rule"); } \
|
||||
virtual ScriptValueP eval(Context& ctx) const; \
|
||||
private: \
|
||||
type1 name1; \
|
||||
}; \
|
||||
SCRIPT_FUNCTION(funname##_rule) { \
|
||||
SCRIPT_PARAM(type1, name1); \
|
||||
return new_intrusive1<ScriptRule_##funname>(name1); \
|
||||
} \
|
||||
SCRIPT_FUNCTION(funname) { \
|
||||
SCRIPT_PARAM(type1, name1); \
|
||||
return ScriptRule_##funname(name1).eval(ctx); \
|
||||
} \
|
||||
ScriptValueP ScriptRule_##funname::eval(Context& ctx) const
|
||||
|
||||
// Utility for defining a script rule with two parameters
|
||||
#define SCRIPT_RULE_2(funname, type1, name1, type2, name2) \
|
||||
class ScriptRule_##funname: public ScriptValue { \
|
||||
public: \
|
||||
inline ScriptRule_##funname(const type1& name1, const type2& name2) \
|
||||
: name1(name1), name2(name2) {} \
|
||||
virtual ScriptType type() const { return SCRIPT_FUNCTION; } \
|
||||
virtual String typeName() const { return _(#funname)_("_rule"); } \
|
||||
virtual ScriptValueP eval(Context& ctx) const; \
|
||||
private: \
|
||||
type1 name1; \
|
||||
type2 name2; \
|
||||
}; \
|
||||
SCRIPT_FUNCTION(funname##_rule) { \
|
||||
SCRIPT_PARAM(type1, name1); \
|
||||
SCRIPT_PARAM(type2, name2); \
|
||||
return new_intrusive2<ScriptRule_##funname>(name1, name2); \
|
||||
} \
|
||||
SCRIPT_FUNCTION(funname) { \
|
||||
SCRIPT_PARAM(type1, name1); \
|
||||
SCRIPT_PARAM(type2, name2); \
|
||||
return ScriptRule_##funname(name1, name2).eval(ctx); \
|
||||
} \
|
||||
ScriptValueP ScriptRule_##funname::eval(Context& ctx) const
|
||||
|
||||
|
||||
// Create a rule for spec_sorting strings
|
||||
SCRIPT_FUNCTION(sort_rule) {
|
||||
SCRIPT_PARAM(String, order);
|
||||
return new_intrusive1<ScriptSortRule>(order);
|
||||
SCRIPT_RULE_1(sort, String, order) {
|
||||
SCRIPT_PARAM(String, input);
|
||||
SCRIPT_RETURN(spec_sort(order, input));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : String stuff
|
||||
@@ -298,16 +332,55 @@ SCRIPT_FUNCTION(contains) {
|
||||
SCRIPT_RETURN(input.find(match) != String::npos);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Tagged stuff
|
||||
|
||||
String replace_tag_contents(String input, const String& tag, const ScriptValueP& contents, Context& ctx) {
|
||||
String ret;
|
||||
size_t pos = input.find(tag);
|
||||
while (pos != String::npos) {
|
||||
// find end of tag and contents
|
||||
size_t end = match_close_tag(input, pos);
|
||||
if (end == String::npos) break; // missing close tag
|
||||
// prepare for call
|
||||
String old_contents = input.substr(pos + tag.size(), end - (pos + tag.size()));
|
||||
ctx.setVariable(_("contents"), toScript(old_contents));
|
||||
// replace
|
||||
ret += input.substr(0, pos); // before tag
|
||||
ret += tag;
|
||||
ret += *contents->eval(ctx);// new contents (call)
|
||||
ret += close_tag(tag);
|
||||
// next
|
||||
input = input.substr(skip_tag(input,end));
|
||||
pos = input.find(tag);
|
||||
}
|
||||
return ret + input;
|
||||
}
|
||||
|
||||
|
||||
// Replace the contents of a specific tag
|
||||
SCRIPT_RULE_2(tag_contents, String, tag, ScriptValueP, contents) {
|
||||
SCRIPT_PARAM(String, input);
|
||||
SCRIPT_RETURN(replace_tag_contents(input, tag, contents, ctx));
|
||||
}
|
||||
|
||||
SCRIPT_RULE_1(tag_remove, String, tag) {
|
||||
SCRIPT_PARAM(String, input);
|
||||
SCRIPT_RETURN(remove_tag(input, tag));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Vector stuff
|
||||
|
||||
/// position of some element in a vector
|
||||
/** 1 based index, 0 if not found */
|
||||
/** 0 based index, -1 if not found */
|
||||
int position_in_vector(const ScriptValueP& of, const ScriptValueP& in, const ScriptValueP& order_by) {
|
||||
return 0;// TODO
|
||||
ScriptType of_t = of->type(), in_t = in->type();
|
||||
if (of_t == SCRIPT_STRING || in_t == SCRIPT_STRING) {
|
||||
return (int)((String)*of).find(*in); // (int)npos == -1
|
||||
}
|
||||
return -1; // TODO
|
||||
}
|
||||
|
||||
// finding positions
|
||||
// finding positions, also of substrings
|
||||
SCRIPT_FUNCTION(position_of) {
|
||||
ScriptValueP of = ctx.getVariable(_("of"));
|
||||
ScriptValueP in = ctx.getVariable(_("in"));
|
||||
@@ -323,15 +396,20 @@ SCRIPT_FUNCTION(number_of_items) {
|
||||
// ----------------------------------------------------------------------------- : Initialize functions
|
||||
|
||||
void init_script_functions(Context& ctx) {
|
||||
ctx.setVariable(_("replace rule"), script_replace_rule);
|
||||
ctx.setVariable(_("filter rule"), script_filter_rule);
|
||||
ctx.setVariable(_("sort rule"), script_sort_rule);
|
||||
ctx.setVariable(_("to upper"), script_to_upper);
|
||||
ctx.setVariable(_("to lower"), script_to_lower);
|
||||
ctx.setVariable(_("to title"), script_to_title);
|
||||
ctx.setVariable(_("substring"), script_substring);
|
||||
ctx.setVariable(_("contains"), script_contains);
|
||||
ctx.setVariable(_("position"), script_position_of);
|
||||
ctx.setVariable(_("number of items"), script_number_of_items);
|
||||
ctx.setVariable(_("replace rule"), script_replace_rule);
|
||||
ctx.setVariable(_("filter rule"), script_filter_rule);
|
||||
ctx.setVariable(_("sort"), script_sort);
|
||||
ctx.setVariable(_("sort rule"), script_sort_rule);
|
||||
ctx.setVariable(_("to upper"), script_to_upper);
|
||||
ctx.setVariable(_("to lower"), script_to_lower);
|
||||
ctx.setVariable(_("to title"), script_to_title);
|
||||
ctx.setVariable(_("substring"), script_substring);
|
||||
ctx.setVariable(_("contains"), script_contains);
|
||||
ctx.setVariable(_("tag contents"), script_tag_contents);
|
||||
ctx.setVariable(_("remove tag"), script_tag_remove);
|
||||
ctx.setVariable(_("tag contents rule"), script_tag_contents_rule);
|
||||
ctx.setVariable(_("tag remove rule"), script_tag_remove_rule);
|
||||
ctx.setVariable(_("position"), script_position_of);
|
||||
ctx.setVariable(_("number of items"), script_number_of_items);
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +329,12 @@ inline ScriptValueP toScript(const shared_ptr<T>& v) { return new_intrusive1<Scr
|
||||
* Throws an error if the parameter is not found.
|
||||
*/
|
||||
#define SCRIPT_PARAM(Type, name) \
|
||||
Type name = *ctx.getVariable(_(#name))
|
||||
Type name = getParam<Type>(ctx.getVariable(_(#name)))
|
||||
|
||||
template <typename T>
|
||||
inline T getParam (const ScriptValueP& value) { return *value; }
|
||||
template <>
|
||||
inline ScriptValueP getParam<ScriptValueP>(const ScriptValueP& value) { return value; }
|
||||
|
||||
/// Retrieve an optional parameter
|
||||
/** Usage:
|
||||
|
||||
@@ -33,6 +33,10 @@ class Defaultable {
|
||||
assert(is_default);
|
||||
value = new_value;
|
||||
}
|
||||
/// Assigning a value, don't change the defaultness
|
||||
inline void assignDontChangeDefault(const T& new_value) {
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
/// Get access to the value
|
||||
inline const T& operator () () const { return value; }
|
||||
|
||||
@@ -82,6 +82,14 @@ String fix_old_tags(const String& str) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Finding tags
|
||||
|
||||
size_t tag_start(const String& str, size_t pos) {
|
||||
size_t start = str.find_last_of(_('<'), pos);
|
||||
if (start == String::npos) return String::npos;
|
||||
size_t end = skip_tag(str, start);
|
||||
if (end <= pos) return String::npos;
|
||||
return start;
|
||||
}
|
||||
|
||||
size_t skip_tag(const String& str, size_t start) {
|
||||
if (start >= str.size()) return String::npos;
|
||||
size_t end = str.find_first_of(_('>'), start);
|
||||
@@ -152,6 +160,43 @@ String anti_tag(const String& tag) {
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Global operations
|
||||
|
||||
String remove_tag(const String& str, const String& tag) {
|
||||
if (tag.size() < 1) return str;
|
||||
String ctag = close_tag(tag);
|
||||
return remove_tag_exact(remove_tag_exact(str, tag), ctag);
|
||||
}
|
||||
|
||||
String remove_tag_exact(const String& str, const String& tag) {
|
||||
String ret; ret.reserve(str.size());
|
||||
size_t start = 0, pos = str.find(tag);
|
||||
while (pos != String::npos) {
|
||||
ret += str.substr(start, pos - start); // before
|
||||
// next
|
||||
start = skip_tag(str, pos);
|
||||
if (start > str.size()) break;
|
||||
pos = str.find(tag, start);
|
||||
}
|
||||
ret += str.substr(start);
|
||||
return ret;
|
||||
}
|
||||
|
||||
String remove_tag_contents(const String& str, const String& tag) {
|
||||
String ret; ret.reserve(str.size());
|
||||
size_t start = 0, pos = str.find(tag);
|
||||
while (pos != String::npos) {
|
||||
size_t end = match_close_tag(str, pos);
|
||||
if (end == String::npos) return ret; // missing close tag
|
||||
ret += str.substr(start, pos - start);
|
||||
// next
|
||||
start = skip_tag(str, end);
|
||||
if (start > str.size()) break;
|
||||
pos = str.find(tag, start);
|
||||
}
|
||||
ret += str.substr(start);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Updates
|
||||
|
||||
/// Return all open or close tags in the given range from a string
|
||||
|
||||
@@ -42,6 +42,14 @@ String fix_old_tags(const String&);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Finding tags
|
||||
|
||||
/// Returns the position of the tag the given position is in
|
||||
/** Returns String::npos if pos is not in a tag.
|
||||
* In a tag is:
|
||||
* < t a g >
|
||||
* n y y y y n
|
||||
*/
|
||||
size_t tag_start(const String& str, size_t pos);
|
||||
|
||||
/// Returns the position just beyond the tag starting at start
|
||||
size_t skip_tag(const String& str, size_t start);
|
||||
|
||||
@@ -53,8 +61,10 @@ size_t match_close_tag(const String& str, size_t start);
|
||||
/** If not found returns String::npos */
|
||||
size_t last_start_tag_before(const String& str, const String& tag, size_t start);
|
||||
|
||||
/// Is the given range entirely contained in a given tag?
|
||||
/** If so: return the start position of that tag, otherwise returns String::npos */
|
||||
/// Is the given range entirely contained in a given tagged block?
|
||||
/** If so: return the start position of that tag, otherwise returns String::npos
|
||||
* A tagged block is everything between <tag>...</tag>
|
||||
*/
|
||||
size_t in_tag(const String& str, const String& tag, size_t start, size_t end);
|
||||
|
||||
/// Return the tag at the given position (without the <>)
|
||||
@@ -78,7 +88,7 @@ String anti_tag(const String& tag);
|
||||
*/
|
||||
String remove_tag(const String& str, const String& tag);
|
||||
|
||||
/// Remove all instances of tags starting with tag
|
||||
/// Remove all instances of tags starting with tag, but not its close tag
|
||||
String remove_tag_exact(const String& str, const String& tag);
|
||||
|
||||
/// Remove all instances of a tag (including contents) from a string
|
||||
|
||||
Reference in New Issue
Block a user