[MERGE] Remove the embedded pychart library, and use the online version
[odoo/odoo.git] /
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
4
5 # Jockey is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2, or (at your option) any
8 # later version.
9 #
10 # Jockey is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 # for more details.
14 #
15 import tick_mark
16 import font
17 import line_style
18 import color
19 import fill_style
20 import chart_object
21 import pychart_util
22 import types
23 import legend_doc
24 import theme
25
26 from pychart_types import *
27 from types import *
28
29 class Entry(chart_object.T):
30     keys = {"line_len" : (UnitType, None,
31                           "Length of the sample line for line plots. If omitted, it is set to be theme.default_font_size"),
32             "rect_size" : (UnitType, None,
33                            "Size of the sample 'blob' for bar range charts. If omitted, it is set to be 70% of theme.default_size"),
34             "tick_mark": (tick_mark.T, None, ""),
35             "line_style": (line_style.T, None, ""),
36             "fill_style": (fill_style.T, None, ""),
37             "label": (StringType, "???", ""),
38             }
39     __doc__ = legend_doc.doc_entry
40 ##AUTOMATICALLY GENERATED
41
42 ##END AUTOMATICALLY GENERATED
43     
44     def label_width(self):
45         return font.text_width(" " + self.label)
46     def get_line_len(self):
47         return self.line_len or theme.default_font_size
48     def get_rect_size(self):
49         return self.rect_size or theme.default_font_size * 7 / 10.0
50         
51     def sample_width(self):
52         w = 0
53         if self.fill_style != None:
54             w += self.get_line_len()
55         elif self.line_style != None:
56             w += self.get_line_len()
57         elif self.tick_mark != None:
58             w += self.tick_mark.size
59         return w
60     def height(self):
61         h = font.text_height(self.label)[0]
62         return h
63     
64     def draw(self, ar, can, x_tick, x_label, y):
65         """Draw a legend entry. X_TICK and X_LABEL are the X location \
66         (in points) of where the sample and label are drawn."""
67
68         rect_size = self.get_rect_size()
69         line_len = self.get_line_len()
70         
71         nr_lines = len(self.label.split("\n"))
72         text_height = font.text_height(self.label)[0]
73         line_height = text_height / float(nr_lines)
74         y_center = y + text_height - line_height/1.5
75             
76         if self.fill_style != None:
77             can.rectangle(self.line_style, self.fill_style,
78                              x_tick, y_center - rect_size/2.0,
79                              x_tick + rect_size,
80                              y_center + rect_size/2.0)
81         elif self.line_style != None:
82             can.line(self.line_style, x_tick, y_center,
83                      x_tick + line_len, y_center)
84             if self.tick_mark != None:
85                 self.tick_mark.draw(can, x_tick + line_len/2.0, y_center)
86         elif self.tick_mark != None:
87             self.tick_mark.draw(can, x_tick, y_center)
88             
89         can.show(x_label, y, self.label)
90
91 __doc__ = """Legend is a rectangular box drawn in a chart to describe
92 the meanings of plots. The contents of a legend box is extracted from
93 plots' "label", "line-style", and "tick-mark" attributes.
94
95 This module exports a single class, legend.T.  Legend.T is a part of
96 an area.T object, and is drawn automatically when area.draw() method
97 is called. """
98
99 class T(chart_object.T):
100     __doc__ = legend_doc.doc
101     keys = {
102         "inter_row_sep": (UnitType, 0,
103                           "Space between each row in the legend."),
104         "inter_col_sep": (UnitType, 0,
105                           "Space between each column in the legend."),
106         "frame_line_style": (line_style.T, line_style.default, ""),
107         "frame_fill_style": (fill_style.T, fill_style.white, ""),
108         "top_fudge": (UnitType, 0,
109                       "Amount of space above the first line."),
110         "bottom_fudge": (UnitType, 3,
111                          "Amount of space below the last line."),
112         "left_fudge": (UnitType, 5,
113                        "Amount of space left of the legend."),
114         "right_fudge": (UnitType, 5,
115                         "Amount of space right of the legend."),
116         "loc": (CoordType, None,
117                 """Bottom-left corner of the legend.
118                 The default location of a legend is the bottom-right end of the chart."""),
119         "shadow": (ShadowType, None, pychart_util.shadow_desc),
120         "nr_rows": (IntType, 9999, "Number of rows in the legend. If the number of plots in a chart is larger than nr_rows, multiple columns are created in the legend."),
121
122         }
123 ##AUTOMATICALLY GENERATED
124
125 ##END AUTOMATICALLY GENERATED
126     def draw(self, ar, entries, can):
127         if not self.loc:
128             x = ar.loc[0] + ar.size[0] * 1.1
129             y = ar.loc[1]
130         else:
131             x = self.loc[0]
132             y = self.loc[1]
133
134         nr_rows = min(self.nr_rows, len(entries))
135         nr_cols = (len(entries)-1) / nr_rows + 1
136         
137         ymin = y
138         max_label_width = [0] * nr_cols
139         max_sample_width = [0] * nr_cols
140         heights = [0] * nr_rows
141         
142         for i in range(len(entries)):
143             l = entries[i]
144             (col, row) = divmod(i, nr_rows)
145             max_label_width[col] = max(l.label_width(), max_label_width[col])
146             max_sample_width[col] = max(l.sample_width(), max_sample_width[col])
147             heights[row] = max(l.height(), heights[row])
148
149         for h in heights:
150             y += h
151         y += self.inter_row_sep * (nr_rows - 1)
152         ymax = y
153
154         tot_width = self.inter_col_sep * (nr_cols -1)
155         for w in max_label_width:
156             tot_width += w
157         for w in max_sample_width:
158             tot_width += w
159             
160         can.rectangle(self.frame_line_style, self.frame_fill_style,
161                       x - self.left_fudge,      
162                       ymin - self.bottom_fudge, 
163                       x + tot_width + self.right_fudge,
164                       ymax + self.top_fudge,
165                       self.shadow)
166
167         for col in range(nr_cols):
168             this_y = y
169             this_x = x
170             for row in range(nr_rows):
171                 idx = col * nr_rows + row
172                 if idx >= len(entries):
173                     continue
174                 this_y -= heights[row]
175                 l = entries[idx]
176                 if row != 0:
177                     this_y -= self.inter_row_sep
178                     
179                 l.draw(ar, can, this_x, this_x + max_sample_width[col], this_y)
180             x += max_label_width[col] + max_sample_width[col] + self.inter_col_sep
181
182