mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
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
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -163,4 +163,151 @@ choice:
|
||||
choice: artifact / blue
|
||||
choice: artifact / black
|
||||
choice: artifact / red
|
||||
choice: artifact / green
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
+16
-1
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<String,Color> choice_colors; ///< Colors for the various choices (when color_cardlist)
|
||||
map<String,Color> 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<String,ScriptableImage> choice_images; ///< Images for the various choices (when RENDER_IMAGE)
|
||||
map<String,Color> 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<String> mask_filename; ///< Filename of an additional mask over the images
|
||||
ImageCombine combine; ///< Combining mode for drawing the images
|
||||
Alignment alignment; ///< Alignment of images
|
||||
|
||||
+14
-4
@@ -8,21 +8,29 @@
|
||||
|
||||
#include <data/statistics.hpp>
|
||||
#include <data/field.hpp>
|
||||
#include <data/field/choice.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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<const ChoiceField*>(&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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String,Color> colors; ///< Colors for the categories
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
@@ -176,6 +176,7 @@ void CardListBase::rebuild() {
|
||||
column_fields.clear();
|
||||
selected_item_pos = -1;
|
||||
onRebuild();
|
||||
if (!set) return;
|
||||
// determine column order
|
||||
map<int,FieldP> 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<ChoiceStyle>(s);
|
||||
if (cs && cs->colors_card_list) {
|
||||
return cs;
|
||||
ChoiceFieldP CardListBase::findColorField() {
|
||||
FOR_EACH(s, set->game->card_fields) {
|
||||
ChoiceFieldP cf = dynamic_pointer_cast<ChoiceField>(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<ChoiceValue>( getCard(pos)->data[color_style->fieldP]);
|
||||
if (!color_field) return nullptr;
|
||||
ChoiceValueP val = static_pointer_cast<ChoiceValue>( 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <gui/control/item_list.hpp>
|
||||
#include <data/set.hpp>
|
||||
|
||||
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<FieldP> 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();
|
||||
|
||||
@@ -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<VoidP>& out) const {
|
||||
if (filter) {
|
||||
FOR_EACH(c, set->cards) {
|
||||
|
||||
@@ -37,7 +37,9 @@ class FilteredCardList : public CardListBase {
|
||||
protected:
|
||||
/// Get only the subset of the cards
|
||||
virtual void getItems(vector<VoidP>& out) const;
|
||||
|
||||
|
||||
virtual void onChangeSet();
|
||||
|
||||
private:
|
||||
CardListFilterP filter; ///< Filter with which this.cards is made
|
||||
};
|
||||
|
||||
+210
-60
@@ -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<String COMMA UInt>);
|
||||
|
||||
template <typename T> 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<String,Color>::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<int>& current) const {
|
||||
draw(dc, axis < current.size() ? current.at(axis) : -1);
|
||||
void Graph1D::draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const {
|
||||
draw(dc, axis < current.size() ? current.at(axis) : -1, layer);
|
||||
}
|
||||
bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector<int>& out) const {
|
||||
int i = findItem(pos, rect);
|
||||
@@ -134,10 +150,10 @@ bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector<int>&
|
||||
|
||||
// ----------------------------------------------------------------------------- : 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<int>& 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<int>& 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<int>& current, DrawLayer layer) const {
|
||||
FOR_EACH_CONST(g, items) {
|
||||
g->draw(dc, current, layer);
|
||||
}
|
||||
}
|
||||
bool GraphContainer::findItem(const RealPoint& pos, const RealRect& rect, vector<int>& 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<BarGraph>(0);
|
||||
//*
|
||||
shared_ptr<GraphContainer> combined(new GraphContainer());
|
||||
combined->add(new_shared1<GraphValueAxis>(0));
|
||||
combined->add(new_shared1<BarGraph>(0));
|
||||
graph = new_shared6<GraphWithMargins>(combined, 23,8,7,20, true);
|
||||
/*/
|
||||
shared_ptr<GraphContainer> combined(new GraphContainer());
|
||||
combined->add(new_shared1<PieGraph>(0));
|
||||
graph = new_shared6<GraphWithMargins>(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&) {
|
||||
|
||||
+67
-12
@@ -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<String,Color>* 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<GraphGroup> 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<String,Color>* 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<int>& current) const = 0;
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& 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<int>& 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<int>& current) const;
|
||||
virtual void draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector<int>& 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<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector<int>& 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<int>& current, DrawLayer layer) const;
|
||||
virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector<int>& out) const;
|
||||
virtual void setData(const GraphDataP& d);
|
||||
|
||||
void add(const GraphP& graph);
|
||||
private:
|
||||
vector<GraphP> 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ class CardsPanel : public SetWindowPanel {
|
||||
// --------------------------------------------------- : Selection
|
||||
virtual CardP selectedCard() const;
|
||||
virtual void selectCard(const CardP& card);
|
||||
virtual void selectFirstCard();
|
||||
|
||||
private:
|
||||
// --------------------------------------------------- : Controls
|
||||
|
||||
@@ -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
|
||||
|
||||
+40
-21
@@ -14,6 +14,7 @@
|
||||
#include <data/statistics.hpp>
|
||||
#include <util/window_id.hpp>
|
||||
#include <util/alignment.hpp>
|
||||
#include <util/tagged_string.hpp>
|
||||
#include <gfx/gfx.hpp>
|
||||
#include <wx/splitter.h>
|
||||
|
||||
@@ -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<GraphAxis>(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<GraphAxis>(
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class StatsPanel : public SetWindowPanel {
|
||||
GraphControl* graph;
|
||||
FilteredCardList* card_list;
|
||||
|
||||
void onCategorySelect();
|
||||
void onGraphSelect(wxCommandEvent&);
|
||||
void filterCards();
|
||||
};
|
||||
|
||||
@@ -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<StyleSheet>();
|
||||
if (stylesheet == set->stylesheet) {
|
||||
// select no special style when selecting the same style as the set default
|
||||
|
||||
+10
-9
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,13 @@ void DataViewer::setCard(const CardP& card, bool refresh) {
|
||||
onChangeSize();
|
||||
}
|
||||
|
||||
void DataViewer::onChangeSet() {
|
||||
viewers.clear();
|
||||
onInit();
|
||||
onChange();
|
||||
onChangeSize();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Viewers
|
||||
|
||||
struct CompareViewer {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user