Commit 4f492f06 authored by twanvl's avatar twanvl

Implemented auto replace, including GUI

parent 5257a369
......@@ -29,6 +29,7 @@ menu:
find: &Find Ctrl+F
find next: Find &Next F3
replace: R&eplace Ctrl+H
auto replace: Auto Rep&lace...
preferences: &Preferences...
cards: &Cards
......@@ -125,6 +126,10 @@ help:
paste: Inserts the text from the clipboard
paste card: Inserts the card from the clipboard
paste keyword: Inserts the keyword from the clipboard
find: Search the card texts
find next: Find the next match
replace: Replace in the card texts
auto replace: What text should automatically be replaced?
preferences: Change the configuration of Magic Set Editor
cards:
......@@ -435,6 +440,10 @@ label:
zoom: Zoom
filter: Filter
# Auto replace
auto match: Match
auto replace: Replace
# Symbol editor
sides: sides
......@@ -484,6 +493,14 @@ button:
# Update checker
close: &Close
# Auto replace
use auto replace: Use auto replace
add item: &Add
remove item: &Remove
defaults: Reset to &Defaults
enabled: Enabled
whole word: Match whole word only
############################################################## Titles in the GUI
title:
......@@ -519,6 +536,8 @@ title:
export cancelled: Export Cancelled
export html: Export to HTML
save html: Export to HTML
# auto replace
auto replaces: Auto Replace
# Package Update Window
package list: Package Updates
......@@ -537,6 +556,7 @@ action:
delete: Delete
cut: Cut
paste: Paste
auto replace: Auto Replace
# Choice/color editors
change: Change %s
......@@ -623,6 +643,7 @@ error:
When you open it, some aspects of the file may be lost.
It is recommended that you upgrade to the latest version.
Visit http:://magicseteditor.sourceforge.net/
word list type not found: The word list type %s was not found (from a <word-list> tag)
# Update checking
checking updates failed: Checking updates failed.
......
......@@ -57,7 +57,8 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT(keyword_modes);
REFLECT(keyword_parameter_types);
REFLECT_NO_SCRIPT(keywords);
REFLECT(word_lists);
REFLECT_NO_SCRIPT(word_lists);
REFLECT_NO_SCRIPT(auto_replaces);
}
void Game::validate(Version v) {
......
......@@ -25,6 +25,7 @@ DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword);
DECLARE_POINTER_TYPE(WordList);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : Game
......@@ -45,13 +46,14 @@ class Game : public Packaged {
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
Dependencies dependent_scripts_cards; ///< scripts that depend on the card list
Dependencies dependent_scripts_keywords; ///< scripts that depend on the keywords
bool dependencies_initialized; ///< are the script dependencies comming from this game all initialized?
......
......@@ -11,6 +11,7 @@
#include <data/stylesheet.hpp>
#include <data/field.hpp>
#include <data/export_template.hpp>
#include <data/word_list.hpp>
#include <util/reflect.hpp>
#include <util/platform.hpp>
#include <util/io/reader.hpp>
......@@ -19,6 +20,8 @@
#include <wx/wfstream.h>
#include <wx/stdpaths.h>
DECLARE_TYPEOF_COLLECTION(AutoReplaceP);
// ----------------------------------------------------------------------------- : Extra types
IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
......@@ -43,7 +46,7 @@ ColumnSettings::ColumnSettings()
// dummy for ColumnSettings reflection
ScriptValueP to_script(const ColumnSettings&) { return script_nil; }
IMPLEMENT_REFLECTION(ColumnSettings) {
IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
REFLECT(width);
REFLECT(position);
REFLECT(visible);
......@@ -53,9 +56,33 @@ GameSettings::GameSettings()
: sort_cards_ascending(true)
, images_export_filename(_("{card.name}.jpg"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE)
, use_auto_replace(true)
, initialized(false)
{}
IMPLEMENT_REFLECTION(GameSettings) {
void GameSettings::initDefaults(const Game& game) {
if (initialized) return;
initialized = true;
// init auto_replaces, copy from game file
FOR_EACH_CONST(ar, game.auto_replaces) {
// do we have this one?
bool already_have = false;
FOR_EACH(ar2, auto_replaces) {
if (ar->match == ar2->match) {
ar2->custom = false;
already_have = true;
break;
}
}
if (!already_have) {
// TODO: when we start saving games, clone here
ar->custom = false;
auto_replaces.push_back(ar);
}
}
}
IMPLEMENT_REFLECTION_NO_SCRIPT(GameSettings) {
REFLECT(default_stylesheet);
REFLECT(default_export);
REFLECT_N("cardlist columns", columns);
......@@ -63,6 +90,8 @@ IMPLEMENT_REFLECTION(GameSettings) {
REFLECT(sort_cards_ascending);
REFLECT(images_export_filename);
REFLECT(images_export_conflicts);
REFLECT(use_auto_replace);
REFLECT(auto_replaces);
}
......@@ -82,7 +111,7 @@ void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_normal_export.isDefault()) card_normal_export.assignDefault(ss.card_normal_export);
}
IMPLEMENT_REFLECTION(StyleSheetSettings) {
IMPLEMENT_REFLECTION_NO_SCRIPT(StyleSheetSettings) {
REFLECT(card_zoom);
REFLECT(card_angle);
REFLECT(card_anti_alias);
......@@ -127,6 +156,7 @@ void Settings::addRecentFile(const String& filename) {
GameSettings& Settings::gameSettingsFor(const Game& game) {
GameSettingsP& gs = game_settings[game.name()];
if (!gs) gs = new_intrusive<GameSettings>();
gs->initDefaults(game);
return *gs;
}
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
......@@ -165,7 +195,7 @@ String Settings::settingsFile() {
return user_settings_dir() + _("mse8.config"); // use different file during development of C++ port
}
IMPLEMENT_REFLECTION(Settings) {
IMPLEMENT_REFLECTION_NO_SCRIPT(Settings) {
REFLECT_ALIAS(300, "style settings", "stylesheet settings");
REFLECT_ALIAS(300, "default style settings", "default stylesheet settings");
REFLECT(locale);
......
......@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(GameSettings);
DECLARE_POINTER_TYPE(StyleSheetSettings);
DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : Extra data structures
......@@ -56,6 +57,9 @@ class GameSettings : public IntrusivePtrBase<GameSettings> {
public:
GameSettings();
/// Where the settings have defaults, initialize with the values from the game
void initDefaults(const Game& g);
String default_stylesheet;
String default_export;
map<String, ColumnSettings> columns;
......@@ -63,8 +67,12 @@ class GameSettings : public IntrusivePtrBase<GameSettings> {
bool sort_cards_ascending;
String images_export_filename;
FilenameConflicts images_export_conflicts;
bool use_auto_replace;
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
DECLARE_REFLECTION();
private:
bool initialized;
};
/// Settings for a StyleSheet
......
......@@ -281,9 +281,16 @@ IMPLEMENT_REFLECTION(Symbol) {
}
double Symbol::aspectRatio() const {
// Margin between the edges and the symbol.
// In each direction take the lowest one
// This is at most 0.5 (if the symbol is just a line in the middle)
// Multiply by 2 (below) to give something in the range [0...1] i.e. [touches the edge...only in the middle]
double margin_x = min(0.4999, max(0., min(min_pos.x, 1-max_pos.x)));
double margin_y = min(0.4999, max(0., min(min_pos.y, 1-max_pos.y)));
// The difference between these two,
// e.g. if the vertical margin is more then the horizontal one, the symbol is 'flat'
double delta = 2 * (margin_y - margin_x);
// The aspect ratio, i.e. width/height
if (delta > 0) {
return 1 / (1 - delta);
} else {
......
......@@ -32,3 +32,18 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(WordList) {
REFLECT(words);
}
// ----------------------------------------------------------------------------- : Auto replace words
AutoReplace::AutoReplace()
: enabled(true)
, whole_word(true)
, custom(true)
{}
IMPLEMENT_REFLECTION_NO_SCRIPT(AutoReplace) {
REFLECT(enabled);
REFLECT(whole_word);
REFLECT(match);
REFLECT(replace);
}
......@@ -14,6 +14,7 @@
DECLARE_POINTER_TYPE(WordListWord);
DECLARE_POINTER_TYPE(WordList);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : WordList
......@@ -38,6 +39,23 @@ class WordList : public WordListWord {
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Auto replace words
/// Autoreplace specific shortcut words
class AutoReplace : public IntrusivePtrVirtualBase {
public:
AutoReplace();
bool enabled;
bool whole_word;
bool custom; ///< Is this a custom auto replace?
String match;
String replace;
inline AutoReplaceP clone() const { return new_intrusive1<AutoReplace>(*this); }
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF
#endif
......@@ -23,6 +23,7 @@
#include <gui/print_window.hpp>
#include <gui/images_export_window.hpp>
#include <gui/html_export_window.hpp>
#include <gui/auto_replace_window.hpp>
#include <gui/icon_menu.hpp>
#include <gui/util.hpp>
#include <util/io/package_manager.hpp>
......@@ -86,9 +87,10 @@ SetWindow::SetWindow(Window* parent, const SetP& set)
menuEdit->Append(ID_EDIT_COPY, _("copy"), _MENU_("copy"), _HELP_("copy"));
menuEdit->Append(ID_EDIT_PASTE, _("paste"), _MENU_("paste"), _HELP_("paste"));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_FIND, _("find"), _MENU_("find"), _(""));
menuEdit->Append(ID_EDIT_FIND_NEXT, _MENU_("find next"), _(""));
menuEdit->Append(ID_EDIT_REPLACE, _MENU_("replace"), _(""));
menuEdit->Append(ID_EDIT_FIND, _("find"), _MENU_("find"), _HELP_("find"));
menuEdit->Append(ID_EDIT_FIND_NEXT, _MENU_("find next"), _HELP_("find next"));
menuEdit->Append(ID_EDIT_REPLACE, _MENU_("replace"), _HELP_("replace"));
menuEdit->Append(ID_EDIT_AUTO_REPLACE, _MENU_("auto replace"), _HELP_("auto replace"));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_PREFERENCES, _MENU_("preferences"), _HELP_("preferences"));
menuBar->Append(menuEdit, _MENU_("edit"));
......@@ -619,6 +621,10 @@ void SetWindow::onReplaceAll(wxFindDialogEvent&) {
current_panel->doReplaceAll(find_data);
}
void SetWindow::onEditAutoReplace(wxCommandEvent&) {
(new AutoReplaceWindow(this, *set->game))->Show();
}
void SetWindow::onEditPreferences(wxCommandEvent&) {
PreferencesWindow wnd(this);
if (wnd.ShowModal() == wxID_OK) {
......@@ -697,6 +703,7 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame)
EVT_MENU (ID_EDIT_FIND, SetWindow::onEditFind)
EVT_MENU (ID_EDIT_FIND_NEXT, SetWindow::onEditFindNext)
EVT_MENU (ID_EDIT_REPLACE, SetWindow::onEditReplace)
EVT_MENU (ID_EDIT_AUTO_REPLACE, SetWindow::onEditAutoReplace)
EVT_MENU (ID_EDIT_PREFERENCES, SetWindow::onEditPreferences)
EVT_MENU (ID_WINDOW_NEW, SetWindow::onWindowNewWindow)
EVT_TOOL_RANGE (ID_WINDOW_MIN, ID_WINDOW_MAX, SetWindow::onWindowSelect)
......
......@@ -131,6 +131,7 @@ class SetWindow : public wxFrame, public SetView {
void onEditFind (wxCommandEvent&);
void onEditFindNext (wxCommandEvent&);
void onEditReplace (wxCommandEvent&);
void onEditAutoReplace (wxCommandEvent&);
void onEditPreferences (wxCommandEvent&);
void onFind (wxFindDialogEvent&);
......
......@@ -12,6 +12,7 @@
#include <gui/drop_down_list.hpp>
#include <data/word_list.hpp>
#include <data/game.hpp>
#include <data/settings.hpp>
#include <data/action/value.hpp>
#include <util/tagged_string.hpp>
#include <util/find_replace.hpp>
......@@ -23,6 +24,7 @@ DECLARE_SHARED_POINTER_TYPE(DropDownList);
DECLARE_TYPEOF_COLLECTION(WordListP);
DECLARE_TYPEOF_COLLECTION(WordListWordP);
DECLARE_TYPEOF_COLLECTION(WordListPosP);
DECLARE_TYPEOF_COLLECTION(AutoReplaceP);
// ----------------------------------------------------------------------------- : TextValueEditorScrollBar
......@@ -273,8 +275,8 @@ bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) {
if (select_words) {
// on the left, swap start and end
bool left = selection_end_i < selection_start_i;
size_t next = nextWordBoundry(index);
size_t prev = prevWordBoundry(index);
size_t next = nextWordBoundary(index);
size_t prev = prevWordBoundary(index);
if (( left && next > max(selection_start_i, selection_end_i)) ||
(!left && prev < min(selection_start_i, selection_end_i))) {
left = !left;
......@@ -294,8 +296,8 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) {
select_words = true;
selecting = true;
size_t index = v.indexAt(style().getRotation().trInv(pos));
moveSelection(TYPE_INDEX, prevWordBoundry(index), true, MOVE_MID);
moveSelection(TYPE_INDEX, nextWordBoundry(index), false, MOVE_MID);
moveSelection(TYPE_INDEX, prevWordBoundary(index), true, MOVE_MID);
moveSelection(TYPE_INDEX, nextWordBoundary(index), false, MOVE_MID);
return true;
}
......@@ -323,17 +325,17 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_LEFT:
// move left (selection?)
if (ev.ControlDown()) {
moveSelection(TYPE_INDEX, prevWordBoundry(selection_end_i),!ev.ShiftDown(), MOVE_LEFT);
moveSelection(TYPE_INDEX, prevWordBoundary(selection_end_i),!ev.ShiftDown(), MOVE_LEFT);
} else {
moveSelection(TYPE_CURSOR, prevCharBoundry(selection_end), !ev.ShiftDown(), MOVE_LEFT);
moveSelection(TYPE_CURSOR, prevCharBoundary(selection_end), !ev.ShiftDown(), MOVE_LEFT);
}
break;
case WXK_RIGHT:
// move left (selection?)
if (ev.ControlDown()) {
moveSelection(TYPE_INDEX, nextWordBoundry(selection_end_i),!ev.ShiftDown(), MOVE_RIGHT);
moveSelection(TYPE_INDEX, nextWordBoundary(selection_end_i),!ev.ShiftDown(), MOVE_RIGHT);
} else {
moveSelection(TYPE_CURSOR, nextCharBoundry(selection_end), !ev.ShiftDown(), MOVE_RIGHT);
moveSelection(TYPE_CURSOR, nextCharBoundary(selection_end), !ev.ShiftDown(), MOVE_RIGHT);
}
break;
case WXK_UP:
......@@ -369,10 +371,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_BACK:
if (selection_start == selection_end) {
// if no selection, select previous character
moveSelectionNoRedraw(TYPE_CURSOR, prevCharBoundry(selection_end), false);
moveSelectionNoRedraw(TYPE_CURSOR, prevCharBoundary(selection_end), false);
if (selection_start == selection_end) {
// Walk over a <sep> as if we are the LEFT key
moveSelection(TYPE_CURSOR, prevCharBoundry(selection_end), true, MOVE_LEFT);
moveSelection(TYPE_CURSOR, prevCharBoundary(selection_end), true, MOVE_LEFT);
return true;
}
}
......@@ -381,10 +383,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_DELETE:
if (selection_start == selection_end) {
// if no selection select next
moveSelectionNoRedraw(TYPE_CURSOR, nextCharBoundry(selection_end), false);
moveSelectionNoRedraw(TYPE_CURSOR, nextCharBoundary(selection_end), false);
if (selection_start == selection_end) {
// Walk over a <sep> as if we are the RIGHT key
moveSelection(TYPE_CURSOR, nextCharBoundry(selection_end), true, MOVE_RIGHT);
moveSelection(TYPE_CURSOR, nextCharBoundary(selection_end), true, MOVE_RIGHT);
}
}
replaceSelection(wxEmptyString, _ACTION_("delete"));
......@@ -411,9 +413,9 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
// this might not work for internationalized input.
// It might also not be portable!
#ifdef UNICODE
replaceSelection(escape(String(ev.GetUnicodeKey(), 1)), _ACTION_("typing"));
replaceSelection(escape(String(ev.GetUnicodeKey(), 1)), _ACTION_("typing"), true);
#else
replaceSelection(escape(String((Char)ev.GetKeyCode(), 1)), _ACTION_("typing"));
replaceSelection(escape(String((Char)ev.GetKeyCode(), 1)), _ACTION_("typing"), true);
#endif
} else {
return false;
......@@ -775,7 +777,7 @@ size_t match_cursor_position(size_t pos1, const String& text1, size_t pos2, cons
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, bool allow_auto_replace, bool select_on_undo) {
if (replacement.empty() && selection_start == selection_end) {
// no text selected, nothing to delete
return;
......@@ -785,10 +787,10 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
fixSelection();
// execute the action before adding it to the stack,
// because we want to run scripts before action listeners see the action
TextValueAction* 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, select_on_undo ? selection_start : selection_end, selection_end, replacement, name);
if (!action) {
// nothing changes, but move the selection anyway
moveSelection(TYPE_CURSOR, selection_start);
moveSelection(TYPE_CURSOR, selection_end);
return;
}
// what we would expect if no scripts take place
......@@ -832,10 +834,31 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
selection_end = selection_start = best_cursor;
fixSelection(TYPE_CURSOR, MOVE_RIGHT);
}
// auto replace after typing?
if (allow_auto_replace) tryAutoReplace();
// scroll with next update
scroll_with_cursor = true;
}
void TextValueEditor::tryAutoReplace() {
size_t end = selection_start_i;
GameSettings& gs = settings.gameSettingsFor(*getSet().game);
FOR_EACH(ar, gs.auto_replaces) {
if (ar->enabled && ar->match.size() <= end) {
size_t start = end - ar->match.size();
if (is_substr(value().value(), start, ar->match) &&
(!ar->whole_word || (isWordBoundary(start) && isWordBoundary(end)))
) {
// replace
selection_start_i = start;
selection_end_i = end;
fixSelection(TYPE_INDEX);
replaceSelection(ar->replace, _ACTION_("auto replace"), false, false);
}
}
}
}
void TextValueEditor::moveSelection(IndexType t, size_t new_end, bool also_move_start, Movement dir) {
size_t old_start = selection_start_i;
size_t old_end = selection_end_i;
......@@ -891,28 +914,64 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) {
}
size_t TextValueEditor::prevCharBoundry(size_t pos) const {
size_t TextValueEditor::prevCharBoundary(size_t pos) const {
return max(0, (int)pos - 1);
}
size_t TextValueEditor::nextCharBoundry(size_t pos) const {
size_t TextValueEditor::nextCharBoundary(size_t pos) const {
return min(index_to_cursor(value().value(), String::npos), pos + 1);
}
size_t TextValueEditor::prevWordBoundry(size_t pos_i) const {
static const Char word_bound_chars[] = _(" ,.:;()\n");
bool isWordBoundaryChar(Char c) {
return c == _(' ') || c == _(',') || c == _('.') || c == _(':') || c == _(';') ||
c == _('(') || c == _(')') || c == _('\n') || isPunct(c);
}
size_t TextValueEditor::prevWordBoundary(size_t pos_i) const {
const String& val = value().value();
size_t p = val.find_last_not_of(_(" ,.:;()\n"), max(0, (int)pos_i - 1));
size_t p = val.find_last_not_of(word_bound_chars, max(0, (int)pos_i - 1));
if (p == String::npos) return 0;
p = val.find_last_of(_(" ,.:;()\n"), p);
p = val.find_last_of(word_bound_chars, p);
if (p == String::npos) return 0;
return p + 1;
}
size_t TextValueEditor::nextWordBoundry(size_t pos_i) const {
size_t TextValueEditor::nextWordBoundary(size_t pos_i) const {
const String& val = value().value();
size_t p = val.find_first_of(_(" ,.:;()\n"), pos_i);
size_t p = val.find_first_of(word_bound_chars, pos_i);
if (p == String::npos) return val.size();
p = val.find_first_not_of(_(" ,.:;()\n"), p);
p = val.find_first_not_of(word_bound_chars, p);
if (p == String::npos) return val.size();
return p;
}
bool TextValueEditor::isWordBoundary(size_t pos_i) const {
const String& val = value().value();
// boundary after?
size_t pos = pos_i;
while (true) {
if (pos >= val.size()) return true;
Char c = val.GetChar(pos);
if (c == _('<')) pos = skip_tag(val,pos); // skip tags
else if (isWordBoundaryChar(c)) return true;
else break;
}
// boundary before?
pos = pos_i;
while (true) {
if (pos == 0) return true;
Char c = val.GetChar(pos - 1);
if (c == _('>')) {
// (try to) skip tags in reverse
while (true) {
if (pos == 0) return false; // not a tag
--pos;
c = val.GetChar(pos - 1);
if (c == _('<')) { --pos; break; } // was a tag
else if (c == _('>')) return false; // was not a tag
}
}
else return isWordBoundaryChar(c);
}
}
void TextValueEditor::select(size_t start, size_t end) {
selection_start = start;
......@@ -1129,7 +1188,7 @@ void TextValueEditor::drawWordListIndicators(RotatedDC& dc) {
if (current && wl->active) {
dc.SetPen (Color(0, 128,255));
dc.SetBrush(Color(64, 160,255));
} else if (current && selection_end_i >= wl->start && selection_end_i <= wl->end) {
} else if (current && selection_end_i >= wl->start && selection_end_i <= wl->end && !dropDownShown()) {
dc.SetPen (Color(64, 160,255));
dc.SetBrush(Color(160,208,255));
} else {
......
......@@ -116,7 +116,9 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
/// Replace the current selection with 'replacement', name the action
/** replacement should be a tagged string (i.e. already escaped) */
void replaceSelection(const String& replacement, const String& name);
void replaceSelection(const String& replacement, const String& name, bool allow_auto_replace = false, bool select_on_undo = true);
/// Try to autoreplace at the position before the cursor
void tryAutoReplace();
/// Make sure the selection satisfies its constraints
/** - selection_start and selection_end are inside the text
......@@ -135,12 +137,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
/// Position of previous visible & selectable character
/** Uses cursor positions */
size_t prevCharBoundry(size_t pos) const;
size_t nextCharBoundry(size_t pos) const;
size_t prevCharBoundary(size_t pos) const;
size_t nextCharBoundary(size_t pos) const;
/// Front of previous word, used witch Ctrl+Left/right
/** Uses character indices */
size_t prevWordBoundry(size_t pos_i) const;
size_t nextWordBoundry(size_t pos_i) const;
size_t prevWordBoundary(size_t pos_i) const;
size_t nextWordBoundary(size_t pos_i) const;
bool isWordBoundary(size_t pos_i) const;
// --------------------------------------------------- : Scrolling
......
......@@ -947,6 +947,12 @@
<File
RelativePath=".\gui\about_window.hpp">
</File>
<File
RelativePath=".\gui\auto_replace_window.cpp">
</File>
<File
RelativePath=".\gui\auto_replace_window.hpp">
</File>
<File
RelativePath=".\gui\card_select_window.cpp">
</File>
......
# This file contains the keys expected to be in MSE locales
# It was automatically generated by tools/locale/locale.pl
# Generated on Fri Aug 17 22:58:51 2007
action:
add control point: 0
add part: 1
add symmetry: 0
backspace: 0
change: 1
change combine mode: 0
change shape name: 0
change symmetry copies: 0
change symmetry type: 0
convert to curve: 0
convert to line: 0
cut: 0
delete: 0
delete point: 0
delete points: 0
duplicate: 1
enter: 0
group parts: 0
insert symbol: 0
lock point: 0
move: 1
move curve: 0
move handle: 0
move symmetry center: 0
move symmetry handle: 0
paste: 0
remove parts: 1
reorder parts: 0
rotate: 1
scale: 1
shear: 1
soft line break: 0
typing: 0
ungroup parts: 0
button:
always: 0
browse: 0
check now: 0
close: 0
edit symbol: 0
hide: 0
high quality: 0
if internet connection exists: 0
insert parameter: 0
keep old: 0
last opened set: 0
move down: 0
move up: 0
never: 0
new set: 0
number: 0
number overwrite: 0
open set: 0
overwrite: 0
refer parameter: 0
select: optional, 0
select all: 0
select none: 0
show: 0
show lines: 0
symbol gallery: optional, 0
use custom styling options: 0
use for all cards: 0
zoom export: 0
error:
aborting parsing: 0
can't convert: 2
can't convert value: 3
checking updates: 0
checking updates failed: 0
coordinates for blending overlap: 0
dimension not found: 1
expected key: 1
file not found: 2
file parse error: 2
has no member: 2
has no member value: 2
images used for blending must have the same size: 0
internal error: 1
newer version: 2
no game specified for the set: 0
no packages: 0
no stylesheet specified for the set: 0
no updates: 0
package not found: 1
package out of date: 3
stylesheet and set refer to different game: 0
unable to open output file: 0
unable to store file: 0
unrecognized value: 1
unsupported field type: 1
unsupported fill type: 1
unsupported format: 1
help:
about: 0
add card: 0
add cards: 0
add keyword: 0
add symmetry: 0
app language: 0
basic shapes: 0
bold: 0
border: 0
card list columns: 0
cards tab: 0
check updates: 0
click to select shape: 0
close symbol editor: 0
copies: 0
copy: 0
copy card: 0
copy keyword: 0
curve segment: 0
cut: 0
cut card: 0
cut keyword: 0
difference: 0
draft tab: optional, 0
drag to draw shape: 0
drag to move curve: 0
drag to move line: 0
drag to move point: 0
drag to resize: 1
drag to rotate: 1
drag to shear: 1
draw ellipse: 0
draw polygon: 0
draw rectangle: 0
draw star: 0
duplicate: 0
ellipse: 0
exit: 0
export: 0
export apprentice: 0
export html: 0
export image: 0
export images: 0
export mws: 0
filename format: 0
free point: 0
grid: 0
group: 0
index: 0
intersect: 0
italic: 0
keywords tab: 0
last opened set: 1
line segment: 0
merge: 0
new set: 0
new symbol: 0
new window: 0
next card: 0
next keyword: 0
open set: 0
open symbol: 0
orientation: 0
overlap: 0
paint: 0
paste: 0
paste card: 0
paste keyword: 0
points: 0
polygon: 0
preferences: 0
previous card: 0
previous keyword: 0
print: 0
print preview: 0
rectangle: 0
redo: 0
reflection: 0
reload data: 0
reminder text: 0
remove card: 0
remove keyword: 0
remove symmetry: 0
rotate: 0
rotate 0: 0
rotate 180: 0
rotate 270: 0
rotate 90: 0
rotate card: 0
rotation: 0
save set: 0
save set as: 0
save symbol: 0
save symbol as: 0
select: 0
set code: 0
set info tab: 0
sides: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symbols: 0
symmetric point: 0
symmetry: 0
undo: 0
ungroup: 0
website: 0
welcome: 0
zoom export: 0
label:
app language: 0
apprentice: 0
apprentice exe: 0
apprentice export cancelled: 0
card display: 0
card notes: 0
cards to export: 0
check at startup: 0
checking requires internet: 0
columns: 0
export filenames: 0
external programs: 0
filename conflicts: 0
filename format: 0
filename is ignored: 0
filter: 0
game type: 0
html export options: 0
html template: 0
keyword: 0
language: 0
match: 0
mode: 0
original: 0
percent of normal: 0
reminder: 0
result: 0
save changes: 1
select cards print: 0
select columns: 0
selection: 0
set code: 0
sides: optional, 0
size: 0
standard keyword: 1
style type: 0
styling options: 0
uses: 0
zoom: 0
menu:
about: 0
add card: 0
add cards: 0
add keyword: 0
basic shapes: 0
bold: 0
card list columns: 0
cards: 0
cards tab: 0
check updates: 0
close symbol editor: 0
copy: 0
cut: 0
draft tab: optional, 0
duplicate: 0
edit: 0
exit: 0
export: 0
export apprentice: 0
export html: 0
export image: 0
export images: 0
export mws: 0
file: 0
find: 0
find next: 0
format: 0
group: 0
help: 0
index: 0
insert symbol: 0
italic: 0
keywords: 0
keywords tab: 0
new set: 0
new symbol: 0
new window: 0
next card: 0
next keyword: 0
open set: 0
open symbol: 0
orientation: 0
paint: 0
paste: 0
points: 0
preferences: 0
previous card: 0
previous keyword: 0
print: 0
print preview: 0
redo: 1
reload data: 0
reminder text: 0
remove card: 0
remove keyword: 0
replace: 0
rotate: 0
rotate 0: 0
rotate 180: 0
rotate 270: 0
rotate 90: 0
save set: 0
save set as: 0
save symbol: 0
save symbol as: 0
select: 0
set info tab: 0
stats tab: 0
store symbol: 0
style tab: 0
symbols: 0
symmetry: 0
tool: 0
undo: 1
ungroup: 0
website: 0
window: 0
title:
%s - magic set editor: 1
about: 0
directories: 0
display: 0
export cancelled: 0
export html: 0
export images: 0
global: 0
locate apprentice: 0
magic set editor: 0
new status: 0
open set: 0
package list: 0
package name: 0
package status: 0
preferences: 0
print preview: 0
save changes: 0
save html: 0
save image: 0
save set: 0
select cards: 0
select cards export: 0
select columns: 0
slice image: 0
symbol editor: 0
untitled: 0
update check: 0
updates: 0
updates availible: 0
tool:
add symmetry: 0
basic shapes: 0
border: 0
cards tab: 0
curve segment: 0
difference: 0
draft tab: optional, 0
ellipse: 0
free point: 0
grid: 0
intersect: 0
keywords tab: 0
line segment: 0
merge: 0
overlap: 0
paint: optional, 0
points: 0
polygon: 0
rectangle: 0
redo: 0
reflection: 0
remove symmetry: 0
rotate: 0
rotation: 0
select: 0
set info tab: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symmetric point: 0
symmetry: 0
undo: 0
tooltip:
add card: 0
add keyword: 0
add symmetry: 0
basic shapes: 0
bold: 0
border: 0
cards tab: 0
copy: 0
curve segment: 0
cut: 0
difference: 0
draft tab: optional, 0
ellipse: 0
export: 0
free point: 0
grid: 0
intersect: 0
italic: 0
keywords tab: 0
line segment: 0
merge: 0
new set: 0
open set: 0
overlap: 0
paint: optional, 0
paste: 0
points: 0
polygon: 0
rectangle: 0
redo: 1
reflection: 0
reminder text: 0
remove card: 0
remove keyword: 0
remove symmetry: 0
rotate: 0
rotate card: 0
rotation: 0
save set: 0
select: 0
set info tab: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symbols: 0
symmetric point: 0
symmetry: 0
undo: 1
type:
boolean: 0
card: 0
circle: 0
collection: 0
collection of: 1
color: 0
do nothing: 0
double: 0
ellipse: 0
field: 0
function: 0
game: 0
group: 0
hexagon: 0
image: 0
install: 0
installed: 0
integer: 0
new mse: 0
nil: 0
object: 0
pentagon: 0
point: 0
points: 0
polygon: 0
rectangle: 0
reflection: 0
rhombus: 0
rotation: 0
set: 0
shape: 0
shapes: 0
square: 0
star: 0
string: 0
style: 0
stylesheet: 0
triangle: 0
uninstall: 0
uninstalled: 0
upgrade: 0
upgradeable: 0
value: 0
# This file contains the keys expected to be in MSE locales
# It was automatically generated by tools/locale/locale.pl
# Generated on Sun Aug 26 21:17:38 2007
action:
add control point: 0
add part: 1
add symmetry: 0
auto replace: 0
backspace: 0
change: 1
change combine mode: 0
change shape name: 0
change symmetry copies: 0
change symmetry type: 0
convert to curve: 0
convert to line: 0
cut: 0
delete: 0
delete point: 0
delete points: 0
duplicate: 1
enter: 0
group parts: 0
insert symbol: 0
lock point: 0
move: 1
move curve: 0
move handle: 0
move symmetry center: 0
move symmetry handle: 0
paste: 0
remove parts: 1
reorder parts: 0
rotate: 1
scale: 1
shear: 1
soft line break: 0
typing: 0
ungroup parts: 0
button:
add item: 0
always: 0
browse: 0
check now: 0
close: 0
defaults: 0
edit symbol: 0
enabled: 0
hide: 0
high quality: 0
if internet connection exists: 0
insert parameter: 0
keep old: 0
last opened set: 0
move down: 0
move up: 0
never: 0
new set: 0
number: 0
number overwrite: 0
open set: 0
overwrite: 0
refer parameter: 0
remove item: 0
select: optional, 0
select all: 0
select none: 0
show: 0
show lines: 0
symbol gallery: optional, 0
use auto replace: 0
use custom styling options: 0
use for all cards: 0
whole word: 0
zoom export: 0
error:
aborting parsing: 0
can't convert: 2
can't convert value: 3
checking updates: 0
checking updates failed: 0
coordinates for blending overlap: 0
dimension not found: 1
expected key: 1
file not found: 2
file parse error: 2
has no member: 2
has no member value: 2
images used for blending must have the same size: 0
internal error: 1
newer version: 2
no game specified for the set: 0
no packages: 0
no stylesheet specified for the set: 0
no updates: 0
package not found: 1
package out of date: 3
stylesheet and set refer to different game: 0
unable to open output file: 0
unable to store file: 0
unrecognized value: 1
unsupported field type: 1
unsupported fill type: 1
unsupported format: 1
word list type not found: 1
help:
about: 0
add card: 0
add cards: 0
add keyword: 0
add symmetry: 0
app language: 0
auto replace: 0
basic shapes: 0
bold: 0
border: 0
card list columns: 0
cards tab: 0
check updates: 0
click to select shape: 0
close symbol editor: 0
copies: 0
copy: 0
copy card: 0
copy keyword: 0
curve segment: 0
cut: 0
cut card: 0
cut keyword: 0
difference: 0
draft tab: optional, 0
drag to draw shape: 0
drag to move curve: 0
drag to move line: 0
drag to move point: 0
drag to resize: 1
drag to rotate: 1
drag to shear: 1
draw ellipse: 0
draw polygon: 0
draw rectangle: 0
draw star: 0
duplicate: 0
ellipse: 0
exit: 0
export: 0
export apprentice: 0
export html: 0
export image: 0
export images: 0
export mws: 0
filename format: 0
find: 0
find next: 0
free point: 0
grid: 0
group: 0
index: 0
intersect: 0
italic: 0
keywords tab: 0
last opened set: 1
line segment: 0
merge: 0
new set: 0
new symbol: 0
new window: 0
next card: 0
next keyword: 0
open set: 0
open symbol: 0
orientation: 0
overlap: 0
paint: 0
paste: 0
paste card: 0
paste keyword: 0
points: 0
polygon: 0
preferences: 0
previous card: 0
previous keyword: 0
print: 0
print preview: 0
rectangle: 0
redo: 0
reflection: 0
reload data: 0
reminder text: 0
remove card: 0
remove keyword: 0
remove symmetry: 0
replace: 0
rotate: 0
rotate 0: 0
rotate 180: 0
rotate 270: 0
rotate 90: 0
rotate card: 0
rotation: 0
save set: 0
save set as: 0
save symbol: 0
save symbol as: 0
select: 0
set code: 0
set info tab: 0
sides: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symbols: 0
symmetric point: 0
symmetry: 0
undo: 0
ungroup: 0
website: 0
welcome: 0
zoom export: 0
label:
app language: 0
apprentice: 0
apprentice exe: 0
apprentice export cancelled: 0
auto match: 0
auto replace: 0
card display: 0
card notes: 0
cards to export: 0
check at startup: 0
checking requires internet: 0
columns: 0
export filenames: 0
external programs: 0
filename conflicts: 0
filename format: 0
filename is ignored: 0
filter: 0
game type: 0
html export options: 0
html template: 0
keyword: 0
language: 0
match: 0
mode: 0
original: 0
percent of normal: 0
reminder: 0
result: 0
save changes: 1
select cards print: 0
select columns: 0
selection: 0
set code: 0
sides: optional, 0
size: 0
standard keyword: 1
style type: 0
styling options: 0
uses: 0
zoom: 0
menu:
about: 0
add card: 0
add cards: 0
add keyword: 0
auto replace: 0
basic shapes: 0
bold: 0
card list columns: 0
cards: 0
cards tab: 0
check updates: 0
close symbol editor: 0
copy: 0
cut: 0
draft tab: optional, 0
duplicate: 0
edit: 0
exit: 0
export: 0
export apprentice: 0
export html: 0
export image: 0
export images: 0
export mws: 0
file: 0
find: 0
find next: 0
format: 0
group: 0
help: 0
index: 0
insert symbol: 0
italic: 0
keywords: 0
keywords tab: 0
new set: 0
new symbol: 0
new window: 0
next card: 0
next keyword: 0
open set: 0
open symbol: 0
orientation: 0
paint: 0
paste: 0
points: 0
preferences: 0
previous card: 0
previous keyword: 0
print: 0
print preview: 0
redo: 1
reload data: 0
reminder text: 0
remove card: 0
remove keyword: 0
replace: 0
rotate: 0
rotate 0: 0
rotate 180: 0
rotate 270: 0
rotate 90: 0
save set: 0
save set as: 0
save symbol: 0
save symbol as: 0
select: 0
set info tab: 0
stats tab: 0
store symbol: 0
style tab: 0
symbols: 0
symmetry: 0
tool: 0
undo: 1
ungroup: 0
website: 0
window: 0
title:
%s - magic set editor: 1
about: 0
auto replaces: 0
directories: 0
display: 0
export cancelled: 0
export html: 0
export images: 0
global: 0
locate apprentice: 0
magic set editor: 0
new status: 0
open set: 0
package list: 0
package name: 0
package status: 0
preferences: 0
print preview: 0
save changes: 0
save html: 0
save image: 0
save set: 0
select cards: 0
select cards export: 0
select columns: 0
slice image: 0
symbol editor: 0
untitled: 0
update check: 0
updates: 0
updates availible: 0
tool:
add symmetry: 0
basic shapes: 0
border: 0
cards tab: 0
curve segment: 0
difference: 0
draft tab: optional, 0
ellipse: 0
free point: 0
grid: 0
intersect: 0
keywords tab: 0
line segment: 0
merge: 0
overlap: 0
paint: optional, 0
points: 0
polygon: 0
rectangle: 0
redo: 0
reflection: 0
remove symmetry: 0
rotate: 0
rotation: 0
select: 0
set info tab: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symmetric point: 0
symmetry: 0
undo: 0
tooltip:
add card: 0
add keyword: 0
add symmetry: 0
basic shapes: 0
bold: 0
border: 0
cards tab: 0
copy: 0
curve segment: 0
cut: 0
difference: 0
draft tab: optional, 0
ellipse: 0
export: 0
free point: 0
grid: 0
intersect: 0
italic: 0
keywords tab: 0
line segment: 0
merge: 0
new set: 0
open set: 0
overlap: 0
paint: optional, 0
paste: 0
points: 0
polygon: 0
rectangle: 0
redo: 1
reflection: 0
reminder text: 0
remove card: 0
remove keyword: 0
remove symmetry: 0
rotate: 0
rotate card: 0
rotation: 0
save set: 0
select: 0
set info tab: 0
smooth point: 0
snap: 0
star: 0
stats tab: 0
store symbol: 0
style tab: 0
subtract: 0
symbols: 0
symmetric point: 0
symmetry: 0
undo: 1
type:
boolean: 0
card: 0
circle: 0
collection: 0
collection of: 1
color: 0
do nothing: 0
double: 0
ellipse: 0
field: 0
function: 0
game: 0
group: 0
hexagon: 0
image: 0
install: 0
installed: 0
integer: 0
new mse: 0
nil: 0
object: 0
pentagon: 0
point: 0
points: 0
polygon: 0
rectangle: 0
reflection: 0
rhombus: 0
rotation: 0
set: 0
shape: 0
shapes: 0
square: 0
star: 0
string: 0
style: 0
stylesheet: 0
triangle: 0
uninstall: 0
uninstalled: 0
upgrade: 0
upgradeable: 0
value: 0
......@@ -64,8 +64,8 @@ void Writer::handle(const String& value) {
// write indentation and key
writeKey();
writeUTF8(stream, _(": "));
if (value.find_first_of(_('\n')) != String::npos) {
// multiline string
if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
// multiline string, or contains leading whitespace
stream.PutChar(_('\n'));
indentation += 1;
// split lines, and write each line
......
......@@ -53,7 +53,8 @@ enum MenuID {
, ID_EDIT_FIND = wxID_FIND
, ID_EDIT_FIND_NEXT = 103
, ID_EDIT_REPLACE = wxID_REPLACE
, ID_EDIT_PREFERENCES = 104
, ID_EDIT_AUTO_REPLACE = 104
, ID_EDIT_PREFERENCES = 105
// Window menu (MainWindow)
, ID_WINDOW_NEW = 201
......@@ -242,7 +243,13 @@ enum ControlID {
, ID_INSTALL
, ID_UPGRADE
, ID_REMOVE
, ID_CANCEL
, ID_CANCEL = wxID_CANCEL
// Auto replace window
, ID_USE_AUTO_REPLACE
, ID_ITEM_VALUE
, ID_ADD_ITEM
, ID_REMOVE_ITEM
, ID_DEFAULTS
};
// ----------------------------------------------------------------------------- : EOF
......
......@@ -16,7 +16,7 @@ close F;
# insert stuff
$hpp =~ s/_\n/_$macro\n/g;
$hpp =~ s/_\r?\n/_$macro\n/g;
$cpp =~ s@<util/prec.hpp>@<$file.hpp>@g;
......
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