1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 # See the COPYING file for license information.
17 # Copyright (c) 2006 Guillaume Chazarain <guichaz@yahoo.fr>
22 assert gtk
.pygtk_version
>= (2, 8)
29 from pysize
.ui
.utils
import human_unit
, min_size_to_consider
, sanitize_string
34 class PysizeWidget_Draw(object):
35 def __init__(self
, options
, args
):
36 self
.connect('expose-event', type(self
)._expose
_event
)
37 self
.modify_font(pango
.FontDescription('Monospace 12'))
38 self
.max_text_height
= self
.measure_font_height()
39 self
.fast
= options
.fast
41 def measure_font_height(self
):
42 w
, h
= self
.create_pango_layout('a').get_pixel_size()
45 def _get_requested_height(self
):
46 return self
.max_text_height
* self
.tree
.root
.size
/ \
47 min_size_to_consider(self
.options
.min_size
)
49 def queue_node_redraw(self
, node
):
51 x0
, x1
, y0
, y1
= map(int, node
.rectangle
)
52 self
.queue_draw_area(x0
, y0
, x1
- x0
, y1
- y0
)
54 def _draw_text(self
, context
, text
, (x0
, x1
, y0
, y1
), accept_ellipse
=True):
55 pl
= self
.create_pango_layout(text
)
56 pl
.set_alignment(pango
.ALIGN_CENTER
)
59 pl
.set_width(int(w
*pango
.SCALE
))
61 ellipse_mode
= pango
.ELLIPSIZE_MIDDLE
63 ellipse_mode
= pango
.ELLIPSIZE_NONE
64 pl
.set_ellipsize(ellipse_mode
)
65 real_w
, real_h
= pl
.get_pixel_size()
66 line_count
= pl
.get_line_count()
67 line_height
= float(real_h
) / line_count
68 if line_height
> self
.max_text_height
:
69 self
.max_text_height
= line_height
70 if line_count
== text
.count('\n') + 1 and real_w
<= w
and real_h
<= h
:
71 context
.move_to(x0
, y0
+ (h
- real_h
) / 2.0)
72 context
.set_source_rgb(0, 0, 0)
73 context
.show_layout(pl
)
76 def _get_node_colors(self
, node
, colors
):
77 def transform(colors
, dr
, dg
, db
):
79 return max(0, min(1, c
))
80 return map(lambda (r
, g
, b
): (clamp(r
+ dr
),
85 if node
.is_real() and not node
.is_dir():
86 colors
= map(lambda (r
, g
, b
): (b
, g
, r
), colors
)
88 if not node
.is_real():
89 colors
= map(lambda c
: (min(c
), max(c
), max(c
)), colors
)
91 size
= node
.get_useful_size() / (self
.tree
.root
.children
[0].size
* 3.0)
92 colors
= transform(colors
, -size
, -size
, -size
)
94 if node
== self
.cursor_node
:
95 colors
= transform(colors
, 0.2, 0.2, 0.2)
96 if node
in self
.selected_nodes
+ [self
.button_press_node
]:
97 colors
= transform(colors
, -0.1, -0.1, -0.1)
100 def _draw_box(self
, context
, x0
, x1
, y0
, y1
, node
):
111 node
.rectangle
= x0
, x1
, y0
, y1
114 context
.rectangle(x0
, y0
, x1
- x0
, y1
- y0
)
115 context
.set_source_rgb(0, 0, 0)
116 context
.stroke_preserve()
118 colors
= self
._get
_node
_colors
(node
, ((0.2, 0.4, 1.0),))
119 context
.set_source_rgb(*colors
[0])
123 context
.arc(x0
+ RADIUS
, y0
+ RADIUS
, RADIUS
,
124 - math
.pi
, - math
.pi
/ 2.0)
125 context
.rel_line_to(x1
- x0
- 2*RADIUS
, 0)
126 context
.arc(x1
- RADIUS
, y0
+ RADIUS
, RADIUS
,
128 context
.rel_line_to(0, y1
- y0
- 2*RADIUS
)
129 context
.arc(x1
- RADIUS
, y1
- RADIUS
, RADIUS
,
131 context
.rel_line_to(- x1
+ x0
+ 2*RADIUS
, 0)
132 context
.arc(x0
+ RADIUS
, y1
- RADIUS
, RADIUS
,
133 math
.pi
/ 2.0, math
.pi
)
136 context
.set_source_rgb(0, 0, 0)
137 context
.stroke_preserve()
139 gradient
= cairo
.LinearGradient(0, y0
, 0, y1
)
140 colors
= self
._get
_node
_colors
(node
, ((0.5, 0.4, 1.0),
142 gradient
.add_color_stop_rgb(0.0, *colors
[0])
143 gradient
.add_color_stop_rgb(1.0, *colors
[1])
144 context
.set_source(gradient
)
147 name
= sanitize_string(node
.get_name())
148 size
= human_unit(node
.size
)
149 position
= x0
, x1
, y0
, y1
150 attempt
= lambda text
, pos
, *flags
: \
151 self
._draw
_text
(context
, text
, pos
, *flags
) or \
152 self
._draw
_text
(context
, text
,
153 (pos
[0] - 1, pos
[1] + 1, pos
[2] - 1, pos
[3] + 1),
155 attempt(name
+ '\n' + size
, position
) or \
156 attempt(name
+ ' ' + size
, position
, False) or \
157 attempt(name
, position
) or \
158 attempt(size
, position
)
160 def _draw_boxes(self
, context
, node
, depth
, offset
):
161 w
= self
.allocation
.width
162 h
= self
.allocation
.height
163 x0
= depth
* (w
- 1.0) / self
.tree
.height
164 x1
= (depth
+ 1.0) * (w
- 1.0) / self
.tree
.height
165 y0
= (h
- 1.0) * offset
/ self
.tree
.root
.size
166 y1
= (h
- 1.0) * (offset
+ node
.size
) / self
.tree
.root
.size
168 self
._draw
_box
(context
, x0
, x1
, y0
, y1
, node
)
170 for child
in node
.children
:
171 self
._draw
_boxes
(context
, child
, depth
, offset
)
174 def _draw(self
, context
):
175 max_text_height_before
= self
.max_text_height
176 context
.set_line_width(LINE_WIDTH
)
178 for child
in self
.tree
.root
.children
:
179 self
._draw
_boxes
(context
, child
, 0, offset
)
181 if self
.max_text_height
!= max_text_height_before
:
182 self
.schedule_new_tree()
184 def _expose_event(self
, event
):
185 context
= self
.window
.cairo_create()
187 # set a clip region for the expose event
188 context
.rectangle(event
.area
.x
, event
.area
.y
,
189 event
.area
.width
, event
.area
.height
)
195 def max_number_of_nodes(self
):
196 return max(2, self
.allocation
.height
/ self
.max_text_height
)
198 def _get_actual_min_size(self
):
199 min_size
= self
.options
.min_size
200 if min_size
== 'auto':
201 min_size
= self
.tree
.root
.size
* self
.min_size_requested()
204 def _zoom(self
, func
):
205 min_size
= self
._get
_actual
_min
_size
()
206 self
.options
.min_size
= func(min_size
)
207 self
.schedule_new_tree()
210 self
._zoom
(lambda min_size
: 'auto')
213 self
._zoom
(lambda min_size
: str(int(min_size
/ 1.5)))
216 self
._zoom
(lambda min_size
: str(int(min_size
* 1.5)))