diff options
Diffstat (limited to 'Desenho.py')
-rw-r--r-- | Desenho.py | 844 |
1 files changed, 361 insertions, 483 deletions
@@ -61,14 +61,12 @@ Walter Bender (walter@laptop.org) """ - -import pygtk -pygtk.require('2.0') -import gtk +from gi.repository import Gdk +from gi.repository import GObject +from gi.repository import PangoCairo import logging import math -import gc -import gobject +import cairo RESIZE_DELAY = 500 # The time to wait for the resize operation to be # executed, after the resize controls are pressed. @@ -85,9 +83,27 @@ class Desenho: """ self._resize_timer = None - #self.d = widget - - def line(self, widget, coords): + self._rainbow_color_list = ['#ff0000', # red + '#ff8000', # orange + '#ffff00', # yellow + '#80ff00', # lime + '#00ff00', # green + '#00ff80', # green water + '#00ffff', # light blue + '#007fff', # almost blue + '#0000ff', # blue + '#8000ff', # indigo + '#ff00ff', # pink violet + '#ff0080'] # violet + self._rainbow_counter = 0 + + self.points = [] + self._last_points_used = self.points + + def clear_control_points(self): + self._last_points_used = [] + + def line(self, widget, coords, temp): """Draw line. @param self -- Desenho.Desenho instance @@ -95,16 +111,21 @@ class Desenho: @param coords -- Two value tuple """ - width, height = widget.window.get_size() - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - widget.pixmap_temp.draw_line(widget.gc_line, widget.oldx, widget.oldy, - coords[0], coords[1]) - #widget.newx = coords[0] - #widget.newy = coords[1] + if temp == True: + ctx = widget.temp_ctx + else: + ctx = widget.drawing_ctx + + ctx.set_line_width(widget.tool['line size']) + ctx.set_line_cap(cairo.LINE_CAP_ROUND) + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.move_to(widget.oldx, widget.oldy) + ctx.line_to(coords[0], coords[1]) + ctx.stroke() + # TODO: clip widget.queue_draw() - def eraser(self, widget, coords, last, size=30, shape='circle'): + def eraser(self, widget, coords, last): """Erase part of the drawing. @param self -- Desenho.Desenho instance @@ -115,11 +136,9 @@ class Desenho: @param shape -- string (default 'circle') """ - widget.desenha = False - self._trace(widget, widget.gc_eraser, coords, last, size, shape) - #widget.queue_draw() + self._trace(widget, coords, last) - def brush(self, widget, coords, last, size=5, shape='circle'): + def brush(self, widget, coords, last): """Paint with brush. @param self -- Desenho.Desenho instance @@ -130,8 +149,7 @@ class Desenho: @param shape -- string (default 'circle') """ - widget.desenha = False - self._trace(widget, widget.gc_brush, coords, last, size, shape) + self._trace(widget, coords, last) def stamp(self, widget, coords, last, stamp_size=20): """Paint with stamp. @@ -143,19 +161,25 @@ class Desenho: @param stamp_size -- integer (default 20) """ + widget.desenha = False - gc = widget.gc_brush width = widget.resized_stamp.get_width() height = widget.resized_stamp.get_height() dx = coords[0] - width / 2 dy = coords[1] - height / 2 - widget.pixmap.draw_pixbuf(gc, widget.resized_stamp, - 0, 0, dx, dy, width, height) - widget.queue_draw() + widget.drawing_ctx.save() + widget.drawing_ctx.translate(dx, dy) + widget.drawing_ctx.rectangle(dx, dy, width, height) + Gdk.cairo_set_source_pixbuf(widget.drawing_ctx, widget.resized_stamp, + 0, 0) + widget.drawing_ctx.paint() + widget.drawing_ctx.restore() + + widget.queue_draw_area(dx, dy, width, height) - def rainbow(self, widget, coords, last, color, size=5, shape='circle'): + def rainbow(self, widget, coords, last): """Paint with rainbow. @param self -- Desenho.Desenho instance @@ -167,52 +191,62 @@ class Desenho: @param shape -- string (default 'circle') """ - colormap = widget.get_colormap() - rainbow_colors = [ - colormap.alloc_color('#ff0000', True, True), # vermelho - colormap.alloc_color('#ff8000', True, True), # laranja - colormap.alloc_color('#ffff00', True, True), # amarelo - colormap.alloc_color('#80ff00', True, True), # verde lima - colormap.alloc_color('#00ff00', True, True), # verde - colormap.alloc_color('#00ff80', True, True), # verde agua - colormap.alloc_color('#00ffff', True, True), # azul claro - colormap.alloc_color('#007fff', True, True), # quase azul - colormap.alloc_color('#0000ff', True, True), # azul - colormap.alloc_color('#8000ff', True, True), # anil - colormap.alloc_color('#ff00ff', True, True), # rosa violeta - colormap.alloc_color('#ff0080', True, True), # violeta - ] - - widget.gc_rainbow.set_foreground(rainbow_colors[color]) - widget.desenha = False - self._trace(widget, widget.gc_rainbow, coords, last, size, shape) + _color_str = self._rainbow_color_list[self._rainbow_counter] + _color = Gdk.color_parse(_color_str) + self._rainbow_counter += 1 + if self._rainbow_counter > 11: + self._rainbow_counter = 0 + + widget.drawing_ctx.set_source_rgba(_color.red, _color.green, + _color.blue, 0.3) + self._old_trace(widget, coords, last) - def _trace(self, widget, gc, coords, last, size, shape): + def _old_trace(self, widget, coords, last): + """ + _old_trace is used only by rainbow + """ + widget.desenha = False + size = widget.tool['line size'] + shape = widget.tool['line shape'] if shape == 'circle': - widget.pixmap.draw_arc(gc, True, - coords[0] - size / 2, coords[1] - size / 2, - size, size, 0, 360 * 64) if last: - gc.set_line_attributes(size, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - widget.pixmap.draw_line(gc, - last[0], last[1], coords[0], coords[1]) - gc.set_line_attributes(0, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) + widget.drawing_ctx.set_line_width(size) + + widget.drawing_ctx.set_line_cap(cairo.LINE_CAP_ROUND) + widget.drawing_ctx.set_line_join(cairo.LINE_JOIN_ROUND) + widget.drawing_ctx.move_to(last[0], last[1]) + widget.drawing_ctx.line_to(coords[0], coords[1]) + widget.drawing_ctx.stroke() + else: + widget.drawing_ctx.move_to(coords[0], + coords[1]) + widget.drawing_ctx.arc(coords[0], coords[1], + size / 2, 0., 2 * math.pi) + widget.drawing_ctx.fill() + elif shape == 'square': - widget.pixmap.draw_rectangle(gc, True, - coords[0] - size / 2, coords[1] - size / 2, size, size) if last: points = [(last[0] - size / 2, last[1] - size / 2), (coords[0] - size / 2, coords[1] - size / 2), (coords[0] + size / 2, coords[1] + size / 2), (last[0] + size / 2, last[1] + size / 2)] - widget.pixmap.draw_polygon(gc, True, points) + for point in points: + widget.drawing_ctx.line_to(*point) + widget.drawing_ctx.fill() points = [(last[0] + size / 2, last[1] - size / 2), (coords[0] + size / 2, coords[1] - size / 2), (coords[0] - size / 2, coords[1] + size / 2), (last[0] - size / 2, last[1] + size / 2)] - widget.pixmap.draw_polygon(gc, True, points) + for point in points: + widget.drawing_ctx.line_to(*point) + widget.drawing_ctx.fill() + else: + widget.drawing_ctx.move_to(coords[0] - size / 2, + coords[1] - size / 2) + widget.drawing_ctx.rectangle(coords[0] - size / 2, + coords[1] - size / 2, size, size) + widget.drawing_ctx.fill() + if last: x = min(coords[0], last[0]) width = max(coords[0], last[0]) - x @@ -224,171 +258,184 @@ class Desenho: else: widget.queue_draw() + def finish_trace(self, widget): + widget.desenha = False + shape = widget.tool['line shape'] + rounded = (shape == 'circle') + self._draw_polygon(widget, False, False, self.points, False, rounded) + if not rounded and len(self.points) == 1: + # draw a square if the mouse was not moved + size = widget.tool['line size'] + coords = self.points[0] + widget.drawing_ctx.save() + if widget.tool['name'] == 'eraser': + color = (1.0, 1.0, 1.0, 1.0) + else: + color = widget.tool['cairo_stroke_color'] + widget.drawing_ctx.set_source_rgba(*color) + widget.drawing_ctx.move_to(coords[0] - size / 2, + coords[1] - size / 2) + widget.drawing_ctx.rectangle(coords[0] - size / 2, + coords[1] - size / 2, size, size) + widget.drawing_ctx.fill() + widget.drawing_ctx.restore() + + self.points = [] + + def _trace(self, widget, coords, last): + widget.desenha = True + size = widget.tool['line size'] + shape = widget.tool['line shape'] + + rounded = (shape == 'circle') + + self.points.append((coords[0], coords[1])) + if last: + self._draw_polygon(widget, True, False, self.points, False, + rounded) + self.clear_control_points() + if last: + x = min(coords[0], last[0]) + width = max(coords[0], last[0]) - x + y = min(coords[1], last[1]) + height = max(coords[1], last[1]) - y + # We add size to avoid drawing dotted lines + widget.queue_draw_area(x - size, y - size, + width + size * 2, height + size * 2) + def square(self, widget, event, coords, temp, fill): """Draw a square. @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() x, y, dx, dy, = self.adjust(widget, coords) + points = [(x, y), (x + dx, y), (x + dx, y + dy), (x, y + dy)] + self._draw_polygon(widget, temp, fill, points) - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_rectangle(widget.gc, True, x, y, dx, dy) - pixmap.draw_rectangle(widget.gc_line, False, x, y, dx, dy) - widget.queue_draw() + def _draw_polygon(self, widget, temp, fill, points, closed=True, + rounded=False): + if not points: + return + if temp == True: + ctx = widget.temp_ctx + else: + ctx = widget.drawing_ctx + + ctx.save() + ctx.new_path() + ctx.move_to(*points[0]) + for point in points: + ctx.line_to(*point) + if closed: + ctx.close_path() + if rounded: + ctx.set_line_cap(cairo.LINE_CAP_ROUND) + ctx.set_line_join(cairo.LINE_JOIN_ROUND) + else: + ctx.set_line_cap(cairo.LINE_CAP_SQUARE) + ctx.set_line_join(cairo.LINE_JOIN_MITER) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.set_line_width(0) + ctx.fill_preserve() + + if widget.tool['name'] == 'eraser': + ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0) + else: + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.set_line_width(widget.tool['line size']) + ctx.stroke() + ctx.restore() + self._last_points_used.extend(points) + area = widget.calculate_damaged_area(self._last_points_used) + widget.queue_draw_area(*area) def triangle(self, widget, coords, temp, fill): """Draw a triangle. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - points = [(widget.oldx, widget.oldy), (widget.oldx + int((coords[0] - widget.oldx) / 2), coords[1]), (coords[0], widget.oldy)] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def trapezoid(self, widget, coords, temp, fill): """Draw a trapezoid. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - dif = int((coords[0] - widget.oldx) / 4) points = [(widget.oldx, widget.oldy), (widget.oldx + dif, coords[1]), (coords[0] - dif, coords[1]), (coords[0], widget.oldy)] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def arrow(self, widget, coords, temp, fill): """Draw a arrow. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) dA = 2 * math.pi / 2 r = math.hypot(y, x) - m = math.sin(A) p = [(widget.oldx, widget.oldy)] - p.append((widget.oldx + int(r * math.cos(A)),\ + p.append((widget.oldx + int(r * math.cos(A)), widget.oldy + int(r * math.sin(A)))) - p.append((widget.oldx + int(0.74 * r * math.cos(A + dA / 6)),\ + p.append((widget.oldx + int(0.74 * r * math.cos(A + dA / 6)), widget.oldy + int(0.74 * r * math.sin(A + dA / 6)))) - p.append((widget.oldx + int(2 * r * math.cos(A + dA / 6 + dA / 20)),\ + p.append((widget.oldx + int(2 * r * math.cos(A + dA / 6 + dA / 20)), widget.oldy + int(2 * r * math.sin(A + dA / 6 + dA / 20)))) p.append((widget.oldx +\ - int(2 * r * math.cos(A + dA / 6 - dA / 20 + dA / 6)),\ + int(2 * r * math.cos(A + dA / 6 - dA / 20 + dA / 6)), \ widget.oldy +\ int(2 * r * math.sin(A + dA / 6 - dA / 20 + dA / 6)))) - p.append((widget.oldx + int(0.74 * r * math.cos(A + dA / 6 + dA / 6)),\ + p.append((widget.oldx + int(0.74 * r * math.cos(A + dA / 6 + dA / 6)), widget.oldy + int(0.74 * r * math.sin(A + dA / 6 + dA / 6)))) - p.append((widget.oldx + int(r * math.cos(A + dA / 2)),\ + p.append((widget.oldx + int(r * math.cos(A + dA / 2)), widget.oldy + int(r * math.sin(A + dA / 2)))) - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - tp = tuple(p) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + + self._draw_polygon(widget, temp, fill, p) def parallelogram(self, widget, coords, temp, fill): """Draw a parallelogram. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = int((coords[0] - widget.oldx) / 4) points = [(widget.oldx, widget.oldy), (coords[0] - x, widget.oldy), (coords[0], coords[1]), (widget.oldx + x, coords[1])] - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, points) - pixmap.draw_polygon(widget.gc_line, False, points) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, points) def star(self, widget, coords, n, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple @param n -- number of sides - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) @@ -398,38 +445,23 @@ class Desenho: widget.oldy + int(r * math.sin(A))), \ (widget.oldx + int(0.4 * r * math.cos(A + dA / 2)), widget.oldy + int(0.4 * r * math.sin(A + dA / 2)))] - for i in range(int(n) - 1): + for _i in range(int(n) - 1): A = A + dA p.append((widget.oldx + int(r * math.cos(A)), \ widget.oldy + int(r * math.sin(A)))) p.append((widget.oldx + int(0.4 * r * math.cos(A + dA / 2)), \ widget.oldy + int(0.4 * r * math.sin(A + dA / 2)))) - tp = tuple(p) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, p) def polygon_regular(self, widget, coords, n, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple @param n -- number of sides - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - x = coords[0] - widget.oldx y = coords[1] - widget.oldy A = math.atan2(y, x) @@ -437,158 +469,117 @@ class Desenho: r = math.hypot(y, x) p = [(widget.oldx + int(r * math.cos(A)), \ widget.oldy + int(r * math.sin(A)))] - for i in range(int(n) - 1): + for _i in range(int(n) - 1): A = A + dA p.append((widget.oldx + int(r * math.cos(A)), \ widget.oldy + int(r * math.sin(A)))) - tp = tuple(p) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.queue_draw() + self._draw_polygon(widget, temp, fill, p) def heart(self, widget, coords, temp, fill): """Draw polygon with n sides. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ if temp == True: - pixmap = widget.pixmap_temp + ctx = widget.temp_ctx else: - pixmap = widget.pixmap - width, height = widget.window.get_size() + ctx = widget.drawing_ctx - if coords[0] < widget.oldx: - x = coords[0] - else: - x = widget.oldx - if coords[1] < widget.oldy: - y = coords[1] - else: - y = widget.oldy - - dx = math.fabs(coords[0] - widget.oldx) dy = math.fabs(coords[1] - widget.oldy) - - w = int(4 * dx) - e = int(4 * dx / math.sqrt(3)) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx), - int(widget.oldy - e / 2), w, e, 180 * 64, 60 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - 3 * dx), - int(widget.oldy - e / 2), w, e, 300 * 64, 60 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx * 0.2), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 180 * 64) - pixmap.draw_arc(widget.gc, True, - int(widget.oldx - dx), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 180 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx), - int(widget.oldy - e / 2), w, e, 180 * 64, 60 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx - w / 2), - int(widget.oldy - e / 2), w, e, 300 * 64, 60 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx * 0.2), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 0, 132 * 64) - pixmap.draw_arc(widget.gc_line, False, - int(widget.oldx - dx), - int(widget.oldy - 0.6 * dx + 2), - int(1.2 * dx), int(1.2 * dx), 48 * 64, 132 * 64) - + r = math.hypot(dy, dy) + w = r / 10.0 + + if w == 0: + # non invertible cairo matrix + return + + ctx.set_line_width(widget.tool['line size']) + line_width = ctx.get_line_width() + + ctx.save() + ctx.new_path() + ctx.translate(widget.oldx, widget.oldy) + ctx.scale(w, w) + ctx.move_to(0, 0) + ctx.curve_to(0, -30, -50, -30, -50, 0) + ctx.curve_to(-50, 30, 0, 35, 0, 60) + ctx.curve_to(0, 35, 50, 30, 50, 0) + ctx.curve_to(50, -30, 0, -30, 0, 0) + + ctx.set_line_width(line_width / w) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + ctx.restore() + + # TODO: clip widget.queue_draw() def circle(self, widget, coords, temp, fill): """Draw a circle. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp + ctx = widget.temp_ctx else: - pixmap = widget.pixmap - width, height = widget.window.get_size() + ctx = widget.drawing_ctx x, y, dx, dy = self.adjust(widget, coords) - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if fill == True: - pixmap.draw_arc(widget.gc, True, x, y, dx, dy, 0, 360 * 64) - pixmap.draw_arc(widget.gc_line, False, x, y, dx, dy, 0, 360 * 64) + if dx == 0 or dy == 0: + # scale by 0 gives error + return + ctx.set_line_width(widget.tool['line size']) + line_width = ctx.get_line_width() + ctx.save() + ctx.new_path() + ctx.translate(x, y) + ctx.scale(dx, dy) + ctx.arc(0., 0., 1., 0., 2 * math.pi) + ctx.set_line_width(line_width / float(min(dx, dy))) + if fill: + ctx.set_source_rgba(*widget.tool['cairo_fill_color']) + ctx.fill_preserve() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + ctx.stroke() + ctx.restore() + # TODO: clip widget.queue_draw() def clear(self, widget): """Clear the drawing. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) """ logging.debug('Desenho.clear') + widget.desenha = False - widget.desenho = [] widget.textos = [] - + x, y = 0, 0 + width, height = widget.get_size() # try to clear a selected area first if widget.is_selected(): - try: - width, height = widget.pixmap_sel.get_size() - - # Clear the selected area - widget.pixmap_sel.draw_rectangle(widget.get_style().white_gc, - True, 0, 0, width, height) - # Draw the selected area in the displayed pixmap - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, - 0, 0, widget.orig_x, widget.orig_y, width, height) - # Draw the selection rectangle - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x, widget.orig_y, width, height) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x - 1, widget.orig_y - 1, - width + 2, height + 2) - - except NameError, message: - logging.debug(message) - except Exception, message: - logging.debug('Unexpected error: %s', message) - else: - width, height = widget.window.get_size() - widget.pixmap.draw_rectangle(widget.get_style().white_gc, True, - 0, 0, width, height) - widget.pixmap_temp.draw_rectangle(widget.get_style().white_gc, - True, 0, 0, width, height) + x, y, width, height = widget.get_selection_bounds() + + widget.drawing_ctx.rectangle(x, y, width, height) + widget.drawing_ctx.set_source_rgb(1.0, 1.0, 1.0) + widget.drawing_ctx.fill() + widget.queue_draw() def text(self, widget, event): """Display and draw text in the drawing area. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param event -- GdkEvent - """ if not widget.text_in_progress: @@ -602,76 +593,54 @@ class Desenho: else: widget.text_in_progress = False - try: - # This works for a gtk.Entry - text = widget.activity.textview.get_text() - except AttributeError: - # This works for a gtk.TextView - buf = widget.activity.textview.get_buffer() - start, end = buf.get_bounds() - text = buf.get_text(start, end) + buf = widget.activity.textview.get_buffer() + start, end = buf.get_bounds() + text = buf.get_text(start, end, True) + + textview = widget.activity.textview + tv_layout = textview.create_pango_layout(text) - layout = widget.activity.textview.create_pango_layout(text) + ctx = widget.drawing_ctx + + ctx.save() + ctx.new_path() + ctx.set_source_rgba(*widget.tool['cairo_stroke_color']) + + pango_layout = PangoCairo.create_layout(ctx) + pango_layout.set_font_description(widget.get_font_description()) + pango_layout.set_text(unicode(text), len(unicode(text))) + + tv_alloc = textview.get_allocation() + ctx.move_to(tv_alloc.x, tv_alloc.y) + PangoCairo.show_layout(ctx, pango_layout) + ctx.stroke() + ctx.restore() - widget.pixmap.draw_layout(widget.gc_brush, - widget.oldx, widget.oldy, layout) - widget.pixmap_temp.draw_layout(widget.gc, - widget.oldx, widget.oldy, layout) widget.activity.textview.hide() + widget.drawing_canvas.flush() try: widget.activity.textview.set_text('') except AttributeError: buf.set_text('') - widget.enableUndo(widget) - + widget.enable_undo() + # TODO: clip widget.queue_draw() - def selection(self, widget, coords, temp=True): + def selection(self, widget, coords): """Make a selection. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp - @param fill -- Fill object - - @return (x0,y0,x1,y1) -- coords of corners """ - width, height = widget.window.get_size() - x, y, dx, dy = self.adjust(widget, coords, True) - - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, - 0, 0, 0, 0, width, height) - if temp: - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - x, y, dx, dy) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - x - 1, y - 1, dx + 2, dy + 2) - else: - try: - del (widget.pixmap_sel) - except: - pass - - widget.pixmap_sel = gtk.gdk.Pixmap(widget.window, dx, dy, -1) - widget.pixmap_sel.draw_drawable(widget.gc, widget.pixmap, - x, y, 0, 0, dx, dy) - widget.pixmap.draw_rectangle(widget.get_style().white_gc, True, - x, y, dx, dy) - widget.orig_x = x - widget.orig_y = y - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - x, y, dx, dy) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - x - 1, y - 1, dx + 2, dy + 2) - + widget.set_selection_bounds(x, y, dx, dy) + # TODO: clip widget.queue_draw() - def moveSelection(self, widget, coords): + def move_selection(self, widget, coords): """Move the selection. @param self -- Desenho.Desenho instance @@ -681,25 +650,34 @@ class Desenho: @param pixbuf_copy -- For import image """ - - width, height = widget.window.get_size() - - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, - 0, 0, 0, 0, width, height) + widget.desenha = True dx = int(coords[0] - widget.oldx) dy = int(coords[1] - widget.oldy) - size = widget.pixmap_sel.get_size() - - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, - 0, 0, widget.orig_x + dx, widget.orig_y + dy, size[0], size[1]) - - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x + dx, widget.orig_y + dy, size[0], size[1]) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x + dx - 1, widget.orig_y + dy - 1, - size[0] + 2, size[1] + 2) + x, y, width, height = widget.get_selection_bounds() + + if widget.pending_clean_selection_background: + # clear the selection background + widget.drawing_ctx.save() + widget.drawing_ctx.new_path() + widget.drawing_ctx.rectangle(x, y, width, height) + widget.drawing_ctx.set_source_rgb(1.0, 1.0, 1.0) + widget.drawing_ctx.fill() + widget.drawing_ctx.restore() + widget.pending_clean_selection_background = False + + selection_surface = widget.get_selection() + widget.oldx = coords[0] + widget.oldy = coords[1] + + new_x, new_y = x + dx, y + dy + widget.temp_ctx.save() + widget.temp_ctx.translate(new_x, new_y) + widget.temp_ctx.set_source_surface(selection_surface) + widget.temp_ctx.paint() + widget.temp_ctx.restore() + widget.set_selection_start(new_x, new_y) widget.queue_draw() @@ -711,173 +689,73 @@ class Desenho: @param height_percent -- Percent of y scale """ - width, height = widget.window.get_size() - widget.desenha = True - widget.selmove = True - - #Create the pixbuf for future resizes - try: - self.pixbuf_resize - except: - size = widget.pixmap_sel.get_size() - self.pixbuf_resize = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, - 8, size[0], size[1]) - self.pixbuf_resize.get_from_drawable(widget.pixmap_sel, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, size[0], size[1]) - - w = self.pixbuf_resize.get_width() - h = self.pixbuf_resize.get_height() - wr = int(w * width_percent) - hr = int(h * height_percent) - - self._draw_selection(widget, wr, hr, is_preview=True) - # Add a timer for resize or update it if there is one already: if self._resize_timer is not None: - gobject.source_remove(self._resize_timer) - self._resize_timer = gobject.timeout_add(RESIZE_DELAY, - self._do_resize, widget, wr, hr) + GObject.source_remove(self._resize_timer) + self._resize_timer = GObject.timeout_add(RESIZE_DELAY, + self._do_resize, widget, width_percent, height_percent) - def _do_resize(self, widget, wr, hr): + def _do_resize(self, widget, width_percent, height_percent): """Do the resize calculation. - """ - resized = self.pixbuf_resize.scale_simple(wr, hr, gtk.gdk.INTERP_HYPER) - - #Copy the resized picture to pixmap_sel - try: - del (widget.pixmap_sel) - except: - pass - widget.pixmap_sel = gtk.gdk.Pixmap(widget.window, wr, hr, -1) - widget.pixmap_sel.draw_pixbuf(widget.get_style().white_gc, resized, - 0, 0, 0, 0, wr, hr) - - #Draw the new pixmap_sel - self._draw_selection(widget, wr, hr) - - # Clean the timer: - self.resize_timer = None - return False - - def _draw_selection(self, widget, wr, hr, is_preview=False): - gc.collect() - width, height = widget.window.get_size() - - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap, - 0, 0, 0, 0, width, height) - if is_preview: - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, - 0, 0, widget.orig_x, widget.orig_y, -1, -1) - else: - widget.pixmap_temp.draw_drawable(widget.gc, widget.pixmap_sel, - 0, 0, widget.orig_x, widget.orig_y, wr, hr) - widget.pixmap_temp.draw_rectangle(widget.gc_selection, False, - widget.orig_x, widget.orig_y, wr, hr) - widget.pixmap_temp.draw_rectangle(widget.gc_selection1, False, - widget.orig_x - 1, widget.orig_y - 1, wr + 2, hr + 2) - + widget.desenha = True + widget.resize_selection_surface(float(width_percent), + float(height_percent)) widget.queue_draw() - gc.collect() - def polygon(self, widget, coords, temp, fill, param=None): + def freeform(self, widget, coords, temp, fill, param=None): """Draw polygon. - @param self -- Desenho.Desenho instance @param widget -- Area object (GtkDrawingArea) @param coords -- Two value tuple - @param temp -- switch between pixmap and pixmap_temp + @param temp -- switch between drawing context and temp context @param fill -- Fill object - """ - if temp == True: - pixmap = widget.pixmap_temp - else: - pixmap = widget.pixmap - width, height = widget.window.get_size() - - pixmap.draw_drawable(widget.gc, widget.pixmap, 0, 0, 0, 0, - width, height) - if param == "moving": # mouse not pressed moving - if not widget.polygon_start: - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - elif widget.polygon_start == True: # Starting a new polygon ? - if param == "motion": - # first press - try: - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.points.append(coords) - except: - pixmap.draw_line(widget.gc_line, widget.oldx, widget.oldy, - coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.oldx, widget.oldy, coords[0], coords[1]) - widget.first = widget.oldx, widget.oldy - widget.points = [widget.first, coords] - widget.enableUndo(widget, overrite=True) - widget.last = coords - else: # param == "release" - # first release - try: - widget.first - widget.points.append(coords) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - except: - widget.first = widget.oldx, widget.oldy - widget.points = [widget.first, coords] - widget.pixmap.draw_line(widget.gc_line, - widget.oldx, widget.oldy, coords[0], coords[1]) - widget.enableUndo(widget, overrite=True) - widget.last = coords - widget.polygon_start = False - else: - if param == "motion": - # print "press" - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) - widget.enableUndo(widget, overrite=True) - widget.last = coords - widget.points.append(coords) - elif param == "release": - # print "release" - x = coords[0] - widget.first[0] - y = coords[1] - widget.first[1] - d = math.hypot(x, y) - if d > 20: # close the polygon ? - pixmap.draw_line(widget.gc_line, - widget.last[0], widget.last[1], coords[0], coords[1]) + if self.points: + if widget.last: + self.points.append((coords[0], coords[1])) + widget.last = [] + else: + self.points[-1] = (coords[0], coords[1]) + elif param == "motion": + # when mousepress or mousemove + if widget.last: + self.points.append((widget.last[0], widget.last[1])) + self.points.append((coords[0], coords[1])) + else: + self.points.append((widget.oldx, widget.oldy)) + self.points.append((coords[0], coords[1])) + widget.enable_undo(overrite=True) + widget.last = coords + elif param == "release": + if len(self.points) > 2: + first = self.points[0] + dx = coords[0] - first[0] + dy = coords[1] - first[1] + d = math.hypot(dx, dy) + if d > 20: widget.last = coords - widget.points.append(coords) + self.points.append(coords) else: - tp = tuple(widget.points) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) + # close the polygon + self.points.append((first[0], first[1])) + self._draw_polygon(widget, False, fill, self.points) widget.last = [] - widget.polygon_start = True - widget.enableUndo(widget, overrite=True) - elif param == "bug": - tp = tuple(widget.points) - if fill == True: - pixmap.draw_polygon(widget.gc, True, tp) - pixmap.draw_polygon(widget.gc_line, False, tp) - widget.last = [] - widget.polygon_start = True - widget.enableUndo(widget, overrite=True) - widget.queue_draw() + self.points = [] + widget.enable_undo(overrite=True) + widget.queue_draw() + return + + widget.desenha = True + # Display the polygon open in the temp canvas + self._draw_polygon(widget, True, False, self.points, closed=False) + self.clear_control_points() def adjust(self, widget, coords, locked=False): - width, height = widget.window.get_size() + width, height = widget.get_size() if widget.oldx > int(coords[0]): xi = int(coords[0]) xf = widget.oldx |