Commit ba314ce2 authored by twanvl's avatar twanvl

Added word lists for choosing things like card type;

Added 'in_place' pattern to spec_sort
parent ca95f047
...@@ -35,6 +35,9 @@ Parts ...@@ -35,6 +35,9 @@ Parts
| @"pattern(.z. xyz)"@ @"yzz"@ Selects all things that match the pattern, where @"."@ is a wildcard. | @"pattern(.z. xyz)"@ @"yzz"@ Selects all things that match the pattern, where @"."@ is a wildcard.
The things matching the wildcards are then sorted using the given pattern (separated by a space), and subsituted back in. The things matching the wildcards are then sorted using the given pattern (separated by a space), and subsituted back in.
So in <tt>"zzyyxxy"</tt> the pattern matches <tt>"<b>zzy</b>xxy"</tt> with wildcards <tt>"zy"</tt> these sort as <tt>"yz"</tt> and in the pattern this becomes <tt>"yzz"</tt>. So in <tt>"zzyyxxy"</tt> the pattern matches <tt>"<b>zzy</b>xxy"</tt> with wildcards <tt>"zy"</tt> these sort as <tt>"yz"</tt> and in the pattern this becomes <tt>"yzz"</tt>.
| @"in_place(yz)"@ @"yyyzxxz"@ Sort everything with the given pattern, and insert the remaining characters in their orignal places.<br/>
For example, in @"zzyyxxy"@ we match <tt>y</tt> and <tt>z</tt>, leaving @"....xx."@.
The result of the pattern is @"yyyzz"@, subsituting that back in gives @"yyyzxxz"@.
The parts are read from left to right, each part that matches something removes it from the input, so for example The parts are read from left to right, each part that matches something removes it from the input, so for example
> sort_text(order: "xx") > sort_text(order: "xx")
......
...@@ -40,6 +40,7 @@ Such a package contains a [[file:format|data file]] called <tt>game</tt> that ha ...@@ -40,6 +40,7 @@ Such a package contains a [[file:format|data file]] called <tt>game</tt> that ha
| @keyword modes@ [[type:list]] of [[type:keyword mode]]s Choices for the 'mode' property of keywords. | @keyword modes@ [[type:list]] of [[type:keyword mode]]s Choices for the 'mode' property of keywords.
| @keyword parameter types@ [[type:list]] of [[type:keyword param type]]s Types of parameters available to keywords. | @keyword parameter types@ [[type:list]] of [[type:keyword param type]]s Types of parameters available to keywords.
| @keywords@ [[type:list]] of [[type:keyword]]s Standard keywords for this game. | @keywords@ [[type:list]] of [[type:keyword]]s Standard keywords for this game.
| @word lists@ [[type:list]] of [[type:word list]]s Word lists that can be used by text fields.
--Examples-- --Examples--
Look at the game files in the standard MSE distribution for examples. Look at the game files in the standard MSE distribution for examples.
...@@ -37,6 +37,8 @@ This is written as the character with code 1 in files. ...@@ -37,6 +37,8 @@ This is written as the character with code 1 in files.
Inserting this tag manually will confuse that function!<br/> Inserting this tag manually will confuse that function!<br/>
This tag can never be selected, and its contents can not be edited. This tag can never be selected, and its contents can not be edited.
| @"<sep-soft>"@ Like @"<sep>"@, only hidden. This is inserted by [[fun:combined_editor]] | @"<sep-soft>"@ Like @"<sep>"@, only hidden. This is inserted by [[fun:combined_editor]]
| @"<word-list-?>"@ Indicate that the text inside the tag should be selected from a [[type:word list]].
The <tt>?</tt> must be the name of a word list in the game.
| any other tag Other tags are ignored. | any other tag Other tags are ignored.
--Related functions-- --Related functions--
......
Data type: word list
A list of words. Used for drop down lists in the text editor, for example for card types.
--Properties--
! Property Type Default Description
| @name@ [[type:string]] ''Required'' Name of this word list, refered to using a @"<word-list-...>"@ tag.
| @words@ [[type:list]] of [[type:word list word]]s ''Required'' The words in the list
--Example--
>word list:
> name: type
> word: Creature
> word: Spell
> word: Artifact
This can be used with for example:
> @"<word-list-type>Creature</word-list-type>"@
Which gives the creature choice, and that can be changed with a drop down list.
Data type: word list word
A word in a [[type:word list]].
--Properties--
! Property Type Default Description
| @name@ [[type:string]] ''Required'' The word
| @line below@ [[type:boolean]] @false@ Display a line below this item in the list?
| @words@ [[type:list]] of [[type:word list word]]s A submenu
A word can also be given in a short form, in that case only the name is specified.
--Example--
In short form:
>word: xyz
Is the same as:
>word:
> name: xyz
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <data/keyword.hpp> #include <data/keyword.hpp>
#include <data/statistics.hpp> #include <data/statistics.hpp>
#include <data/pack.hpp> #include <data/pack.hpp>
#include <data/word_list.hpp>
#include <util/io/package_manager.hpp> #include <util/io/package_manager.hpp>
#include <script/script.hpp> #include <script/script.hpp>
...@@ -56,7 +57,7 @@ IMPLEMENT_REFLECTION(Game) { ...@@ -56,7 +57,7 @@ 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(word_lists);
} }
void Game::validate(Version v) { void Game::validate(Version v) {
......
...@@ -24,6 +24,7 @@ DECLARE_POINTER_TYPE(PackType); ...@@ -24,6 +24,7 @@ DECLARE_POINTER_TYPE(PackType);
DECLARE_POINTER_TYPE(KeywordParam); DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode); DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword); DECLARE_POINTER_TYPE(Keyword);
DECLARE_POINTER_TYPE(WordList);
// ----------------------------------------------------------------------------- : Game // ----------------------------------------------------------------------------- : Game
...@@ -43,6 +44,7 @@ class Game : public Packaged { ...@@ -43,6 +44,7 @@ class Game : public Packaged {
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
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
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
......
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <data/word_list.hpp>
// ----------------------------------------------------------------------------- : WordList
WordListWord::WordListWord()
: line_below(false)
, is_prefix(false)
{}
IMPLEMENT_REFLECTION_NO_SCRIPT(WordListWord) {
if (line_below || is_prefix || isGroup() || (tag.reading() && tag.isComplex())) {
// complex value
REFLECT(name);
REFLECT(line_below);
REFLECT(is_prefix);
REFLECT(words);
} else {
REFLECT_NAMELESS(name);
}
}
IMPLEMENT_REFLECTION_NO_SCRIPT(WordList) {
REFLECT(name);
REFLECT(words);
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2007 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_WORD_LIST
#define HEADER_DATA_WORD_LIST
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/reflect.hpp>
DECLARE_POINTER_TYPE(WordListWord);
DECLARE_POINTER_TYPE(WordList);
// ----------------------------------------------------------------------------- : WordList
/// A word in a WordList
class WordListWord : public IntrusivePtrBase<WordListWord> {
public:
WordListWord();
String name; ///< Name of the list / the word
bool line_below; ///< Line below in the list?
bool is_prefix; ///< Is this a prefix before other words?
vector<WordListWordP> words; ///< Sublist
inline bool isGroup() const { return !words.empty(); }
DECLARE_REFLECTION();
};
/// A list of words for a drop down box
class WordList : public WordListWord {
public:
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : EOF
#endif
...@@ -217,7 +217,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) { ...@@ -217,7 +217,7 @@ void DataEditor::onMotion(wxMouseEvent& ev) {
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) ); wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
FOR_EACH_EDITOR_REVERSE { // find high z index fields first FOR_EACH_EDITOR_REVERSE { // find high z index fields first
if (v->containsPoint(pos) && v->getField()->editable) { if (v->containsPoint(pos) && v->getField()->editable) {
wxCursor c = e->cursor(); wxCursor c = e->cursor(pos);
if (c.Ok()) SetCursor(c); if (c.Ok()) SetCursor(c);
else SetCursor(wxCURSOR_ARROW); else SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(v->getField()->description); if (frame) frame->SetStatusText(v->getField()->description);
......
...@@ -37,13 +37,9 @@ class DropDownHider : public wxEvtHandler { ...@@ -37,13 +37,9 @@ class DropDownHider : public wxEvtHandler {
// don't just use ev.Skip(), because this event handler will be removed by hiding, // don't just use ev.Skip(), because this event handler will be removed by hiding,
// so there will be no next handler to skip to // so there will be no next handler to skip to
wxEvtHandler* nh = GetNextHandler(); wxEvtHandler* nh = GetNextHandler();
list.hide(false);
if (nh) nh->ProcessEvent(ev); if (nh) nh->ProcessEvent(ev);
list.hide(false);
return false; return false;
} else if (t == wxEVT_MOTION) {
// send along all motion events
list.ProcessEvent(ev);
return wxEvtHandler::ProcessEvent(ev);
} else { } else {
// if (t !=10093 && t !=10098 && t !=10097 && t !=10099 && t !=10004 && t !=10062 // if (t !=10093 && t !=10098 && t !=10097 && t !=10099 && t !=10004 && t !=10062
// && t !=10025 && t !=10035 && t !=10034 && t !=10036 && t !=10042 && t !=10119) // && t !=10025 && t !=10035 && t !=10034 && t !=10036 && t !=10042 && t !=10119)
...@@ -142,25 +138,31 @@ void DropDownList::show(bool in_place, wxPoint pos) { ...@@ -142,25 +138,31 @@ void DropDownList::show(bool in_place, wxPoint pos) {
if (selected_item == NO_SELECTION && itemCount() > 0) selected_item = 0; // select first item by default if (selected_item == NO_SELECTION && itemCount() > 0) selected_item = 0; // select first item by default
mouse_down = false; mouse_down = false;
Window::Show(); Window::Show();
if (!parent_menu && GetParent()->HasCapture()) {
// release capture on parent
GetParent()->ReleaseMouse();
}
// fix drop down arrow // fix drop down arrow
redrawArrowOnParent(); redrawArrowOnParent();
} }
void DropDownList::hide(bool event, bool allow_veto) { void DropDownList::hide(bool event, bool allow_veto) {
// send event // hide?
if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) { bool keep_open = event && allow_veto && stayOpen();
bool close = select(selected_item); if (keep_open) {
if (allow_veto && !close) { Refresh(false);
Refresh(false); } else {
return; // hide root
DropDownList* root = this;
while (root->parent_menu) {
root = root->parent_menu;
} }
root->realHide();
} }
// hide root // send event
DropDownList* root = this; if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) {
while (root->parent_menu) { select(selected_item);
root = root->parent_menu;
} }
root->realHide();
} }
void DropDownList::realHide() { void DropDownList::realHide() {
...@@ -292,7 +294,13 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) { ...@@ -292,7 +294,13 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
// ----------------------------------------------------------------------------- : DropDownList : Events // ----------------------------------------------------------------------------- : DropDownList : Events
void DropDownList::onLeftDown(wxMouseEvent&) { void DropDownList::onLeftDown(wxMouseEvent& ev) {
wxSize cs = GetClientSize();
if (ev.GetX() < 0 || ev.GetX() >= cs.x || ev.GetY() < marginH || ev.GetY() >= cs.y) {
hide(false);
ev.Skip();
return;
}
mouse_down = true; // prevent closing on mouseup of the click that opened the window mouse_down = true; // prevent closing on mouseup of the click that opened the window
} }
...@@ -309,7 +317,10 @@ void DropDownList::onMotion(wxMouseEvent& ev) { ...@@ -309,7 +317,10 @@ void DropDownList::onMotion(wxMouseEvent& ev) {
// size // size
wxSize cs = GetClientSize(); wxSize cs = GetClientSize();
// find selected item // find selected item
if (ev.GetX() < marginW || ev.GetX() + marginW >= cs.GetWidth() || ev.GetY() < marginH || ev.GetY() + marginH >= cs.GetHeight()) return; if (ev.GetX() < marginW || ev.GetX() + marginW >= cs.GetWidth() || ev.GetY() < marginH || ev.GetY() + marginH >= cs.GetHeight()) {
ev.Skip();
return;
}
int startY = marginH; int startY = marginH;
size_t count = itemCount(); size_t count = itemCount();
for (size_t i = 0 ; i < count ; ++i) { for (size_t i = 0 ; i < count ; ++i) {
......
...@@ -48,11 +48,11 @@ class DropDownList : public wxPopupWindow { ...@@ -48,11 +48,11 @@ class DropDownList : public wxPopupWindow {
static const size_t NO_SELECTION = (size_t)-1; static const size_t NO_SELECTION = (size_t)-1;
/// Signal that the list is closed and something is selected /// Signal that the list is closed and something is selected
/** Returns true if the event was handled and the list should be hidden, virtual void select(size_t selection) = 0;
* false keeps the list open. */
virtual bool select(size_t selection) = 0;
/// When the list is being opened, what should be selected? /// When the list is being opened, what should be selected?
virtual size_t selection() const = 0; virtual size_t selection() const = 0;
/** Should the list stay open after selecting something? */
virtual bool stayOpen() const { return false; }
// --------------------------------------------------- : Item information // --------------------------------------------------- : Item information
/// Number of items /// Number of items
...@@ -60,7 +60,7 @@ class DropDownList : public wxPopupWindow { ...@@ -60,7 +60,7 @@ class DropDownList : public wxPopupWindow {
/// Text of an item /// Text of an item
virtual String itemText(size_t item) const = 0; virtual String itemText(size_t item) const = 0;
/// Draw an icon at the specified location /// Draw an icon at the specified location
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const = 0; virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {}
/// Is there a line below an item? /// Is there a line below an item?
virtual bool lineBelow(size_t item) const { return false; } virtual bool lineBelow(size_t item) const { return false; }
/// Should the item be highlighted? /// Should the item be highlighted?
......
...@@ -231,14 +231,13 @@ void DropDownChoiceList::onShow() { ...@@ -231,14 +231,13 @@ void DropDownChoiceList::onShow() {
generateThumbnailImages(); generateThumbnailImages();
} }
bool DropDownChoiceList::select(size_t item) { void DropDownChoiceList::select(size_t item) {
if (isFieldDefault(item)) { if (isFieldDefault(item)) {
dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() ); dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() );
} else { } else {
ChoiceField::ChoiceP choice = getChoice(item); ChoiceField::ChoiceP choice = getChoice(item);
dynamic_cast<ChoiceValueEditor&>(cve).change( field().choices->choiceName(choice->first_id) ); dynamic_cast<ChoiceValueEditor&>(cve).change( field().choices->choiceName(choice->first_id) );
} }
return true;
} }
size_t DropDownChoiceList::selection() const { size_t DropDownChoiceList::selection() const {
......
...@@ -96,7 +96,7 @@ class DropDownChoiceList : public DropDownChoiceListBase { ...@@ -96,7 +96,7 @@ class DropDownChoiceList : public DropDownChoiceListBase {
protected: protected:
virtual void onShow(); virtual void onShow();
virtual bool select(size_t item); virtual void select(size_t item);
virtual size_t selection() const; virtual size_t selection() const;
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const; virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
}; };
......
...@@ -27,7 +27,7 @@ class DropDownColorList : public DropDownList { ...@@ -27,7 +27,7 @@ class DropDownColorList : public DropDownList {
virtual String itemText(size_t item) const; virtual String itemText(size_t item) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
virtual bool select(size_t item); virtual void select(size_t item);
virtual size_t selection() const; virtual size_t selection() const;
private: private:
...@@ -112,7 +112,7 @@ size_t DropDownColorList::selection() const { ...@@ -112,7 +112,7 @@ size_t DropDownColorList::selection() const {
} }
return selection; return selection;
} }
bool DropDownColorList::select(size_t item) { void DropDownColorList::select(size_t item) {
if (isDefault(item)) { if (isDefault(item)) {
cve.change( Defaultable<Color>()); cve.change( Defaultable<Color>());
} else if (isCustom(item)) { } else if (isCustom(item)) {
...@@ -120,7 +120,6 @@ bool DropDownColorList::select(size_t item) { ...@@ -120,7 +120,6 @@ bool DropDownColorList::select(size_t item) {
} else { } else {
cve.change(field().choices[item - hasDefault()]->color); cve.change(field().choices[item - hasDefault()]->color);
} }
return true;
} }
// ----------------------------------------------------------------------------- : ColorValueEditor // ----------------------------------------------------------------------------- : ColorValueEditor
......
...@@ -113,7 +113,7 @@ class ValueEditor { ...@@ -113,7 +113,7 @@ class ValueEditor {
// --------------------------------------------------- : Other // --------------------------------------------------- : Other
/// The cursor type to use when the mouse is over this control /// The cursor type to use when the mouse is over this control
virtual wxCursor cursor() const { return wxCursor(); } virtual wxCursor cursor(const RealPoint& pos) const { return wxCursor(); }
/// Determines prefered size in the native look, update the style /// Determines prefered size in the native look, update the style
virtual void determineSize(bool force_fit = false) {} virtual void determineSize(bool force_fit = false) {}
/// Should a label and control border be drawn in the native look? /// Should a label and control border be drawn in the native look?
......
...@@ -20,13 +20,15 @@ class DropDownMultipleChoiceList : public DropDownChoiceListBase { ...@@ -20,13 +20,15 @@ class DropDownMultipleChoiceList : public DropDownChoiceListBase {
protected: protected:
virtual void onShow(); virtual void onShow();
virtual bool select(size_t item); virtual void select(size_t item);
virtual size_t selection() const; virtual size_t selection() const;
virtual bool stayOpen() const { return true; }
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const; virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const;
virtual void onMotion(wxMouseEvent&); virtual void onMouseLeave(wxMouseEvent&);
private: private:
DECLARE_EVENT_TABLE();
bool kept_open; ///< Was the list kept open after selecting a choice, if so, be eager to close it bool kept_open; ///< Was the list kept open after selecting a choice, if so, be eager to close it
}; };
...@@ -38,7 +40,7 @@ DropDownMultipleChoiceList::DropDownMultipleChoiceList ...@@ -38,7 +40,7 @@ DropDownMultipleChoiceList::DropDownMultipleChoiceList
icon_size.width += 16; icon_size.width += 16;
} }
bool DropDownMultipleChoiceList::select(size_t item) { void DropDownMultipleChoiceList::select(size_t item) {
MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve); MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve);
if (isFieldDefault(item)) { if (isFieldDefault(item)) {
mcve.toggleDefault(); mcve.toggleDefault();
...@@ -49,7 +51,6 @@ bool DropDownMultipleChoiceList::select(size_t item) { ...@@ -49,7 +51,6 @@ bool DropDownMultipleChoiceList::select(size_t item) {
// keep the box open // keep the box open
DropDownChoiceListBase::onShow(); // update 'enabled' DropDownChoiceListBase::onShow(); // update 'enabled'
kept_open = true; kept_open = true;
return false;
} }
void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const {
...@@ -92,16 +93,21 @@ DropDownList* DropDownMultipleChoiceList::createSubMenu(ChoiceField::ChoiceP gro ...@@ -92,16 +93,21 @@ DropDownList* DropDownMultipleChoiceList::createSubMenu(ChoiceField::ChoiceP gro
return new DropDownMultipleChoiceList(const_cast<DropDownMultipleChoiceList*>(this), true, cve, group); return new DropDownMultipleChoiceList(const_cast<DropDownMultipleChoiceList*>(this), true, cve, group);
} }
void DropDownMultipleChoiceList::onMotion(wxMouseEvent& ev) { void DropDownMultipleChoiceList::onMouseLeave(wxMouseEvent& ev) {
if (kept_open) { if (kept_open) {
wxSize cs = GetClientSize(); wxSize cs = GetClientSize();
if (ev.GetX() < 0 || ev.GetY() < 0 || ev.GetX() >= cs.x || ev.GetY() >= cs.y) { if (ev.GetX() < 0 || ev.GetY() < 0 || ev.GetX() >= cs.x || ev.GetY() >= cs.y) {
hide(false); // outside box; hide it hide(false); // outside box; hide it
ev.Skip();
return;
} }
} }
DropDownChoiceListBase::onMotion(ev);
} }
BEGIN_EVENT_TABLE(DropDownMultipleChoiceList, DropDownChoiceListBase)
EVT_LEAVE_WINDOW(DropDownMultipleChoiceList::onMouseLeave)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------- : MultipleChoiceValueEditor // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor
IMPLEMENT_VALUE_EDITOR(MultipleChoice) {} IMPLEMENT_VALUE_EDITOR(MultipleChoice) {}
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
#include <gui/value/text.hpp> #include <gui/value/text.hpp>
#include <gui/icon_menu.hpp> #include <gui/icon_menu.hpp>
#include <gui/util.hpp> #include <gui/util.hpp>
#include <gui/drop_down_list.hpp>
#include <data/word_list.hpp>
#include <data/game.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>
...@@ -16,6 +19,10 @@ ...@@ -16,6 +19,10 @@
#include <wx/clipbrd.h> #include <wx/clipbrd.h>
#include <wx/caret.h> #include <wx/caret.h>
DECLARE_SHARED_POINTER_TYPE(DropDownList);
DECLARE_TYPEOF_COLLECTION(WordListP);
DECLARE_TYPEOF_COLLECTION(WordListPosP);
// ----------------------------------------------------------------------------- : TextValueEditorScrollBar // ----------------------------------------------------------------------------- : TextValueEditorScrollBar
/// A scrollbar to scroll a TextValueEditor /// A scrollbar to scroll a TextValueEditor
...@@ -54,12 +61,97 @@ END_EVENT_TABLE () ...@@ -54,12 +61,97 @@ END_EVENT_TABLE ()
// ----------------------------------------------------------------------------- : WordListPos
class WordListPos : public IntrusivePtrBase<WordListPos> {
public:
WordListPos(size_t start, size_t end, WordListP word_list)
: start(start), end(end)
, rect(-1,-1,-1,-1)
, word_list(word_list)
, active(false)
{}
const size_t start, end; ///< Start and ending indices
RealRect rect; ///< Rectangle around word list text
WordListP word_list; ///< Word list to use
bool active; ///< Is the list dropped down right now?
};
class DropDownWordList : public DropDownList {
public:
DropDownWordList(Window* parent, bool is_submenu, TextValueEditor& tve, const WordListPosP& pos, const WordListWordP& list);
void setWords(const WordListPosP& pos2);
protected:
virtual void onShow();
virtual size_t itemCount() const { return words->words.size(); }
virtual bool lineBelow(size_t item) const { return words->words[item]->line_below; }
virtual String itemText(size_t item) const { return words->words[item]->name; }
virtual DropDownList* submenu(size_t item) const;
virtual size_t selection() const;
virtual void select(size_t item);
private:
TextValueEditor& tve;
WordListPosP pos;
mutable vector<DropDownListP> submenus;
WordListWordP words; ///< The words we are listing
};
DropDownWordList::DropDownWordList(Window* parent, bool is_submenu, TextValueEditor& tve, const WordListPosP& pos, const WordListWordP& words)
: DropDownList(parent, is_submenu, is_submenu ? nullptr : &tve)
, tve(tve), pos(pos)
, words(words)
{
item_size.height = max(16., item_size.height);
}
void DropDownWordList::setWords(const WordListPosP& pos2) {
if (words != pos2->word_list) {
// switch to different list
submenus.clear();
}
pos = pos2;
words = pos2->word_list;
}
void DropDownWordList::onShow() {
pos->active = true;
}
DropDownList* DropDownWordList::submenu(size_t item) const {
if (item >= submenus.size()) submenus.resize(item + 1);
if (submenus[item]) return submenus[item].get();
WordListWordP word = words->words[item];
if (word->isGroup()) {
// create submenu
submenus[item].reset(new DropDownWordList(const_cast<DropDownWordList*>(this), true, tve, pos, word));
}
return submenus[item].get();
}
size_t DropDownWordList::selection() const {
// TODO: find selection
return NO_SELECTION;
}
void DropDownWordList::select(size_t item) {
pos->active = false;
tve.selection_start_i = pos->start;
tve.selection_end_i = pos->end;
tve.fixSelection(TYPE_INDEX);
tve.replaceSelection(words->words[item]->name, _ACTION_("?????"));
}
// ----------------------------------------------------------------------------- : TextValueEditor // ----------------------------------------------------------------------------- : TextValueEditor
IMPLEMENT_VALUE_EDITOR(Text) IMPLEMENT_VALUE_EDITOR(Text)
, selection_start (0), selection_end (0) , selection_start (0), selection_end (0)
, selection_start_i(0), selection_end_i(0) , selection_start_i(0), selection_end_i(0)
, select_words(false) , selecting(false), select_words(false)
, scrollbar(nullptr), scroll_with_cursor(false) , scrollbar(nullptr), scroll_with_cursor(false)
{ {
if (viewer.nativeLook() && field().multi_line) { if (viewer.nativeLook() && field().multi_line) {
...@@ -75,16 +167,33 @@ TextValueEditor::~TextValueEditor() { ...@@ -75,16 +167,33 @@ TextValueEditor::~TextValueEditor() {
bool TextValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) {
select_words = false; select_words = false;
moveSelection(TYPE_INDEX, v.indexAt(style().getRotation().trInv(pos)), !ev.ShiftDown(), MOVE_MID); RealPoint pos2 = style().getRotation().trInv(pos);
// on word list dropdown button?
WordListPosP wl_pos = findWordList(pos2);
if (wl_pos) {
// show dropdown
if (drop_down) {
drop_down->setWords(wl_pos);
} else {
drop_down.reset(new DropDownWordList(&editor(), false, *this, wl_pos, wl_pos->word_list));
}
drop_down->show(false, wxPoint(0,0)); // TODO: position?
} else {
// no, select text
selecting = true;
moveSelection(TYPE_INDEX, v.indexAt(pos2), !ev.ShiftDown(), MOVE_MID);
}
return true; return true;
} }
bool TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) { bool TextValueEditor::onLeftUp(const RealPoint& pos, wxMouseEvent&) {
// TODO: lookup position of click? // TODO: lookup position of click?
selecting = false;
return false; return false;
} }
bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) { bool TextValueEditor::onMotion(const RealPoint& pos, wxMouseEvent& ev) {
if (ev.LeftIsDown()) { if (dropDownShown()) return false;
if (ev.LeftIsDown() && selecting) {
size_t index = v.indexAt(style().getRotation().trInv(pos)); size_t index = v.indexAt(style().getRotation().trInv(pos));
if (select_words) { if (select_words) {
// on the left, swap start and end // on the left, swap start and end
...@@ -126,6 +235,10 @@ bool TextValueEditor::onRightDown(const RealPoint& pos, wxMouseEvent& ev) { ...@@ -126,6 +235,10 @@ bool TextValueEditor::onRightDown(const RealPoint& pos, wxMouseEvent& ev) {
// ----------------------------------------------------------------------------- : Keyboard // ----------------------------------------------------------------------------- : Keyboard
bool TextValueEditor::onChar(wxKeyEvent& ev) { bool TextValueEditor::onChar(wxKeyEvent& ev) {
if (dropDownShown()) {
// forward to drop down list
return drop_down->onCharInParent(ev);
}
if (ev.AltDown()) return false; if (ev.AltDown()) return false;
fixSelection(); fixSelection();
switch (ev.GetKeyCode()) { switch (ev.GetKeyCode()) {
...@@ -293,6 +406,8 @@ void TextValueEditor::draw(RotatedDC& dc) { ...@@ -293,6 +406,8 @@ void TextValueEditor::draw(RotatedDC& dc) {
prepareDrawScrollbar(dc); prepareDrawScrollbar(dc);
// draw text // draw text
TextValueViewer::draw(dc); TextValueViewer::draw(dc);
// draw word list thingamajigies
drawWordListIndicators(dc);
// draw selection // draw selection
if (isCurrent()) { if (isCurrent()) {
v.drawSelection(dc, style(), selection_start_i, selection_end_i); v.drawSelection(dc, style(), selection_start_i, selection_end_i);
...@@ -306,8 +421,11 @@ void TextValueEditor::draw(RotatedDC& dc) { ...@@ -306,8 +421,11 @@ void TextValueEditor::draw(RotatedDC& dc) {
wxCursor rotated_ibeam; wxCursor rotated_ibeam;
wxCursor TextValueEditor::cursor() const { wxCursor TextValueEditor::cursor(const RealPoint& pos) const {
if (viewer.getRotation().sideways() ^ style().getRotation().sideways()) { // 90 or 270 degrees RealPoint pos2 = style().getRotation().trInv(pos);
if (findWordList(pos2)) {
return wxCursor();
} else if (viewer.getRotation().sideways() ^ style().getRotation().sideways()) { // 90 or 270 degrees
if (!rotated_ibeam.Ok()) { if (!rotated_ibeam.Ok()) {
rotated_ibeam = wxCursor(load_resource_cursor(_("rot_text"))); rotated_ibeam = wxCursor(load_resource_cursor(_("rot_text")));
} }
...@@ -321,10 +439,12 @@ void TextValueEditor::onValueChange() { ...@@ -321,10 +439,12 @@ void TextValueEditor::onValueChange() {
TextValueViewer::onValueChange(); TextValueViewer::onValueChange();
selection_start = selection_end = 0; selection_start = selection_end = 0;
selection_start_i = selection_end_i = 0; selection_start_i = selection_end_i = 0;
findWordLists();
} }
void TextValueEditor::onAction(const Action& action, bool undone) { void TextValueEditor::onAction(const Action& action, bool undone) {
TextValueViewer::onValueChange(); TextValueViewer::onValueChange();
findWordLists();
TYPE_CASE(action, TextValueAction) { TYPE_CASE(action, TextValueAction) {
selection_start = action.selection_start; selection_start = action.selection_start;
selection_end = action.selection_end; selection_end = action.selection_end;
...@@ -439,6 +559,11 @@ void TextValueEditor::doFormat(int type) { ...@@ -439,6 +559,11 @@ void TextValueEditor::doFormat(int type) {
// ----------------------------------------------------------------------------- : Selection // ----------------------------------------------------------------------------- : Selection
void TextValueEditor::showCaret() { void TextValueEditor::showCaret() {
if (dropDownShown()) {
wxCaret* caret = editor().GetCaret();
if (caret && caret->IsVisible()) caret->Hide();
return;
}
// Rotation // Rotation
Rotation rot(viewer.getRotation()); Rotation rot(viewer.getRotation());
Rotater rot2(rot, style().getRotation()); Rotater rot2(rot, style().getRotation());
...@@ -855,3 +980,88 @@ void TextValueEditor::prepareDrawScrollbar(RotatedDC& dc) { ...@@ -855,3 +980,88 @@ void TextValueEditor::prepareDrawScrollbar(RotatedDC& dc) {
style().width.mutate() += scrollbar_width; style().width.mutate() += scrollbar_width;
} }
} }
// ----------------------------------------------------------------------------- : Word lists
bool TextValueEditor::dropDownShown() {
return drop_down && drop_down->IsShown();
}
void TextValueEditor::findWordLists() {
word_lists.clear();
// for each word list...
const String& str = value().value();
size_t pos = str.find(_("<word-list-"));
while (pos != String::npos) {
size_t type_end = str.find_first_of(_('>'), pos);
size_t end = match_close_tag_end(str, pos);
if (type_end == String::npos || end == String::npos) return;
String name = str.substr(pos + 11, type_end - pos - 11);
WordListP word_list;
// find word list type
FOR_EACH(wl, getSet().game->word_lists) {
if (wl->name == name) {
word_list = wl;
break;
}
}
if (!word_list) {
throw Error(_ERROR_1_("word list type not found", name));
}
// add to word_lists
word_lists.push_back(new_intrusive3<WordListPos>(pos, end, word_list));
// next
pos = str.find(_("<word-list-"), end);
}
}
void TextValueEditor::drawWordListIndicators(RotatedDC& dc) {
bool current = isCurrent();
FOR_EACH(wl, word_lists) {
RealRect& r = wl->rect;
if (r.height < 0) {
// find the rectangle for this indicator
RealRect start = v.charRect(wl->start);
RealRect end = v.charRect(wl->end);
r.x = start.x;
r.y = start.y;
r.width = end.right() - start.left() + 0.5;
r.height = end.bottom() - start.top();
}
// draw background
if (current && wl->active) {
dc.SetPen (Color(0, 128,255));
dc.SetBrush(Color(128,192,255));
} else if (current) {
dc.SetPen (Color(64, 160,255));
dc.SetBrush(Color(160,208,255));
} else {
dc.SetPen (Color(128,128,128));
dc.SetBrush(Color(192,192,192));
}
dc.DrawRectangle(RealRect(style().left + r.right(), style().top + r.top(), 9, r.height));
// draw foreground
/*
dc.SetPen (*wxTRANSPARENT_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
wxPoint poly[] = {dc.tr(RealPoint(0,0)), dc.tr(RealPoint(5,0)), dc.tr(RealPoint(3,2))};
dc.getDC().DrawPolygon(3, poly, style().left + r.right() + 2, style().top + r.bottom() - 5);
*/
dc.SetPen (*wxBLACK_PEN);
double x = style().left + r.right(), y = style().top + r.bottom();
dc.DrawLine(RealPoint(x + 4, y - 3), RealPoint(x + 5, y - 3));
dc.DrawLine(RealPoint(x + 3, y - 4), RealPoint(x + 6, y - 4));
dc.DrawLine(RealPoint(x + 2, y - 5), RealPoint(x + 7, y - 5));
}
}
WordListPosP TextValueEditor::findWordList(const RealPoint& pos) const {
FOR_EACH_CONST(wl, word_lists) {
const RealRect& r = wl->rect;
if (pos.x >= r.right() && pos.x < r.right() + 9 &&
pos.y >= r.top() && pos.y < r.bottom()) {
return wl;
}
}
return WordListPosP();
}
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <render/value/text.hpp> #include <render/value/text.hpp>
class TextValueEditorScrollBar; class TextValueEditorScrollBar;
DECLARE_POINTER_TYPE(WordListPos);
DECLARE_SHARED_POINTER_TYPE(DropDownWordList);
// ----------------------------------------------------------------------------- : TextValueEditor // ----------------------------------------------------------------------------- : TextValueEditor
...@@ -85,7 +87,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -85,7 +87,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
// --------------------------------------------------- : Other // --------------------------------------------------- : Other
virtual wxCursor cursor() const; virtual wxCursor cursor(const RealPoint& pos) const;
virtual void determineSize(bool force_fit = false); virtual void determineSize(bool force_fit = false);
virtual void onShow(bool); virtual void onShow(bool);
virtual void draw(RotatedDC&); virtual void draw(RotatedDC&);
...@@ -94,9 +96,11 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -94,9 +96,11 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
private: private:
size_t selection_start, selection_end; ///< Cursor position/selection (if any), cursor positions size_t selection_start, selection_end; ///< Cursor position/selection (if any), cursor positions
size_t selection_start_i, selection_end_i; ///< Cursor position/selection, character indices size_t selection_start_i, selection_end_i; ///< Cursor position/selection, character indices
bool selecting; ///< Selecting text?
bool select_words; ///< Select whole words when dragging the mouse? bool select_words; ///< Select whole words when dragging the mouse?
TextValueEditorScrollBar* scrollbar; ///< Scrollbar for multiline fields in native look TextValueEditorScrollBar* scrollbar; ///< Scrollbar for multiline fields in native look
bool scroll_with_cursor; ///< When the cursor moves, should the scrollposition change? bool scroll_with_cursor; ///< When the cursor moves, should the scrollposition change?
vector<WordListPosP> word_lists; ///< Word lists in the text
// --------------------------------------------------- : Selection / movement // --------------------------------------------------- : Selection / movement
...@@ -147,6 +151,22 @@ class TextValueEditor : public TextValueViewer, public ValueEditor { ...@@ -147,6 +151,22 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
bool ensureCaretVisible(); bool ensureCaretVisible();
/// Prepare for drawing if there is a scrollbar /// Prepare for drawing if there is a scrollbar
void prepareDrawScrollbar(RotatedDC& dc); void prepareDrawScrollbar(RotatedDC& dc);
// --------------------------------------------------- : Word lists
friend class DropDownWordList;
DropDownWordListP drop_down;
bool dropDownShown();
/// Find all word lists in the current value
void findWordLists();
/// Draw word list indicators
void drawWordListIndicators(RotatedDC& dc);
/// Find a WordListPos under the mouse cursor (if any), pos is in internal coordinates
WordListPosP findWordList(const RealPoint& pos) const;
/// Show a word list drop down menu
void wordListDropDown(const WordListPosP& pos);
}; };
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
......
...@@ -1891,6 +1891,12 @@ ...@@ -1891,6 +1891,12 @@
<File <File
RelativePath=".\data\symbol_font.hpp"> RelativePath=".\data\symbol_font.hpp">
</File> </File>
<File
RelativePath=".\data\word_list.cpp">
</File>
<File
RelativePath=".\data\word_list.hpp">
</File>
</Filter> </Filter>
<Filter <Filter
Name="base" Name="base"
......
...@@ -112,7 +112,7 @@ class SimpleTextElement : public TextElement { ...@@ -112,7 +112,7 @@ class SimpleTextElement : public TextElement {
/// A text element that uses a normal font /// A text element that uses a normal font
class FontTextElement : public SimpleTextElement { class FontTextElement : public SimpleTextElement {
public: public:
FontTextElement(const String& text, size_t start ,size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style) FontTextElement(const String& text, size_t start, size_t end, const FontP& font, DrawWhat draw_as, LineBreak break_style)
: SimpleTextElement(text, start, end) : SimpleTextElement(text, start, end)
, font(font), draw_as(draw_as), break_style(break_style) , font(font), draw_as(draw_as), break_style(break_style)
{} {}
...@@ -130,7 +130,7 @@ class FontTextElement : public SimpleTextElement { ...@@ -130,7 +130,7 @@ class FontTextElement : public SimpleTextElement {
/// A text element that uses a symbol font /// A text element that uses a symbol font
class SymbolTextElement : public SimpleTextElement { class SymbolTextElement : public SimpleTextElement {
public: public:
SymbolTextElement(const String& text, size_t start ,size_t end, const SymbolFontRef& font, Context* ctx) SymbolTextElement(const String& text, size_t start, size_t end, const SymbolFontRef& font, Context* ctx)
: SimpleTextElement(text, start, end) : SimpleTextElement(text, start, end)
, font(font), ctx(*ctx) , font(font), ctx(*ctx)
{} {}
...@@ -149,7 +149,7 @@ class SymbolTextElement : public SimpleTextElement { ...@@ -149,7 +149,7 @@ class SymbolTextElement : public SimpleTextElement {
/// A TextElement consisting of sub elements /// A TextElement consisting of sub elements
class CompoundTextElement : public TextElement { class CompoundTextElement : public TextElement {
public: public:
CompoundTextElement(const String& text, size_t start ,size_t end) : TextElement(text, start, end) {} CompoundTextElement(const String& text, size_t start, size_t end) : TextElement(text, start, end) {}
virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw (RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const; virtual void getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
...@@ -162,7 +162,7 @@ class CompoundTextElement : public TextElement { ...@@ -162,7 +162,7 @@ class CompoundTextElement : public TextElement {
/// A TextElement drawn using a grey background /// A TextElement drawn using a grey background
class AtomTextElement : public CompoundTextElement { class AtomTextElement : public CompoundTextElement {
public: public:
AtomTextElement(const String& text, size_t start ,size_t end) : CompoundTextElement(text, start, end) {} AtomTextElement(const String& text, size_t start, size_t end) : CompoundTextElement(text, start, end) {}
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
}; };
...@@ -170,7 +170,7 @@ class AtomTextElement : public CompoundTextElement { ...@@ -170,7 +170,7 @@ class AtomTextElement : public CompoundTextElement {
/// A TextElement drawn using a red wavy underline /// A TextElement drawn using a red wavy underline
class ErrorTextElement : public CompoundTextElement { class ErrorTextElement : public CompoundTextElement {
public: public:
ErrorTextElement(const String& text, size_t start ,size_t end) : CompoundTextElement(text, start, end) {} ErrorTextElement(const String& text, size_t start, size_t end) : CompoundTextElement(text, start, end) {}
virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const; virtual void draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const;
}; };
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
DECLARE_TYPEOF_COLLECTION(FieldP); DECLARE_TYPEOF_COLLECTION(FieldP);
DECLARE_TYPEOF_COLLECTION(TextValue*); DECLARE_TYPEOF_COLLECTION(TextValue*);
DECLARE_TYPEOF_COLLECTION(String); DECLARE_TYPEOF_COLLECTION(String);
DECLARE_TYPEOF_COLLECTION(pair<String COMMA bool>);
DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP); DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP);
// ----------------------------------------------------------------------------- : Combined editor // ----------------------------------------------------------------------------- : Combined editor
...@@ -53,41 +54,43 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) { ...@@ -53,41 +54,43 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) {
} }
// split the value // split the value
SCRIPT_PARAM(String, value); SCRIPT_PARAM(String, value);
vector<String> value_parts; vector<pair<String,bool> > value_parts; // (value part, is empty)
size_t pos = value.find(_("<sep")); size_t pos = value.find(_("<sep"));
while (pos != String::npos) { while (pos != String::npos) {
value_parts.push_back(value.substr(0, pos)); String part = value.substr(0, pos);
value_parts.push_back(make_pair(part, false));
value = value.substr(min(match_close_tag_end(value,pos), value.size())); value = value.substr(min(match_close_tag_end(value,pos), value.size()));
pos = value.find(_("<sep")); pos = value.find(_("<sep"));
} }
value_parts.push_back(value); value_parts.push_back(make_pair(value, false));
value_parts.resize(values.size()); // TODO: what if there are more value_parts than values? value_parts.resize(values.size()); // TODO: what if there are more value_parts than values?
// update the values if our input value is newer? // update the values if our input value is newer?
Age new_value_update = last_update_age(); Age new_value_update = last_update_age();
FOR_EACH_2(v, values, nv, value_parts) { FOR_EACH_2(v, values, nv, value_parts) {
if (v->value() != nv && v->last_update < new_value_update) { if (v->value() != nv.first && v->last_update < new_value_update) {
// TODO : type over v->value.assign(nv.first);
v->value.assign(nv);
v->update(ctx); v->update(ctx);
} }
nv = v->value(); nv.first = v->value();
nv.second = index_to_untagged(nv.first, nv.first.size()) == 0;
} }
// options // options
SCRIPT_PARAM_DEFAULT_N(bool, _("hide when empty"), hide_when_empty, false); SCRIPT_PARAM_DEFAULT_N(bool, _("hide when empty"), hide_when_empty, false);
SCRIPT_PARAM_DEFAULT_N(bool, _("soft before empty"), soft_before_empty, false); SCRIPT_PARAM_DEFAULT_N(bool, _("soft before empty"), soft_before_empty, false);
// recombine the parts // recombine the parts
String new_value = value_parts.front(); String new_value = value_parts.front().first;
bool new_value_empty = value_parts.front().second;
for (size_t i = 1 ; i < value_parts.size() ; ++i) { for (size_t i = 1 ; i < value_parts.size() ; ++i) {
if (value_parts[i].empty() && new_value.empty() && hide_when_empty) { if (value_parts[i].second && new_value_empty && hide_when_empty) {
// no separator // no separator
} else if (value_parts[i].empty() && soft_before_empty) { } else if (value_parts[i].second && soft_before_empty) {
// soft separator // soft separator
new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>"); new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>");
} else { } else {
// normal separator // normal separator
new_value += _("<sep>") + separators[i - 1] + _("</sep>"); new_value += _("<sep>") + separators[i - 1] + _("</sep>");
new_value += value_parts[i];
} }
new_value += value_parts[i].first;
} }
SCRIPT_RETURN(new_value); SCRIPT_RETURN(new_value);
} }
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
const Char REMOVED = _('\2'); const Char REMOVED = _('\2');
const Char PLACEHOLDER = _('\3'); const Char PLACEHOLDER = _('\3');
String spec_sort(const String& spec, String& input, String& ret);
// ----------------------------------------------------------------------------- : Iterator for reading specs // ----------------------------------------------------------------------------- : Iterator for reading specs
/// Iterator over a sort specification (for spec_sort) /// Iterator over a sort specification (for spec_sort)
...@@ -213,6 +215,24 @@ void pattern_sort(const String& pattern, const String& spec, String& input, Stri ...@@ -213,6 +215,24 @@ void pattern_sort(const String& pattern, const String& spec, String& input, Stri
} }
} }
/// Sort things in place, keep the rest of the input
void in_place_sort(const String& spec, String& input, String& ret) {
String result;
spec_sort(spec, input, result);
// restore into the same order as in 'input'
size_t pos_r = 0;
FOR_EACH(c, input) {
if (c == REMOVED) {
if (pos_r < result.size()) {
ret += result.GetChar(pos_r++);
}
} else {
ret += c;
}
}
input.clear(); // we ate all the input
}
// ----------------------------------------------------------------------------- : spec_sort // ----------------------------------------------------------------------------- : spec_sort
String spec_sort(const String& spec, String& input, String& ret) { String spec_sort(const String& spec, String& input, String& ret) {
...@@ -247,7 +267,7 @@ String spec_sort(const String& spec, String& input, String& ret) { ...@@ -247,7 +267,7 @@ String spec_sort(const String& spec, String& input, String& ret) {
} else if (it.keyword(_("compound("))) { // compound item } else if (it.keyword(_("compound("))) { // compound item
compound_sort(it.readParam(_(')')), input, ret); compound_sort(it.readParam(_(')')), input, ret);
} else if (it.keyword(_("pattern("))) { // recurse with pattern } else if (it.keyword(_("pattern("))) { // recurse with pattern
String pattern; String pattern;
// read pattern // read pattern
...@@ -262,6 +282,11 @@ String spec_sort(const String& spec, String& input, String& ret) { ...@@ -262,6 +282,11 @@ String spec_sort(const String& spec, String& input, String& ret) {
// sort // sort
pattern_sort(pattern, sub_spec, input, ret); pattern_sort(pattern, sub_spec, input, ret);
} else if (it.keyword(_("in_place("))) { // recurse without pattern
// read spec to apply to pattern
String sub_spec = it.readRawParam(_(')'));
in_place_sort(sub_spec, input, ret);
} else if (it.keyword(_("any()"))) { // remaining input } else if (it.keyword(_("any()"))) { // remaining input
FOR_EACH(d, input) { FOR_EACH(d, input) {
if (d != REMOVED) { if (d != REMOVED) {
......
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