From 3b6743b110e22027f3b1eb1c5d32283ccf54e013 Mon Sep 17 00:00:00 2001 From: twanvl Date: Fri, 11 May 2007 14:10:29 +0000 Subject: [PATCH] Added support for custom colors for graphs; Moved 'choice colors' from styel to field, split into 'choice colors' and 'choice colors cardlist'; Added support for pie graphs (no gui to use them, though); Fixed bugs caused by selecting a card before the set was changed on all panels. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@336 0fc631ac-6414-0410-93d0-97cfa31319b6 --- .../magic-blends.mse-include/card-backgrounds | 13 - data/magic-blends.mse-include/card-colors | 149 +++++++++- data/magic.mse-game/game | 23 ++ data/vs-extended-art.mse-style/style | 9 - data/vs-standard.mse-style/style | 9 - data/vs.mse-game/game | 17 +- data/yugioh-standard.mse-style/style | 12 - data/yugioh.mse-game/game | 11 + src/data/field/choice.cpp | 5 +- src/data/field/choice.hpp | 4 +- src/data/statistics.cpp | 18 +- src/data/statistics.hpp | 14 +- src/gui/control/card_list.cpp | 23 +- src/gui/control/card_list.hpp | 7 +- src/gui/control/filtered_card_list.cpp | 6 + src/gui/control/filtered_card_list.hpp | 4 +- src/gui/control/graph.cpp | 270 ++++++++++++++---- src/gui/control/graph.hpp | 79 ++++- src/gui/control/item_list.cpp | 4 + src/gui/control/item_list.hpp | 2 + src/gui/set/cards_panel.cpp | 5 + src/gui/set/cards_panel.hpp | 1 + src/gui/set/panel.hpp | 1 + src/gui/set/stats_panel.cpp | 61 ++-- src/gui/set/stats_panel.hpp | 1 + src/gui/set/style_panel.cpp | 2 +- src/gui/set/window.cpp | 19 +- src/render/card/viewer.cpp | 7 + src/render/card/viewer.hpp | 3 + src/script/functions/basic.cpp | 2 +- src/util/rotation.cpp | 25 ++ src/util/rotation.hpp | 4 + src/util/window_id.hpp | 10 +- 33 files changed, 634 insertions(+), 186 deletions(-) diff --git a/data/magic-blends.mse-include/card-backgrounds b/data/magic-blends.mse-include/card-backgrounds index 2a5674b8..a8150155 100644 --- a/data/magic-blends.mse-include/card-backgrounds +++ b/data/magic-blends.mse-include/card-backgrounds @@ -133,16 +133,3 @@ choice images: hybrid vertical black / artifact : script: card_hybrid_2v("ba") hybrid vertical red / artifact : script: card_hybrid_2v("ra") hybrid vertical green / artifact : script: card_hybrid_2v("ga") - -# Also define colors for card list -card list colors: true -choice colors: - white : rgb(130,130,110) - blue : rgb(0,64,168) - black : rgb(0,0,0) - red : rgb(168,0,0) - green : rgb(0,168,0) - colorless : rgb(72,90,100) - artifact : rgb(72,90,100) - multicolor : rgb(120,120,0) - land : rgb(84,84,60) diff --git a/data/magic-blends.mse-include/card-colors b/data/magic-blends.mse-include/card-colors index c93fa80d..708fcc5b 100644 --- a/data/magic-blends.mse-include/card-colors +++ b/data/magic-blends.mse-include/card-colors @@ -163,4 +163,151 @@ choice: choice: artifact / blue choice: artifact / black choice: artifact / red - choice: artifact / green \ No newline at end of file + choice: artifact / green + +# Also define colors for statistics +choice colors: + white : rgb(255,237,202) + blue : rgb(42,141,255) + black : rgb(33,33,33) + red : rgb(255,52,0) + green : rgb(138,230,0) + colorless : rgb(122,85,85) + artifact : rgb(188,192,195) + multicolor : rgb(255,188,14) + land : rgb(109,62,39) + # Sub menus, same colors + multicolor 2 color white / blue : rgb(255,188,14) + multicolor 2 color blue / black : rgb(255,188,14) + multicolor 2 color black / red : rgb(255,188,14) + multicolor 2 color red / green : rgb(255,188,14) + multicolor 2 color green / white : rgb(255,188,14) + multicolor 2 color white / black : rgb(255,188,14) + multicolor 2 color blue / red : rgb(255,188,14) + multicolor 2 color black / green : rgb(255,188,14) + multicolor 2 color red / white : rgb(255,188,14) + multicolor 2 color green / blue : rgb(255,188,14) + multicolor 3 color white / blue / black : rgb(255,188,14) + multicolor 3 color blue / black / red : rgb(255,188,14) + multicolor 3 color black / red / green : rgb(255,188,14) + multicolor 3 color red / green / white : rgb(255,188,14) + multicolor 3 color green / white / blue : rgb(255,188,14) + multicolor 3 color white / black / red : rgb(255,188,14) + multicolor 3 color blue / red / green : rgb(255,188,14) + multicolor 3 color black / green / white : rgb(255,188,14) + multicolor 3 color red / white / blue : rgb(255,188,14) + multicolor 3 color green / blue / black : rgb(255,188,14) + multicolor 4 color white / blue / black / red : rgb(255,188,14) + multicolor 4 color blue / black / red / green : rgb(255,188,14) + multicolor 4 color black / red / green / white : rgb(255,188,14) + multicolor 4 color red / green / white / blue : rgb(255,188,14) + multicolor 4 color green / white / blue / black : rgb(255,188,14) + multicolor 5 color white / blue / black / red / green : rgb(255,188,14) + multicolor 5 color horizontal white / blue / black / red / green: rgb(255,188,14) + land 1 color white : rgb(109,62,39) + land 1 color blue : rgb(109,62,39) + land 1 color black : rgb(109,62,39) + land 1 color red : rgb(109,62,39) + land 1 color green : rgb(109,62,39) + land 1 color multicolor : rgb(109,62,39) + land 2 color white / blue : rgb(109,62,39) + land 2 color blue / black : rgb(109,62,39) + land 2 color black / red : rgb(109,62,39) + land 2 color red / green : rgb(109,62,39) + land 2 color green / white : rgb(109,62,39) + land 2 color white / black : rgb(109,62,39) + land 2 color blue / red : rgb(109,62,39) + land 2 color black / green : rgb(109,62,39) + land 2 color red / white : rgb(109,62,39) + land 2 color green / blue : rgb(109,62,39) + hybrid 2 color white / blue : rgb(243,26,136) # purple + hybrid 2 color blue / black : rgb(243,26,136) + hybrid 2 color black / red : rgb(243,26,136) + hybrid 2 color red / green : rgb(243,26,136) + hybrid 2 color green / white : rgb(243,26,136) + hybrid 2 color white / black : rgb(243,26,136) + hybrid 2 color blue / red : rgb(243,26,136) + hybrid 2 color black / green : rgb(243,26,136) + hybrid 2 color red / white : rgb(243,26,136) + hybrid 2 color green / blue : rgb(243,26,136) + hybrid 2 color artifact / white : rgb(243,26,136) + hybrid 2 color artifact / blue : rgb(243,26,136) + hybrid 2 color artifact / black : rgb(243,26,136) + hybrid 2 color artifact / red : rgb(243,26,136) + hybrid 2 color artifact / green : rgb(243,26,136) + hybrid 3 color white / blue / black : rgb(243,26,136) + hybrid 3 color blue / black / red : rgb(243,26,136) + hybrid 3 color black / red / green : rgb(243,26,136) + hybrid 3 color red / green / white : rgb(243,26,136) + hybrid 3 color green / white / blue : rgb(243,26,136) + hybrid 3 color white / black / red : rgb(243,26,136) + hybrid 3 color blue / red / green : rgb(243,26,136) + hybrid 3 color black / green / white : rgb(243,26,136) + hybrid 3 color red / white / blue : rgb(243,26,136) + hybrid 3 color green / blue / black : rgb(243,26,136) + hybrid 3 color horizontal white / blue / black : rgb(243,26,136) + hybrid 3 color horizontal blue / black / red : rgb(243,26,136) + hybrid 3 color horizontal black / red / green : rgb(243,26,136) + hybrid 3 color horizontal red / green / white : rgb(243,26,136) + hybrid 3 color horizontal green / white / blue : rgb(243,26,136) + hybrid 3 color horizontal white / black / red : rgb(243,26,136) + hybrid 3 color horizontal blue / red / green : rgb(243,26,136) + hybrid 3 color horizontal black / green / white : rgb(243,26,136) + hybrid 3 color horizontal red / white / blue : rgb(243,26,136) + hybrid 3 color horizontal green / blue / black : rgb(243,26,136) + hybrid 4 color white / blue / black / red : rgb(243,26,136) + hybrid 4 color blue / black / red / green : rgb(243,26,136) + hybrid 4 color black / red / green / white : rgb(243,26,136) + hybrid 4 color red / green / white / blue : rgb(243,26,136) + hybrid 4 color green / white / blue / black : rgb(243,26,136) + hybrid 4 color horizontal white / blue / black / red : rgb(243,26,136) + hybrid 4 color horizontal blue / black / red / green : rgb(243,26,136) + hybrid 4 color horizontal black / red / green / white : rgb(243,26,136) + hybrid 4 color horizontal red / green / white / blue : rgb(243,26,136) + hybrid 4 color horizontal green / white / blue / black : rgb(243,26,136) + hybrid 5 color white / blue / black / red / green : rgb(243,26,136) + hybrid 5 color horizontal white / blue / black / red / green : rgb(243,26,136) + hybrid vertical white / blue : rgb(243,26,136) + hybrid vertical white / black : rgb(243,26,136) + hybrid vertical white / red : rgb(243,26,136) + hybrid vertical white / green : rgb(243,26,136) + hybrid vertical blue / white : rgb(243,26,136) + hybrid vertical blue / black : rgb(243,26,136) + hybrid vertical blue / red : rgb(243,26,136) + hybrid vertical blue / green : rgb(243,26,136) + hybrid vertical black / white : rgb(243,26,136) + hybrid vertical black / blue : rgb(243,26,136) + hybrid vertical black / red : rgb(243,26,136) + hybrid vertical black / green : rgb(243,26,136) + hybrid vertical red / white : rgb(243,26,136) + hybrid vertical red / blue : rgb(243,26,136) + hybrid vertical red / black : rgb(243,26,136) + hybrid vertical red / green : rgb(243,26,136) + hybrid vertical green / white : rgb(243,26,136) + hybrid vertical green / blue : rgb(243,26,136) + hybrid vertical green / red : rgb(243,26,136) + hybrid vertical green / black : rgb(243,26,136) + hybrid vertical artifact / white : rgb(243,26,136) + hybrid vertical artifact / blue : rgb(243,26,136) + hybrid vertical artifact / black : rgb(243,26,136) + hybrid vertical artifact / red : rgb(243,26,136) + hybrid vertical artifact / green : rgb(243,26,136) + hybrid vertical white / artifact : rgb(243,26,136) + hybrid vertical blue / artifact : rgb(243,26,136) + hybrid vertical black / artifact : rgb(243,26,136) + hybrid vertical red / artifact : rgb(243,26,136) + hybrid vertical green / artifact : rgb(243,26,136) + # TODO : Add the rest + +# ... and for the card list +choice colors cardlist: + white : rgb(130,130,110) + blue : rgb(0,64,168) + black : rgb(0,0,0) + red : rgb(168,0,0) + green : rgb(0,168,0) + colorless : rgb(72,90,100) + artifact : rgb(72,90,100) + multicolor : rgb(120,120,0) + land : rgb(84,84,60) + # TODO : Add the rest diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index 54c4bf23..81cfb37a 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -333,6 +333,7 @@ init script: number_of_items(in: sort(order: "WUBRG")) # colored mana - number_of_items(in: sort(order:"/")) # guild mana, W/U -> 2 - 1 } + primary_card_color := filter_rule(match:"^[^ ]+") # TODO : somewhere else? #card to conversion: @@ -624,6 +625,12 @@ card field: card list visible: true card list column: 6 description: The rarity of the card, to edit the symbol switch to the 'set info' tab + choice colors: + basic land: rgb(109,62,39) + common: rgb(33,33,33) + uncommon: rgb(224,224,224) + rare: rgb(214,196,94) + special: rgb(58,7,80) ############################# Text box card field: @@ -947,6 +954,22 @@ card field: ############################################################## Statistics categories +statistics dimension: + name: card color + script: primary_card_color(card.card_color) + icon: stats/card_color.png + colors: + white : rgb(255,237,202) + blue : rgb(42,141,255) + black : rgb(33,33,33) + red : rgb(255,52,0) + green : rgb(138,230,0) + colorless : rgb(122,85,85) + artifact : rgb(188,192,195) + multicolor : rgb(255,188,14) + land : rgb(109,62,39) + hybrid : rgb(243,26,136) + statistics dimension: name: converted mana cost script: cmc(card.casting_cost) diff --git a/data/vs-extended-art.mse-style/style b/data/vs-extended-art.mse-style/style index af346327..1d7f3827 100644 --- a/data/vs-extended-art.mse-style/style +++ b/data/vs-extended-art.mse-style/style @@ -34,15 +34,6 @@ card style: equipment: card-equipment.jpg location: card-location.jpg plot twist: card-plot-twist.jpg - colors card list: true - choice colors: - character: rgb(120,18,0) - character dual: rgb(120,18,0) - concealed: rgb(26,26,26) - concealed dual: rgb(26,26,26) - equipment: rgb(80,80,80) - location: rgb(30,110,0) - plot twist: rgb(10,0,110) ############################# Name line name: diff --git a/data/vs-standard.mse-style/style b/data/vs-standard.mse-style/style index 5420413d..b5830ba7 100644 --- a/data/vs-standard.mse-style/style +++ b/data/vs-standard.mse-style/style @@ -32,15 +32,6 @@ card style: equipment: card-equipment.jpg location: card-location.jpg plot twist: card-plot-twist.jpg - colors card list: true - choice colors: - character: rgb(120,18,0) - character dual: rgb(120,18,0) - concealed: rgb(26,26,26) - concealed dual: rgb(26,26,26) - equipment: rgb(80,80,80) - location: rgb(30,110,0) - plot twist: rgb(10,0,110) ############################# Name line name: diff --git a/data/vs.mse-game/game b/data/vs.mse-game/game index d071e4c8..6569abf5 100644 --- a/data/vs.mse-game/game +++ b/data/vs.mse-game/game @@ -186,7 +186,22 @@ card field: choice: location choice: plot twist default: card_type() - show statistics: false + choice colors: + character: rgb(240,36,0) + character dual: rgb(240,36,0) + concealed: rgb(33,33,33) + concealed dual: rgb(33,33,33) + equipment: rgb(160,160,160) + location: rgb(60,220,0) + plot twist: rgb(20,0,220) + choice colors cardlist: + character: rgb(120,18,0) + character dual: rgb(120,18,0) + concealed: rgb(26,26,26) + concealed dual: rgb(26,26,26) + equipment: rgb(80,80,80) + location: rgb(30,110,0) + plot twist: rgb(10,0,110) ############################# Name line card field: diff --git a/data/yugioh-standard.mse-style/style b/data/yugioh-standard.mse-style/style index 08919a9c..b1692da8 100644 --- a/data/yugioh-standard.mse-style/style +++ b/data/yugioh-standard.mse-style/style @@ -34,18 +34,6 @@ card style: legendary dragons: card-dragons.jpg spell card: card-spell.jpg trap card: card-trap.jpg - colors card list: true - choice colors: - normal monster: rgb(120,18,0) - effect monster: rgb(120,18,0) - ritual monster: rgb(26,26,26) - fusion monster: rgb(26,26,26) - obelisk: rgb(26,26,26) - slifer: rgb(26,26,26) - ra: rgb(26,26,26) - legendary dragons: rgb(26,26,26) - spell card: rgb(80,80,80) - trap card: rgb(30,110,0) ############################# Name line name: diff --git a/data/yugioh.mse-game/game b/data/yugioh.mse-game/game index a70901c4..072a83cc 100644 --- a/data/yugioh.mse-game/game +++ b/data/yugioh.mse-game/game @@ -147,6 +147,17 @@ card field: choice: spell card choice: trap card default: card_type() + choice colors cardlist: + normal monster: rgb(120,18,0) + effect monster: rgb(120,18,0) + ritual monster: rgb(26,26,26) + fusion monster: rgb(26,26,26) + obelisk: rgb(26,26,26) + slifer: rgb(26,26,26) + ra: rgb(26,26,26) + legendary dragons: rgb(26,26,26) + spell card: rgb(80,80,80) + trap card: rgb(30,110,0) ############################# Name line card field: diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index 15b3bbcc..1104844f 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -41,6 +41,8 @@ IMPLEMENT_REFLECTION(ChoiceField) { REFLECT_IF_READING { choices->initIds(); } + REFLECT(choice_colors); + REFLECT(choice_colors_cardlist); } // ----------------------------------------------------------------------------- : ChoiceField::Choice @@ -162,7 +164,6 @@ ChoiceStyle::ChoiceStyle(const ChoiceFieldP& field) : Style(field) , popup_style(POPUP_DROPDOWN) , render_style(RENDER_TEXT) - , colors_card_list(false) , combine(COMBINE_NORMAL) , alignment(ALIGN_STRETCH) , angle(0) @@ -242,10 +243,8 @@ IMPLEMENT_REFLECTION(ChoiceStyle) { REFLECT(combine); REFLECT(alignment); REFLECT(angle); - REFLECT(colors_card_list); REFLECT(font); REFLECT(choice_images); - REFLECT(choice_colors); } // ----------------------------------------------------------------------------- : ChoiceValue diff --git a/src/data/field/choice.hpp b/src/data/field/choice.hpp index c8514332..8bd562ce 100644 --- a/src/data/field/choice.hpp +++ b/src/data/field/choice.hpp @@ -37,6 +37,8 @@ class ChoiceField : public Field { OptionalScript default_script; ///< Script that generates the default value String initial; ///< Initial choice of a new value, or "" String default_name; ///< Name of "default" value + map choice_colors; ///< Colors for the various choices (when color_cardlist) + map choice_colors_cardlist; ///< Colors for the various choices, for in the card list virtual void initDependencies(Context&, const Dependency&) const; @@ -131,8 +133,6 @@ class ChoiceStyle : public Style { ChoiceRenderStyle render_style; ///< Style of rendering Font font; ///< Font for drawing text (when RENDER_TEXT) map choice_images; ///< Images for the various choices (when RENDER_IMAGE) - map choice_colors; ///< Colors for the various choices (when color_cardlist) - bool colors_card_list; ///< Does this field determine colors of the rows in the card list? Scriptable mask_filename; ///< Filename of an additional mask over the images ImageCombine combine; ///< Combining mode for drawing the images Alignment alignment; ///< Alignment of images diff --git a/src/data/statistics.cpp b/src/data/statistics.cpp index c3840021..e17e12f2 100644 --- a/src/data/statistics.cpp +++ b/src/data/statistics.cpp @@ -8,21 +8,29 @@ #include #include +#include // ----------------------------------------------------------------------------- : Statistics dimension StatsDimension::StatsDimension() - : automatic(false) - , numeric(false) + : automatic (false) + , numeric (false) + , show_empty(false) {} StatsDimension::StatsDimension(const Field& field) - : automatic(true) + : automatic (true) , name (field.name) , description (field.description) , icon_filename(field.icon_filename) - , numeric(false) + , numeric (false) + , show_empty (false) { + // choice colors? + const ChoiceField* choice_field = dynamic_cast(&field); + if (choice_field) { + colors = choice_field->choice_colors; + } // initialize script, card.{field_name} Script& s = script.getScript(); s.addInstruction(I_GET_VAR, string_to_variable(_("card"))); @@ -37,6 +45,8 @@ IMPLEMENT_REFLECTION(StatsDimension) { REFLECT_N("icon", icon_filename); REFLECT(script); REFLECT(numeric); + REFLECT(show_empty); + REFLECT(colors); } } diff --git a/src/data/statistics.hpp b/src/data/statistics.hpp index f5a9219c..3f897840 100644 --- a/src/data/statistics.hpp +++ b/src/data/statistics.hpp @@ -26,12 +26,14 @@ class StatsDimension { StatsDimension(); StatsDimension(const Field&); - bool automatic; ///< Based on a card field? - String name; ///< Name of this dimension - String description; ///< Description, used in status bar - String icon_filename; ///< Icon for lists - OptionalScript script; ///< Script that determines the value(s) - bool numeric; ///< Are the values numeric? If so, they require special sorting + bool automatic; ///< Based on a card field? + String name; ///< Name of this dimension + String description; ///< Description, used in status bar + String icon_filename; ///< Icon for lists + OptionalScript script; ///< Script that determines the value(s) + bool numeric; ///< Are the values numeric? If so, they require special sorting + bool show_empty; ///< Should "" be shown? + map colors; ///< Colors for the categories DECLARE_REFLECTION(); }; diff --git a/src/gui/control/card_list.cpp b/src/gui/control/card_list.cpp index c07cb668..f37459dc 100644 --- a/src/gui/control/card_list.cpp +++ b/src/gui/control/card_list.cpp @@ -176,6 +176,7 @@ void CardListBase::rebuild() { column_fields.clear(); selected_item_pos = -1; onRebuild(); + if (!set) return; // determine column order map new_column_fields; FOR_EACH(f, set->game->card_fields) { @@ -197,7 +198,7 @@ void CardListBase::rebuild() { column_fields.push_back(f.second); } // find field that determines color - color_style = findColorStyle(); + color_field = findColorField(); // determine sort settings GameSettings& gs = settings.gameSettingsFor(*set->game); sort_ascending = gs.sort_cards_ascending; @@ -216,18 +217,16 @@ void CardListBase::rebuild() { ++i; } refreshList(); - // select a card if possible - selectItemPos(0, true); } -ChoiceStyleP CardListBase::findColorStyle() { - FOR_EACH(s, set->stylesheet->card_style) { - ChoiceStyleP cs = dynamic_pointer_cast(s); - if (cs && cs->colors_card_list) { - return cs; +ChoiceFieldP CardListBase::findColorField() { + FOR_EACH(s, set->game->card_fields) { + ChoiceFieldP cf = dynamic_pointer_cast(s); + if (cf && !cf->choice_colors_cardlist.empty()) { + return cf; } } - return ChoiceStyleP(); + return ChoiceFieldP(); } // ----------------------------------------------------------------------------- : CardListBase : Columns @@ -270,10 +269,10 @@ int CardListBase::OnGetItemImage(long pos) const { } wxListItemAttr* CardListBase::OnGetItemAttr(long pos) const { - if (!color_style) return nullptr; - ChoiceValueP val = static_pointer_cast( getCard(pos)->data[color_style->fieldP]); + if (!color_field) return nullptr; + ChoiceValueP val = static_pointer_cast( getCard(pos)->data[color_field]); assert(val); - item_attr.SetTextColour(color_style->choice_colors[val->value()]); // if it doesn't exist we get black + item_attr.SetTextColour(color_field->choice_colors_cardlist[val->value()]); // if it doesn't exist we get black return &item_attr; } diff --git a/src/gui/control/card_list.hpp b/src/gui/control/card_list.hpp index b00aacfd..b211c10a 100644 --- a/src/gui/control/card_list.hpp +++ b/src/gui/control/card_list.hpp @@ -13,7 +13,7 @@ #include #include -DECLARE_POINTER_TYPE(ChoiceStyle); +DECLARE_POINTER_TYPE(ChoiceField); DECLARE_POINTER_TYPE(Field); // ----------------------------------------------------------------------------- : Events @@ -102,14 +102,13 @@ class CardListBase : public ItemList, public SetView { // --------------------------------------------------- : Data private: // display stuff - ChoiceStyleP color_style; ///< Style (and field) to use for text color (optional) + ChoiceFieldP color_field; ///< Field to use for text color (optional) vector column_fields; ///< The field to use for each column (by column index) mutable wxListItemAttr item_attr; // for OnGetItemAttr /// Find the field that determines the color, if any. - /** Note: Uses only fields from the set's default style */ - ChoiceStyleP findColorStyle(); + ChoiceFieldP findColorField(); /// Store the column sizes in the settings void storeColumns(); diff --git a/src/gui/control/filtered_card_list.cpp b/src/gui/control/filtered_card_list.cpp index 90451116..f40fa63e 100644 --- a/src/gui/control/filtered_card_list.cpp +++ b/src/gui/control/filtered_card_list.cpp @@ -21,6 +21,12 @@ void FilteredCardList::setFilter(const CardListFilterP& filter) { rebuild(); } +void FilteredCardList::onChangeSet() { + // clear filter before changing set, the filter might not make sense for a different set + filter = CardListFilterP(); + CardListBase::onChangeSet(); +} + void FilteredCardList::getItems(vector& out) const { if (filter) { FOR_EACH(c, set->cards) { diff --git a/src/gui/control/filtered_card_list.hpp b/src/gui/control/filtered_card_list.hpp index 7da7ad64..d61d0396 100644 --- a/src/gui/control/filtered_card_list.hpp +++ b/src/gui/control/filtered_card_list.hpp @@ -37,7 +37,9 @@ class FilteredCardList : public CardListBase { protected: /// Get only the subset of the cards virtual void getItems(vector& out) const; - + + virtual void onChangeSet(); + private: CardListFilterP filter; ///< Filter with which this.cards is made }; diff --git a/src/gui/control/graph.cpp b/src/gui/control/graph.cpp index d47a555d..97e59f8a 100644 --- a/src/gui/control/graph.cpp +++ b/src/gui/control/graph.cpp @@ -14,9 +14,12 @@ DECLARE_TYPEOF_COLLECTION(GraphAxisP); DECLARE_TYPEOF_COLLECTION(GraphElementP); DECLARE_TYPEOF_COLLECTION(GraphGroup); +DECLARE_TYPEOF_COLLECTION(GraphP); DECLARE_TYPEOF_COLLECTION(int); DECLARE_TYPEOF(map); +template inline T sgn(T v) { return v < 0 ? -1 : 1; } + // ----------------------------------------------------------------------------- : Events DEFINE_EVENT_TYPE(EVENT_GRAPH_SELECT); @@ -60,6 +63,7 @@ GraphData::GraphData(const GraphDataPre& d) } else { a->groups.push_back(GraphGroup(is, it->second)); a->max = max(a->max, it->second); + a->total += it->second; left--; } if (i > 100) { @@ -75,14 +79,26 @@ GraphData::GraphData(const GraphDataPre& d) FOR_EACH(c, counts) { a->groups.push_back(GraphGroup(c.first, c.second)); a->max = max(a->max, c.second); + a->total += c.second; } } - // find some nice colors for the groups - if (a->auto_color) { + // colors + if (a->auto_color == AUTO_COLOR_NO && a->colors) { + // use colors from the table + FOR_EACH(g, a->groups) { + map::const_iterator it = a->colors->find(g.name); + if (it != a->colors->end()) { + g.color = it->second; + } + } + } else { + // find some nice colors for the groups double hue = 0.6; // start hue bool first = true; FOR_EACH(g, a->groups) { - double amount = double(g.size) / size; // amount this group takes + double amount = a->auto_color == AUTO_COLOR_EVEN + ? 1. / a->groups.size() + : double(g.size) / a->total; // amount this group takes if (!first) hue += amount/2; g.color = hsl2rgb(hue, 1.0, 0.5); hue += amount / 2; @@ -118,8 +134,8 @@ GraphData::GraphData(const GraphDataPre& d) // ----------------------------------------------------------------------------- : Graph1D -void Graph1D::draw(RotatedDC& dc, const vector& current) const { - draw(dc, axis < current.size() ? current.at(axis) : -1); +void Graph1D::draw(RotatedDC& dc, const vector& current, DrawLayer layer) const { + draw(dc, axis < current.size() ? current.at(axis) : -1, layer); } bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector& out) const { int i = findItem(pos, rect); @@ -134,10 +150,10 @@ bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector& // ----------------------------------------------------------------------------- : Bar Graph -void BarGraph::draw(RotatedDC& dc, int current) const { +void BarGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const { if (!data) return; // Rectangle for bars - RealRect rect = dc.getInternalRect().move(23, 8, -30, -28); + RealRect rect = dc.getInternalRect(); // Bar sizes GraphAxis& axis = axis_data(); int count = int(axis.groups.size()); @@ -145,56 +161,39 @@ void BarGraph::draw(RotatedDC& dc, int current) const { double width = width_space / 5 * 4; double space = width_space / 5; double step_height = rect.height / axis.max; // multiplier for bar height - // Highlight current column - Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - if (current >= 0) { - double x = rect.x + width_space * current; - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(lerp(bg, axis.groups[current].color, 0.25)); - dc.DrawRectangle(RealRect(x + space / 2 - 5, rect.bottom() + 1 + 15, - width + 10, -step_height * axis.groups[current].size - 1.999 - 20)); - dc.SetBrush(lerp(bg, axis.groups[current].color, 0.5)); - dc.DrawRectangle(RealRect(x + space / 2 - 2, rect.bottom() + 1 + 2, - width + 4, -step_height * axis.groups[current].size - 1.999 - 4)); - } - // How many labels and lines to draw? - dc.SetFont(*wxNORMAL_FONT); - UInt label_step = UInt(max(1.0, (dc.GetCharHeight() + 1) / step_height)); - // Draw backlines (horizontal) and value labels - dc.SetPen(lerp(bg, fg, 0.5)); - for (UInt i = 0 ; i <= axis.max ; ++i) { - if (i % label_step == 0) { - int y = int(rect.bottom() - i * step_height); - dc.DrawLine(RealPoint(rect.left() - 2, y), RealPoint(rect.right(), y)); - // draw label, aligned middle right - String label; label << i; - RealSize text_size = dc.GetTextExtent(label); - dc.DrawText(label, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(rect.x - 4, y, 0, 0))); + if (layer == LAYER_SELECTION) { + // Highlight current column + Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + if (current >= 0) { + double x = rect.x + width_space * current; + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(lerp(bg, axis.groups[current].color, 0.25)); + dc.DrawRectangle(RealRect(x + space / 2 - 5, rect.y + 1 - 15 * sgn(step_height), + width + 10, step_height * axis.groups[current].size + (1.999 + 20) * sgn(step_height))); + dc.SetBrush(lerp(bg, axis.groups[current].color, 0.5)); + dc.DrawRectangle(RealRect(x + space / 2 - 2, rect.y + 1 - 2 * sgn(step_height), + width + 4, step_height * axis.groups[current].size + (1.999 + 4) * sgn(step_height))); + } + } else if (layer == LAYER_VALUES) { + // Draw bars + Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + dc.SetPen(fg); + double x = rect.x; + FOR_EACH_CONST(g, axis.groups) { + // draw bar + dc.SetBrush(g.color); + dc.DrawRectangle(RealRect(x + space / 2, rect.y - 1 * sgn(step_height), width, (int)(rect.y + step_height * g.size) - rect.y + 1 * sgn(step_height))); + // draw label, aligned bottom center + RealSize text_size = dc.GetTextExtent(g.name); + dc.SetClippingRegion(RealRect(x + 2, rect.y + 3, width_space - 4, text_size.height)); + dc.DrawText(g.name, align_in_rect(ALIGN_TOP_CENTER, text_size, RealRect(x, rect.y + 3, width_space, 0))); + dc.DestroyClippingRegion(); + x += width_space; } } - // Draw axes - dc.SetPen(fg); - dc.DrawLine(rect.bottomLeft() - RealSize(2,0), rect.bottomRight()); - dc.DrawLine(rect.topLeft(), rect.bottomLeft()); - // Draw bars - double x = rect.x; - FOR_EACH_CONST(g, axis.groups) { - // draw bar - dc.SetBrush(g.color); - dc.DrawRectangle(RealRect(x + space / 2, rect.bottom() + 1, width, (int)(rect.bottom() - step_height * g.size) - rect.bottom() - 1)); - // draw label, aligned bottom center - RealSize text_size = dc.GetTextExtent(g.name); - dc.SetClippingRegion(RealRect(x + 2, rect.bottom() + 3, width_space - 4, text_size.height)); - dc.DrawText(g.name, align_in_rect(ALIGN_TOP_CENTER, text_size, RealRect(x, rect.bottom() + 3, width_space, 0))); - dc.DestroyClippingRegion(); - x += width_space; - } } -int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const { +int BarGraph::findItem(const RealPoint& pos, const RealRect& rect) const { if (!data) return -1; - // Rectangle for bars - RealRect rect = rect1.move(23, 8, -30, -28); // Bar sizes GraphAxis& axis = axis_data(); int count = int(axis.groups.size()); @@ -203,10 +202,10 @@ int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const { // Find column in which this point could be located int col = int((pos.x - rect.x) / width_space); double in_col = (pos.x - rect.x) - col * width_space; - if (in_col < space / 2 || // left - in_col > width_space - space / 2 || // right - pos.y > rect.bottom() || // below - pos.y < rect.top()) { // above + if (in_col < space / 2 || // left + in_col > width_space - space / 2 || // right + pos.y > max(rect.top(), rect.bottom()) || // below + pos.y < min(rect.top(), rect.bottom())) { // above return -1; } if (col < 0 || (size_t)col >= axis_data().groups.size()) { @@ -218,18 +217,165 @@ int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const { // ----------------------------------------------------------------------------- : Pie Graph -// ----------------------------------------------------------------------------- : Scatter Graph +void PieGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const { + if (!data) return; + // Rectangle for the pie + GraphAxis& axis = axis_data(); + RealRect rect = dc.getInternalRect(); + double size = min(rect.width, rect.height); + RealSize pie_size(size, size); + RealSize pie_size_large(size+20, size+20); + RealPoint pie_pos = rect.position() + rect.size() / 2; + //RealPoint pos = align_in_rect(ALIGN_MIDDLE_CENTER, RealSize(size,size), rect); + // draw items + if (layer == LAYER_VALUES) { + Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + dc.SetPen(fg); + // draw pies + double angle = 0; + int i = 0; + FOR_EACH_CONST(g, axis.groups) { + // draw pie + dc.SetBrush(g.color); + if (g.size > 0) { + double end_angle = angle + 2 * M_PI * (double)g.size / axis.total; + dc.DrawEllipticArc(pie_pos, i == current ? pie_size_large : pie_size, angle, end_angle); + angle = end_angle; + } + ++i; + } + // draw spokes + if (axis.groups.size() > 1) { + angle = 0; + FOR_EACH_CONST(g, axis.groups) { + if (g.size > 0) { + dc.DrawEllipticSpoke(pie_pos, pie_size, angle); + angle += 2 * M_PI * (double)g.size / axis.total; + } + } + } + } +} +int PieGraph::findItem(const RealPoint& pos, const RealRect& rect) const { + if (!data) return -1; + // Rectangle for the pie + GraphAxis& axis = axis_data(); + double size = min(rect.width, rect.height); + RealPoint pie_pos = rect.position() + rect.size() / 2; + // position in circle + Vector2D delta = pos - pie_pos; + if (delta.lengthSqr() > size*size) { + return -1; // outside circle + } + double pos_angle = atan2(-delta.y, delta.x); // in range [-pi..pi] + if (pos_angle < 0) pos_angle += 2 * M_PI; + // find angle + double angle = 0; + int i = 0; + FOR_EACH_CONST(g, axis.groups) { + angle += 2 * M_PI * (double)g.size / axis.total; + if (angle > pos_angle) return i; + ++i; + } + return -1; //should not happen +} + +// ----------------------------------------------------------------------------- : Scatter Plot // ----------------------------------------------------------------------------- : Graph Legend +// ----------------------------------------------------------------------------- : Graph value axis + +void GraphValueAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const { + if (layer != LAYER_AXES) return; + if (!data) return; + // How many labels and lines to draw? + RealRect rect = dc.getInternalRect(); + GraphAxis& axis = axis_data(); + double step_height = rect.height / axis.max; // height of a single value + dc.SetFont(*wxNORMAL_FONT); + UInt label_step = UInt(max(1.0, (dc.GetCharHeight() + 1) / step_height)); // values per labeled line + // Colors + Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + // Draw backlines (horizontal) and value labels + dc.SetPen(lerp(bg, fg, 0.5)); + for (UInt i = 0 ; i <= axis.max ; ++i) { + if (i % label_step == 0) { + int y = rect.top() + i * step_height; + dc.DrawLine(RealPoint(rect.left() - 2, y), RealPoint(rect.right(), y)); + // draw label, aligned middle right + String label; label << i; + RealSize text_size = dc.GetTextExtent(label); + dc.DrawText(label, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(rect.x - 4, y, 0, 0))); + } + } + // Draw axes + dc.SetPen(fg); + dc.DrawLine(rect.topLeft() - RealSize(2,0), rect.topRight()); + dc.DrawLine(rect.topLeft(), rect.bottomLeft()); +} + +// ----------------------------------------------------------------------------- : Graph with margins + +void GraphWithMargins::draw(RotatedDC& dc, const vector& current, DrawLayer layer) const { + RealRect inner = dc.getInternalRect().move(margin_left, margin_top, - margin_left - margin_right, - margin_top - margin_bottom); + if (upside_down) { inner.y += inner.height; inner.height = -inner.height; } + Rotation new_size(0, inner); + Rotater rot(dc, new_size); + graph->draw(dc, current, layer); +} +bool GraphWithMargins::findItem(const RealPoint& pos, const RealRect& rect, vector& out) const { + RealRect inner = rect.move(margin_left, margin_top, - margin_left - margin_right, - margin_top - margin_bottom); + if (upside_down) { inner.y += inner.height; inner.height = -inner.height; } + return graph->findItem(pos, inner, out); +} +void GraphWithMargins::setData(const GraphDataP& d) { + Graph::setData(d); + graph->setData(d); +} + +// ----------------------------------------------------------------------------- : Graph Container + +void GraphContainer::draw(RotatedDC& dc, const vector& current, DrawLayer layer) const { + FOR_EACH_CONST(g, items) { + g->draw(dc, current, layer); + } +} +bool GraphContainer::findItem(const RealPoint& pos, const RealRect& rect, vector& out) const { + FOR_EACH_CONST_REVERSE(g, items) { + if (g->findItem(pos, rect, out)) return true; + } + return false; +} +void GraphContainer::setData(const GraphDataP& d) { + Graph::setData(d); + FOR_EACH(g, items) { + g->setData(d); + } +} +void GraphContainer::add(const GraphP& graph) { + items.push_back(graph); +} + + // ----------------------------------------------------------------------------- : GraphControl GraphControl::GraphControl(Window* parent, int id) : wxControl(parent, id) { - graph = new_shared1(0); + //* + shared_ptr combined(new GraphContainer()); + combined->add(new_shared1(0)); + combined->add(new_shared1(0)); + graph = new_shared6(combined, 23,8,7,20, true); + /*/ + shared_ptr combined(new GraphContainer()); + combined->add(new_shared1(0)); + graph = new_shared6(combined, 20,20,20,20, false); + //*/ } void GraphControl::setData(const GraphDataPre& data) { @@ -250,7 +396,11 @@ void GraphControl::onPaint(wxPaintEvent&) { rdc.SetPen(*wxTRANSPARENT_PEN); rdc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); rdc.DrawRectangle(rdc.getInternalRect()); - if (graph) graph->draw(rdc, current_item); + if (graph) { + for (int layer = LAYER_BOTTOM ; layer < LAYER_COUNT ; ++layer) { + graph->draw(rdc, current_item, (DrawLayer)layer); + } + } } void GraphControl::onSize(wxSizeEvent&) { diff --git a/src/gui/control/graph.hpp b/src/gui/control/graph.hpp index c88816be..3f0f46bd 100644 --- a/src/gui/control/graph.hpp +++ b/src/gui/control/graph.hpp @@ -39,22 +39,33 @@ class GraphGroup { UInt size; ///< Number of elements in this group }; +/// Automatic coloring mode +enum AutoColor +{ AUTO_COLOR_NO +, AUTO_COLOR_EVEN +, AUTO_COLOR_WEIGHTED +}; + /// An axis in a graph, consists of a list of groups /** The sum of groups.sum = sum of all elements in the data */ class GraphAxis { public: - GraphAxis(const String& name, bool auto_color = true, bool numeric = false) + GraphAxis(const String& name, AutoColor auto_color = AUTO_COLOR_EVEN, bool numeric = false, const map* colors = nullptr) : name(name) , auto_color(auto_color) , numeric(numeric) , max(0) + , total(0) + , colors(colors) {} String name; ///< Name/label of this axis - bool auto_color; ///< Automatically assign colors to the groups on this axis + AutoColor auto_color; ///< Automatically assign colors to the groups on this axis vector groups; ///< Groups along this axis bool numeric; ///< Numeric axis? UInt max; ///< Maximum size of the groups + UInt total; ///< Sum of the size of all groups + const map* colors; ///< Colors for each choice (optional }; /// A single data point of a graph @@ -87,13 +98,21 @@ class GraphData { // ----------------------------------------------------------------------------- : Graph +enum DrawLayer +{ LAYER_BOTTOM = 0 +, LAYER_SELECTION = 0 +, LAYER_AXES +, LAYER_VALUES +, LAYER_COUNT +}; + /// A type of graph /** It is rendered into a sub-rectangle of the screen */ class Graph { public: virtual ~Graph() {} /// Draw this graph, filling the internalRect() of the dc. - virtual void draw(RotatedDC& dc, const vector& current) const = 0; + virtual void draw(RotatedDC& dc, const vector& current, DrawLayer layer) const = 0; /// Find the item at the given position, the rectangle gives the screen size virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector& out) const { return false; } /// Change the data @@ -110,13 +129,13 @@ class Graph { class Graph1D : public Graph { public: inline Graph1D(size_t axis) : axis(axis) {} - virtual void draw(RotatedDC& dc, const vector& current) const; + virtual void draw(RotatedDC& dc, const vector& current, DrawLayer layer) const; virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector& out) const; protected: size_t axis; /// Find an item, return the position along the axis, or -1 if not found - virtual int findItem(const RealPoint& pos, const RealRect& rect) const = 0; - virtual void draw(RotatedDC& dc, int current) const = 0; + virtual int findItem(const RealPoint& pos, const RealRect& rect) const { return -1; } + virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const = 0; inline GraphAxis& axis_data() const { return *data->axes.at(axis); } }; @@ -124,7 +143,7 @@ class Graph1D : public Graph { class BarGraph : public Graph1D { public: inline BarGraph(size_t axis) : Graph1D(axis) {} - virtual void draw(RotatedDC& dc, int current) const; + virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const; virtual int findItem(const RealPoint& pos, const RealRect& rect) const; }; @@ -136,7 +155,7 @@ class BarGraph : public Graph1D { class PieGraph : public Graph1D { public: inline PieGraph(size_t axis) : Graph1D(axis) {} - virtual void draw(RotatedDC& dc, int current) const; + virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const; virtual int findItem(const RealPoint& pos, const RealRect& rect) const; }; @@ -144,7 +163,7 @@ class PieGraph : public Graph1D { class GraphLegend : public Graph1D { public: inline GraphLegend(size_t axis) : Graph1D(axis) {} - virtual void draw(RotatedDC& dc, int current) const; + virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const; virtual int findItem(const RealPoint& pos, const RealRect& rect) const; }; @@ -155,9 +174,43 @@ class GraphLegend : public Graph1D { // virtual void draw(RotatedDC& dc) const; //}; -//class GraphValueAxis { -// virtual void draw(RotatedDC& dc) const; -//}; +/// Draws an a vertical axis for counts +class GraphValueAxis : public Graph1D { + public: + inline GraphValueAxis(size_t axis) : Graph1D(axis) {} + virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const; +}; + +/// A graph with margins +class GraphWithMargins : public Graph { + public: + inline GraphWithMargins(const GraphP& graph, + double margin_left, double margin_top, double margin_right, double margin_bottom, + bool upside_down = false) + : graph(graph) + , margin_left(margin_left), margin_top(margin_top), margin_right(margin_right), margin_bottom(margin_bottom) + , upside_down(upside_down) + {} + virtual void draw(RotatedDC& dc, const vector& current, DrawLayer layer) const; + virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector& out) const; + virtual void setData(const GraphDataP& d); + private: + double margin_left, margin_top, margin_right, margin_bottom; + bool upside_down; // put the coordinate system upside down, since graphs are usually bottom-to-top + const GraphP graph; +}; + +/// A display containing multiple graphs +class GraphContainer : public Graph { + public: + virtual void draw(RotatedDC& dc, const vector& current, DrawLayer layer) const; + virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector& out) const; + virtual void setData(const GraphDataP& d); + + void add(const GraphP& graph); + private: + vector items; +}; // ----------------------------------------------------------------------------- : Graph control @@ -167,6 +220,8 @@ class GraphControl : public wxControl { /// Create a graph control GraphControl(Window* parent, int id); + /// Set the type of graph used, from a number of predefined choices + void setLayout(); /// Update the data in the graph void setData(const GraphDataPre& data); /// Update the data in the graph diff --git a/src/gui/control/item_list.cpp b/src/gui/control/item_list.cpp index bccd890a..9f8df836 100644 --- a/src/gui/control/item_list.cpp +++ b/src/gui/control/item_list.cpp @@ -39,6 +39,10 @@ void ItemList::selectNext() { assert(selected_item_pos + 1 < (long)sorted_list.size()); selectItemPos(selected_item_pos + 1, true); } +void ItemList::selectFirst() { + assert(0 < (long)sorted_list.size()); + selectItemPos(0, true); +} // ----------------------------------------------------------------------------- : ItemList : Selection (private) diff --git a/src/gui/control/item_list.hpp b/src/gui/control/item_list.hpp index b465c0cb..00664a98 100644 --- a/src/gui/control/item_list.hpp +++ b/src/gui/control/item_list.hpp @@ -34,6 +34,8 @@ class ItemList : public wxListView { void selectPrevious(); /// Move the selection to the next item (if possible) void selectNext(); + /// Move the selection to the first item (if possible) + void selectFirst(); // --------------------------------------------------- : Virtual interface protected: diff --git a/src/gui/set/cards_panel.cpp b/src/gui/set/cards_panel.cpp index db54f6bf..8a5c2b6a 100644 --- a/src/gui/set/cards_panel.cpp +++ b/src/gui/set/cards_panel.cpp @@ -340,3 +340,8 @@ void CardsPanel::selectCard(const CardP& card) { notes->setValue(card ? &card->notes : nullptr); Layout(); } + +void CardsPanel::selectFirstCard() { + if (!set) return; // we want onChangeSet first + card_list->selectFirst(); +} diff --git a/src/gui/set/cards_panel.hpp b/src/gui/set/cards_panel.hpp index d6e83dc9..82f276f4 100644 --- a/src/gui/set/cards_panel.hpp +++ b/src/gui/set/cards_panel.hpp @@ -68,6 +68,7 @@ class CardsPanel : public SetWindowPanel { // --------------------------------------------------- : Selection virtual CardP selectedCard() const; virtual void selectCard(const CardP& card); + virtual void selectFirstCard(); private: // --------------------------------------------------- : Controls diff --git a/src/gui/set/panel.hpp b/src/gui/set/panel.hpp index d9b06cee..1aeb05c6 100644 --- a/src/gui/set/panel.hpp +++ b/src/gui/set/panel.hpp @@ -68,6 +68,7 @@ class SetWindowPanel : public wxPanel, public SetView { // --------------------------------------------------- : Selection virtual CardP selectedCard() const { return CardP(); } ///< Return the currently selected card, or CardP() virtual void selectCard(const CardP& card) {} ///< Switch the view to another card + virtual void selectFirstCard() {} ///< Switch the view to the first card }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gui/set/stats_panel.cpp b/src/gui/set/stats_panel.cpp index 83e971e5..aca6e750 100644 --- a/src/gui/set/stats_panel.cpp +++ b/src/gui/set/stats_panel.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,8 @@ class StatCategoryList : public GalleryList { void StatCategoryList::show(const GameP& game) { this->game = game; update(); + // select first item + selection = itemCount() > 0 ? 0 : NO_SELECTION; } size_t StatCategoryList::itemCount() const { @@ -74,13 +77,11 @@ void StatCategoryList::drawItem(DC& dc, int x, int y, size_t item, bool selected // draw name RealRect rect(RealPoint(x + 24, y), RealSize(item_size.x - 30, item_size.y)); String str = capitalize(cat.name); -// dc.SetFont(wxFont(9.5 * text_scaling, wxSWISS, wxNORMAL, wxNORMAL, false,_("Arial"))); dc.SetFont(*wxNORMAL_FONT); int w, h; dc.GetTextExtent(str, &w, &h); RealSize size = RealSize(w,h); RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, size, rect); -// draw_resampled_text(dc, RealRect(pos, size), 0, 0, 0, str); dc.DrawText(str, (int)pos.x, (int)pos.y); } @@ -110,30 +111,13 @@ StatsPanel::StatsPanel(Window* parent, int id) void StatsPanel::onChangeSet() { card_list->setSet(set); categories->show(set->game); - filterCards(); + onCategorySelect(); } void StatsPanel::onCommand(int id) { switch (id) { case ID_FIELD_LIST: { - // change graph data - if (categories->hasSelection()) { - StatsCategory& cat = categories->getSelection(); - GraphDataPre d; - FOR_EACH(dim, cat.dimensions) { - d.axes.push_back(new_shared3(dim->name, true, dim->numeric)); - } - FOR_EACH(card, set->cards) { - Context& ctx = set->getContext(card); - GraphElementP e(new GraphElement); - FOR_EACH(dim, cat.dimensions) { - e->values.push_back(*dim->script.invoke(ctx)); - } - d.elements.push_back(e); - } - graph->setData(d); - filterCards(); - } + onCategorySelect(); break; } } @@ -158,6 +142,41 @@ class StatsFilter : public CardListFilter { Set& set; }; +void StatsPanel::onCategorySelect() { + // change graph data + if (categories->hasSelection()) { + StatsCategory& cat = categories->getSelection(); + GraphDataPre d; + FOR_EACH(dim, cat.dimensions) { + d.axes.push_back(new_shared4( + dim->name, + dim->colors.empty() ? AUTO_COLOR_EVEN : AUTO_COLOR_NO, + dim->numeric, + &dim->colors + ) + ); + } + FOR_EACH(card, set->cards) { + Context& ctx = set->getContext(card); + GraphElementP e(new GraphElement); + bool show = true; + FOR_EACH(dim, cat.dimensions) { + String value = untag(dim->script.invoke(ctx)->toString()); + e->values.push_back(value); + if (value.empty() && !dim->show_empty) { + // don't show this element + show = false; + break; + } + } + if (show) { + d.elements.push_back(e); + } + } + graph->setData(d); + filterCards(); + } +} void StatsPanel::onGraphSelect(wxCommandEvent&) { filterCards(); } diff --git a/src/gui/set/stats_panel.hpp b/src/gui/set/stats_panel.hpp index 03f445e7..ece7696d 100644 --- a/src/gui/set/stats_panel.hpp +++ b/src/gui/set/stats_panel.hpp @@ -40,6 +40,7 @@ class StatsPanel : public SetWindowPanel { GraphControl* graph; FilteredCardList* card_list; + void onCategorySelect(); void onGraphSelect(wxCommandEvent&); void filterCards(); }; diff --git a/src/gui/set/style_panel.cpp b/src/gui/set/style_panel.cpp index 6a3e4b8d..a682895b 100644 --- a/src/gui/set/style_panel.cpp +++ b/src/gui/set/style_panel.cpp @@ -77,7 +77,7 @@ void StylePanel::selectCard(const CardP& card) { // ----------------------------------------------------------------------------- : Events void StylePanel::onStyleSelect(wxCommandEvent&) { - if (list->hasSelection()) { + if (list->hasSelection() && card) { StyleSheetP stylesheet = list->getSelection(); if (stylesheet == set->stylesheet) { // select no special style when selecting the same style as the set default diff --git a/src/gui/set/window.cpp b/src/gui/set/window.cpp index d256590d..2812a7b8 100644 --- a/src/gui/set/window.cpp +++ b/src/gui/set/window.cpp @@ -131,15 +131,12 @@ SetWindow::SetWindow(Window* parent, const SetP& set) SetSizer(s); // panels - // NOTE: place the CardsPanel last in the panels list, - // this way the card list is the last to be told of a set change - // this way everyone else already uses the new set when it sends a CardSelectEvent - addPanel(menuWindow, tabBar, new CardsPanel (this, wxID_ANY), 4, _("cards tab")); - addPanel(menuWindow, tabBar, new SetInfoPanel (this, wxID_ANY), 0, _("set info tab")); - addPanel(menuWindow, tabBar, new StylePanel (this, wxID_ANY), 1, _("style tab")); - addPanel(menuWindow, tabBar, new KeywordsPanel(this, wxID_ANY), 2, _("keywords tab")); - addPanel(menuWindow, tabBar, new StatsPanel (this, wxID_ANY), 3, _("stats tab")); -// addPanel(*s, *menuWindow, *tabBar, new DraftPanel (&this, wxID_ANY), 4, _("F10")) + addPanel(menuWindow, tabBar, new CardsPanel (this, wxID_ANY), 0, _("cards tab")); + addPanel(menuWindow, tabBar, new SetInfoPanel (this, wxID_ANY), 1, _("set info tab")); + addPanel(menuWindow, tabBar, new StylePanel (this, wxID_ANY), 2, _("style tab")); + addPanel(menuWindow, tabBar, new KeywordsPanel(this, wxID_ANY), 3, _("keywords tab")); + addPanel(menuWindow, tabBar, new StatsPanel (this, wxID_ANY), 4, _("stats tab")); +// addPanel(*s, *menuWindow, *tabBar, new DraftPanel (&this, wxID_ANY), 5, _("F10")) selectPanel(ID_WINDOW_CARDS); // select cards panel // loose ends @@ -241,6 +238,10 @@ void SetWindow::onChangeSet() { FOR_EACH(p, panels) { p->setSet(set); } + // only after setSet select a card + FOR_EACH(p, panels) { + p->selectFirstCard(); + } fixMinWindowSize(); } diff --git a/src/render/card/viewer.cpp b/src/render/card/viewer.cpp index 49003b33..2a525741 100644 --- a/src/render/card/viewer.cpp +++ b/src/render/card/viewer.cpp @@ -93,6 +93,13 @@ void DataViewer::setCard(const CardP& card, bool refresh) { onChangeSize(); } +void DataViewer::onChangeSet() { + viewers.clear(); + onInit(); + onChange(); + onChangeSize(); +} + // ----------------------------------------------------------------------------- : Viewers struct CompareViewer { diff --git a/src/render/card/viewer.hpp b/src/render/card/viewer.hpp index bc28caca..55362fab 100644 --- a/src/render/card/viewer.hpp +++ b/src/render/card/viewer.hpp @@ -67,6 +67,9 @@ class DataViewer : public SetView { /** \param refresh: Always refresh, even if this card is already shown */ void setCard(const CardP& card, bool refresh = false); + /// Clear data + virtual void onChangeSet(); + // --------------------------------------------------- : The viewers protected: /// Set the styles for the data to be shown, recreating the viewers diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index d46880f9..0994ef1c 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -330,7 +330,7 @@ SCRIPT_FUNCTION(replace) { class ScriptFilterRule : public ScriptValue { public: virtual ScriptType type() const { return SCRIPT_FUNCTION; } - virtual String typeName() const { return _("replace_rule"); } + virtual String typeName() const { return _("filter_rule"); } virtual ScriptValueP eval(Context& ctx) const { SCRIPT_PARAM(String, input); String ret; diff --git a/src/util/rotation.cpp b/src/util/rotation.cpp index 69167194..2a810705 100644 --- a/src/util/rotation.cpp +++ b/src/util/rotation.cpp @@ -172,6 +172,31 @@ void RotatedDC::DrawRoundedRectangle(const RealRect& r, double radius) { dc.DrawRoundedRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height, trS(radius)); } +/// Convert radians to degrees +double rad_to_deg(double rad) { return rad * (180.0 / M_PI); } +/// Convert degrees to radians +double deg_to_rad(double deg) { return deg * (M_PI / 180.0); } + +void RotatedDC::DrawEllipticArc(const RealPoint& center, const RealSize& size, double start, double end) { + wxPoint c_ext = tr(center - size/2); + wxSize s_ext = trNoNeg(size); + dc.DrawEllipticArc(c_ext.x, c_ext.y, s_ext.x, s_ext.y, rad_to_deg(start) + angle, rad_to_deg(end) + angle); +} +void RotatedDC::DrawEllipticSpoke(const RealPoint& center, const RealSize& size, double angle) { + wxPoint c_ext = tr(center - size/2); + wxSize s_ext = trNoNeg(size); + double rot_angle = angle + deg_to_rad(this->angle); + double sin_angle = sin(rot_angle), cos_angle = cos(rot_angle); + // position of center and of point on the boundary can vary because of rounding errors, + // this code matches DrawEllipticArc (at least on windows xp). + dc.DrawLine( + c_ext.x + int( 0.5 * (s_ext.x + cos_angle) ), // center + c_ext.y + int( 0.5 * (s_ext.y - sin_angle) ), + c_ext.x + int( 0.5 + 0.5 * (s_ext.x-1) * (1 + cos_angle) ), // boundary + c_ext.y + int( 0.5 + 0.5 * (s_ext.y-1) * (1 - sin_angle) ) + ); +} + // ----------------------------------------------------------------------------- : Forwarded properties void RotatedDC::SetPen(const wxPen& pen) { dc.SetPen(pen); } diff --git a/src/util/rotation.hpp b/src/util/rotation.hpp index 17c1e454..8eebabf3 100644 --- a/src/util/rotation.hpp +++ b/src/util/rotation.hpp @@ -146,6 +146,10 @@ class RotatedDC : public Rotation { void DrawLine (const RealPoint& p1, const RealPoint& p2); void DrawRectangle(const RealRect& r); void DrawRoundedRectangle(const RealRect& r, double radius); + /// Draw an arc of an ellipse, angles are in radians + void DrawEllipticArc(const RealPoint& center, const RealSize& size, double start, double end); + /// Draw spokes of an ellipse + void DrawEllipticSpoke(const RealPoint& center, const RealSize& size, double start); // Fill the dc with the color of the current brush void Fill(); diff --git a/src/util/window_id.hpp b/src/util/window_id.hpp index e58f2af4..2444d142 100644 --- a/src/util/window_id.hpp +++ b/src/util/window_id.hpp @@ -57,11 +57,11 @@ enum MenuID { // Window menu (MainWindow) , ID_WINDOW_NEW = 201 , ID_WINDOW_MIN = 202 -, ID_WINDOW_CARDS = ID_WINDOW_MIN + 4 // see SetWindow::SetWindow -, ID_WINDOW_SET = ID_WINDOW_MIN + 0 -, ID_WINDOW_STYLE = ID_WINDOW_MIN + 1 -, ID_WINDOW_KEYWORDS = ID_WINDOW_MIN + 2 -, ID_WINDOW_STATS = ID_WINDOW_MIN + 3 +, ID_WINDOW_CARDS = ID_WINDOW_MIN + 0 +, ID_WINDOW_SET = ID_WINDOW_MIN + 1 +, ID_WINDOW_STYLE = ID_WINDOW_MIN + 2 +, ID_WINDOW_KEYWORDS = ID_WINDOW_MIN + 3 +, ID_WINDOW_STATS = ID_WINDOW_MIN + 4 , ID_WINDOW_MAX = 220 // Help menu (MainWindow)