"Fossies" - the Fresh Open Source Software Archive 
Member "pysize-0.2/pysize/ui/gtk/pysize_widget_draw.py" (11 Mar 2007, 9261 Bytes) of package /linux/privat/old/pysize-0.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
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.
5 #
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.
10 #
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.
14 #
15 # See the COPYING file for license information.
16 #
17 # Copyright (c) 2006, 2007 Guillaume Chazarain <guichaz@yahoo.fr>
18
19 import pygtk
20 pygtk.require('2.0')
21 import gtk
22 assert gtk.pygtk_version >= (2, 8)
23 import pango
24 import math
25 import cairo
26
27 from pysize.ui.utils import human_unit, min_size_to_consider, sanitize_string
28
29 RADIUS = 10
30 LINE_WIDTH = 4
31
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
38 def measure_font_height(self):
39 w, h = self.create_pango_layout('a').get_pixel_size()
40 return h
41
42 def _get_requested_height(self):
43 return self.max_text_height * self.tree.root.size / \
44 min_size_to_consider(self.options.min_size)
45
46 def queue_node_redraw(self, node):
47 if node and node.rectangle:
48 x0, x1, y0, y1 = map(int, node.rectangle)
49 self.queue_draw_area(x0 - LINE_WIDTH, y0 - LINE_WIDTH,
50 x1 - x0 + 2*LINE_WIDTH, y1 - y0 + 2*LINE_WIDTH)
51
52 def _make_draw_labels_lambda(self, context, text, (x0, x1, y0, y1),
53 accept_ellipse=True):
54 pl = self.create_pango_layout(text)
55 pl.set_alignment(pango.ALIGN_CENTER)
56 w = x1 - x0
57 h = y1 - y0
58 pl.set_width(int(w*pango.SCALE))
59 if accept_ellipse:
60 ellipse_mode = pango.ELLIPSIZE_MIDDLE
61 else:
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 y0 += (h - real_h) / 2.0
71 def draw(context):
72 context.move_to(x0, y0)
73 context.show_layout(pl)
74 return draw
75
76 def _get_node_colors(self, node, is_selected, size_color, colors):
77 def transform(colors, dr, dg, db):
78 def clamp(c):
79 return max(0, min(1, c))
80 return map(lambda (r, g, b): (clamp(r + dr),
81 clamp(g + dg),
82 clamp(b + db)),
83 colors)
84
85 if node.is_real() and not node.is_dir():
86 colors = map(lambda (r, g, b): (b, g, r), colors)
87
88 if node.is_real():
89 size_delta = size_color(node.size)
90 colors = transform(colors, size_delta, size_delta, size_delta)
91 else:
92 colors = map(lambda c: (min(c), max(c), max(c)), colors)
93
94 if node == self.cursor_node:
95 colors = transform(colors, 0.2, 0.2, 0.2)
96 if is_selected:
97 colors = transform(colors, -0.4, -0.4, -0.4)
98 return colors
99
100 def _draw_box(self, context, x0, x1, y0, y1, node, size_color):
101 is_selected = set(node.get_fullpaths()) <= self.selected_paths
102 colors = self._get_node_colors(node, is_selected, size_color,
103 ((0.5, 0.4, 1.0), (0.2, 0.4, 1.0)))
104 context.set_source_rgb(0, 0, 0)
105 first_time = not node.rectangle
106 context.new_path()
107 if first_time:
108 if x0 == 0.0:
109 x0 += LINE_WIDTH/2.0
110 else:
111 x0 += LINE_WIDTH/4.0
112 x1 -= LINE_WIDTH/4.0
113 if y0 == 0.0:
114 y0 += LINE_WIDTH/2.0
115 else:
116 y0 += LINE_WIDTH/4.0
117 y1 -= LINE_WIDTH/4.0
118 node.rectangle = x0, x1, y0, y1
119 context.arc(x0 + RADIUS, y0 + RADIUS, RADIUS,
120 - math.pi, - math.pi / 2.0)
121 context.rel_line_to(x1 - x0 - 2*RADIUS, 0)
122 context.arc(x1 - RADIUS, y0 + RADIUS, RADIUS,
123 - math.pi / 2.0, 0)
124 context.rel_line_to(0, y1 - y0 - 2*RADIUS)
125 context.arc(x1 - RADIUS, y1 - RADIUS, RADIUS,
126 0, math.pi / 2.0)
127 context.rel_line_to(- x1 + x0 + 2*RADIUS, 0)
128 context.arc(x0 + RADIUS, y1 - RADIUS, RADIUS,
129 math.pi / 2.0, math.pi)
130 context.close_path()
131 node.cairo_box_path = context.copy_path()
132 else:
133 context.append_path(node.cairo_box_path)
134 context.stroke_preserve()
135
136 gradient = cairo.LinearGradient(0, y0, 0, y1)
137
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)
141 context.fill()
142
143 if is_selected:
144 context.set_source_rgb(1, 1, 1)
145 else:
146 context.set_source_rgb(0, 0, 0)
147 if first_time:
148 name = sanitize_string(node.get_name())
149 size = human_unit(node.size)
150 position = x0, x1, y0, y1
151 attempt = lambda text, pos, *flags: \
152 self._make_draw_labels_lambda(context, text, pos, *flags) or \
153 self._make_draw_labels_lambda(context, text,
154 (pos[0] - 1, pos[1] + 1, pos[2] - 1, pos[3] + 1),
155 *flags)
156 node.draw_labels_lambda = attempt(name + '\n' + size, position) or \
157 attempt(name + ' ' + size, position, False) or \
158 attempt(name, position) or \
159 attempt(size, position) or \
160 (lambda context: None)
161 node.draw_labels_lambda(context)
162
163 @staticmethod
164 def _intersect(clip, x0, x1, y0, y1):
165 cx0, cx1, cy0, cy1 = clip.x, clip.x + clip.width, \
166 clip.y, clip.y + clip.height
167 return x0 <= cx1 and x1 >= cx0 and y0 <= cy1 and y1 >= cy0
168
169 def _draw_boxes(self, context, clip, node, depth, offset, size_color):
170 w = self.allocation.width
171 h = self.allocation.height
172 x0 = depth * (w - 1.0) / (self.tree.height or 1)
173 x1 = (depth + 1.0) * (w - 1.0) / (self.tree.height or 1)
174 y0 = (h - 1.0) * offset / self.tree.root.size
175 y1 = (h - 1.0) * (offset + node.size) / self.tree.root.size
176
177 if self._intersect(clip, x0, x1, y0, y1):
178 self._draw_box(context, x0, x1, y0, y1, node, size_color)
179 depth += 1
180 for child in node.children:
181 self._draw_boxes(context, clip, child, depth, offset, size_color)
182 offset += child.size
183
184 def _draw(self, context, clip):
185 max_text_height_before = self.max_text_height
186 if self.tree.root.children:
187 max_size = self.tree.root.children[0].size
188 min_size = self.tree.root.minimum_node_size()
189 diff = max(1, max_size - min_size)
190 def size_color(size):
191 return 0.3 - (size - min_size) / (2.0 * diff)
192 else:
193 def size_color(size):
194 return 0
195 context.set_line_width(LINE_WIDTH)
196 offset = 0
197 for child in self.tree.root.children or [self.tree.root]:
198 if child.size:
199 self._draw_boxes(context, clip, child, 0, offset, size_color)
200 offset += child.size
201 if self.max_text_height != max_text_height_before:
202 self.schedule_new_tree()
203
204 def _expose_event(self, event):
205 context = self.window.cairo_create()
206
207 # set a clip region for the expose event
208 context.rectangle(event.area.x, event.area.y,
209 event.area.width, event.area.height)
210 context.clip()
211
212 self._draw(context, event.area)
213 return False
214
215 def max_number_of_nodes(self):
216 return max(2, self.allocation.height / self.max_text_height)
217
218 def _get_actual_min_size(self):
219 min_size = self.options.min_size
220 if min_size == 'auto':
221 min_size = self.tree.root.size * self.min_size_requested()
222 return int(min_size)
223
224 def _zoom(self, func):
225 min_size = self._get_actual_min_size()
226 self.options.min_size = func(min_size)
227 self.schedule_new_tree()
228
229 def zoom_fit(self):
230 self._zoom(lambda min_size: 'auto')
231
232 def zoom_in(self):
233 self._zoom(lambda min_size: str(int(min_size / 1.5)))
234
235 def zoom_out(self):
236 self._zoom(lambda min_size: str(int(min_size * 1.5)))