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
- LINE_WIDTH
, y0
- LINE_WIDTH
,
53 x1
- x0
+ 2*LINE_WIDTH
, y1
- y0
+ 2*LINE_WIDTH
)
55 def _draw_text(self
, context
, text
, (x0
, x1
, y0
, y1
), accept_ellipse
=True):
56 pl
= self
.create_pango_layout(text
)
57 pl
.set_alignment(pango
.ALIGN_CENTER
)
60 pl
.set_width(int(w
*pango
.SCALE
))
62 ellipse_mode
= pango
.ELLIPSIZE_MIDDLE
64 ellipse_mode
= pango
.ELLIPSIZE_NONE
65 pl
.set_ellipsize(ellipse_mode
)
66 real_w
, real_h
= pl
.get_pixel_size()
67 line_count
= pl
.get_line_count()
68 line_height
= float(real_h
) / line_count
69 if line_height
> self
.max_text_height
:
70 self
.max_text_height
= line_height
71 if line_count
== text
.count('\n') + 1 and real_w
<= w
and real_h
<= h
:
72 context
.move_to(x0
, y0
+ (h
- real_h
) / 2.0)
73 context
.show_layout(pl
)
76 def _get_node_colors(self
, node
, is_selected
, size_color
, 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_delta
= size_color(node
.size
)
92 colors
= transform(colors
, size_delta
, size_delta
, size_delta
)
94 if node
== self
.cursor_node
:
95 colors
= transform(colors
, 0.2, 0.2, 0.2)
97 colors
= transform(colors
, -0.4, -0.4, -0.4)
100 def _draw_box(self
, context
, x0
, x1
, y0
, y1
, node
, size_color
):
111 node
.rectangle
= x0
, x1
, y0
, y1
112 is_selected
= set(node
.get_fullpaths()) <= self
.selected_paths
113 colors
= self
._get
_node
_colors
(node
, is_selected
, size_color
,
114 ((0.5, 0.4, 1.0), (0.2, 0.4, 1.0)))
115 context
.set_source_rgb(0, 0, 0)
117 context
.rectangle(x0
, y0
, x1
- x0
, y1
- y0
)
118 context
.stroke_preserve()
120 context
.set_source_rgb(*colors
[0])
124 context
.arc(x0
+ RADIUS
, y0
+ RADIUS
, RADIUS
,
125 - math
.pi
, - math
.pi
/ 2.0)
126 context
.rel_line_to(x1
- x0
- 2*RADIUS
, 0)
127 context
.arc(x1
- RADIUS
, y0
+ RADIUS
, RADIUS
,
129 context
.rel_line_to(0, y1
- y0
- 2*RADIUS
)
130 context
.arc(x1
- RADIUS
, y1
- RADIUS
, RADIUS
,
132 context
.rel_line_to(- x1
+ x0
+ 2*RADIUS
, 0)
133 context
.arc(x0
+ RADIUS
, y1
- RADIUS
, RADIUS
,
134 math
.pi
/ 2.0, math
.pi
)
136 context
.stroke_preserve()
138 gradient
= cairo
.LinearGradient(0, y0
, 0, y1
)
140 gradient
.add_color_stop_rgb(0.0, *colors
[0])
141 gradient
.add_color_stop_rgb(1.0, *colors
[1])
142 context
.set_source(gradient
)
145 name
= sanitize_string(node
.get_name())
146 size
= human_unit(node
.size
)
147 position
= x0
, x1
, y0
, y1
149 context
.set_source_rgb(1, 1, 1)
151 context
.set_source_rgb(0, 0, 0)
152 attempt
= lambda text
, pos
, *flags
: \
153 self
._draw
_text
(context
, text
, pos
, *flags
) or \
154 self
._draw
_text
(context
, text
,
155 (pos
[0] - 1, pos
[1] + 1, pos
[2] - 1, pos
[3] + 1),
157 attempt(name
+ '\n' + size
, position
) or \
158 attempt(name
+ ' ' + size
, position
, False) or \
159 attempt(name
, position
) or \
160 attempt(size
, position
)
162 def _draw_boxes(self
, context
, node
, depth
, offset
, size_color
):
163 w
= self
.allocation
.width
164 h
= self
.allocation
.height
165 x0
= depth
* (w
- 1.0) / (self
.tree
.height
or 1)
166 x1
= (depth
+ 1.0) * (w
- 1.0) / (self
.tree
.height
or 1)
167 y0
= (h
- 1.0) * offset
/ self
.tree
.root
.size
168 y1
= (h
- 1.0) * (offset
+ node
.size
) / self
.tree
.root
.size
170 self
._draw
_box
(context
, x0
, x1
, y0
, y1
, node
, size_color
)
172 for child
in node
.children
:
173 self
._draw
_boxes
(context
, child
, depth
, offset
, size_color
)
176 def _draw(self
, context
):
177 max_text_height_before
= self
.max_text_height
178 if self
.tree
.root
.children
:
179 max_size
= self
.tree
.root
.children
[0].size
180 min_size
= self
.tree
.root
.minimum_node_size()
181 diff
= max_size
- min_size
182 def size_color(size
):
183 return 0.3 - (size
- min_size
) / (2.0 * diff
)
185 def size_color(size
):
187 context
.set_line_width(LINE_WIDTH
)
189 for child
in self
.tree
.root
.children
or [self
.tree
.root
]:
191 self
._draw
_boxes
(context
, child
, 0, offset
, size_color
)
193 if self
.max_text_height
!= max_text_height_before
:
194 self
.schedule_new_tree()
196 def _expose_event(self
, event
):
197 context
= self
.window
.cairo_create()
199 # set a clip region for the expose event
200 context
.rectangle(event
.area
.x
, event
.area
.y
,
201 event
.area
.width
, event
.area
.height
)
207 def max_number_of_nodes(self
):
208 return max(2, self
.allocation
.height
/ self
.max_text_height
)
210 def _get_actual_min_size(self
):
211 min_size
= self
.options
.min_size
212 if min_size
== 'auto':
213 min_size
= self
.tree
.root
.size
* self
.min_size_requested()
216 def _zoom(self
, func
):
217 min_size
= self
._get
_actual
_min
_size
()
218 self
.options
.min_size
= func(min_size
)
219 self
.schedule_new_tree()
222 self
._zoom
(lambda min_size
: 'auto')
225 self
._zoom
(lambda min_size
: str(int(min_size
/ 1.5)))
228 self
._zoom
(lambda min_size
: str(int(min_size
* 1.5)))