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
| @"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.
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
> sort_text(order: "xx")
......
......@@ -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 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.
| @word lists@ [[type:list]] of [[type:word list]]s Word lists that can be used by text fields.
--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.
Inserting this tag manually will confuse that function!<br/>
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]]
| @"<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.
--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 @@
#include <data/keyword.hpp>
#include <data/statistics.hpp>
#include <data/pack.hpp>
#include <data/word_list.hpp>
#include <util/io/package_manager.hpp>
#include <script/script.hpp>
......@@ -56,7 +57,7 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT(keyword_modes);
REFLECT(keyword_parameter_types);
REFLECT_NO_SCRIPT(keywords);
// REFLECT(word_lists);
REFLECT(word_lists);
}
void Game::validate(Version v) {
......
......@@ -24,6 +24,7 @@ DECLARE_POINTER_TYPE(PackType);
DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword);
DECLARE_POINTER_TYPE(WordList);
// ----------------------------------------------------------------------------- : Game
......@@ -43,6 +44,7 @@ class Game : public Packaged {
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<PackTypeP> pack_types; ///< Types of random card packs to generate
vector<WordListP> word_lists; ///< Word lists for editing with a drop down list
bool has_keywords; ///< Does this game use keywords?
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) {
wxFrame* frame = dynamic_cast<wxFrame*>( wxGetTopLevelParent(this) );
FOR_EACH_EDITOR_REVERSE { // find high z index fields first
if (v->containsPoint(pos) && v->getField()->editable) {
wxCursor c = e->cursor();
wxCursor c = e->cursor(pos);
if (c.Ok()) SetCursor(c);
else SetCursor(wxCURSOR_ARROW);
if (frame) frame->SetStatusText(v->getField()->description);
......
......@@ -37,13 +37,9 @@ class DropDownHider : public wxEvtHandler {
// 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
wxEvtHandler* nh = GetNextHandler();
list.hide(false);
if (nh) nh->ProcessEvent(ev);
list.hide(false);
return false;
} else if (t == wxEVT_MOTION) {
// send along all motion events
list.ProcessEvent(ev);
return wxEvtHandler::ProcessEvent(ev);
} else {
// 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)
......@@ -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
mouse_down = false;
Window::Show();
if (!parent_menu && GetParent()->HasCapture()) {
// release capture on parent
GetParent()->ReleaseMouse();
}
// fix drop down arrow
redrawArrowOnParent();
}
void DropDownList::hide(bool event, bool allow_veto) {
// send event
if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) {
bool close = select(selected_item);
if (allow_veto && !close) {
Refresh(false);
return;
// hide?
bool keep_open = event && allow_veto && stayOpen();
if (keep_open) {
Refresh(false);
} else {
// hide root
DropDownList* root = this;
while (root->parent_menu) {
root = root->parent_menu;
}
root->realHide();
}
// hide root
DropDownList* root = this;
while (root->parent_menu) {
root = root->parent_menu;
// send event
if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) {
select(selected_item);
}
root->realHide();
}
void DropDownList::realHide() {
......@@ -292,7 +294,13 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) {
// ----------------------------------------------------------------------------- : 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
}
......@@ -309,7 +317,10 @@ void DropDownList::onMotion(wxMouseEvent& ev) {
// size
wxSize cs = GetClientSize();
// 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;
size_t count = itemCount();
for (size_t i = 0 ; i < count ; ++i) {
......
......@@ -48,11 +48,11 @@ class DropDownList : public wxPopupWindow {
static const size_t NO_SELECTION = (size_t)-1;
/// Signal that the list is closed and something is selected
/** Returns true if the event was handled and the list should be hidden,
* false keeps the list open. */
virtual bool select(size_t selection) = 0;
virtual void select(size_t selection) = 0;
/// When the list is being opened, what should be selected?
virtual size_t selection() const = 0;
/** Should the list stay open after selecting something? */
virtual bool stayOpen() const { return false; }
// --------------------------------------------------- : Item information
/// Number of items
......@@ -60,7 +60,7 @@ class DropDownList : public wxPopupWindow {
/// Text of an item
virtual String itemText(size_t item) const = 0;
/// 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?
virtual bool lineBelow(size_t item) const { return false; }
/// Should the item be highlighted?
......
......@@ -231,14 +231,13 @@ void DropDownChoiceList::onShow() {
generateThumbnailImages();
}
bool DropDownChoiceList::select(size_t item) {
void DropDownChoiceList::select(size_t item) {
if (isFieldDefault(item)) {
dynamic_cast<ChoiceValueEditor&>(cve).change( Defaultable<String>() );
} else {
ChoiceField::ChoiceP choice = getChoice(item);
dynamic_cast<ChoiceValueEditor&>(cve).change( field().choices->choiceName(choice->first_id) );
}
return true;
}
size_t DropDownChoiceList::selection() const {
......
......@@ -96,7 +96,7 @@ class DropDownChoiceList : public DropDownChoiceListBase {
protected:
virtual void onShow();
virtual bool select(size_t item);
virtual void select(size_t item);
virtual size_t selection() const;
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const;
};
......
......@@ -27,7 +27,7 @@ class DropDownColorList : public DropDownList {
virtual String itemText(size_t item) 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;
private:
......@@ -112,7 +112,7 @@ size_t DropDownColorList::selection() const {
}
return selection;
}
bool DropDownColorList::select(size_t item) {
void DropDownColorList::select(size_t item) {
if (isDefault(item)) {
cve.change( Defaultable<Color>());
} else if (isCustom(item)) {
......@@ -120,7 +120,6 @@ bool DropDownColorList::select(size_t item) {
} else {
cve.change(field().choices[item - hasDefault()]->color);
}
return true;
}
// ----------------------------------------------------------------------------- : ColorValueEditor
......
......@@ -113,7 +113,7 @@ class ValueEditor {
// --------------------------------------------------- : Other
/// 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
virtual void determineSize(bool force_fit = false) {}
/// Should a label and control border be drawn in the native look?
......
......@@ -20,13 +20,15 @@ class DropDownMultipleChoiceList : public DropDownChoiceListBase {
protected:
virtual void onShow();
virtual bool select(size_t item);
virtual void select(size_t item);
virtual size_t selection() const;
virtual bool stayOpen() const { return true; }
virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) 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:
DECLARE_EVENT_TABLE();
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
icon_size.width += 16;
}
bool DropDownMultipleChoiceList::select(size_t item) {
void DropDownMultipleChoiceList::select(size_t item) {
MultipleChoiceValueEditor& mcve = dynamic_cast<MultipleChoiceValueEditor&>(cve);
if (isFieldDefault(item)) {
mcve.toggleDefault();
......@@ -49,7 +51,6 @@ bool DropDownMultipleChoiceList::select(size_t item) {
// keep the box open
DropDownChoiceListBase::onShow(); // update 'enabled'
kept_open = true;
return false;
}
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
return new DropDownMultipleChoiceList(const_cast<DropDownMultipleChoiceList*>(this), true, cve, group);
}
void DropDownMultipleChoiceList::onMotion(wxMouseEvent& ev) {
void DropDownMultipleChoiceList::onMouseLeave(wxMouseEvent& ev) {
if (kept_open) {
wxSize cs = GetClientSize();
if (ev.GetX() < 0 || ev.GetY() < 0 || ev.GetX() >= cs.x || ev.GetY() >= cs.y) {
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
IMPLEMENT_VALUE_EDITOR(MultipleChoice) {}
......
This diff is collapsed.
......@@ -15,6 +15,8 @@
#include <render/value/text.hpp>
class TextValueEditorScrollBar;
DECLARE_POINTER_TYPE(WordListPos);
DECLARE_SHARED_POINTER_TYPE(DropDownWordList);
// ----------------------------------------------------------------------------- : TextValueEditor
......@@ -85,7 +87,7 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
// --------------------------------------------------- : Other
virtual wxCursor cursor() const;
virtual wxCursor cursor(const RealPoint& pos) const;
virtual void determineSize(bool force_fit = false);
virtual void onShow(bool);
virtual void draw(RotatedDC&);
......@@ -94,9 +96,11 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
private:
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
bool selecting; ///< Selecting text?
bool select_words; ///< Select whole words when dragging the mouse?
TextValueEditorScrollBar* scrollbar; ///< Scrollbar for multiline fields in native look
bool scroll_with_cursor; ///< When the cursor moves, should the scrollposition change?
vector<WordListPosP> word_lists; ///< Word lists in the text
// --------------------------------------------------- : Selection / movement
......@@ -147,6 +151,22 @@ class TextValueEditor : public TextValueViewer, public ValueEditor {
bool ensureCaretVisible();
/// Prepare for drawing if there is a scrollbar
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
......
......@@ -1891,6 +1891,12 @@
<File
RelativePath=".\data\symbol_font.hpp">
</File>
<File
RelativePath=".\data\word_list.cpp">
</File>
<File
RelativePath=".\data\word_list.hpp">
</File>
</Filter>
<Filter
Name="base"
......
......@@ -112,7 +112,7 @@ class SimpleTextElement : public TextElement {
/// A text element that uses a normal font
class FontTextElement : public SimpleTextElement {
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)
, font(font), draw_as(draw_as), break_style(break_style)
{}
......@@ -130,7 +130,7 @@ class FontTextElement : public SimpleTextElement {
/// A text element that uses a symbol font
class SymbolTextElement : public SimpleTextElement {
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)
, font(font), ctx(*ctx)
{}
......@@ -149,7 +149,7 @@ class SymbolTextElement : public SimpleTextElement {
/// A TextElement consisting of sub elements
class CompoundTextElement : public TextElement {
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 getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>& out) const;
......@@ -162,7 +162,7 @@ class CompoundTextElement : public TextElement {
/// A TextElement drawn using a grey background
class AtomTextElement : public CompoundTextElement {
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;
};
......@@ -170,7 +170,7 @@ class AtomTextElement : public CompoundTextElement {
/// A TextElement drawn using a red wavy underline
class ErrorTextElement : public CompoundTextElement {
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;
};
......
......@@ -18,6 +18,7 @@
DECLARE_TYPEOF_COLLECTION(FieldP);
DECLARE_TYPEOF_COLLECTION(TextValue*);
DECLARE_TYPEOF_COLLECTION(String);
DECLARE_TYPEOF_COLLECTION(pair<String COMMA bool>);
DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP);
// ----------------------------------------------------------------------------- : Combined editor
......@@ -53,41 +54,43 @@ SCRIPT_FUNCTION_WITH_DEP(combined_editor) {
}
// split the 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"));
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()));
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?
// update the values if our input value is newer?
Age new_value_update = last_update_age();
FOR_EACH_2(v, values, nv, value_parts) {
if (v->value() != nv && v->last_update < new_value_update) {
// TODO : type over
v->value.assign(nv);
if (v->value() != nv.first && v->last_update < new_value_update) {
v->value.assign(nv.first);
v->update(ctx);
}
nv = v->value();
nv.first = v->value();
nv.second = index_to_untagged(nv.first, nv.first.size()) == 0;
}
// options
SCRIPT_PARAM_DEFAULT_N(bool, _("hide when empty"), hide_when_empty, false);
SCRIPT_PARAM_DEFAULT_N(bool, _("soft before empty"), soft_before_empty, false);
// 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) {
if (value_parts[i].empty() && new_value.empty() && hide_when_empty) {
if (value_parts[i].second && new_value_empty && hide_when_empty) {
// no separator
} else if (value_parts[i].empty() && soft_before_empty) {
} else if (value_parts[i].second && soft_before_empty) {
// soft separator
new_value += _("<sep-soft>") + separators[i - 1] + _("</sep-soft>");
} else {
// normal separator
new_value += _("<sep>") + separators[i - 1] + _("</sep>");
new_value += value_parts[i];
}
new_value += value_parts[i].first;
}
SCRIPT_RETURN(new_value);
}
......
......@@ -12,6 +12,8 @@
const Char REMOVED = _('\2');
const Char PLACEHOLDER = _('\3');
String spec_sort(const String& spec, String& input, String& ret);
// ----------------------------------------------------------------------------- : Iterator for reading specs
/// Iterator over a sort specification (for spec_sort)
......@@ -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
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
compound_sort(it.readParam(_(')')), input, ret);
} else if (it.keyword(_("pattern("))) { // recurse with pattern
String pattern;
// read pattern
......@@ -262,6 +282,11 @@ String spec_sort(const String& spec, String& input, String& ret) {
// sort
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
FOR_EACH(d, input) {
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