--- /dev/null
+package "poefilter"
+{
+ require "mspcore";
+ require "mspdatafile";
+
+ program "poefilter"
+ {
+ source "source";
+ };
+};
--- /dev/null
+#include "category.h"
+#include "choicecondition.h"
+#include "filter.h"
+#include "rangecondition.h"
+#include "theme.h"
+
+using namespace std;
+using namespace Msp;
+
+Category::Category():
+ condition(0),
+ font_size(1.0f),
+ order(0),
+ sound_type(0),
+ sound_volume(100)
+{ }
+
+Category::Category(const Category &other):
+ condition(other.condition ? other.condition->clone() : 0),
+ font_size(other.font_size),
+ border_color(other.border_color),
+ order(other.order),
+ sound_type(other.sound_type),
+ sound_volume(other.sound_volume)
+{ }
+
+Category &Category::operator=(const Category &other)
+{
+ delete condition;
+ condition = (other.condition ? other.condition->clone() : 0);
+ font_size = other.font_size;
+ border_color = other.border_color;
+ order = other.order;
+ sound_type = other.sound_type;
+ sound_volume = other.sound_volume;
+ return *this;
+}
+
+Category::~Category()
+{
+ delete condition;
+}
+
+void Category::create_statements(list<FilterStatement> &st, const Theme &theme) const
+{
+ st.clear();
+ st.push_back(FilterStatement());
+ st.back().add_line(format("SetFontSize %d", static_cast<int>(font_size*theme.get_base_font_size()+0.5)));
+ if(!border_color.empty())
+ {
+ const Color &color = theme.get_color(border_color);
+ st.back().add_line(format("SetBorderColor %d %d %d", color.r, color.g, color.b));
+ }
+ if(sound_type)
+ st.back().add_line(format("PlayAlertSound %d %d", sound_type, sound_volume));
+ if(condition)
+ condition->add_lines(st);
+}
+
+
+Category::Loader::Loader(Category &c, CompoundCondition *n):
+ DataFile::ObjectLoader<Category>(c),
+ compound(n)
+{
+ add("alert_sound", &Category::sound_type, &Category::sound_volume);
+ add("and", &Loader::and_);
+ add("base_type", &Loader::condition<BaseTypeCondition>);
+ add("border_color", &Category::border_color);
+ add("class", &Loader::condition<ClassCondition>);
+ add_range<DropLevelCondition>("drop_level");
+ add("font_size", &Category::font_size);
+ add_range<HeightCondition>("height");
+ add_range<ItemLevelCondition>("item_level");
+ add("linked_colors", &Loader::linked_colors);
+ add_range<LinkedSocketsCondition>("linked_sockets");
+ add_range<SocketsCondition>("sockets");
+ add("or", &Loader::or_);
+ add("order", &Category::order);
+ add_range<QualityCondition>("quality");
+ add_range<RarityCondition>("rarity");
+ add_range<WidthCondition>("width");
+}
+
+template<typename T>
+void Category::Loader::add_range(const string &keyword)
+{
+ add(keyword, &Loader::condition<T>);
+ add(keyword, &Loader::condition_range<T>);
+ add("min_"+keyword, &Loader::condition_min<T>);
+ add("max_"+keyword, &Loader::condition_max<T>);
+}
+
+void Category::Loader::add_condition(Condition *cond)
+{
+ if(compound)
+ compound->add(cond);
+ else if(!obj.condition)
+ obj.condition = cond;
+ else
+ {
+ compound = new AndCondition;
+ compound->add(obj.condition);
+ compound->add(cond);
+ obj.condition = compound;
+ }
+}
+
+void Category::Loader::and_()
+{
+ RefPtr<AndCondition> cond = new AndCondition;
+ Loader sub_ldr(obj, cond.get());
+ load_sub_with(sub_ldr);
+ add_condition(cond.release());
+}
+
+template<typename T>
+void Category::Loader::condition(typename T::Type value)
+{
+ add_condition(new T(value));
+}
+
+template<typename T>
+void Category::Loader::condition_max(typename T::Type max)
+{
+ add_condition(new T(T::Traits::get_min(), max));
+}
+
+template<typename T>
+void Category::Loader::condition_min(typename T::Type min)
+{
+ add_condition(new T(min, T::Traits::get_max()));
+}
+
+template<typename T>
+void Category::Loader::condition_range(typename T::Type min, typename T::Type max)
+{
+ add_condition(new T(min, max));
+}
+
+void Category::Loader::linked_colors(const LinkedColorsCondition::Colors &colors)
+{
+ add_condition(new LinkedColorsCondition(colors));
+}
+
+void Category::Loader::or_()
+{
+ RefPtr<OrCondition> cond = new OrCondition;
+ Loader sub_ldr(obj, cond.get());
+ load_sub_with(sub_ldr);
+ add_condition(cond.release());
+}
--- /dev/null
+#ifndef CATEGORY_H_
+#define CATEGORY_H_
+
+#include <list>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include "condition.h"
+#include "rarity.h"
+
+class FilterStatement;
+class Theme;
+
+class Category
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Category>
+ {
+ private:
+ CompoundCondition *compound;
+
+ public:
+ Loader(Category &, CompoundCondition * = 0);
+
+ protected:
+ template<typename T>
+ void add_range(const std::string &);
+
+ void add_condition(Condition *);
+
+ private:
+ void and_();
+
+ template<typename T>
+ void condition(typename T::Type);
+
+ template<typename T>
+ void condition_max(typename T::Type);
+
+ template<typename T>
+ void condition_min(typename T::Type);
+
+ template<typename T>
+ void condition_range(typename T::Type, typename T::Type);
+
+ void linked_colors(const LinkedColorsCondition::Colors &);
+ void or_();
+ };
+
+private:
+ Condition *condition;
+ float font_size;
+ std::string border_color;
+ unsigned order;
+ unsigned sound_type;
+ unsigned sound_volume;
+
+public:
+ Category();
+ Category(const Category &);
+ Category &operator=(const Category &);
+ ~Category();
+
+ unsigned get_order() const { return order; }
+ void create_statements(std::list<FilterStatement> &, const Theme &) const;
+};
+
+#endif
--- /dev/null
+#ifndef CHOICECONDITION_H_
+#define CHOICECONDITION_H_
+
+#include "condition.h"
+#include "filter.h"
+
+template<typename T>
+class ChoiceCondition: public Condition
+{
+public:
+ typedef T Traits;
+ typedef typename Traits::Type Type;
+
+private:
+ Type value;
+
+public:
+ ChoiceCondition(Type);
+
+ virtual Condition *clone() const;
+ virtual bool can_merge(const Condition &, const CompoundCondition &) const;
+ virtual void add_lines(std::list<FilterStatement> &) const;
+ virtual void add_merged_lines(const std::list<Condition *> &, const CompoundCondition &, std::list<FilterStatement> &) const;
+};
+
+
+struct ClassTraits
+{
+ typedef std::string Type;
+ static const char *get_keyword() { return "Class"; }
+};
+
+typedef ChoiceCondition<ClassTraits> ClassCondition;
+
+
+struct BaseTypeTraits
+{
+ typedef std::string Type;
+ static const char *get_keyword() { return "BaseType"; }
+};
+
+typedef ChoiceCondition<BaseTypeTraits> BaseTypeCondition;
+
+
+template<typename Traits>
+ChoiceCondition<Traits>::ChoiceCondition(Type v):
+ value(v)
+{ }
+
+template<typename Traits>
+Condition *ChoiceCondition<Traits>::clone() const
+{
+ return new ChoiceCondition<Traits>(value);
+}
+
+template<typename Traits>
+bool ChoiceCondition<Traits>::can_merge(const Condition &other, const CompoundCondition &parent) const
+{
+ return dynamic_cast<const ChoiceCondition<Traits> *>(&other) && dynamic_cast<const OrCondition *>(&parent);
+}
+
+template<typename Traits>
+void ChoiceCondition<Traits>::add_lines(std::list<FilterStatement> &st) const
+{
+ const char *keyword = Traits::get_keyword();
+ FilterStatement::add_line(st, Msp::format("%s \"%s\"", keyword, value));
+}
+
+template<typename Traits>
+void ChoiceCondition<Traits>::add_merged_lines(const std::list<Condition *> &conditions, const CompoundCondition &parent, std::list<FilterStatement> &st) const
+{
+ if(dynamic_cast<const OrCondition *>(&parent))
+ {
+ std::string line = Traits::get_keyword();
+ for(std::list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+ line += Msp::format(" \"%s\"", static_cast<const ChoiceCondition<Traits> *>(*i)->value);
+ FilterStatement::add_line(st, line);
+ }
+}
+
+#endif
--- /dev/null
+#include "condition.h"
+#include "filter.h"
+
+using namespace std;
+using namespace Msp;
+
+CompoundCondition::~CompoundCondition()
+{
+ for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+ delete *i;
+}
+
+void CompoundCondition::clone_to(CompoundCondition &other) const
+{
+ for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+ other.add((*i)->clone());
+}
+
+void CompoundCondition::add(Condition *cond)
+{
+ conditions.push_back(cond);
+}
+
+
+Condition *AndCondition::clone() const
+{
+ AndCondition *result = new AndCondition;
+ clone_to(*result);
+ return result;
+}
+
+void AndCondition::add_lines(list<FilterStatement> &st) const
+{
+ for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+ (*i)->add_lines(st);
+}
+
+
+Condition *OrCondition::clone() const
+{
+ OrCondition *result = new OrCondition;
+ clone_to(*result);
+ return result;
+}
+
+void OrCondition::add_lines(list<FilterStatement> &st) const
+{
+ bool merge = conditions.size()>1;
+ for(list<Condition *>::const_iterator i=conditions.begin(); (merge && ++i!=conditions.end()); )
+ merge = conditions.front()->can_merge(**i, *this);
+
+ if(merge)
+ conditions.front()->add_merged_lines(conditions, *this, st);
+ else
+ {
+ list<FilterStatement> result;
+ for(list<Condition *>::const_iterator i=conditions.begin(); i!=conditions.end(); ++i)
+ {
+ list<FilterStatement> sub_result = st;
+ (*i)->add_lines(sub_result);
+ result.splice(result.end(), sub_result);
+ }
+ swap(result, st);
+ }
+}
+
+
+LinkedColorsCondition::LinkedColorsCondition(const Colors &c):
+ colors(c)
+{ }
+
+Condition *LinkedColorsCondition::clone() const
+{
+ return new LinkedColorsCondition(colors);
+}
+
+void LinkedColorsCondition::add_lines(list<FilterStatement> &st) const
+{
+ FilterStatement::add_line(st, format("SocketGroup %s", colors.colors));
+}
+
+
+void operator>>(const LexicalConverter &conv, LinkedColorsCondition::Colors &colors)
+{
+ const string &str = conv.get();
+ bool rgb = true;
+ for(string::const_iterator i=str.begin(); (rgb && i!=str.end()); ++i)
+ rgb = (*i=='R' || *i=='G' || *i=='B');
+ if(str.size()>6 || !rgb)
+ throw lexical_error(format("conversion of '%s' to LinkedColorsCondition::Colors", str));
+
+ fill(colors.colors, colors.colors+7, '\0');
+ copy(str.begin(), str.end(), colors.colors);
+}
--- /dev/null
+#ifndef CONDITION_H_
+#define CONDITION_H_
+
+#include <list>
+#include <string>
+#include <msp/strings/format.h>
+#include "rarity.h"
+
+class CompoundCondition;
+class FilterStatement;
+
+class Condition
+{
+protected:
+ Condition() { }
+public:
+ virtual ~Condition() { }
+
+ virtual Condition *clone() const = 0;
+ virtual bool can_merge(const Condition &, const CompoundCondition &) const { return false; }
+ virtual void add_lines(std::list<FilterStatement> &) const = 0;
+ virtual void add_merged_lines(const std::list<Condition *> &, const CompoundCondition &, std::list<FilterStatement> &) const { }
+};
+
+
+class CompoundCondition: public Condition
+{
+protected:
+ std::list<Condition *> conditions;
+
+public:
+ virtual ~CompoundCondition();
+
+protected:
+ void clone_to(CompoundCondition &) const;
+
+public:
+ void add(Condition *);
+};
+
+
+class AndCondition: public CompoundCondition
+{
+public:
+ virtual Condition *clone() const;
+ virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+class OrCondition: public CompoundCondition
+{
+public:
+ virtual Condition *clone() const;
+ virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+class LinkedColorsCondition: public Condition
+{
+public:
+ struct Colors
+ {
+ char colors[7];
+ };
+
+private:
+ Colors colors;
+
+public:
+ LinkedColorsCondition(const Colors &);
+
+ virtual Condition *clone() const;
+ virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+void operator>>(const Msp::LexicalConverter &, LinkedColorsCondition::Colors &);
+
+#endif
--- /dev/null
+#include <algorithm>
+#include <msp/core/maputils.h>
+#include <msp/io/print.h>
+#include "category.h"
+#include "filter.h"
+#include "poefilter.h"
+
+using namespace std;
+using namespace Msp;
+
+FilterStatement::FilterStatement():
+ show(true)
+{ }
+
+void FilterStatement::set_show(bool s)
+{
+ show = s;
+}
+
+void FilterStatement::add_line(const string &l)
+{
+ lines.push_back(l);
+}
+
+void FilterStatement::add_line(list<FilterStatement> &st, const string &line)
+{
+ for(list<FilterStatement>::iterator i=st.begin(); i!=st.end(); ++i)
+ i->add_line(line);
+}
+
+void FilterStatement::write(IO::Base &out) const
+{
+ if(show)
+ out.write("Show\n");
+ else
+ out.write("Hide\n");
+
+ for(list<string>::const_iterator i=lines.begin(); i!=lines.end(); ++i)
+ IO::print(out, "\t%s\n", *i);
+}
+
+
+Filter::Filter():
+ abstract(false)
+{ }
+
+void Filter::write(IO::Base &out, const Theme &theme) const
+{
+ for(list<const Category *>::const_iterator i=categories.begin(); i!=categories.end(); ++i)
+ {
+ list<FilterStatement> st;
+ //IO::print(out, "# %s\n", i->first);
+ (*i)->create_statements(st, theme);
+ for(list<FilterStatement>::const_iterator j=st.begin(); j!=st.end(); ++j)
+ j->write(out);
+ }
+
+ out.write("# unmatched\nHide\n");
+}
+
+
+Filter::Loader::Loader(Filter &f, const PoeFilter &p):
+ DataFile::ObjectLoader<Filter>(f),
+ poe(p)
+{
+ add("abstract", &Filter::abstract);
+ add("hide", &Loader::hide);
+ add("include", &Loader::include);
+ add("show", &Loader::show);
+}
+
+string Filter::Loader::glob_to_re(const string &glob)
+{
+ string result = "^";
+ for(string::const_iterator i=glob.begin(); i!=glob.end(); ++i)
+ {
+ if(*i=='*')
+ result += '.';
+ else if(!isalnum(*i))
+ result += '\\';
+ result += *i;
+ }
+ result += '$';
+
+ return result;
+}
+
+bool Filter::Loader::category_order(const Category *c1, const Category *c2)
+{
+ return c1->get_order()<c2->get_order();
+}
+
+void Filter::Loader::hide(const string &name)
+{
+ list<const Category *> categs;
+ if(name.find('*')!=string::npos)
+ {
+ poe.find_categories(glob_to_re(name), categs);
+
+ if(categs.empty())
+ throw key_error(name);
+ }
+ else
+ categs.push_back(&poe.get_category(name));
+
+ for(list<const Category *>::const_iterator i=categs.begin(); i!=categs.end(); ++i)
+ {
+ list<const Category *>::iterator j = find(obj.categories.begin(), obj.categories.end(), *i);
+ if(j!=obj.categories.end())
+ obj.categories.erase(j);
+ }
+}
+
+void Filter::Loader::include(const string &name)
+{
+ const Filter &base = poe.get_filter(name);
+ obj.categories.insert(obj.categories.end(), base.categories.begin(), base.categories.end());
+}
+
+void Filter::Loader::show(const string &name)
+{
+ list<const Category *> categs;
+ if(name.find('*')!=string::npos)
+ {
+ poe.find_categories(glob_to_re(name), categs);
+
+ if(categs.empty())
+ throw key_error(name);
+
+ categs.sort(&category_order);
+ }
+ else
+ categs.push_back(&poe.get_category(name));
+
+ for(list<const Category *>::const_iterator i=categs.begin(); i!=categs.end(); ++i)
+ if(find(obj.categories.begin(), obj.categories.end(), *i)==obj.categories.end())
+ obj.categories.push_back(*i);
+}
--- /dev/null
+#ifndef FILTER_H_
+#define FILTER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include <msp/io/base.h>
+
+class Category;
+class PoeFilter;
+class Theme;
+
+class FilterStatement
+{
+private:
+ bool show;
+ std::list<std::string> lines;
+
+public:
+ FilterStatement();
+
+ void set_show(bool);
+ void add_line(const std::string &);
+
+ static void add_line(std::list<FilterStatement> &, const std::string &);
+
+ void write(Msp::IO::Base &) const;
+};
+
+
+class Filter
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Filter>
+ {
+ private:
+ const PoeFilter &poe;
+
+ public:
+ Loader(Filter &, const PoeFilter &);
+
+ private:
+ static std::string glob_to_re(const std::string &);
+ static bool category_order(const Category *, const Category *);
+
+ void hide(const std::string &);
+ void include(const std::string &);
+ void show(const std::string &);
+ };
+
+private:
+ std::list<const Category *> categories;
+ bool abstract;
+
+public:
+ Filter();
+
+ bool is_abstract() const { return abstract; }
+ void write(Msp::IO::Base &, const Theme &) const;
+};
+
+#endif
--- /dev/null
+#include <msp/core/getopt.h>
+#include <msp/core/maputils.h>
+#include <msp/io/file.h>
+#include "category.h"
+#include "filter.h"
+#include "poefilter.h"
+
+using namespace std;
+using namespace Msp;
+
+PoeFilter::PoeFilter(int argc, char **argv)
+{
+ GetOpt getopt;
+ getopt.add_argument("filename", filename, GetOpt::REQUIRED_ARG);
+ getopt(argc, argv);
+}
+
+int PoeFilter::main()
+{
+ DataFile::load(*this, filename);
+
+ for(map<string, Filter>::const_iterator i=filters.begin(); i!=filters.end(); ++i)
+ if(!i->second.is_abstract())
+ {
+ IO::BufferedFile out(i->first+".filter", IO::M_WRITE);
+ i->second.write(out, theme);
+ }
+
+ return 0;
+}
+
+const Category &PoeFilter::get_category(const string &name) const
+{
+ return get_item(categories, name);
+}
+
+void PoeFilter::find_categories(const Regex &re, list<const Category *> &categs) const
+{
+ for(map<string, Category>::const_iterator i=categories.begin(); i!=categories.end(); ++i)
+ if(re.match(i->first))
+ categs.push_back(&i->second);
+}
+
+const Filter &PoeFilter::get_filter(const string &name) const
+{
+ return get_item(filters, name);
+}
+
+
+PoeFilter::Loader::Loader(PoeFilter &f):
+ DataFile::ObjectLoader<PoeFilter>(f)
+{
+ add("category", &Loader::category);
+ add("filter", &Loader::filter);
+ add("theme", &Loader::theme);
+}
+
+void PoeFilter::Loader::category(const string &name)
+{
+ Category cat;
+ load_sub(cat);
+ obj.categories[name] = cat;
+}
+
+void PoeFilter::Loader::filter(const string &name)
+{
+ Filter flt;
+ load_sub(flt, obj);
+ obj.filters[name] = flt;
+}
+
+void PoeFilter::Loader::theme()
+{
+ load_sub(obj.theme);
+}
--- /dev/null
+#ifndef POEFILTER_H_
+#define POEFILTER_H_
+
+#include <map>
+#include <string>
+#include <msp/core/application.h>
+#include <msp/strings/regex.h>
+#include "theme.h"
+
+class Category;
+class Filter;
+
+class PoeFilter: public Msp::RegisteredApplication<PoeFilter>
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<PoeFilter>
+ {
+ public:
+ Loader(PoeFilter &);
+
+ private:
+ void category(const std::string &);
+ void filter(const std::string &);
+ void theme();
+ };
+
+private:
+ std::string filename;
+ Theme theme;
+ std::map<std::string, Category> categories;
+ std::map<std::string, Filter> filters;
+
+public:
+ PoeFilter(int, char **);
+
+ virtual int main();
+
+ const Category &get_category(const std::string &) const;
+ void find_categories(const Msp::Regex &, std::list<const Category *> &) const;
+ const Filter &get_filter(const std::string &) const;
+};
+
+#endif
--- /dev/null
+#ifndef RANGECONDITION_H_
+#define RANGECONDITION_H_
+
+#include "condition.h"
+#include "filter.h"
+
+template<typename T>
+class RangeCondition: public Condition
+{
+public:
+ typedef T Traits;
+ typedef typename Traits::Type Type;
+
+protected:
+ Type min;
+ Type max;
+
+public:
+ RangeCondition(Type);
+ RangeCondition(Type, Type);
+
+ virtual Condition *clone() const;
+ virtual void add_lines(std::list<FilterStatement> &) const;
+};
+
+
+struct RarityTraits
+{
+ typedef Rarity Type;
+ static Rarity get_min() { return NORMAL; }
+ static Rarity get_max() { return UNIQUE; }
+ static const char *get_keyword() { return "Rarity"; }
+};
+
+typedef RangeCondition<RarityTraits> RarityCondition;
+
+
+struct QualityTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 0; }
+ static unsigned get_max() { return 20; }
+ static const char *get_keyword() { return "Quality"; }
+};
+
+typedef RangeCondition<QualityTraits> QualityCondition;
+
+
+struct ItemLevelTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 0; }
+ static unsigned get_max() { return 100; }
+ static const char *get_keyword() { return "ItemLevel"; }
+};
+
+typedef RangeCondition<ItemLevelTraits> ItemLevelCondition;
+
+
+struct DropLevelTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 0; }
+ static unsigned get_max() { return 100; }
+ static const char *get_keyword() { return "DropLevel"; }
+};
+
+typedef RangeCondition<DropLevelTraits> DropLevelCondition;
+
+
+struct WidthTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 1; }
+ static unsigned get_max() { return 2; }
+ static const char *get_keyword() { return "Width"; }
+};
+
+typedef RangeCondition<WidthTraits> WidthCondition;
+
+
+struct HeightTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 1; }
+ static unsigned get_max() { return 4; }
+ static const char *get_keyword() { return "Height"; }
+};
+
+typedef RangeCondition<HeightTraits> HeightCondition;
+
+
+struct SocketsTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 1; }
+ static unsigned get_max() { return 6; }
+ static const char *get_keyword() { return "Sockets"; }
+};
+
+typedef RangeCondition<SocketsTraits> SocketsCondition;
+
+
+struct LinkedSocketsTraits
+{
+ typedef unsigned Type;
+ static unsigned get_min() { return 1; }
+ static unsigned get_max() { return 6; }
+ static const char *get_keyword() { return "LinkedSockets"; }
+};
+
+typedef RangeCondition<LinkedSocketsTraits> LinkedSocketsCondition;
+
+
+template<typename Traits>
+RangeCondition<Traits>::RangeCondition(Type v):
+ min(v),
+ max(v)
+{ }
+
+template<typename Traits>
+RangeCondition<Traits>::RangeCondition(Type n, Type x):
+ min(n),
+ max(x)
+{ }
+
+template<typename Traits>
+Condition *RangeCondition<Traits>::clone() const
+{
+ return new RangeCondition<Traits>(min, max);
+}
+
+template<typename Traits>
+void RangeCondition<Traits>::add_lines(std::list<FilterStatement> &st) const
+{
+ const char *keyword = Traits::get_keyword();
+ if(min==max)
+ FilterStatement::add_line(st, Msp::format("%s %s", keyword, min));
+ else
+ {
+ if(min!=Traits::get_min())
+ FilterStatement::add_line(st, Msp::format("%s >= %s", keyword, min));
+ if(max!=Traits::get_max())
+ FilterStatement::add_line(st, Msp::format("%s <= %s", keyword, max));
+ }
+}
+
+#endif
--- /dev/null
+#include <msp/strings/format.h>
+#include "rarity.h"
+
+using namespace std;
+using namespace Msp;
+
+void operator>>(const LexicalConverter &conv, Rarity &rarity)
+{
+ const string &str = conv.get();
+ if(str=="Normal")
+ rarity = NORMAL;
+ else if(str=="Magic")
+ rarity = MAGIC;
+ else if(str=="Rare")
+ rarity = RARE;
+ else if(str=="Unique")
+ rarity = UNIQUE;
+ else
+ throw lexical_error(format("Conversion of %s to Rarity", str));
+}
+
+void operator<<(LexicalConverter &conv, Rarity rarity)
+{
+ switch(rarity)
+ {
+ case NORMAL: conv.result("Normal"); return;
+ case MAGIC: conv.result("Magic"); return;
+ case RARE: conv.result("Rare"); return;
+ case UNIQUE: conv.result("Unique"); return;
+ default: conv.result(format("Rarity(%d)", static_cast<int>(rarity))); return;
+ }
+}
--- /dev/null
+#ifndef RARITY_H_
+#define RARITY_H_
+
+#include <msp/strings/lexicalcast.h>
+
+enum Rarity
+{
+ NORMAL,
+ MAGIC,
+ RARE,
+ UNIQUE
+};
+
+void operator>>(const Msp::LexicalConverter &, Rarity &);
+void operator<<(Msp::LexicalConverter &, Rarity);
+
+#endif
--- /dev/null
+#include <msp/core/maputils.h>
+#include "theme.h"
+
+using namespace std;
+using namespace Msp;
+
+Color::Color():
+ r(200),
+ g(200),
+ b(200)
+{ }
+
+Color::Color(unsigned r_, unsigned g_, unsigned b_):
+ r(r_),
+ g(g_),
+ b(b_)
+{ }
+
+
+Theme::Theme():
+ base_font_size(32)
+{ }
+
+void Theme::set_color(const string &name, const Color &color)
+{
+ colors[name] = color;
+}
+
+const Color &Theme::get_color(const string &name) const
+{
+ return get_item(colors, name);
+}
+
+void Theme::set_base_font_size(unsigned size)
+{
+ base_font_size = size;
+}
+
+
+Theme::Loader::Loader(Theme &t):
+ DataFile::ObjectLoader<Theme>(t)
+{
+ add("base_font_size", &Theme::base_font_size);
+ add("color", &Loader::color);
+}
+
+void Theme::Loader::color(const string &name, unsigned r, unsigned g, unsigned b)
+{
+ obj.set_color(name, Color(r, g, b));
+}
--- /dev/null
+#ifndef THEME_H_
+#define THEME_H_
+
+#include <map>
+#include <string>
+#include <msp/datafile/objectloader.h>
+
+struct Color
+{
+ unsigned r;
+ unsigned g;
+ unsigned b;
+
+ Color();
+ Color(unsigned, unsigned, unsigned);
+};
+
+
+class Theme
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Theme>
+ {
+ public:
+ Loader(Theme &);
+
+ private:
+ void color(const std::string &, unsigned, unsigned, unsigned);
+ };
+
+private:
+ typedef std::map<std::string, Color> ColorMap;
+
+ ColorMap colors;
+ unsigned base_font_size;
+
+public:
+ Theme();
+
+ void set_color(const std::string &, const Color &);
+ const Color &get_color(const std::string &) const;
+
+ void set_base_font_size(unsigned);
+ unsigned get_base_font_size() const { return base_font_size; }
+};
+
+#endif