diff --git a/src/data/action/value.hpp b/src/data/action/value.hpp index d7ad28c0..a85c838c 100644 --- a/src/data/action/value.hpp +++ b/src/data/action/value.hpp @@ -94,6 +94,29 @@ class TextToggleReminderAction : public ValueAction { Char old; ///< Old value of the & new_value); + virtual void perform(bool to_undo); + bool merge(const SimpleTextValueAction& action); + private: + Defaultable new_value; +}; + +/// An action from "Replace All"; just a bunch of value actions performed in sequence +class ReplaceAllAction : public Action { + public: + ~ReplaceAllAction(); + + virtual String getName(bool to_undo) const; + virtual void perform(bool to_undo); + + vector actions; +}; + // ----------------------------------------------------------------------------- : Event /// Notification that a script caused a value to change diff --git a/src/gui/control/card_editor.cpp b/src/gui/control/card_editor.cpp index 116914fd..361b22c5 100644 --- a/src/gui/control/card_editor.cpp +++ b/src/gui/control/card_editor.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include DECLARE_TYPEOF_COLLECTION(ValueViewerP); @@ -133,6 +134,22 @@ void DataEditor::onInit() { current_viewer = nullptr; current_editor = nullptr; } +// ----------------------------------------------------------------------------- : Search / replace + +bool DataEditor::search(FindInfo& find, bool from_start) { + bool include = from_start; + for (size_t i = 0 ; i < by_tab_index.size() ; ++i) { + ValueViewer& viewer = *by_tab_index[find.forward() ? i : by_tab_index.size() - i - 1]; + if (&viewer == current_viewer) include = true; + if (include) { + ValueEditor* editor = viewer.getEditor(); + if (editor && editor->search(find, from_start || &viewer != current_viewer)) { + return true; // done + } + } + } + return false; // not done +} // ----------------------------------------------------------------------------- : Clipboard & Formatting diff --git a/src/gui/control/card_editor.hpp b/src/gui/control/card_editor.hpp index 328831b0..b0f77f0a 100644 --- a/src/gui/control/card_editor.hpp +++ b/src/gui/control/card_editor.hpp @@ -66,7 +66,7 @@ class DataEditor : public CardViewer { /** If from_start == false: searches only from the current selection onward (or backward) * If from_start == true: searches everything * - * Returns true when more searching is needed. + * Returns true if we are done and searching should be ended. */ bool search(FindInfo& find, bool from_start); diff --git a/src/gui/control/card_list.hpp b/src/gui/control/card_list.hpp index 44ba8b5a..b00aacfd 100644 --- a/src/gui/control/card_list.hpp +++ b/src/gui/control/card_list.hpp @@ -69,11 +69,12 @@ class CardListBase : public ItemList, public SetView { virtual void onAction(const Action&, bool undone); // --------------------------------------------------- : The cards + public: + /// Return the card at the given position in the sorted card list + inline CardP getCard(long pos) const { return static_pointer_cast(getItem(pos)); } protected: /// Get a list of all cards virtual void getItems(vector& out) const; - /// Return the card at the given position in the sorted card list - inline CardP getCard(long pos) const { return static_pointer_cast(getItem(pos)); } /// Rebuild the card list (clear all vectors and fill them again) void rebuild(); diff --git a/src/gui/set/cards_panel.cpp b/src/gui/set/cards_panel.cpp index be880a49..cce05ef0 100644 --- a/src/gui/set/cards_panel.cpp +++ b/src/gui/set/cards_panel.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -266,6 +268,61 @@ void CardsPanel::doPaste() { CUT_COPY_PASTE(doPaste, ;) } // ----------------------------------------------------------------------------- : Searching +class CardsPanel::SearchFindInfo : public FindInfo { + public: + SearchFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {} + virtual bool handle(const CardP& card, const TextValueP& value, size_t pos) { + // Select the card + panel.card_list->setCard(card); + return true; + } + private: + CardsPanel& panel; +}; + +class CardsPanel::ReplaceFindInfo : public FindInfo { + public: + ReplaceFindInfo(CardsPanel& panel, wxFindReplaceData& what) : FindInfo(what), panel(panel) {} + virtual bool handle(const CardP& card, const TextValueP& value, size_t pos) { + // Select the card + panel.card_list->setCard(card); + // Replace + panel.editor->insert(escape(what.GetReplaceString()), _("Replace")); + return true; + } + private: + CardsPanel& panel; +}; + +bool CardsPanel::doFind(wxFindReplaceData& what) { + SearchFindInfo find(*this, what); + return search(find, false); +} +bool CardsPanel::doReplace(wxFindReplaceData& what) { + ReplaceFindInfo find(*this, what); + return search(find, false); +} +bool CardsPanel::doReplaceAll(wxFindReplaceData& what) { + return false; // TODO +} + +bool CardsPanel::search(FindInfo& find, bool from_start) { + bool include = from_start; + CardP current = card_list->getCard(); + for (size_t i = 0 ; i < set->cards.size() ; ++i) { + CardP card = card_list->getCard( (long) (find.forward() ? i : set->cards.size() - i - 1) ); + if (card == current) include = true; + if (include) { + editor->setCard(card); + if (editor->search(find, from_start || card != current)) { + return true; // done + } + } + } + editor->setCard(current); + return false; +} + // ----------------------------------------------------------------------------- : Selection CardP CardsPanel::selectedCard() const { diff --git a/src/gui/set/cards_panel.hpp b/src/gui/set/cards_panel.hpp index 0fe89f15..d6e83dc9 100644 --- a/src/gui/set/cards_panel.hpp +++ b/src/gui/set/cards_panel.hpp @@ -18,6 +18,7 @@ class DataEditor; class TextCtrl; class IconMenu; class HoverButton; +class FindInfo; // ----------------------------------------------------------------------------- : CardsPanel @@ -39,7 +40,6 @@ class CardsPanel : public SetWindowPanel { // --------------------------------------------------- : Actions virtual bool wantsToHandle(const Action&, bool undone) const; - public: // --------------------------------------------------- : Clipboard virtual bool canCut() const; @@ -50,39 +50,19 @@ class CardsPanel : public SetWindowPanel { virtual void doPaste(); // --------------------------------------------------- : Searching (find/replace) -#if 0 - virtual bool canFind() const; - virtual bool canReplace() const; - virtual bool doFind(wxFindReplaceData& what); - virtual bool doReplace(wxFindReplaceData& what); + + virtual bool canFind() const { return true; } + virtual bool canReplace() const { return true; } + virtual bool doFind (wxFindReplaceData&); + virtual bool doReplace (wxFindReplaceData&); + virtual bool doReplaceAll(wxFindReplaceData&); private: - // Functions that handle finding - typedef void (CardsPanel::*FindHandler)(const CardP&, const TextValueP&, const size_t, const size_t, wxFindReplaceData&); - - /// Execute a find (or replace), and start with the currently selected card and value - /** if findSame==true then find will also find the currently highlighted word - * Returns true if found - */ - bool find(FindReplaceData& what, const FindHandler& handler, bool findSame = false); - - /// find handler : select found value - void handleFind(const CardP& card, const TextValueP& value, size_t start, size_t end, FindReplaceData& what); - - /// replace handler : replace found value, move selection to end - void handleReplace(const CardP& card, const TextValueP& value, size_t start, size_t end, FindReplaceData& what); - - /// Find in all cards - /** NOTE: this function is essentially the same as findInCard */ - bool findInCards(const CardP& firstCard, const ValueP& firstValue, int firstChar, FindReplaceData& what, const FindHandler& handler); - - /// Find in a card, if firstValue is specified start searching there - /** NOTE: this function is essentially the same as findInCards */ - bool findInCard(const CardP& card, const ValueP& firstValue, int firstChar, FindReplaceData& what, const FindHandler& handler); - - /// Find the current search string in the specified value - /** if searchDir = up searches from the end and only before firstChar, unless firstChar == -1 */ - bool findInValue(const CardP& crd_, virtual const ValueP& value, int firstChar, FindReplaceData& what, const FindHandler& handler); -#endif + /// Do a search or replace action for the given FindInfo in all cards + bool search(FindInfo& find, bool from_start); + class SearchFindInfo; + class ReplaceFindInfo; + friend class CardsPanel::SearchFindInfo; + friend class CardsPanel::ReplaceFindInfo; public: // --------------------------------------------------- : Selection diff --git a/src/gui/set/panel.hpp b/src/gui/set/panel.hpp index 038d9afa..d9b06cee 100644 --- a/src/gui/set/panel.hpp +++ b/src/gui/set/panel.hpp @@ -61,8 +61,9 @@ class SetWindowPanel : public wxPanel, public SetView { // --------------------------------------------------- : Searching (find/replace) virtual bool canFind() const { return false; } ///< Is finding possible? virtual bool canReplace() const { return false; } ///< Is replacing possible? - virtual bool doFind(wxFindReplaceData&) { return false; } ///< Find the next math - virtual bool doReplace(wxFindReplaceData&) { return false; } ///< Replace the next match + virtual bool doFind (wxFindReplaceData&) { return false; } ///< Find the next math + virtual bool doReplace (wxFindReplaceData&) { return false; } ///< Replace the next match + virtual bool doReplaceAll(wxFindReplaceData&) { return false; } ///< Replace all matches // --------------------------------------------------- : Selection virtual CardP selectedCard() const { return CardP(); } ///< Return the currently selected card, or CardP() diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index b7516f58..b04dbc7b 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -43,6 +43,7 @@ SetWindow::SetWindow(Window* parent, const SetP& set) : wxFrame(parent, wxID_ANY, _TITLE_("magic set editor"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE) , current_panel(nullptr) , find_dialog(nullptr) + , find_data(wxFR_DOWN) , number_of_recent_sets(0) { SetIcon(load_resource_icon(_("app"))); @@ -537,7 +538,7 @@ void SetWindow::onReplace (wxFindDialogEvent&) { current_panel->doReplace(find_data); } void SetWindow::onReplaceAll(wxFindDialogEvent&) { - // todo + current_panel->doReplaceAll(find_data); } void SetWindow::onEditPreferences(wxCommandEvent&) { @@ -627,10 +628,10 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame) EVT_GALLERY_SELECT (ID_FIELD_LIST, SetWindow::onChildMenu) // for StatsPanel, because it is not a EVT_TOOL EVT_UPDATE_UI (wxID_ANY, SetWindow::onUpdateUI) -// EVT_FIND (wxID_ANY, SetWindow::onFind) -// EVT_FIND_NEXT (wxID_ANY, SetWindow::onFindNext) -// EVT_FIND_REPLACE (wxID_ANY, SetWindow::onReplace) -// EVT_FIND_REPLACE_ALL(wxID_ANY, SetWindow::onReplaceAll) + EVT_FIND (wxID_ANY, SetWindow::onFind) + EVT_FIND_NEXT (wxID_ANY, SetWindow::onFindNext) + EVT_FIND_REPLACE (wxID_ANY, SetWindow::onReplace) + EVT_FIND_REPLACE_ALL(wxID_ANY, SetWindow::onReplaceAll) EVT_CLOSE ( SetWindow::onClose) EVT_IDLE ( SetWindow::onIdle) EVT_CARD_SELECT (wxID_ANY, SetWindow::onCardSelect) diff --git a/src/gui/value/editor.hpp b/src/gui/value/editor.hpp index c90d8a30..f552128e 100644 --- a/src/gui/value/editor.hpp +++ b/src/gui/value/editor.hpp @@ -106,9 +106,9 @@ class ValueEditor { * excluding the sellection itself. * If from_start == true: searches everything * - * Returns true when more searching is needed. + * Returns true if we are done and searching should be ended. */ - bool search(FindInfo& find, bool from_start); + virtual bool search(FindInfo& find, bool from_start) { return false; } // --------------------------------------------------- : Other diff --git a/src/gui/value/text.cpp b/src/gui/value/text.cpp index 3b66fd7c..3ad594a5 100644 --- a/src/gui/value/text.cpp +++ b/src/gui/value/text.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -195,7 +196,8 @@ bool TextValueEditor::onChar(wxKeyEvent& ev) { } break; default: - if (ev.GetKeyCode() >= _(' ') /*&& ev.GetKeyCode() == (int)ev.GetRawKeyCode()*/) { +// if (ev.GetKeyCode() >= _(' ') /*&& ev.GetKeyCode() == (int)ev.GetRawKeyCode()*/) { + if (ev.GetKeyCode() >= _(' ') && ev.GetKeyCode() == (int)ev.GetRawKeyCode()) { // TODO: Find a more correct way to determine normal characters, // this might not work for internationalized input. // It might also not be portable! @@ -656,6 +658,60 @@ size_t TextValueEditor::move(size_t pos, size_t start, size_t end, Movement dir) else return start; } +// ----------------------------------------------------------------------------- : Search / replace + +bool is_word_end(const String& s, size_t pos) { + if (pos == 0 || pos >= s.size()) return true; + Char c = s.GetChar(pos); + return isSpace(c) || isPunct(c); +} + +// is find.findString() at postion pos of s +bool TextValueEditor::matchSubstr(const String& s, size_t pos, FindInfo& find) { + if (find.wholeWord()) { + if (!is_word_end(s, pos - 1) || !is_word_end(s, pos + find.findString().size())) return false; + } + if (find.caseSensitive()) { + if (!is_substr(s, pos, find.findString())) return false; + } else { + if (!is_substr(s, pos, find.findString().Lower())) return false; + } + // handle + if (find.select()) { + editor().select(this); + editor().SetFocus(); + selection_start_i = untagged_to_index(value().value(), pos, true); + selection_end_i = untagged_to_index(value().value(), pos + find.findString().size(), true); + fixSelection(TYPE_INDEX); + } + if (find.handle(viewer.getCard(), valueP(), pos)) { + return true; + } else { + // TODO: string might have changed when doing replace all + return false; + } +} + +bool TextValueEditor::search(FindInfo& find, bool from_start) { + String v = untag(value().value()); + if (!find.caseSensitive()) v.LowerCase(); + if (find.forward()) { + size_t start = min(v.size(), max(selection_start, selection_end)); + size_t end = max(0, (int)v.size() - (int)find.findString().size()); + for (size_t i = start ; i <= end ; ++i) { + if (matchSubstr(v, i, find)) return true; + } + } else { + size_t start = 0; + int end = (int)min(selection_start, selection_end) - (int)find.findString().size(); + if (end < 0) return false; + for (size_t i = end ; i >= start ; --i) { + if (matchSubstr(v, i, find)) return true; + } + } + return false; +} + // ----------------------------------------------------------------------------- : Native look / scrollbar void TextValueEditor::determineSize(bool force_fit) { diff --git a/src/gui/value/text.hpp b/src/gui/value/text.hpp index 69968170..36b8bd34 100644 --- a/src/gui/value/text.hpp +++ b/src/gui/value/text.hpp @@ -15,24 +15,6 @@ #include class TextValueEditorScrollBar; -class wxFindReplaceData; -DECLARE_POINTER_TYPE(Card); - -// ----------------------------------------------------------------------------- : Search/replace - -/// Information for search/replace -class FindInfo { - public: - FindInfo(wxFindReplaceData& what) : what(what) {} - virtual ~FindInfo() {} - - /// Handle that a match was found. - /** Should return whether more searching is needed. - */ - virtual bool handle(const CardP& card, const TextValueP& value, size_t start, size_t end) = 0; - - wxFindReplaceData& what; ///< What to search for, the direction to search in -}; // ----------------------------------------------------------------------------- : TextValueEditor @@ -94,6 +76,13 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { virtual void insert(const String& text, const String& action_name); + // --------------------------------------------------- : Search/replace + + virtual bool search(FindInfo& find, bool from_start); + private: + bool matchSubstr(const String& s, size_t pos, FindInfo& find); + public: + // --------------------------------------------------- : Other virtual wxCursor cursor() const; diff --git a/src/mse.vcproj b/src/mse.vcproj index 0a34df9e..55eddd45 100644 --- a/src/mse.vcproj +++ b/src/mse.vcproj @@ -1919,6 +1919,9 @@ + + @@ -2930,10 +2933,10 @@ RelativePath="..\conversion-todo.txt"> + RelativePath="..\data\nl.mse-locale\locale"> + RelativePath="..\data\en.mse-locale\locale"> diff --git a/src/render/card/viewer.cpp b/src/render/card/viewer.cpp index dc59abc7..efa89876 100644 --- a/src/render/card/viewer.cpp +++ b/src/render/card/viewer.cpp @@ -68,7 +68,8 @@ Rotation DataViewer::getRotation() const { // ----------------------------------------------------------------------------- : Setting data void DataViewer::setCard(const CardP& card) { - if (!card) return; // TODO: clear vie? + if (!card) return; // TODO: clear viewer? + if (this->card == card) return; // already set assert(set); this->card = card; stylesheet = set->stylesheetFor(card); diff --git a/src/render/text/viewer.cpp b/src/render/text/viewer.cpp index 17fa636b..c4b21c59 100644 --- a/src/render/text/viewer.cpp +++ b/src/render/text/viewer.cpp @@ -317,7 +317,7 @@ void TextViewer::prepareLines(RotatedDC& dc, const String& text, const TextStyle vector chars; scale = 1; double min_scale = elements.minScale(); - double scale_step = max(0.1,elements.scaleStep()); + double scale_step = max(0.05,elements.scaleStep()); while (true) { double next_scale = scale - scale_step; bool last = next_scale < min_scale; diff --git a/src/util/error.cpp b/src/util/error.cpp index b89af690..5fd8d2db 100644 --- a/src/util/error.cpp +++ b/src/util/error.cpp @@ -61,7 +61,7 @@ void handle_error(const String& e, bool allow_duplicate = true, bool now = true) // TODO: thread safety if (!allow_duplicate) { FOR_EACH(pe, previous_errors) { - if (e == pe) return; + if (e == pe) return; } previous_errors.push_back(e); } diff --git a/src/util/string.hpp b/src/util/string.hpp index 99df5750..3a4a40fb 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -78,6 +78,7 @@ inline bool isDigit(Char c) { return IF_UNICODE( iswdigit(c) , isdigit(c) ); } inline bool isAlnum(Char c) { return IF_UNICODE( iswalnum(c) , isalnum(c) ); } inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper(c) ); } inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower(c) ); } +inline bool isPunct(Char c) { return IF_UNICODE( iswpunct(c) , ispunct(c) ); } // Character conversions inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); } inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }