"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)))