Commit 4f492f06 authored by twanvl's avatar twanvl

Implemented auto replace, including GUI

parent 5257a369
...@@ -29,6 +29,7 @@ menu: ...@@ -29,6 +29,7 @@ menu:
find: &Find Ctrl+F find: &Find Ctrl+F
find next: Find &Next F3 find next: Find &Next F3
replace: R&eplace Ctrl+H replace: R&eplace Ctrl+H
auto replace: Auto Rep&lace...
preferences: &Preferences... preferences: &Preferences...
cards: &Cards cards: &Cards
...@@ -125,6 +126,10 @@ help: ...@@ -125,6 +126,10 @@ help:
paste: Inserts the text from the clipboard paste: Inserts the text from the clipboard
paste card: Inserts the card from the clipboard paste card: Inserts the card from the clipboard
paste keyword: Inserts the keyword 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 preferences: Change the configuration of Magic Set Editor
cards: cards:
...@@ -435,6 +440,10 @@ label: ...@@ -435,6 +440,10 @@ label:
zoom: Zoom zoom: Zoom
filter: Filter filter: Filter
# Auto replace
auto match: Match
auto replace: Replace
# Symbol editor # Symbol editor
sides: sides sides: sides
...@@ -485,6 +494,14 @@ button: ...@@ -485,6 +494,14 @@ button:
# Update checker # Update checker
close: &Close 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 ############################################################## Titles in the GUI
title: title:
magic set editor: Magic Set Editor magic set editor: Magic Set Editor
...@@ -519,6 +536,8 @@ title: ...@@ -519,6 +536,8 @@ title:
export cancelled: Export Cancelled export cancelled: Export Cancelled
export html: Export to HTML export html: Export to HTML
save html: Export to HTML save html: Export to HTML
# auto replace
auto replaces: Auto Replace
# Package Update Window # Package Update Window
package list: Package Updates package list: Package Updates
...@@ -537,6 +556,7 @@ action: ...@@ -537,6 +556,7 @@ action:
delete: Delete delete: Delete
cut: Cut cut: Cut
paste: Paste paste: Paste
auto replace: Auto Replace
# Choice/color editors # Choice/color editors
change: Change %s change: Change %s
...@@ -623,6 +643,7 @@ error: ...@@ -623,6 +643,7 @@ error:
When you open it, some aspects of the file may be lost. When you open it, some aspects of the file may be lost.
It is recommended that you upgrade to the latest version. It is recommended that you upgrade to the latest version.
Visit http:://magicseteditor.sourceforge.net/ 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 # Update checking
checking updates failed: Checking updates failed. checking updates failed: Checking updates failed.
......
...@@ -57,7 +57,8 @@ IMPLEMENT_REFLECTION(Game) { ...@@ -57,7 +57,8 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT(keyword_modes); REFLECT(keyword_modes);
REFLECT(keyword_parameter_types); REFLECT(keyword_parameter_types);
REFLECT_NO_SCRIPT(keywords); REFLECT_NO_SCRIPT(keywords);
REFLECT(word_lists); REFLECT_NO_SCRIPT(word_lists);
REFLECT_NO_SCRIPT(auto_replaces);
} }
void Game::validate(Version v) { void Game::validate(Version v) {
......
...@@ -25,6 +25,7 @@ DECLARE_POINTER_TYPE(KeywordParam); ...@@ -25,6 +25,7 @@ DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode); DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword); DECLARE_POINTER_TYPE(Keyword);
DECLARE_POINTER_TYPE(WordList); DECLARE_POINTER_TYPE(WordList);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : Game // ----------------------------------------------------------------------------- : Game
...@@ -45,6 +46,7 @@ class Game : public Packaged { ...@@ -45,6 +46,7 @@ class Game : public Packaged {
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate 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<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? bool has_keywords; ///< Does this game use keywords?
OptionalScript keyword_match_script; ///< For the keyword editor OptionalScript keyword_match_script; ///< For the keyword editor
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/export_template.hpp> #include <data/export_template.hpp>
#include <data/word_list.hpp>
#include <util/reflect.hpp> #include <util/reflect.hpp>
#include <util/platform.hpp> #include <util/platform.hpp>
#include <util/io/reader.hpp> #include <util/io/reader.hpp>
...@@ -19,6 +20,8 @@ ...@@ -19,6 +20,8 @@
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <wx/stdpaths.h> #include <wx/stdpaths.h>
DECLARE_TYPEOF_COLLECTION(AutoReplaceP);
// ----------------------------------------------------------------------------- : Extra types // ----------------------------------------------------------------------------- : Extra types
IMPLEMENT_REFLECTION_ENUM(CheckUpdates) { IMPLEMENT_REFLECTION_ENUM(CheckUpdates) {
...@@ -43,7 +46,7 @@ ColumnSettings::ColumnSettings() ...@@ -43,7 +46,7 @@ ColumnSettings::ColumnSettings()
// dummy for ColumnSettings reflection // dummy for ColumnSettings reflection
ScriptValueP to_script(const ColumnSettings&) { return script_nil; } ScriptValueP to_script(const ColumnSettings&) { return script_nil; }
IMPLEMENT_REFLECTION(ColumnSettings) { IMPLEMENT_REFLECTION_NO_SCRIPT(ColumnSettings) {
REFLECT(width); REFLECT(width);
REFLECT(position); REFLECT(position);
REFLECT(visible); REFLECT(visible);
...@@ -53,9 +56,33 @@ GameSettings::GameSettings() ...@@ -53,9 +56,33 @@ GameSettings::GameSettings()
: sort_cards_ascending(true) : sort_cards_ascending(true)
, images_export_filename(_("{card.name}.jpg")) , images_export_filename(_("{card.name}.jpg"))
, images_export_conflicts(CONFLICT_NUMBER_OVERWRITE) , 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_stylesheet);
REFLECT(default_export); REFLECT(default_export);
REFLECT_N("cardlist columns", columns); REFLECT_N("cardlist columns", columns);
...@@ -63,6 +90,8 @@ IMPLEMENT_REFLECTION(GameSettings) { ...@@ -63,6 +90,8 @@ IMPLEMENT_REFLECTION(GameSettings) {
REFLECT(sort_cards_ascending); REFLECT(sort_cards_ascending);
REFLECT(images_export_filename); REFLECT(images_export_filename);
REFLECT(images_export_conflicts); REFLECT(images_export_conflicts);
REFLECT(use_auto_replace);
REFLECT(auto_replaces);
} }
...@@ -82,7 +111,7 @@ void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) { ...@@ -82,7 +111,7 @@ void StyleSheetSettings::useDefault(const StyleSheetSettings& ss) {
if (card_normal_export.isDefault()) card_normal_export.assignDefault(ss.card_normal_export); 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_zoom);
REFLECT(card_angle); REFLECT(card_angle);
REFLECT(card_anti_alias); REFLECT(card_anti_alias);
...@@ -127,6 +156,7 @@ void Settings::addRecentFile(const String& filename) { ...@@ -127,6 +156,7 @@ void Settings::addRecentFile(const String& filename) {
GameSettings& Settings::gameSettingsFor(const Game& game) { GameSettings& Settings::gameSettingsFor(const Game& game) {
GameSettingsP& gs = game_settings[game.name()]; GameSettingsP& gs = game_settings[game.name()];
if (!gs) gs = new_intrusive<GameSettings>(); if (!gs) gs = new_intrusive<GameSettings>();
gs->initDefaults(game);
return *gs; return *gs;
} }
ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) { ColumnSettings& Settings::columnSettingsFor(const Game& game, const Field& field) {
...@@ -165,7 +195,7 @@ String Settings::settingsFile() { ...@@ -165,7 +195,7 @@ String Settings::settingsFile() {
return user_settings_dir() + _("mse8.config"); // use different file during development of C++ port 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, "style settings", "stylesheet settings");
REFLECT_ALIAS(300, "default style settings", "default stylesheet settings"); REFLECT_ALIAS(300, "default style settings", "default stylesheet settings");
REFLECT(locale); REFLECT(locale);
......
...@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(GameSettings); ...@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(GameSettings);
DECLARE_POINTER_TYPE(StyleSheetSettings); DECLARE_POINTER_TYPE(StyleSheetSettings);
DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : Extra data structures // ----------------------------------------------------------------------------- : Extra data structures
...@@ -56,6 +57,9 @@ class GameSettings : public IntrusivePtrBase<GameSettings> { ...@@ -56,6 +57,9 @@ class GameSettings : public IntrusivePtrBase<GameSettings> {
public: public:
GameSettings(); GameSettings();
/// Where the settings have defaults, initialize with the values from the game
void initDefaults(const Game& g);
String default_stylesheet; String default_stylesheet;
String default_export; String default_export;
map<String, ColumnSettings> columns; map<String, ColumnSettings> columns;
...@@ -63,8 +67,12 @@ class GameSettings : public IntrusivePtrBase<GameSettings> { ...@@ -63,8 +67,12 @@ class GameSettings : public IntrusivePtrBase<GameSettings> {
bool sort_cards_ascending; bool sort_cards_ascending;
String images_export_filename; String images_export_filename;
FilenameConflicts images_export_conflicts; FilenameConflicts images_export_conflicts;
bool use_auto_replace;
vector<AutoReplaceP> auto_replaces; ///< Things to autoreplace in textboxes
DECLARE_REFLECTION(); DECLARE_REFLECTION();
private:
bool initialized;
}; };
/// Settings for a StyleSheet /// Settings for a StyleSheet
......
...@@ -281,9 +281,16 @@ IMPLEMENT_REFLECTION(Symbol) { ...@@ -281,9 +281,16 @@ IMPLEMENT_REFLECTION(Symbol) {
} }
double Symbol::aspectRatio() const { 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_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))); 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); double delta = 2 * (margin_y - margin_x);
// The aspect ratio, i.e. width/height
if (delta > 0) { if (delta > 0) {
return 1 / (1 - delta); return 1 / (1 - delta);
} else { } else {
......
...@@ -32,3 +32,18 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(WordList) { ...@@ -32,3 +32,18 @@ IMPLEMENT_REFLECTION_NO_SCRIPT(WordList) {
REFLECT(words); 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 @@ ...@@ -14,6 +14,7 @@
DECLARE_POINTER_TYPE(WordListWord); DECLARE_POINTER_TYPE(WordListWord);
DECLARE_POINTER_TYPE(WordList); DECLARE_POINTER_TYPE(WordList);
DECLARE_POINTER_TYPE(AutoReplace);
// ----------------------------------------------------------------------------- : WordList // ----------------------------------------------------------------------------- : WordList
...@@ -38,6 +39,23 @@ class WordList : public WordListWord { ...@@ -38,6 +39,23 @@ class WordList : public WordListWord {
DECLARE_REFLECTION(); 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 // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <gui/print_window.hpp> #include <gui/print_window.hpp>
#include <gui/images_export_window.hpp> #include <gui/images_export_window.hpp>
#include <gui/html_export_window.hpp> #include <gui/html_export_window.hpp>
#include <gui/auto_replace_window.hpp>
#include <gui/icon_menu.hpp> #include <gui/icon_menu.hpp>
#include <gui/util.hpp> #include <gui/util.hpp>
#include <util/io/package_manager.hpp> #include <util/io/package_manager.hpp>
...@@ -86,9 +87,10 @@ SetWindow::SetWindow(Window* parent, const SetP& set) ...@@ -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_COPY, _("copy"), _MENU_("copy"), _HELP_("copy"));
menuEdit->Append(ID_EDIT_PASTE, _("paste"), _MENU_("paste"), _HELP_("paste")); menuEdit->Append(ID_EDIT_PASTE, _("paste"), _MENU_("paste"), _HELP_("paste"));
menuEdit->AppendSeparator(); menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_FIND, _("find"), _MENU_("find"), _("")); menuEdit->Append(ID_EDIT_FIND, _("find"), _MENU_("find"), _HELP_("find"));
menuEdit->Append(ID_EDIT_FIND_NEXT, _MENU_("find next"), _("")); menuEdit->Append(ID_EDIT_FIND_NEXT, _MENU_("find next"), _HELP_("find next"));
menuEdit->Append(ID_EDIT_REPLACE, _MENU_("replace"), _("")); menuEdit->Append(ID_EDIT_REPLACE, _MENU_("replace"), _HELP_("replace"));
menuEdit->Append(ID_EDIT_AUTO_REPLACE, _MENU_("auto replace"), _HELP_("auto replace"));
menuEdit->AppendSeparator(); menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_PREFERENCES, _MENU_("preferences"), _HELP_("preferences")); menuEdit->Append(ID_EDIT_PREFERENCES, _MENU_("preferences"), _HELP_("preferences"));
menuBar->Append(menuEdit, _MENU_("edit")); menuBar->Append(menuEdit, _MENU_("edit"));
...@@ -619,6 +621,10 @@ void SetWindow::onReplaceAll(wxFindDialogEvent&) { ...@@ -619,6 +621,10 @@ void SetWindow::onReplaceAll(wxFindDialogEvent&) {
current_panel->doReplaceAll(find_data); current_panel->doReplaceAll(find_data);
} }
void SetWindow::onEditAutoReplace(wxCommandEvent&) {
(new AutoReplaceWindow(this, *set->game))->Show();
}
void SetWindow::onEditPreferences(wxCommandEvent&) { void SetWindow::onEditPreferences(wxCommandEvent&) {
PreferencesWindow wnd(this); PreferencesWindow wnd(this);
if (wnd.ShowModal() == wxID_OK) { if (wnd.ShowModal() == wxID_OK) {
...@@ -697,6 +703,7 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame) ...@@ -697,6 +703,7 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame)
EVT_MENU (ID_EDIT_FIND, SetWindow::onEditFind) EVT_MENU (ID_EDIT_FIND, SetWindow::onEditFind)
EVT_MENU (ID_EDIT_FIND_NEXT, SetWindow::onEditFindNext) EVT_MENU (ID_EDIT_FIND_NEXT, SetWindow::onEditFindNext)
EVT_MENU (ID_EDIT_REPLACE, SetWindow::onEditReplace) 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_EDIT_PREFERENCES, SetWindow::onEditPreferences)
EVT_MENU (ID_WINDOW_NEW, SetWindow::onWindowNewWindow) EVT_MENU (ID_WINDOW_NEW, SetWindow::onWindowNewWindow)
EVT_TOOL_RANGE (ID_WINDOW_MIN, ID_WINDOW_MAX, SetWindow::onWindowSelect) EVT_TOOL_RANGE (ID_WINDOW_MIN, ID_WINDOW_MAX, SetWindow::onWindowSelect)
......
...@@ -131,6 +131,7 @@ class SetWindow : public wxFrame, public SetView { ...@@ -131,6 +131,7 @@ class SetWindow : public wxFrame, public SetView {
void onEditFind (wxCommandEvent&); void onEditFind (wxCommandEvent&);
void onEditFindNext (wxCommandEvent&); void onEditFindNext (wxCommandEvent&);
void onEditReplace (wxCommandEvent&); void onEditReplace (wxCommandEvent&);
void onEditAutoReplace (wxCommandEvent&);
void onEditPreferences (wxCommandEvent&); void onEditPreferences (wxCommandEvent&);
void onFind (wxFindDialogEvent&); void onFind (wxFindDialogEvent&);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <gui/drop_down_list.hpp> #include <gui/drop_down_list.hpp>
#include <data/word_list.hpp> #include <data/word_list.hpp>
#include <data/game.hpp> #include <data/game.hpp>
#include <data/settings.hpp>
#include <data/action/value.hpp> #include <data/action/value.hpp>
#include <util/tagged_string.hpp> #include <util/tagged_string.hpp>
#include <util/find_replace.hpp> #include <util/find_replace.hpp>
...@@ -23,6 +24,7 @@ DECLARE_SHARED_POINTER_TYPE(DropDownList); ...@@ -23,6 +24,7 @@ DECLARE_SHARED_POINTER_TYPE(DropDownList);
DECLARE_TYPEOF_COLLECTION(WordListP); DECLARE_TYPEOF_COLLECTION(WordListP);
DECLARE_TYPEOF_COLLECTION(WordListWordP); DECLARE_TYPEOF_COLLECTION(WordListWordP);
DECLARE_TYPEOF_COLLECTION(WordListPosP); DECLARE_TYPEOF_COLLECTION(WordListPosP);
DECLARE_TYPEOF_COLLECTION(AutoReplaceP);
// ----------------------------------------------------------------------------- : TextValueEditorScrollBar // ----------------------------------------------------------------------------- : TextValueEditorScrollBar
...@@ -273,8 +275,8 @@ bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -273,8 +275,8 @@ bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) {
if (select_words) { if (select_words) {
// on the left, swap start and end // on the left, swap start and end
bool left = selection_end_i < selection_start_i; bool left = selection_end_i < selection_start_i;
size_t next = nextWordBoundry(index); size_t next = nextWordBoundary(index);
size_t prev = prevWordBoundry(index); size_t prev = prevWordBoundary(index);
if (( left && next > max(selection_start_i, selection_end_i)) || if (( left && next > max(selection_start_i, selection_end_i)) ||
(!left && prev < min(selection_start_i, selection_end_i))) { (!left && prev < min(selection_start_i, selection_end_i))) {
left = !left; left = !left;
...@@ -294,8 +296,8 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -294,8 +296,8 @@ bool TextValueEditor::onLeftDClick(const RealPoint& pos, wxMouseEvent& ev) {
select_words = true; select_words = true;
selecting = true; selecting = true;
size_t index = v.indexAt(style().getRotation().trInv(pos)); size_t index = v.indexAt(style().getRotation().trInv(pos));
moveSelection(TYPE_INDEX, prevWordBoundry(index), true, MOVE_MID); moveSelection(TYPE_INDEX, prevWordBoundary(index), true, MOVE_MID);
moveSelection(TYPE_INDEX, nextWordBoundry(index), false, MOVE_MID); moveSelection(TYPE_INDEX, nextWordBoundary(index), false, MOVE_MID);
return true; return true;
} }
...@@ -323,17 +325,17 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { ...@@ -323,17 +325,17 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_LEFT: case WXK_LEFT:
// move left (selection?) // move left (selection?)
if (ev.ControlDown()) { 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 { } else {
moveSelection(TYPE_CURSOR, prevCharBoundry(selection_end), !ev.ShiftDown(), MOVE_LEFT); moveSelection(TYPE_CURSOR, prevCharBoundary(selection_end), !ev.ShiftDown(), MOVE_LEFT);
} }
break; break;
case WXK_RIGHT: case WXK_RIGHT:
// move left (selection?) // move left (selection?)
if (ev.ControlDown()) { 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 { } else {
moveSelection(TYPE_CURSOR, nextCharBoundry(selection_end), !ev.ShiftDown(), MOVE_RIGHT); moveSelection(TYPE_CURSOR, nextCharBoundary(selection_end), !ev.ShiftDown(), MOVE_RIGHT);
} }
break; break;
case WXK_UP: case WXK_UP:
...@@ -369,10 +371,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { ...@@ -369,10 +371,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_BACK: case WXK_BACK:
if (selection_start == selection_end) { if (selection_start == selection_end) {
// if no selection, select previous character // 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) { if (selection_start == selection_end) {
// Walk over a <sep> as if we are the LEFT key // 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; return true;
} }
} }
...@@ -381,10 +383,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { ...@@ -381,10 +383,10 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
case WXK_DELETE: case WXK_DELETE:
if (selection_start == selection_end) { if (selection_start == selection_end) {
// if no selection select next // if no selection select next
moveSelectionNoRedraw(TYPE_CURSOR, nextCharBoundry(selection_end), false); moveSelectionNoRedraw(TYPE_CURSOR, nextCharBoundary(selection_end), false);
if (selection_start == selection_end) { if (selection_start == selection_end) {
// Walk over a <sep> as if we are the RIGHT key // 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")); replaceSelection(wxEmptyString, _ACTION_("delete"));
...@@ -411,9 +413,9 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { ...@@ -411,9 +413,9 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) {
// this might not work for internationalized input. // this might not work for internationalized input.
// It might also not be portable! // It might also not be portable!
#ifdef UNICODE #ifdef UNICODE
replaceSelection(escape(String(ev.GetUnicodeKey(), 1)), _ACTION_("typing")); replaceSelection(escape(String(ev.GetUnicodeKey(), 1)), _ACTION_("typing"), true);
#else #else
replaceSelection(escape(String((Char)ev.GetKeyCode(), 1)), _ACTION_("typing")); replaceSelection(escape(String((Char)ev.GetKeyCode(), 1)), _ACTION_("typing"), true);
#endif #endif
} else { } else {
return false; return false;
...@@ -775,7 +777,7 @@ size_t match_cursor_position(size_t pos1, const String& text1, size_t pos2, cons ...@@ -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 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) { if (replacement.empty() && selection_start == selection_end) {
// no text selected, nothing to delete // no text selected, nothing to delete
return; return;
...@@ -785,10 +787,10 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& ...@@ -785,10 +787,10 @@ 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
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) { 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_end);
return; return;
} }
// what we would expect if no scripts take place // what we would expect if no scripts take place
...@@ -832,10 +834,31 @@ void TextValueEditor::replaceSelection(const String& replacement, const String& ...@@ -832,10 +834,31 @@ void TextValueEditor::replaceSelection(const String& replacement, const String&
selection_end = selection_start = best_cursor; selection_end = selection_start = best_cursor;
fixSelection(TYPE_CURSOR, MOVE_RIGHT); fixSelection(TYPE_CURSOR, MOVE_RIGHT);
} }
// auto replace after typing?
if (allow_auto_replace) tryAutoReplace();
// scroll with next update // scroll with next update
scroll_with_cursor = true; 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) { 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_start = selection_start_i;
size_t old_end = selection_end_i; size_t old_end = selection_end_i;
...@@ -891,28 +914,64 @@ void TextValueEditor::fixSelection(IndexType t, Movement dir) { ...@@ -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); 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); 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(); 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; 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; if (p == String::npos) return 0;
return p + 1; 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(); 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(); 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(); if (p == String::npos) return val.size();
return p; 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) { void TextValueEditor::select(size_t start, size_t end) {
selection_start = start; selection_start = start;
...@@ -1129,7 +1188,7 @@ void TextValueEditor::drawWordListIndicators(RotatedDC& dc) { ...@@ -1129,7 +1188,7 @@ void TextValueEditor::drawWordListIndicators(RotatedDC& dc) {
if (current && wl->active) { if (current && wl->active) {
dc.SetPen (Color(0, 128,255)); dc.SetPen (Color(0, 128,255));
dc.SetBrush(Color(64, 160,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.SetPen (Color(64, 160,255));
dc.SetBrush(Color(160,208,255)); dc.SetBrush(Color(160,208,255));
} else { } else {
......
...@@ -116,7 +116,9 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -116,7 +116,9 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
/// Replace the current selection with 'replacement', name the action /// Replace the current selection with 'replacement', name the action
/** replacement should be a tagged string (i.e. already escaped) */ /** 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 /// Make sure the selection satisfies its constraints
/** - selection_start and selection_end are inside the text /** - selection_start and selection_end are inside the text
...@@ -135,12 +137,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -135,12 +137,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
/// Position of previous visible & selectable character /// Position of previous visible & selectable character
/** Uses cursor positions */ /** Uses cursor positions */
size_t prevCharBoundry(size_t pos) const; size_t prevCharBoundary(size_t pos) const;
size_t nextCharBoundry(size_t pos) const; size_t nextCharBoundary(size_t pos) const;
/// Front of previous word, used witch Ctrl+Left/right /// Front of previous word, used witch Ctrl+Left/right
/** Uses character indices */ /** Uses character indices */
size_t prevWordBoundry(size_t pos_i) const; size_t prevWordBoundary(size_t pos_i) const;
size_t nextWordBoundry(size_t pos_i) const; size_t nextWordBoundary(size_t pos_i) const;
bool isWordBoundary(size_t pos_i) const;
// --------------------------------------------------- : Scrolling // --------------------------------------------------- : Scrolling
......
...@@ -947,6 +947,12 @@ ...@@ -947,6 +947,12 @@
<File <File
RelativePath=".\gui\about_window.hpp"> RelativePath=".\gui\about_window.hpp">
</File> </File>
<File
RelativePath=".\gui\auto_replace_window.cpp">
</File>
<File
RelativePath=".\gui\auto_replace_window.hpp">
</File>
<File <File
RelativePath=".\gui\card_select_window.cpp"> RelativePath=".\gui\card_select_window.cpp">
</File> </File>
......
# This file contains the keys expected to be in MSE locales # This file contains the keys expected to be in MSE locales
# It was automatically generated by tools/locale/locale.pl # It was automatically generated by tools/locale/locale.pl
# Generated on Fri Aug 17 22:58:51 2007 # Generated on Sun Aug 26 21:17:38 2007
action: action:
add control point: 0 add control point: 0
add part: 1 add part: 1
add symmetry: 0 add symmetry: 0
auto replace: 0
backspace: 0 backspace: 0
change: 1 change: 1
change combine mode: 0 change combine mode: 0
...@@ -38,11 +39,14 @@ action: ...@@ -38,11 +39,14 @@ action:
typing: 0 typing: 0
ungroup parts: 0 ungroup parts: 0
button: button:
add item: 0
always: 0 always: 0
browse: 0 browse: 0
check now: 0 check now: 0
close: 0 close: 0
defaults: 0
edit symbol: 0 edit symbol: 0
enabled: 0
hide: 0 hide: 0
high quality: 0 high quality: 0
if internet connection exists: 0 if internet connection exists: 0
...@@ -58,14 +62,17 @@ button: ...@@ -58,14 +62,17 @@ button:
open set: 0 open set: 0
overwrite: 0 overwrite: 0
refer parameter: 0 refer parameter: 0
remove item: 0
select: optional, 0 select: optional, 0
select all: 0 select all: 0
select none: 0 select none: 0
show: 0 show: 0
show lines: 0 show lines: 0
symbol gallery: optional, 0 symbol gallery: optional, 0
use auto replace: 0
use custom styling options: 0 use custom styling options: 0
use for all cards: 0 use for all cards: 0
whole word: 0
zoom export: 0 zoom export: 0
error: error:
aborting parsing: 0 aborting parsing: 0
...@@ -96,6 +103,7 @@ error: ...@@ -96,6 +103,7 @@ error:
unsupported field type: 1 unsupported field type: 1
unsupported fill type: 1 unsupported fill type: 1
unsupported format: 1 unsupported format: 1
word list type not found: 1
help: help:
about: 0 about: 0
add card: 0 add card: 0
...@@ -103,6 +111,7 @@ help: ...@@ -103,6 +111,7 @@ help:
add keyword: 0 add keyword: 0
add symmetry: 0 add symmetry: 0
app language: 0 app language: 0
auto replace: 0
basic shapes: 0 basic shapes: 0
bold: 0 bold: 0
border: 0 border: 0
...@@ -142,6 +151,8 @@ help: ...@@ -142,6 +151,8 @@ help:
export images: 0 export images: 0
export mws: 0 export mws: 0
filename format: 0 filename format: 0
find: 0
find next: 0
free point: 0 free point: 0
grid: 0 grid: 0
group: 0 group: 0
...@@ -180,6 +191,7 @@ help: ...@@ -180,6 +191,7 @@ help:
remove card: 0 remove card: 0
remove keyword: 0 remove keyword: 0
remove symmetry: 0 remove symmetry: 0
replace: 0
rotate: 0 rotate: 0
rotate 0: 0 rotate 0: 0
rotate 180: 0 rotate 180: 0
...@@ -215,6 +227,8 @@ label: ...@@ -215,6 +227,8 @@ label:
apprentice: 0 apprentice: 0
apprentice exe: 0 apprentice exe: 0
apprentice export cancelled: 0 apprentice export cancelled: 0
auto match: 0
auto replace: 0
card display: 0 card display: 0
card notes: 0 card notes: 0
cards to export: 0 cards to export: 0
...@@ -255,6 +269,7 @@ menu: ...@@ -255,6 +269,7 @@ menu:
add card: 0 add card: 0
add cards: 0 add cards: 0
add keyword: 0 add keyword: 0
auto replace: 0
basic shapes: 0 basic shapes: 0
bold: 0 bold: 0
card list columns: 0 card list columns: 0
...@@ -331,6 +346,7 @@ menu: ...@@ -331,6 +346,7 @@ menu:
title: title:
%s - magic set editor: 1 %s - magic set editor: 1
about: 0 about: 0
auto replaces: 0
directories: 0 directories: 0
display: 0 display: 0
export cancelled: 0 export cancelled: 0
......
...@@ -64,8 +64,8 @@ void Writer::handle(const String& value) { ...@@ -64,8 +64,8 @@ void Writer::handle(const String& value) {
// write indentation and key // write indentation and key
writeKey(); writeKey();
writeUTF8(stream, _(": ")); writeUTF8(stream, _(": "));
if (value.find_first_of(_('\n')) != String::npos) { if (value.find_first_of(_('\n')) != String::npos || (!value.empty() && isSpace(value.GetChar(0)))) {
// multiline string // multiline string, or contains leading whitespace
stream.PutChar(_('\n')); stream.PutChar(_('\n'));
indentation += 1; indentation += 1;
// split lines, and write each line // split lines, and write each line
......
...@@ -53,7 +53,8 @@ enum MenuID { ...@@ -53,7 +53,8 @@ enum MenuID {
, ID_EDIT_FIND = wxID_FIND , ID_EDIT_FIND = wxID_FIND
, ID_EDIT_FIND_NEXT = 103 , ID_EDIT_FIND_NEXT = 103
, ID_EDIT_REPLACE = wxID_REPLACE , ID_EDIT_REPLACE = wxID_REPLACE
, ID_EDIT_PREFERENCES = 104 , ID_EDIT_AUTO_REPLACE = 104
, ID_EDIT_PREFERENCES = 105
// Window menu (MainWindow) // Window menu (MainWindow)
, ID_WINDOW_NEW = 201 , ID_WINDOW_NEW = 201
...@@ -242,7 +243,13 @@ enum ControlID { ...@@ -242,7 +243,13 @@ enum ControlID {
, ID_INSTALL , ID_INSTALL
, ID_UPGRADE , ID_UPGRADE
, ID_REMOVE , 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 // ----------------------------------------------------------------------------- : EOF
......
...@@ -16,7 +16,7 @@ close F; ...@@ -16,7 +16,7 @@ close F;
# insert stuff # insert stuff
$hpp =~ s/_\n/_$macro\n/g; $hpp =~ s/_\r?\n/_$macro\n/g;
$cpp =~ s@<util/prec.hpp>@<$file.hpp>@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