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)
27 from pysize
.ui
.utils
import human_unit
, min_size_to_consider
, sanitize_string
32 class PysizeWidget_Draw(object):
33 def __init__(self
, options
, args
):
34 self
.connect('expose-event', type(self
)._expose
_event
)
35 self
.modify_font(pango
.FontDescription('Monospace 12'))
36 self
.max_text_height
= self
.measure_font_height()
37 self
.fast
= options
.fast
39 def measure_font_height(self
):
40 w
, h
= self
.create_pango_layout('a').get_pixel_size()
43 def _get_requested_height(self
):
44 return self
.max_text_height
* self
.tree
.root
.size
/ \
45 min_size_to_consider(self
.options
.min_size
)
47 def queue_node_redraw(self
, node
):
49 x0
, x1
, y0
, y1
= map(int, node
.rectangle
)
50 self
.queue_draw_area(x0
- LINE_WIDTH
, y0
- LINE_WIDTH
,
51 x1
- x0
+ 2*LINE_WIDTH
, y1
- y0
+ 2*LINE_WIDTH
)
53 def _draw_text(self
, context
, text
, (x0
, x1
, y0
, y1
), accept_ellipse
=True):
54 pl
= self
.create_pango_layout(text
)
55 pl
.set_alignment(pango
.ALIGN_CENTER
)
58 pl
.set_width(int(w
*pango
.SCALE
))
60 ellipse_mode
= pango
.ELLIPSIZE_MIDDLE
62 ellipse_mode
= pango
.ELLIPSIZE_NONE
63 pl
.set_ellipsize(ellipse_mode
)
64 real_w
, real_h
= pl
.get_pixel_size()
65 line_count
= pl
.get_line_count()
66 line_height
= float(real_h
) / line_count
67 if line_height
> self
.max_text_height
:
68 self
.max_text_height
= line_height
69 if line_count
== text
.count('\n') + 1 and real_w
<= w
and real_h
<= h
:
70 context
.move_to(x0
, y0
+ (h
- real_h
) / 2.0)
71 context
.show_layout(pl
)
74 def _get_node_colors(self
, node
, is_selected
, size_color
, colors
):
75 def transform(colors
, dr
, dg
, db
):
77 return max(0, min(1, c
))
78 return map(lambda (r
, g
, b
): (clamp(r
+ dr
),
83 if node
.is_real() and not node
.is_dir():
84 colors
= map(lambda (r
, g
, b
): (b
, g
, r
), colors
)
86 if not node
.is_real():
87 colors
= map(lambda c
: (min(c
), max(c
), max(c
)), colors
)
89 size_delta
= size_color(node
.size
)
90 colors
= transform(colors
, size_delta
, size_delta
, size_delta
)
92 if node
== self
.cursor_node
:
93 colors
= transform(colors
, 0.2, 0.2, 0.2)
95 colors
= transform(colors
, -0.4, -0.4, -0.4)
98 def _draw_box(self
, context
, x0
, x1
, y0
, y1
, node
, size_color
):
109 node
.rectangle
= x0
, x1
, y0
, y1
110 is_selected
= set(node
.get_fullpaths()) <= self
.selected_paths
111 colors
= self
._get
_node
_colors
(node
, is_selected
, size_color
,
112 ((0.5, 0.4, 1.0), (0.2, 0.4, 1.0)))
113 context
.set_source_rgb(0, 0, 0)
115 context
.rectangle(x0
, y0
, x1
- x0
, y1
- y0
)
116 context
.stroke_preserve()
118 context
.set_source_rgb(*colors
[0])
122 context
.arc(x0
+ RADIUS
, y0
+ RADIUS
, RADIUS
,
123 - math
.pi
, - math
.pi
/ 2.0)
124 context
.rel_line_to(x1
- x0
- 2*RADIUS
, 0)
125 context
.arc(x1
- RADIUS
, y0
+ RADIUS
, RADIUS
,
127 context
.rel_line_to(0, y1
- y0
- 2*RADIUS
)
128 context
.arc(x1
- RADIUS
, y1
- RADIUS
, RADIUS
,
130 context
.rel_line_to(- x1
+ x0
+ 2*RADIUS
, 0)
131 context
.arc(x0
+ RADIUS
, y1
- RADIUS
, RADIUS
,
132 math
.pi
/ 2.0, math
.pi
)
134 context
.stroke_preserve()
136 gradient
= cairo
.LinearGradient(0, y0
, 0, y1
)
138 gradient
.add_color_stop_rgb(0.0, *colors
[0])
139 gradient
.add_color_stop_rgb(1.0, *colors
[1])
140 context
.set_source(gradient
)
143 name
= sanitize_string(node
.get_name())
144 size
= human_unit(node
.size
)
145 position
= x0
, x1
, y0
, y1
147 context
.set_source_rgb(1, 1, 1)
149 context
.set_source_rgb(0, 0, 0)
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
, size_color
):
161 w
= self
.allocation
.width
162 h
= self
.allocation
.height
163 x0
= depth
* (w
- 1.0) / (self
.tree
.height
or 1)
164 x1
= (depth
+ 1.0) * (w
- 1.0) / (self
.tree
.height
or 1)
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
, size_color
)
170 for child
in node
.children
:
171 self
._draw
_boxes
(context
, child
, depth
, offset
, size_color
)
174 def _draw(self
, context
):
175 max_text_height_before
= self
.max_text_height
176 if self
.tree
.root
.children
:
177 max_size
= self
.tree
.root
.children
[0].size
178 min_size
= self
.tree
.root
.minimum_node_size()
179 diff
= max(1, max_size
- min_size
)
180 def size_color(size
):
181 return 0.3 - (size
- min_size
) / (2.0 * diff
)
183 def size_color(size
):
185 context
.set_line_width(LINE_WIDTH
)
187 for child
in self
.tree
.root
.children
or [self
.tree
.root
]:
189 self
._draw
_boxes
(context
, child
, 0, offset
, size_color
)
191 if self
.max_text_height
!= max_text_height_before
:
192 self
.schedule_new_tree()
194 def _expose_event(self
, event
):
195 context
= self
.window
.cairo_create()
197 # set a clip region for the expose event
198 context
.rectangle(event
.area
.x
, event
.area
.y
,
199 event
.area
.width
, event
.area
.height
)
205 def max_number_of_nodes(self
):
206 return max(2, self
.allocation
.height
/ self
.max_text_height
)
208 def _get_actual_min_size(self
):
209 min_size
= self
.options
.min_size
210 if min_size
== 'auto':
211 min_size
= self
.tree
.root
.size
* self
.min_size_requested()
214 def _zoom(self
, func
):
215 min_size
= self
._get
_actual
_min
_size
()
216 self
.options
.min_size
= func(min_size
)
217 self
.schedule_new_tree()
220 self
._zoom
(lambda min_size
: 'auto')
223 self
._zoom
(lambda min_size
: str(int(min_size
/ 1.5)))
226 self
._zoom
(lambda min_size
: str(int(min_size
* 1.5)))