mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 13:37:00 -04:00
Cursor now moves correctly with script updates; tries to stay outside <sym> tags;
Closing </kw> tags no longer end up in keyword parameters git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@515 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -61,6 +61,8 @@ class TextValueAction : public ValueAction {
|
|||||||
virtual void perform(bool to_undo);
|
virtual void perform(bool to_undo);
|
||||||
virtual bool merge(const Action& action);
|
virtual bool merge(const Action& action);
|
||||||
|
|
||||||
|
inline const String& newValue() const { return new_value(); }
|
||||||
|
|
||||||
/// The modified selection
|
/// The modified selection
|
||||||
size_t selection_start, selection_end;
|
size_t selection_start, selection_end;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -528,6 +528,8 @@ String KeywordDatabase::expand(const String& text,
|
|||||||
// note: start_u can be (uint)-1 when len_u == 0
|
// note: start_u can be (uint)-1 when len_u == 0
|
||||||
size_t part_end = len_u > 0 ? untagged_to_index(s, start_u + len_u, true) : start;
|
size_t part_end = len_u > 0 ? untagged_to_index(s, start_u + len_u, true) : start;
|
||||||
String part = s.substr(start, part_end - start);
|
String part = s.substr(start, part_end - start);
|
||||||
|
// strip left over </kw tags
|
||||||
|
part = remove_tag(part,_("</kw-"));
|
||||||
if ((j % 2) == 0) {
|
if ((j % 2) == 0) {
|
||||||
// parameter
|
// parameter
|
||||||
KeywordParam& kwp = *kw->parameters[j/2-1];
|
KeywordParam& kwp = *kw->parameters[j/2-1];
|
||||||
|
|||||||
+55
-24
@@ -504,6 +504,27 @@ void TextValueEditor::showCaret() {
|
|||||||
void TextValueEditor::insert(const String& text, const String& action_name) {
|
void TextValueEditor::insert(const String& text, const String& action_name) {
|
||||||
replaceSelection(text, action_name);
|
replaceSelection(text, action_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// compare two cursor positions, determine how much the text matches before and after
|
||||||
|
size_t match_cursor_position(size_t pos1, const String& text1, size_t pos2, const String& text2) {
|
||||||
|
size_t penalty = 0; // penalty for case mismatches
|
||||||
|
size_t before;
|
||||||
|
for (before = 0 ; before < min(pos1,pos2) ; ++before) {
|
||||||
|
Char c1 = text1.GetChar(pos1-before-1), c2 = text2.GetChar(pos2-before-1);
|
||||||
|
if (toLower(c1) != toLower(c2)) break;
|
||||||
|
else if (c1 != c2) ++penalty;
|
||||||
|
}
|
||||||
|
if (pos1 == before && pos2 == before) ++before; // bonus points for matching start of string
|
||||||
|
size_t after;
|
||||||
|
for (after = 0 ; after < min(text1.size() - pos1, text2.size() - pos2) ; ++after) {
|
||||||
|
Char c1 = text1.GetChar(pos1+after), c2 = text2.GetChar(pos2+after);
|
||||||
|
if (toLower(c1) != toLower(c2)) break;
|
||||||
|
else if (c1 != c2) ++penalty;
|
||||||
|
}
|
||||||
|
if (pos1+after == text1.size() && pos2+after == text2.size()) ++after; // bonus points for matching end of string
|
||||||
|
return 1000 * before + 2 * after - penalty; // matching 'before' is more important
|
||||||
|
}
|
||||||
|
|
||||||
void TextValueEditor::replaceSelection(const String& replacement, const String& name) {
|
void TextValueEditor::replaceSelection(const String& replacement, const String& name) {
|
||||||
if (replacement.empty() && selection_start == selection_end) {
|
if (replacement.empty() && selection_start == selection_end) {
|
||||||
// no text selected, nothing to delete
|
// no text selected, nothing to delete
|
||||||
@@ -514,43 +535,53 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
|
|||||||
fixSelection();
|
fixSelection();
|
||||||
// execute the action before adding it to the stack,
|
// execute the action before adding it to the stack,
|
||||||
// because we want to run scripts before action listeners see the action
|
// because we want to run scripts before action listeners see the action
|
||||||
ValueAction* action = typing_action(valueP(), selection_start_i, selection_end_i, selection_start, selection_end, replacement, name);
|
TextValueAction* action = typing_action(valueP(), selection_start_i, selection_end_i, selection_start, selection_end, replacement, name);
|
||||||
if (!action) {
|
if (!action) {
|
||||||
// nothing changes, but move the selection anyway
|
// nothing changes, but move the selection anyway
|
||||||
moveSelection(TYPE_CURSOR, selection_start);
|
moveSelection(TYPE_CURSOR, selection_start);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// what we would expect if no scripts take place
|
||||||
|
String expected_value = untag_for_cursor(action->newValue());
|
||||||
|
size_t expected_cursor = min(selection_start, selection_end) + untag(replacement).size();
|
||||||
// perform the action
|
// perform the action
|
||||||
// NOTE: this calls our onAction, invalidating the text viewer and moving the selection around the new text
|
// NOTE: this calls our onAction, invalidating the text viewer and moving the selection around the new text
|
||||||
getSet().actions.add(action);
|
getSet().actions.add(action);
|
||||||
// move cursor
|
// move cursor
|
||||||
if (field().move_cursor_with_sort && replacement.size() == 1) {
|
{
|
||||||
String val = value().value();
|
String real_value = untag_for_cursor(value().value());
|
||||||
Char typed = replacement.GetChar(0);
|
// where real and expected value are the same, nothing has happend, so don't look there
|
||||||
Char typedU = toUpper(typed);
|
size_t start, end_min;
|
||||||
Char cur = val.GetChar(selection_start_i);
|
for (start = 0 ; start < min(real_value.size(), expected_value.size()) ; ++start) {
|
||||||
// the cursor may have moved because of sorting...
|
if (real_value.GetChar(start) != expected_value.GetChar(start)) break;
|
||||||
// is 'replacement' just after the current cursor?
|
}
|
||||||
if (selection_start_i >= 0 && selection_start_i < val.size() && (cur == typed || cur == typedU)) {
|
for (end_min = 0 ; end_min < min(real_value.size(), expected_value.size()) ; ++end_min) {
|
||||||
// no need to move cursor in a special way
|
if (real_value.GetChar(real_value.size() - end_min - 1) !=
|
||||||
selection_end_i = selection_start_i = min(selection_end_i, selection_start_i) + 1;
|
expected_value.GetChar(expected_value.size() - end_min - 1)) break;
|
||||||
|
}
|
||||||
|
// what is the best cursor position?
|
||||||
|
size_t best_cursor = expected_cursor;
|
||||||
|
if (real_value.size() < expected_value.size()
|
||||||
|
&& expected_cursor < expected_value.size()
|
||||||
|
&& expected_value.GetChar(expected_cursor) == _('\3') // \3 == <sep>
|
||||||
|
&& real_value.GetChar(start) == _('\3') // \3 == <sep>
|
||||||
|
&& real_value.size() - end_min == start) {
|
||||||
|
// exception for type-over separators
|
||||||
|
best_cursor = start + 1;
|
||||||
} else {
|
} else {
|
||||||
// find the last occurence of 'replacement' in the value
|
// try to find the best match
|
||||||
size_t pos = val.find_last_of(typed);
|
size_t best_match = 0;
|
||||||
if (pos == String::npos) {
|
for (size_t i = min(start, expected_cursor) ; i <= real_value.size() - end_min ; ++i) {
|
||||||
// try upper case
|
size_t match = match_cursor_position(expected_cursor, expected_value, i, real_value);
|
||||||
pos = val.find_last_of(typedU);
|
if (match > best_match) {
|
||||||
}
|
best_match = match;
|
||||||
if (pos != String::npos) {
|
best_cursor = i;
|
||||||
selection_end_i = selection_start_i = pos + 1;
|
}
|
||||||
} else {
|
|
||||||
selection_end_i = selection_start_i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
selection_end = selection_start = best_cursor;
|
||||||
selection_end_i = selection_start_i = min(selection_end_i, selection_start_i) + replacement.size();
|
fixSelection(TYPE_CURSOR, MOVE_RIGHT);
|
||||||
}
|
}
|
||||||
fixSelection(TYPE_INDEX, MOVE_RIGHT);
|
|
||||||
// scroll with next update
|
// scroll with next update
|
||||||
scroll_with_cursor = true;
|
scroll_with_cursor = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,10 @@ size_t cursor_to_index(const String& str, size_t cursor, Movement dir) {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (starts_with(tag1, _("/sym"))) {
|
||||||
|
// we like to be inside <b> and <i> tags, but outside <sym> tags
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -310,6 +314,28 @@ size_t cursor_to_index(const String& str, size_t cursor, Movement dir) {
|
|||||||
return dir <= 0 /*MOVE_LEFT*/ ? start : end - 1;
|
return dir <= 0 /*MOVE_LEFT*/ ? start : end - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String untag_for_cursor(const String& str) {
|
||||||
|
String ret; ret.reserve(str.size());
|
||||||
|
for (size_t i = 0 ; i < str.size() ; ) {
|
||||||
|
Char c = str.GetChar(i);
|
||||||
|
if (c == _('<')) {
|
||||||
|
if (is_substr(str, i, _("<atom"))) {
|
||||||
|
i = match_close_tag_end(str, i);
|
||||||
|
ret += _('\2'); // use a random character here
|
||||||
|
} else if (is_substr(str, i, _("<sep"))) {
|
||||||
|
i = match_close_tag_end(str, i);
|
||||||
|
ret += _('\3'); // use a random character here
|
||||||
|
} else {
|
||||||
|
i = skip_tag(str, i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Untagged position
|
// ----------------------------------------------------------------------------- : Untagged position
|
||||||
|
|
||||||
size_t untagged_to_index(const String& str, size_t pos, bool inside) {
|
size_t untagged_to_index(const String& str, size_t pos, bool inside) {
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ void cursor_to_index_range(const String& str, size_t cursor, size_t& begin, size
|
|||||||
/// Find the character index corresponding to the given cursor position
|
/// Find the character index corresponding to the given cursor position
|
||||||
size_t cursor_to_index(const String& str, size_t cursor, Movement dir = MOVE_MID);
|
size_t cursor_to_index(const String& str, size_t cursor, Movement dir = MOVE_MID);
|
||||||
|
|
||||||
|
/// Untag a string for use with cursors, <atom>...</atom> becomes a single character.
|
||||||
|
/** This string should only be used for cursor position calculations. */
|
||||||
|
String untag_for_cursor(const String& str);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Untagged position
|
// ----------------------------------------------------------------------------- : Untagged position
|
||||||
|
|
||||||
/// Find the tagged position corresponding to the given untagged position.
|
/// Find the tagged position corresponding to the given untagged position.
|
||||||
|
|||||||
Reference in New Issue
Block a user