Center the text, so that there is no need to adjust it to the right.
[pysize.git] / pysize / ui / gtk / pysize_widget_draw.py
blob6c3e89cd55a3fb8fd36dda215ae72a7ad74f4bda
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 pysize.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 pl.set_alignment(pango.ALIGN_CENTER)
34 w = x1 - x0
35 h = y1 - y0
36 pl.set_width(int(w*pango.SCALE))
37 pl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
38 (real_w, real_h) = pl.get_pixel_size()
39 if real_h > h:
40 return False
41 context.move_to(x0, y0+(h-real_h)/2.0)
42 context.set_source_rgb(0, 0, 0)
43 context.show_layout(pl)
44 return True
46 def _get_node_colors(self, node, colors):
47 if node == self.cursor_node:
48 colors = map(lambda c: c+0.1, colors)
49 if node in self.selected_nodes + [self.button_press_node]:
50 colors = map(lambda c: c-0.3, colors)
51 return colors
53 def _draw_box(self, context, x0, x1, y0, y1, node):
54 if x0 == 0.0:
55 x0 += LINE_WIDTH/2.0
56 else:
57 x0 += LINE_WIDTH/4.0
58 x1 -= LINE_WIDTH/4.0
59 if y0 == 0.0:
60 y0 += LINE_WIDTH/2.0
61 else:
62 y0 += LINE_WIDTH/4.0
63 y1 -= LINE_WIDTH/4.0
64 node.rectangle = (x0, x1, y0, y1)
66 if CAIRO_IS_FAST:
67 context.new_path()
68 context.arc(x0 + RADIUS, y0 + RADIUS, RADIUS,
69 - math.pi, - math.pi / 2.0)
70 context.rel_line_to(x1 - x0 - 2*RADIUS, 0)
71 context.arc(x1 - RADIUS, y0 + RADIUS, RADIUS,
72 - math.pi / 2.0, 0)
73 context.rel_line_to(0, y1 - y0 - 2*RADIUS)
74 context.arc(x1 - RADIUS, y1 - RADIUS, RADIUS,
75 0, math.pi / 2.0)
76 context.rel_line_to(- x1 + x0 + 2*RADIUS, 0)
77 context.arc(x0 + RADIUS, y1 - RADIUS, RADIUS,
78 math.pi / 2.0, math.pi)
79 context.close_path()
81 context.set_source_rgb(0, 0, 0)
82 context.stroke_preserve()
84 gradient = cairo.LinearGradient(0, y0, 0, y1)
85 colors = self._get_node_colors(node, [0.5, 0.5, 1.0, 0.0, 0.5, 1.0])
86 gradient.add_color_stop_rgb(0.0, *colors[:3])
87 gradient.add_color_stop_rgb(1.0, *colors[3:])
88 context.set_source(gradient)
89 context.fill()
90 else:
91 context.rectangle(x0, y0, x1 - x0, y1 - y0)
92 context.set_source_rgb(0, 0, 0)
93 context.stroke_preserve()
95 colors = self._get_node_colors(node, [0, 0.5, 1.0])
96 context.set_source_rgb(*colors)
97 context.fill()
99 name = node.get_name()
100 size = human_unit(node.size)
101 self._draw_text(context, name + '\n' + size, x0, x1, y0, y1) or \
102 self._draw_text(context, name, x0, x1, y0, y1) or \
103 self._draw_text(context, size, x0, x1, y0, y1)
105 def _draw_boxes(self, context, node, depth, offset):
106 w = self.allocation.width
107 h = self.allocation.height
108 x0 = depth * (w - 1.0) / self.tree.height
109 x1 = (depth + 1.0) * (w - 1.0) / self.tree.height
110 y0 = (h - 1.0) * offset / self.tree.root.size
111 y1 = (h - 1.0) * (offset + node.size) / self.tree.root.size
113 self._draw_box(context, x0, x1, y0, y1, node)
114 depth += 1
115 for child in node.children:
116 self._draw_boxes(context, child, depth, offset)
117 offset += child.size
119 def _draw(self, context):
120 context.set_line_width(LINE_WIDTH)
121 offset = 0
122 for child in self.tree.root.children:
123 self._draw_boxes(context, child, 0, offset)
124 offset += child.size
126 def _expose_event(self, event):
127 context = self.window.cairo_create()
129 # set a clip region for the expose event
130 context.rectangle(event.area.x, event.area.y,
131 event.area.width, event.area.height)
132 context.clip()
134 self._draw(context)
135 return False
137 def max_number_of_items(self):
138 return self.allocation.height / (2*RADIUS)
140 def _get_actual_min_size(self):
141 min_size = self.options.min_size
142 if min_size == 'auto':
143 min_size = self.tree.root.size * self.min_size_requested()
144 return int(min_size)
146 def _zoom(self, func):
147 min_size = self._get_actual_min_size()
148 self.options.min_size = func(min_size)
149 self.schedule_new_tree()
151 def zoom_fit(self):
152 self._zoom(lambda min_size: 'auto')
154 def zoom_in(self):
155 self._zoom(lambda min_size: str(int(min_size / 1.5)))
157 def zoom_out(self):
158 self._zoom(lambda min_size: str(int(min_size * 1.5)))