Increase zoom ratio
[pysize.git] / ui / gtk / pysize_widget_draw.py
blobb21c9bc9843d4eaba6fecaa74305acea7977a221
1 import pygtk
2 pygtk.require('2.0')
3 import gtk
4 assert gtk.pygtk_version >= (2, 8)
5 import pango
6 import gobject
7 import math
8 import cairo
10 from ui.utils import human_unit, min_size_to_consider
12 RADIUS = 20.0
13 LINE_WIDTH = 4.0
14 CAIRO_IS_FAST = True
16 class PysizeWidget_Draw(object):
17 def __init__(self, options, args):
18 self.connect('expose-event', type(self)._expose_event)
19 self.modify_font(pango.FontDescription('Monospace 12'))
21 # An entry needs at least the equivalent of 2*RADIUS in height
22 def _get_requested_height(self):
23 return 2 * RADIUS * self.tree.root.size / \
24 min_size_to_consider(self.options.min_size)
26 def queue_node_redraw(self, node):
27 if node:
28 (x0, x1, y0, y1) = map(int, node.rectangle)
29 self.queue_draw_area(x0, y0, x1 - x0, y1 - y0)
31 def _draw_text(self, context, text, x0, x1, y0, y1):
32 pl = self.create_pango_layout(text)
33 w = x1 - x0
34 h = y1 - y0
35 pl.set_width(int(w*pango.SCALE))
36 pl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
37 (real_w, real_h) = pl.get_pixel_size()
38 if real_w > w or real_h > h:
39 return False
40 if real_w+w/20.0 < w:
41 x0 += w/20.0
42 context.move_to(x0, y0+(h-real_h)/2.0)
43 context.set_source_rgb(0, 0, 0)
44 context.show_layout(pl)
45 return True
47 def _get_node_colors(self, node, colors):
48 if node == self.cursor_node:
49 colors = map(lambda c: c+0.1, colors)
50 if node in self.selected_nodes + [self.button_press_node]:
51 colors = map(lambda c: c-0.3, colors)
52 return colors
54 def _draw_box(self, context, x0, x1, y0, y1, node):
55 if x0 == 0.0:
56 x0 += LINE_WIDTH/2.0
57 else:
58 x0 += LINE_WIDTH/4.0
59 x1 -= LINE_WIDTH/4.0
60 if y0 == 0.0:
61 y0 += LINE_WIDTH/2.0
62 else:
63 y0 += LINE_WIDTH/4.0
64 y1 -= LINE_WIDTH/4.0
65 node.rectangle = (x0, x1, y0, y1)
67 if CAIRO_IS_FAST:
68 context.new_path()
69 context.arc(x0 + RADIUS, y0 + RADIUS, RADIUS,
70 - math.pi, - math.pi / 2.0)
71 context.rel_line_to(x1 - x0 - 2*RADIUS, 0)
72 context.arc(x1 - RADIUS, y0 + RADIUS, RADIUS,
73 - math.pi / 2.0, 0)
74 context.rel_line_to(0, y1 - y0 - 2*RADIUS)
75 context.arc(x1 - RADIUS, y1 - RADIUS, RADIUS,
76 0, math.pi / 2.0)
77 context.rel_line_to(- x1 + x0 + 2*RADIUS, 0)
78 context.arc(x0 + RADIUS, y1 - RADIUS, RADIUS,
79 math.pi / 2.0, math.pi)
80 context.close_path()
82 context.set_source_rgb(0, 0, 0)
83 context.stroke_preserve()
85 gradient = cairo.LinearGradient(0, y0, 0, y1)
86 colors = self._get_node_colors(node, [0.5, 0.5, 1.0, 0.0, 0.5, 1.0])
87 gradient.add_color_stop_rgb(0.0, *colors[:3])
88 gradient.add_color_stop_rgb(1.0, *colors[3:])
89 context.set_source(gradient)
90 context.fill()
91 else:
92 context.rectangle(x0, y0, x1 - x0, y1 - y0)
93 context.set_source_rgb(0, 0, 0)
94 context.stroke_preserve()
96 colors = self._get_node_colors(node, [0, 0.5, 1.0])
97 context.set_source_rgb(*colors)
98 context.fill()
100 name = node.get_name()
101 size = human_unit(node.size)
102 if not self._draw_text(context, name + '\n' + size, x0, x1, y0, y1):
103 first, second = name, size
104 if not node.is_real():
105 first, second = second, first
106 if not self._draw_text(context, first, x0, x1, y0, y1):
107 self._draw_text(context, second, x0, x1, y0, y1)
109 def _draw_boxes(self, context, node, depth, offset):
110 w = self.allocation.width
111 h = self.allocation.height
112 x0 = depth * (w - 1.0) / self.tree.height
113 x1 = (depth + 1.0) * (w - 1.0) / self.tree.height
114 y0 = (h - 1.0) * offset / self.tree.root.size
115 y1 = (h - 1.0) * (offset + node.size) / self.tree.root.size
117 self._draw_box(context, x0, x1, y0, y1, node)
118 depth += 1
119 for child in node.get_children():
120 self._draw_boxes(context, child, depth, offset)
121 offset += child.size
123 def _draw(self, context):
124 context.set_line_width(LINE_WIDTH)
125 offset = 0
126 for child in self.tree.root.get_children():
127 self._draw_boxes(context, child, 0, offset)
128 offset += child.size
130 def _expose_event(self, event):
131 context = self.window.cairo_create()
133 # set a clip region for the expose event
134 context.rectangle(event.area.x, event.area.y,
135 event.area.width, event.area.height)
136 context.clip()
138 self._draw(context)
139 return False
141 def max_number_of_items(self):
142 return self.allocation.height / (2*RADIUS)
144 def _set_actual_min_size(self):
145 if self.options.min_size == 'auto':
146 self.options.min_size = str(int(self.tree.root.size *
147 self.min_size_requested()))
149 def _zoom(self, func):
150 self._set_actual_min_size()
151 min_size = int(self.options.min_size)
152 self.options.min_size = func(min_size)
153 self.schedule_new_tree()
155 def zoom_fit(self):
156 self._zoom(lambda min_size: 'auto')
158 def zoom_in(self):
159 self._zoom(lambda min_size: str(int(min_size / 1.5)))
161 def zoom_out(self):
162 self._zoom(lambda min_size: str(int(min_size * 1.5)))