Commit a8a645b8 authored by twanvl's avatar twanvl

Moved spec_sort to separate file, added more advanced options;

Split 'sort' script function into 'sort_text' and 'sort_list';
Double clicking card in stats panel switches to cards panel
parent 8a327508
......@@ -33,6 +33,7 @@ DECLARE_TYPEOF_COLLECTION(CardListBase*);
// ----------------------------------------------------------------------------- : Events
DEFINE_EVENT_TYPE(EVENT_CARD_SELECT);
DEFINE_EVENT_TYPE(EVENT_CARD_ACTIVATE);
// ----------------------------------------------------------------------------- : CardListBase
......@@ -352,11 +353,18 @@ void CardListBase::onContextMenu(wxContextMenuEvent&) {
}
}
void CardListBase::onItemActivate(wxListEvent& ev) {
selectItemPos(ev.GetIndex(), false);
CardSelectEvent event(getCard(), EVENT_CARD_ACTIVATE);
ProcessEvent(event);
}
// ----------------------------------------------------------------------------- : CardListBase : Event table
BEGIN_EVENT_TABLE(CardListBase, ItemList)
EVT_LIST_COL_RIGHT_CLICK (wxID_ANY, CardListBase::onColumnRightClick)
EVT_LIST_COL_END_DRAG (wxID_ANY, CardListBase::onColumnResize)
EVT_LIST_ITEM_ACTIVATED (wxID_ANY, CardListBase::onItemActivate)
EVT_CHAR ( CardListBase::onChar)
EVT_MOTION ( CardListBase::onDrag)
EVT_MENU (ID_SELECT_COLUMNS, CardListBase::onSelectColumns)
......
......@@ -19,17 +19,25 @@ DECLARE_POINTER_TYPE(Field);
// ----------------------------------------------------------------------------- : Events
DECLARE_EVENT_TYPE(EVENT_CARD_SELECT, <not used>)
/// Handle CardSelectEvents
DECLARE_EVENT_TYPE(EVENT_CARD_SELECT, <not used>)
DECLARE_EVENT_TYPE(EVENT_CARD_ACTIVATE, <not used>)
/// Handle EVENT_CARD_SELECT events
#define EVT_CARD_SELECT(id, handler) \
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_SELECT, id, -1, \
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
/// Handle EVENT_CARD_ACTIVATE events
#define EVT_CARD_ACTIVATE(id, handler) \
DECLARE_EVENT_TABLE_ENTRY(EVENT_CARD_ACTIVATE, id, -1, \
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) \
(void (wxEvtHandler::*)(CardSelectEvent&)) (&handler), (wxObject*) NULL),
/// The event of selecting a card
struct CardSelectEvent : public wxCommandEvent {
inline CardSelectEvent(const CardP& card)
: wxCommandEvent(EVENT_CARD_SELECT), card(card)
inline CardSelectEvent(const CardP& card, int type = EVENT_CARD_SELECT)
: wxCommandEvent(type), card(card)
{}
CardP card; ///< The selected card
......@@ -122,11 +130,12 @@ class CardListBase : public ItemList, public SetView {
// --------------------------------------------------- : Window events
DECLARE_EVENT_TABLE();
void onColumnRightClick(wxListEvent& ev);
void onColumnResize (wxListEvent& ev);
void onSelectColumns (wxCommandEvent& ev);
void onChar (wxKeyEvent& ev);
void onDrag (wxMouseEvent& ev);
void onColumnRightClick(wxListEvent&);
void onColumnResize (wxListEvent&);
void onItemActivate (wxListEvent&);
void onSelectColumns (wxCommandEvent&);
void onChar (wxKeyEvent&);
void onDrag (wxMouseEvent&);
void onContextMenu (wxContextMenuEvent&);
};
......
......@@ -199,12 +199,19 @@ void SetWindow::addPanel(IconMenu* windowMenu, wxToolBar* tabBar, SetWindowPanel
void SetWindow::selectPanel(int id) {
SetWindowPanel* toSelect = panels.at(id - ID_WINDOW_MIN);
if (current_panel != toSelect) {
// destroy & create menus
if (current_panel) current_panel->destroyUI(GetToolBar(), GetMenuBar());
current_panel = toSelect;
current_panel->initUI(GetToolBar(), GetMenuBar());
if (current_panel == toSelect) {
// don't change, but fix tab bar
wxToolBar* tabBar = (wxToolBar*)FindWindow(ID_TAB_BAR);
int wid = ID_WINDOW_MIN;
FOR_EACH(p, panels) {
tabBar->ToggleTool(wid++, p == current_panel);
}
return;
}
// destroy & create menus
if (current_panel) current_panel->destroyUI(GetToolBar(), GetMenuBar());
current_panel = toSelect;
current_panel->initUI(GetToolBar(), GetMenuBar());
// show/hide panels and select tabs
wxSizer* sizer = GetSizer();
wxToolBar* tabBar = (wxToolBar*)FindWindow(ID_TAB_BAR);
......@@ -282,6 +289,9 @@ void SetWindow::onCardSelect(CardSelectEvent& ev) {
p->selectCard(ev.card);
}
}
void SetWindow::onCardActivate(CardSelectEvent& ev) {
selectPanel(ID_WINDOW_CARDS);
}
void SetWindow::fixMinWindowSize() {
current_panel->SetMinSize(current_panel->GetSizer()->GetMinSize());
......@@ -699,5 +709,6 @@ BEGIN_EVENT_TABLE(SetWindow, wxFrame)
EVT_CLOSE ( SetWindow::onClose)
EVT_IDLE ( SetWindow::onIdle)
EVT_CARD_SELECT (wxID_ANY, SetWindow::onCardSelect)
EVT_CARD_ACTIVATE (wxID_ANY, SetWindow::onCardActivate)
EVT_SIZE_CHANGE (wxID_ANY, SetWindow::onSizeChange)
END_EVENT_TABLE ()
......@@ -76,6 +76,7 @@ class SetWindow : public wxFrame, public SetView {
private:
/// A different card has been selected
void onCardSelect(CardSelectEvent&);
void onCardActivate(CardSelectEvent&);
// minSize = mainSizer->getMinWindowSize(this)
// but wx made that private
......
......@@ -2178,6 +2178,12 @@
<File
RelativePath=".\util\smart_ptr.hpp">
</File>
<File
RelativePath=".\util\spec_sort.cpp">
</File>
<File
RelativePath=".\util\spec_sort.hpp">
</File>
<File
RelativePath=".\util\string.cpp">
<FileConfiguration
......
......@@ -107,7 +107,7 @@ void SymbolViewer::draw(DC& dc) {
}
}
// Draw all parts
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, false, borderDC, interiorDC);
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, true, borderDC, interiorDC);
// Output the final parts from the buffer
if (buffersFilled) {
combineBuffers(dc, borderDC.get(), interiorDC.get());
......
......@@ -10,6 +10,7 @@
#include <script/functions/functions.hpp>
#include <script/functions/util.hpp>
#include <util/tagged_string.hpp>
#include <util/spec_sort.hpp>
#include <util/error.hpp>
#include <data/set.hpp>
#include <data/card.hpp>
......@@ -258,17 +259,23 @@ SCRIPT_FUNCTION_DEPENDENCIES(position_of) {
};
// finding sizes
SCRIPT_FUNCTION(number_of_items) {
SCRIPT_PARAM(ScriptValueP, in);
if (ScriptObject<Set*>* setobj = dynamic_cast<ScriptObject<Set*>*>(in.get())) {
int script_length_of(Context& ctx, const ScriptValueP& collection) {
if (ScriptObject<Set*>* setobj = dynamic_cast<ScriptObject<Set*>*>(collection.get())) {
Set* set = setobj->getValue();
SCRIPT_OPTIONAL_PARAM_(ScriptValueP, filter);
if (filter == script_nil) filter = ScriptValueP();
SCRIPT_RETURN(set->numberOfCards(filter));
return set->numberOfCards(filter);
} else {
SCRIPT_RETURN(in->itemCount());
return collection->itemCount();
}
}
SCRIPT_FUNCTION(length) {
SCRIPT_PARAM(ScriptValueP, input);
SCRIPT_RETURN(script_length_of(ctx, input));
}
SCRIPT_FUNCTION(number_of_items) {
SCRIPT_PARAM(ScriptValueP, in);
SCRIPT_RETURN(script_length_of(ctx, in));
}
// filtering items from a list
SCRIPT_FUNCTION(filter_list) {
......@@ -287,6 +294,12 @@ SCRIPT_FUNCTION(filter_list) {
return ret;
}
SCRIPT_FUNCTION(sort_list) {
SCRIPT_PARAM(ScriptValueP, input);
SCRIPT_PARAM_N(ScriptValueP, _("order by"), order_by);
return sort_script(ctx, input, *order_by);
}
// ----------------------------------------------------------------------------- : Keywords
SCRIPT_RULE_2_N_DEP(expand_keywords, ScriptValueP, _("default expand"), default_expand,
......@@ -516,110 +529,7 @@ SCRIPT_FUNCTION(match) {
return match_rule(ctx)->eval(ctx);
}
// ----------------------------------------------------------------------------- : Rules : sort
/// Sort a string using a specification using the shortest cycle metric, see spec_sort
String cycle_sort(const String& spec, const String& input) {
size_t size = spec.size();
vector<UInt> counts;
// count occurences of each char in spec
FOR_EACH_CONST(s, spec) {
UInt c = 0;
FOR_EACH_CONST(i, input) {
if (s == i) c++;
}
counts.push_back(c);
}
// determine best start point
size_t best_start = 0;
UInt best_start_score = 0xffffffff;
for (size_t start = 0 ; start < size ; ++start) {
// score of a start position, can be considered as:
// - count saturated to binary
// - rotated left by start
// - interpreted as a binary number, but without trailing 0s
UInt score = 0, mul = 1;
for (size_t i = 0 ; i < size ; ++i) {
mul *= 2;
if (counts[(start + i) % size]) {
score = score * mul + 1;
mul = 1;
}
}
if (score < best_start_score) {
best_start_score = score;
best_start = start;
}
}
// return string
String ret;
for (size_t i = 0 ; i < size ; ++i) {
size_t pos = (best_start + i) % size;
ret.append(counts[pos], spec[pos]);
}
return ret;
}
/// Sort a string using a sort specification
/** The specificatio can contain:
* - a = all 'a's go here
* - [abc] = 'a', 'b' and 'c' go here, in the same order as in the input
* - <abc> = 'a', 'b' and 'c' go here in that order, and only zero or one time.
* - (abc) = 'a', 'b' and 'c' go here, in the shortest order
* consider the specified characters as a clockwise circle
* then returns the input in the order that:
* 1. takes the shortest clockwise path over this circle.
* 2. has _('holes') early, a hole means a character that is in the specification
* but not in the input
* 3. prefer the one that comes the earliest in the expression (a in this case)
*
* example:
* spec_sort("XYZ<0123456789>(WUBRG)",..) // used by magic
* "W1G") -> "1GW" // could be "W...G" or "...GW", second is shorter
* "GRBUWWUG") -> "WWUUBRGG" // no difference by rule 1,2, could be "WUBRG", "UBRGW", etc.
* // becomes _("WUBRG") by rule 3
* "WUR") -> "RWU" // by rule 1 could be "R WU" or "WU R", "RWU" has an earlier hole
*/
String spec_sort(const String& spec, const String& input) {
String ret;
for (size_t pos = 0 ; pos < spec.size() ; ++pos) {
Char c = spec.GetChar(pos);
if (c == _('<')) { // keep only a single copy
for ( ++pos ; pos < spec.size() ; ++pos) {
Char c = spec.GetChar(pos);
if (c == _('>')) break;
if (input.find_first_of(c) != String::npos) {
ret += c; // input contains c
}
}
if (pos == String::npos) throw ParseError(_("Expected '>' in sort_rule specification"));
} else if (c == _('[')) { // in any order
size_t end = spec.find_first_of(_(']'));
if (end == String::npos) throw ParseError(_("Expected ']' in sort_rule specification"));
FOR_EACH_CONST(d, input) {
size_t in_spec = spec.find_first_of(d, pos);
if (in_spec < end) {
ret += d; // d is in the part between [ and ]
}
}
pos = end;
} else if (c == _('(')) { // in a cycle
size_t end = spec.find_first_of(_(')'), pos);
if (end == String::npos) throw ParseError(_("Expected ')' in sort_rule specification"));
ret += cycle_sort(spec.substr(pos + 1, end - pos - 1), input);
pos = end;
} else { // single char
FOR_EACH_CONST(d, input) {
if (c == d) ret += c;
}
}
}
return ret;
}
// ----------------------------------------------------------------------------- : Rules : sort text
// Sort using spec_sort
class ScriptRule_sort_order: public ScriptValue {
......@@ -637,19 +547,6 @@ class ScriptRule_sort_order: public ScriptValue {
private:
String order;
};
// Sort using sort_script
class ScriptRule_sort_order_by: public ScriptValue {
public:
inline ScriptRule_sort_order_by(const ScriptValueP& order_by) : order_by(order_by) {}
virtual ScriptType type() const { return SCRIPT_FUNCTION; }
virtual String typeName() const { return _("sort_rule"); }
virtual ScriptValueP eval(Context& ctx) const {
SCRIPT_PARAM(ScriptValueP, input);
return sort_script(ctx, input, *order_by);
}
private:
ScriptValueP order_by;
};
// Sort a string alphabetically
class ScriptRule_sort: public ScriptValue {
public:
......@@ -670,23 +567,15 @@ class ScriptRule_sort: public ScriptValue {
SCRIPT_FUNCTION(sort_rule) {
SCRIPT_OPTIONAL_PARAM(String, order) {
return new_intrusive1<ScriptRule_sort_order >(order);
}
SCRIPT_OPTIONAL_PARAM_N(ScriptValueP, _("order by"), order_by) {
return new_intrusive1<ScriptRule_sort_order_by>(order_by);
} else {
return new_intrusive <ScriptRule_sort >();
return new_intrusive1<ScriptRule_sort_order>(order);
}
return new_intrusive <ScriptRule_sort>();
}
SCRIPT_FUNCTION(sort) {
SCRIPT_FUNCTION(sort_text) {
SCRIPT_OPTIONAL_PARAM(String, order) {
return ScriptRule_sort_order (order ).eval(ctx);
}
SCRIPT_OPTIONAL_PARAM_N(ScriptValueP, _("order by"), order_by) {
return ScriptRule_sort_order_by(order_by).eval(ctx);
} else {
return ScriptRule_sort ( ).eval(ctx);
return ScriptRule_sort_order(order).eval(ctx);
}
return ScriptRule_sort().eval(ctx);
}
// ----------------------------------------------------------------------------- : Init
......@@ -711,8 +600,10 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("tag remove rule"), script_tag_remove_rule);
// collection
ctx.setVariable(_("position"), script_position_of);
ctx.setVariable(_("length"), script_number_of_items);
ctx.setVariable(_("number of items"), script_number_of_items);
ctx.setVariable(_("filter list"), script_filter_list);
ctx.setVariable(_("sort list"), script_sort_list);
// keyword
ctx.setVariable(_("expand keywords"), script_expand_keywords);
ctx.setVariable(_("expand keywords rule"), script_expand_keywords_rule);
......@@ -721,8 +612,7 @@ void init_script_basic_functions(Context& ctx) {
ctx.setVariable(_("replace"), script_replace);
ctx.setVariable(_("filter text"), script_filter_text);
ctx.setVariable(_("match"), script_match);
ctx.setVariable(_("sort"), script_sort);
ctx.setVariable(_("sort list"), script_sort);
ctx.setVariable(_("sort text"), script_sort_text);
ctx.setVariable(_("replace rule"), script_replace_rule);
ctx.setVariable(_("filter rule"), script_filter_rule);
ctx.setVariable(_("match rule"), script_match_rule);
......
......@@ -92,8 +92,7 @@
SCRIPT_OPTIONAL_PARAM_N(Type, _(#name), name)
/// Retrieve a named optional parameter
#define SCRIPT_OPTIONAL_PARAM_N(Type, str, name) \
ScriptValueP name##_ = ctx.getVariableOpt(str); \
Type name = name##_ ? from_script<Type>(name##_) : Type(); \
SCRIPT_OPTIONAL_PARAM_N_(Type, str, name) \
if (name##_)
/// Retrieve an optional parameter, can't be used as an if statement
......@@ -102,7 +101,8 @@
/// Retrieve a named optional parameter, can't be used as an if statement
#define SCRIPT_OPTIONAL_PARAM_N_(Type, str, name) \
ScriptValueP name##_ = ctx.getVariableOpt(str); \
Type name = name##_ ? from_script<Type>(name##_) : Type();
Type name = name##_ && name##_ != script_nil \
? from_script<Type>(name##_) : Type();
/// Retrieve an optional parameter with a default value
#define SCRIPT_PARAM_DEFAULT(Type, name, def) \
......
//+----------------------------------------------------------------------------+
//| 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 <util/spec_sort.hpp>
#include <util/error.hpp>
const Char REMOVED = _('\2');
const Char PLACEHOLDER = _('\3');
// ----------------------------------------------------------------------------- : Iterator for reading specs
/// Iterator over a sort specification (for spec_sort)
class SpecIterator {
public:
SpecIterator(const String& spec, size_t pos = 0)
: spec(spec), pos(pos)
{}
Char value; ///< Current character
bool escaped; ///< Was the current character escaped?
bool preceded_by_space; ///< Was there a ' ' before this character?
/// Move to the next item in the specification.
/** returns false if we are at the end or encounter close.
*/
bool nextUntil(Char close, bool skip_space = true) {
if (pos >= spec.size()) {
value = 0;
if (close == 0) {
return false;
} else {
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
}
}
value = spec.GetChar(pos++);
preceded_by_space = false;
// skip whitespace
if (skip_space) {
while (value == _(' ')) {
if (pos >= spec.size()) {
if (close == 0) {
return false;
} else {
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
}
}
value = spec.GetChar(pos++);
preceded_by_space = true;
}
}
// escape?
if (value == _('\\')) {
escaped = true;
if (pos >= spec.size()) {
throw ParseError(String::Format(_("Expected '%c' in sort_rule specification"),close));
}
value = spec.GetChar(pos++);
} else {
escaped = false;
if (value == close) return false;
}
return true;
}
/// Read a whole parameter, terminated by close
String readParam(Char close, bool skip_space = true) {
String ret;
while(nextUntil(close)) ret += value;
return ret;
}
/// Read a parameter, matches nested parentheses, keeps escape sequences
String readRawParam(Char close1, Char close2 = 0) {
String ret;
int parens = 0;
while (nextUntil(0, false)) {
if (escaped) {
ret += _('\\');
} else {
if (parens == 0 && (value == close1 || value == close2)) break;
if (value == _('(')) parens++;
else if (value == _(')')) parens--;
}
ret += value;
}
return ret;
}
/// Does the current position match a keyword? If so, skip it
bool keyword(const Char* kw) {
if (value == kw[0]) {
if (is_substr(spec, pos, kw + 1)) {
pos += wxStrlen(kw + 1);
return true;
}
}
return false;
}
private:
const String& spec;
size_t pos;
};
// ----------------------------------------------------------------------------- : Sort functions
/// Sort a string using a specification using the shortest cycle method, see spec_sort
/** Removed used characters from input! */
void cycle_sort(const String& spec, String& input, String& ret) {
size_t size = spec.size();
vector<UInt> counts;
// count occurences of each char in spec
FOR_EACH_CONST(s, spec) {
UInt c = 0;
FOR_EACH(i, input) {
if (s == i) {
i = REMOVED; // remove
c++;
}
}
counts.push_back(c);
}
// determine best start point
size_t best_start = 0;
UInt best_start_score = 0xffffffff;
for (size_t start = 0 ; start < size ; ++start) {
// score of a start position, can be considered as:
// - count saturated to binary
// - rotated left by start
// - interpreted as a binary number, but without trailing 0s
UInt score = 0, mul = 1;
for (size_t i = 0 ; i < size ; ++i) {
mul *= 2;
if (counts[(start + i) % size]) {
score = score * mul + 1;
mul = 1;
}
}
if (score < best_start_score) {
best_start_score = score;
best_start = start;
}
}
// return string
for (size_t i = 0 ; i < size ; ++i) {
size_t pos = (best_start + i) % size;
ret.append(counts[pos], spec[pos]);
}
}
/// Sort a string, keeping the characters in the original order
/** Removed used characters from input! */
void mixed_sort(const String& spec, String& input, String& ret) {
FOR_EACH(c, input) {
if (spec.find(c) != String::npos) {
ret += c;
c = REMOVED;
}
}
}
/// Sort a string, find a compound item
/** Removed used characters from input! */
void compound_sort(const String& spec, String& input, String& ret) {
while (size_t pos = input.find(spec)) {
ret += spec;
for (size_t j = 0 ; j < spec.size() ; ++j) input.SetChar(pos + j, REMOVED);
pos = input.find(spec, pos + 1);
}
}
/// Sort things matching a pattern
void pattern_sort(const String& pattern, const String& spec, String& input, String& ret) {
if (pattern.size() > input.size()) return;
size_t end = input.size() - pattern.size() + 1;
for (size_t pos = 0 ; pos < end ; ++pos) {
// does the pattern match here?
String placeholders;
bool match = true;
for (size_t j = 0 ; j < pattern.size() ; ++j) {
Char c = input.GetChar(pos + j);
Char p = pattern.GetChar(j);
if (c == REMOVED) { match = false; break; }
else if (p == PLACEHOLDER) {
placeholders += c;
} else if (c != p) { match = false; break; }
}
// do we have a match?
if (match) {
// sort placeholders
String new_placeholders = spec_sort(spec, placeholders);
if (new_placeholders.size() == placeholders.size()) {
// add to output, erase from input
size_t ph = 0;
for (size_t j = 0 ; j < pattern.size() ; ++j) {
Char p = pattern.GetChar(j);
if (p == PLACEHOLDER) {
ret += new_placeholders.GetChar(ph++);
} else {
ret += p;
}
input.SetChar(pos + j, REMOVED);
}
// erase from input
pos += pattern.size() - 1;
}
}
}
}
// ----------------------------------------------------------------------------- : spec_sort
String spec_sort(const String& spec, String& input, String& ret) {
SpecIterator it(spec);
while(it.nextUntil(0)) {
if (it.value == _('<')) { // keep only a single copy
while (it.nextUntil(_('>'))) {
size_t pos = input.find_first_of(it.value);
if (pos != String::npos) {
input.SetChar(pos, REMOVED);
ret += it.value; // input contains it.value
}
}
} else if (it.keyword(_("once("))) {
while (it.nextUntil(_(')'))) {
size_t pos = input.find_first_of(it.value);
if (pos != String::npos) {
input.SetChar(pos, REMOVED);
ret += it.value; // input contains it.value
}
}
} else if (it.value == _('[')) { // in input order
mixed_sort(it.readParam(_(']')), input, ret);
} else if (it.keyword(_("mixed("))) {
mixed_sort(it.readParam(_(')')), input, ret);
} else if (it.keyword(_("cycle("))) {
cycle_sort(it.readParam(_(')')), input, 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
while (it.nextUntil(_(' '), false)) {
if (it.value == _('.') && !it.escaped) {
it.value = PLACEHOLDER;
}
pattern += it.value;
}
// read spec to apply to pattern
String sub_spec = it.readRawParam(_(')'));
// sort
pattern_sort(pattern, sub_spec, input, ret);
} else if (it.keyword(_("any()"))) { // remaining input
FOR_EACH(d, input) {
if (d != REMOVED) {
ret += d;
d = REMOVED;
}
}
} else if (it.keyword(_("reverse_order("))) { // reverse order of preference
size_t old_ret_size = ret.size();
while (it.value != _(')')) {
size_t before_ret_size = ret.size();
String sub_spec = it.readRawParam(_(')'),_(' '));
spec_sort(sub_spec, input, ret);
// reverse this item
reverse(ret.begin() + before_ret_size, ret.end());
}
// re-reverse all items
reverse(ret.begin() + old_ret_size, ret.end());
} else if (it.keyword(_("ordered("))) { // in spec order
while (it.nextUntil(_(')'))) {
FOR_EACH(d, input) {
if (d == it.value) {
ret += d;
d = REMOVED;
}
}
}
} else { // single char
FOR_EACH(d, input) {
if (d == it.value) {
ret += d;
d = REMOVED;
}
}
}
}
return ret;
}
String spec_sort(const String& spec, String input) {
String ret;
spec_sort(spec, input, ret);
return ret;
}
//+----------------------------------------------------------------------------+
//| 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_UTIL_SPEC_SORT
#define HEADER_UTIL_SPEC_SORT
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : spec_sort
/// Sort a string using a sort specification
/** The specificatio can contain:
* - a = all 'a's go here
* - fun(A) = different behaviour
* - [abc] = mixed(abc)
* - <abc> = once(abc)
* - \? = an escaped ?
*
* 'Functions' are:
* - ordered(abc) = 'a', 'b' and 'c' go here, in that order
* - mixed(abc) = 'a', 'b' and 'c' go here, in the same order as in the input
* - once(abc) = 'a', 'b' and 'c' go here in that order, and only zero or one time.
* - cycle(abc) = 'a', 'b' and 'c' go here, in the shortest order
* consider the specified characters as a clockwise circle
* then returns the input in the order that:
* 1. takes the shortest clockwise path over this circle.
* 2. has _('holes') early, a hole means a character that is in the specification
* but not in the input
* 3. prefer the one that comes the earliest in the expression (a in this case)
* - compound(abc) = the connect sting "abc" goes gere
* - pattern(.. sort) = sort the things matching the pattern using 'sort' and replace them in the pattern
*
* example:
* spec_sort("XYZ<0123456789>cycle(WUBRG)",..) // used by magic
* "W1G") -> "1GW" // could be "W...G" or "...GW", second is shorter
* "GRBUWWUG") -> "WWUUBRGG" // no difference by rule 1,2, could be "WUBRG", "UBRGW", etc.
* // becomes _("WUBRG") by rule 3
* "WUR") -> "RWU" // by rule 1 could be "R WU" or "WU R", "RWU" has an earlier hole
*/
String spec_sort(const String& spec, String input);
// ----------------------------------------------------------------------------- : EOF
#endif
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