7 assert gtk
.pygtk_version
>= (2, 8)
10 from core
.pysize_global_fs_cache
import drop_caches
11 from core
.pysize_fs_tree
import pysize_tree
12 from ui
.utils
import human_unit
, min_size_to_consider
13 from threaded_pysize_tree
import threaded_pysize_tree
20 def __init__(self
, x
, y
):
24 class PysizeWidget(gtk
.DrawingArea
):
27 (gobject
.SIGNAL_RUN_LAST
,
28 gobject
.TYPE_NONE
, (gobject
.TYPE_PYOBJECT
,)),
29 'building-tree-state-changed':
30 (gobject
.SIGNAL_RUN_LAST
,
31 gobject
.TYPE_NONE
, (gobject
.TYPE_BOOLEAN
,))
34 def __init__(self
, options
, args
):
35 super(PysizeWidget
, self
).__init
__()
36 self
.connect('expose-event', self
._expose
)
37 self
.connect('realize', self
._realize
)
38 self
.connect('motion-notify-event', self
._motion
)
39 self
.connect('configure-event', self
._configure
)
40 self
.connect('key-press-event', self
._key
_press
)
41 self
.connect('button-press-event', self
._button
_press
)
42 self
.connect('button-release-event', self
._button
_release
)
43 self
.set_flags(gtk
.CAN_FOCUS
)
44 self
.modify_font(pango
.FontDescription('Monospace 12'))
45 self
.options
= options
47 self
.selected_nodes
= []
48 self
._set
_cursor
_node
(None)
49 self
.button_press_node
= None
50 self
.tree
= pysize_tree(None)
51 self
.tree_builder
= threaded_pysize_tree()
52 self
.tree_builder
.completion
.add_observer(self
._new
_tree
_callback
)
54 # An entry needs at least the equivalent of 2*RADIUS in height
55 def _get_requested_height(self
):
56 return 2 * RADIUS
* self
.tree
.root
.size
/ \
57 min_size_to_consider(self
.options
.min_size
)
59 # Called when the threaded_pysize_tree built a new tree for us
60 def _new_tree_callback(self
, tree
):
64 emit_sig
= lambda: self
.emit('building-tree-state-changed', not tree
)
65 gobject
.idle_add(emit_sig
)
66 if self
.options
.min_size
!= 'auto' and tree
:
67 height
= self
._get
_requested
_height
()
68 self
.set_size_request(-1, int(height
))
70 def _set_cursor_node(self
, cursor_node
):
71 self
.cursor_node
= cursor_node
72 self
.emit('hover-changed', cursor_node
)
74 def _event_point(self
, event
):
76 x
, y
, state
= event
.window
.get_pointer()
77 point
= point_class(x
, y
)
79 point
= point_class(event
.x
, event
.y
)
82 def _get_node_here(self
, event
):
83 point
= point_class(event
.x
, event
.y
)
84 for node
in self
.tree
.root
:
85 if node
.contains_point(point
):
88 def _queue_node_redraw(self
, node
):
90 (x0
, x1
, y0
, y1
) = map(int, node
.rectangle
)
91 self
.queue_draw_area(x0
, y0
, x1
- x0
, y1
- y0
)
93 def _button_press(self
, widget
, event
):
94 node
= self
._get
_node
_here
(event
)
95 if node
!= self
.button_press_node
:
96 self
._queue
_node
_redraw
(self
.button_press_node
)
97 self
._queue
_node
_redraw
(node
)
98 self
.button_press_node
= node
100 def _button_release(self
, widget
, event
):
101 node
= self
._get
_node
_here
(event
)
102 self
._queue
_node
_redraw
(self
.button_press_node
)
103 self
._queue
_node
_redraw
(node
)
104 same_as_press
= node
== self
.button_press_node
105 self
.button_press_node
= None
107 for n
in self
.selected_nodes
:
108 self
._queue
_node
_redraw
(n
)
109 self
._queue
_node
_redraw
(node
)
110 if event
.state
& gtk
.gdk
.CONTROL_MASK
:
111 self
.selected_nodes
.append(node
)
113 self
.selected_nodes
= [node
]
115 def _key_press(self
, widget
, event
):
117 gtk
.keysyms
.Left
: self
.tree
.get_parent
,
118 gtk
.keysyms
.Up
: self
.tree
.get_previous_sibling
,
119 gtk
.keysyms
.Right
: self
.tree
.get_first_child
,
120 gtk
.keysyms
.Down
: self
.tree
.get_next_sibling
123 action
= directions
.get(event
.keyval
, None)
124 if action
and not self
.cursor_node
is None:
125 new_selection
= action(self
.cursor_node
)
126 if new_selection
not in (None, self
.tree
.root
):
127 self
._queue
_node
_redraw
(self
.cursor_node
)
128 self
._queue
_node
_redraw
(new_selection
)
129 self
._set
_cursor
_node
(new_selection
)
131 def _realize(self
, widget
):
132 events
= self
.window
.get_events()
133 events |
= gtk
.gdk
.POINTER_MOTION_MASK
134 events |
= gtk
.gdk
.POINTER_MOTION_HINT_MASK
135 events |
= gtk
.gdk
.KEY_PRESS_MASK
136 events |
= gtk
.gdk
.BUTTON_PRESS_MASK
137 events |
= gtk
.gdk
.BUTTON_RELEASE_MASK
138 self
.window
.set_events(events
)
139 self
.window
.get_pointer()
141 def refresh_tree(self
):
143 self
._schedule
_new
_tree
()
145 def _schedule_new_tree(self
):
146 self
.tree_builder
.schedule(self
.args
[0], self
.options
.max_depth
,
147 min_size_to_consider(self
.options
.min_size
,
148 self
.allocation
.height
/
151 def _configure(self
, widget
, event
):
152 self
._schedule
_new
_tree
()
154 def _motion(self
, widget
, event
):
155 point
= self
._event
_point
(event
)
156 prev_selection
= new_selection
= self
.cursor_node
157 if not (self
.cursor_node
and self
.cursor_node
.contains_point(point
)):
158 for node
in self
.tree
.root
:
159 was_selected
= node
== prev_selection
160 if node
.contains_point(point
) != (node
== prev_selection
):
161 if node
== prev_selection
:
165 self
._queue
_node
_redraw
(prev_selection
)
166 self
._queue
_node
_redraw
(new_selection
)
167 if prev_selection
== new_selection
:
168 self
.window
.get_pointer()
170 self
._set
_cursor
_node
(new_selection
)
172 def _expose(self
, widget
, event
):
173 context
= widget
.window
.cairo_create()
175 # set a clip region for the expose event
176 context
.rectangle(event
.area
.x
, event
.area
.y
,
177 event
.area
.width
, event
.area
.height
)
181 self
.window
.get_pointer()
184 def _draw_text(self
, context
, text
, x0
, x1
, y0
, y1
):
185 pl
= self
.create_pango_layout(text
)
188 pl
.set_width(int(w
*pango
.SCALE
))
189 pl
.set_ellipsize(pango
.ELLIPSIZE_MIDDLE
)
190 (real_w
, real_h
) = pl
.get_pixel_size()
191 if real_w
> w
or real_h
> h
:
193 if real_w
+w
/20.0 < w
:
195 context
.move_to(x0
, y0
+(h
-real_h
)/2.0)
196 context
.set_source_rgb(0, 0, 0)
197 context
.show_layout(pl
)
200 def _get_node_colors(self
, node
, colors
):
201 if node
== self
.cursor_node
:
202 colors
= map(lambda c
: c
+0.1, colors
)
203 if node
in self
.selected_nodes
+ [self
.button_press_node
]:
204 colors
= map(lambda c
: c
-0.3, colors
)
207 def _draw_box(self
, context
, x0
, x1
, y0
, y1
, node
):
218 node
.rectangle
= (x0
, x1
, y0
, y1
)
222 context
.arc(x0
+ RADIUS
, y0
+ RADIUS
, RADIUS
,
223 - math
.pi
, - math
.pi
/ 2.0)
224 context
.rel_line_to(x1
- x0
- 2*RADIUS
, 0)
225 context
.arc(x1
- RADIUS
, y0
+ RADIUS
, RADIUS
,
227 context
.rel_line_to(0, y1
- y0
- 2*RADIUS
)
228 context
.arc(x1
- RADIUS
, y1
- RADIUS
, RADIUS
,
230 context
.rel_line_to(- x1
+ x0
+ 2*RADIUS
, 0)
231 context
.arc(x0
+ RADIUS
, y1
- RADIUS
, RADIUS
,
232 math
.pi
/ 2.0, math
.pi
)
235 context
.set_source_rgb(0, 0, 0)
236 context
.stroke_preserve()
238 gradient
= cairo
.LinearGradient(0, y0
, 0, y1
)
239 colors
= self
._get
_node
_colors
(node
, [0.5, 0.5, 1.0, 0.0, 0.5, 1.0])
240 gradient
.add_color_stop_rgb(0.0, *colors
[:3])
241 gradient
.add_color_stop_rgb(1.0, *colors
[3:])
242 context
.set_source(gradient
)
245 context
.rectangle(x0
, y0
, x1
- x0
, y1
- y0
)
246 context
.set_source_rgb(0, 0, 0)
247 context
.stroke_preserve()
249 colors
= self
._get
_node
_colors
(node
, [0, 0.5, 1.0])
250 context
.set_source_rgb(*colors
)
253 name
= node
.get_name()
254 size
= human_unit(node
.size
)
255 if not self
._draw
_text
(context
, name
+ '\n' + size
, x0
, x1
, y0
, y1
):
256 first
, second
= name
, size
257 if not node
.is_real():
258 first
, second
= second
, first
259 if not self
._draw
_text
(context
, first
, x0
, x1
, y0
, y1
):
260 self
._draw
_text
(context
, second
, x0
, x1
, y0
, y1
)
262 def _draw_boxes(self
, context
, node
, depth
, offset
):
263 w
= self
.allocation
.width
264 if (self
.options
.min_size
== 'auto'):
265 h
= self
.allocation
.height
267 h
= self
._get
_requested
_height
()
268 x0
= depth
* (w
- 1.0) / self
.tree
.height
269 x1
= (depth
+ 1.0) * (w
- 1.0) / self
.tree
.height
270 y0
= (h
- 1.0) * offset
/ self
.tree
.root
.size
271 y1
= (h
- 1.0) * (offset
+ node
.size
) / self
.tree
.root
.size
273 self
._draw
_box
(context
, x0
, x1
, y0
, y1
, node
)
275 for child
in node
.get_children():
276 self
._draw
_boxes
(context
, child
, depth
, offset
)
279 def _draw(self
, context
):
280 context
.set_line_width(LINE_WIDTH
)
282 for child
in self
.tree
.root
.get_children():
283 self
._draw
_boxes
(context
, child
, 0, offset
)