From 90266dc1c7ce8ac91a69d52ddb748458947fa729 Mon Sep 17 00:00:00 2001 From: Pootle daemon Date: Sat, 11 Aug 2012 04:31:57 +0000 Subject: Merge branch 'master' of git.sugarlabs.org:paint/mainline --- diff --git a/Area.py b/Area.py index a81cda3..fbf2327 100644 --- a/Area.py +++ b/Area.py @@ -62,26 +62,32 @@ Walter Bender (walter@laptop.org) """ -import gtk -import gobject +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GdkPixbuf +from gi.repository import GObject +from gi.repository import Pango + import logging import os import tempfile import math -import pango +import cairo +import StringIO +import array from Desenho import Desenho from urlparse import urlparse -from sugar.graphics.style import zoom +from sugar3.graphics.style import zoom FALLBACK_FILL = True -try: - from fill import fill - FALLBACK_FILL = False -except: - logging.debug('No valid fill binaries. Using slower python code') - pass +#try: +# from fill import fill +# FALLBACK_FILL = False +#except: +# logging.debug('No valid fill binaries. Using slower python code') +# pass ##Tools and events manipulation are handle with this class. @@ -89,35 +95,35 @@ TARGET_URI = 0 MAX_UNDO_STEPS = 12 -class Area(gtk.DrawingArea): +class Area(Gtk.DrawingArea): __gsignals__ = { - 'undo': (gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ([])), - 'redo': (gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ([])), - 'action-saved': (gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ([])), - 'select': (gobject.SIGNAL_ACTION, gobject.TYPE_NONE, ([])), + 'undo': (GObject.SignalFlags.ACTION, None, ([])), + 'redo': (GObject.SignalFlags.ACTION, None, ([])), + 'action-saved': (GObject.SignalFlags.ACTION, None, ([])), + 'select': (GObject.SignalFlags.ACTION, None, ([])), } def __init__(self, activity): """ Initialize the object from class Area which is derived - from gtk.DrawingArea. + from Gtk.DrawingArea. @param self -- the Area object (GtkDrawingArea) @param activity -- the parent window """ - gtk.DrawingArea.__init__(self) - - self.set_events(gtk.gdk.POINTER_MOTION_MASK | - gtk.gdk.POINTER_MOTION_HINT_MASK | - gtk.gdk.BUTTON_PRESS_MASK | - gtk.gdk.BUTTON_RELEASE_MASK | - gtk.gdk.EXPOSURE_MASK | - gtk.gdk.LEAVE_NOTIFY_MASK | - gtk.gdk.ENTER_NOTIFY_MASK | - gtk.gdk.KEY_PRESS_MASK) - - self.connect("expose_event", self.expose) + GObject.GObject.__init__(self) + + self.set_events(Gdk.EventMask.POINTER_MOTION_MASK | + Gdk.EventMask.POINTER_MOTION_HINT_MASK | + Gdk.EventMask.BUTTON_PRESS_MASK | + Gdk.EventMask.BUTTON_RELEASE_MASK | + Gdk.EventMask.EXPOSURE_MASK | + Gdk.EventMask.LEAVE_NOTIFY_MASK | + Gdk.EventMask.ENTER_NOTIFY_MASK | + Gdk.EventMask.KEY_PRESS_MASK) + + self.connect("draw", self.draw) self.connect("motion_notify_event", self.mousemove) self.connect("button_press_event", self.mousedown) self.connect("button_release_event", self.mouseup) @@ -125,70 +131,60 @@ class Area(gtk.DrawingArea): self.connect("leave_notify_event", self.mouseleave) self.connect("enter_notify_event", self.mouseenter) - target = [('text/uri-list', 0, TARGET_URI)] - self.drag_dest_set(gtk.DEST_DEFAULT_ALL, target, - gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) + target = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)] + self.drag_dest_set(Gtk.DestDefaults.ALL, target, + Gdk.DragAction.COPY | Gdk.DragAction.MOVE) self.connect('drag_data_received', self.drag_data_received) - self.set_flags(gtk.CAN_FOCUS) + self.set_can_focus(True) self.grab_focus() + # TODO gtk3 + # self.set_extension_events(Gdk.EXTENSION_EVENTS_CURSOR) - self.set_extension_events(gtk.gdk.EXTENSION_EVENTS_CURSOR) ## Define which tool is been used. ## It is now described as a dictionnary, ## with the following keys: ## - 'name' : a string ## - 'line size' : a integer ## - 'stamp size' : a integer - ## - 'fill color' : a gtk.gdk.Color object - ## - 'stroke color' : a gtk.gdk.Color object ## - 'line shape' : a string - 'circle' or 'square', for now ## - 'fill' : a Boolean value ## - 'vertices' : a integer ## All values migth be None, execept in 'name' key. self.tool = { - 'name': 'pencil', + 'name': 'brush', 'line size': 4, 'stamp size': self._get_stamp_size(), - 'fill color': None, - 'stroke color': None, 'line shape': 'circle', 'fill': True, - 'vertices': 6.0} + 'cairo_stroke_color': (0.0, 0.0, 0.0, 1.0), + 'cairo_fill_color': (0.0, 0.0, 0.0, 1.0), + 'alpha': 1.0, + 'vertices': 6.0, + 'font_description': 'Sans 12'} self.desenha = False - self.selmove = False - self.sel_get_out = False + self._selmove = False self.oldx = 0 self.oldy = 0 - self.polygon_start = True - self.points = [] - self.gc = None - self.gc_rainbow = None - self.gc_line = None - self.gc_eraser = None - self.gc_brush = None - self.gc_selection = None - self.pixmap = None - self.pixmap_temp = None - self.pixmap_sel = None - self.desenho = [] + self.drawing_canvas = None self.textos = [] self.text_in_progress = False self.activity = activity self.d = Desenho(self) self.last = [] - self.rainbow_counter = 0 self.keep_aspect_ratio = False self.keep_shape_ratio = False - self.font_description = pango.FontDescription() - self.font_description.set_family('Sans') - self.font_description.set_size(12) + self._font_description = None + self.set_font_description( + Pango.FontDescription(self.tool['font_description'])) - self._set_selection_bounds(0, 0, 0, 0) + # selection properties + self.clear_selection() + self.pending_clean_selection_background = False - # List of pixmaps for the Undo function: + # List of pixbuf for the Undo function: self._undo_list = [] self._undo_index = None @@ -197,68 +193,86 @@ class Area(gtk.DrawingArea): self.x_cursor = 0 self.y_cursor = 0 + def set_font_description(self, fd): + self._font_description = fd + self.activity.textview.modify_font(fd) + self.tool['font_description'] = fd.to_string() + + def get_font_description(self): + return Pango.FontDescription(self.tool['font_description']) + def _get_stamp_size(self): """Set the stamp initial size, based on the display DPI.""" return zoom(44) + def load_from_file(self, file_path): + self.drawing_canvas = cairo.ImageSurface.create_from_png(file_path) + def setup(self, width, height): """Configure the Area object.""" - if self.pixmap: - # Already set up - return - - logging.debug('Area.setup: w=%s h=%s' % (width, height)) + logging.debug('Area.setup: w=%s h=%s', width, height) - win = self.window self.set_size_request(width, height) - ##It is the main pixmap, who is display most of the time. - self.pixmap = gtk.gdk.Pixmap(win, width, height, -1) - self.pixmap.draw_rectangle(self.get_style().white_gc, True, - 0, 0, width, height) - ##This pixmap is showed when we need show something and not draw it. - self.pixmap_temp = gtk.gdk.Pixmap(win, width, height, -1) - self.pixmap_temp.draw_rectangle(self.get_style().white_gc, True, - 0, 0, width, height) - - ##When something is selected this pixmap draw and rectangular box - # out of the selection - #self.pixmap_sel = gtk.gdk.Pixmap(win, width, height, -1) - #self.pixmap_sel.draw_rectangle(self.get_style().white_gc, True, - # 0, 0, width, height) + ##It is the main canvas, who is display most of the time + # if is not None was read from a file + if self.drawing_canvas is None: + self.drawing_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + self.drawing_ctx = cairo.Context(self.drawing_canvas) + # paint background white + self.drawing_ctx.rectangle(0, 0, width, height) + self.drawing_ctx.set_source_rgb(1.0, 1.0, 1.0) + self.drawing_ctx.fill() + else: + self.drawing_ctx = cairo.Context(self.drawing_canvas) - self.gc = win.new_gc() - self.gc_eraser = win.new_gc() - colormap = self.get_colormap() - self.white = colormap.alloc_color('#ffffff', True, True) # white - self.black = colormap.alloc_color('#000000', True, True) # black + ##This canvas is showed when we need show something and not draw it. + self.temp_canvas = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + self.temp_ctx = cairo.Context(self.temp_canvas) + self._init_temp_canvas() - self.gc_eraser.set_foreground(self.white) - self.gc_rainbow = win.new_gc() + self.enable_undo() - self.gc_brush = win.new_gc() - self.gc_brush.set_foreground(self.black) + # Setting a initial tool + self.set_tool(self.tool) - self.gc_line = win.new_gc() + return True - self.gc_selection = win.new_gc() - self.gc_selection.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_selection.set_foreground(self.black) + def get_size(self): + rect = self.get_allocation() + return rect.width, rect.height - #this make another white line out of the black line - self.gc_selection1 = win.new_gc() - self.gc_selection1.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_selection1.set_foreground(self.white) + def _init_temp_canvas(self, area=None): + #logging.error('init_temp_canvas.') + #self.drawing_canvas.flush() + if area == None: + width, height = self.get_size() + self.temp_ctx.rectangle(0, 0, width, height) + else: + self.temp_ctx.rectangle(area.x, area.y, area.width, area.height) + self.temp_ctx.set_source_surface(self.drawing_canvas) + self.temp_ctx.paint() - self.enableUndo(self, size=(width, height)) + def display_selection_border(self, ctx): + if not self.is_selected(): + return + x, y, width, height = self.get_selection_bounds() - # Setting a initial tool - self.set_tool(self.tool) + ctx.save() + ctx.set_line_width(1) + ctx.set_source_rgba(1., 1., 1., 1.) + ctx.rectangle(x, y, width, height) + ctx.stroke_preserve() - return True + ctx.set_line_cap(cairo.LINE_CAP_ROUND) + ctx.set_line_join(cairo.LINE_JOIN_ROUND) + ctx.set_dash([5, 5], 0) + ctx.set_source_rgba(0., 0., 0., 1.) + ctx.stroke() + ctx.restore() def configure_line(self, size): """Configure the new line's size. @@ -267,11 +281,10 @@ class Area(gtk.DrawingArea): @param size -- the size of the new line """ - self.gc_line.set_line_attributes(size, gtk.gdk.LINE_SOLID, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) + self.drawing_ctx.set_line_width(size) - def expose(self, widget, event): - """ This function define which pixmap will be showed to the user. + def draw(self, widget, context): + """ This function define which canvas will be showed to the user. Show up the Area object (GtkDrawingArea). @param self -- the Area object (GtkDrawingArea) @@ -279,17 +292,27 @@ class Area(gtk.DrawingArea): @param event -- GdkEvent """ - area = event.area - if self.desenha or self.selmove: - widget.window.draw_drawable(self.gc, self.pixmap_temp, - area[0], area[1], area[0], area[1], area[2], area[3]) - else: - widget.window.draw_drawable(self.gc, self.pixmap, - area[0], area[1], area[0], area[1], area[2], area[3]) - self.show_tool_shape(widget) - return False +# area = event.area - def show_tool_shape(self, widget): +# context = self.get_window().cairo_create() +# context.rectangle(area.x, area.y, area.width, area.height) +# context.clip() + + if self.desenha: + #logging.error('Expose use temp canvas area %s', area) + # Paint the canvas in the widget: + context.set_source_surface(self.temp_canvas) + context.paint() + else: + #logging.error('Expose use drawing canvas area %s', area) + context.set_source_surface(self.drawing_canvas) + context.paint() + self.show_tool_shape(context) + # TODO: gtk3 how get the area to avoid redrawing all ? + self._init_temp_canvas() # area) + self.display_selection_border(context) + + def show_tool_shape(self, context): """ Show the shape of the tool selected for pencil, brush, rainbow and eraser @@ -297,24 +320,31 @@ class Area(gtk.DrawingArea): if self.tool['name'] in ['pencil', 'eraser', 'brush', 'rainbow', 'stamp']: if not self.drawing: + context.set_source_rgba(*self.tool['cairo_stroke_color']) + context.set_line_width(1) # draw stamp border in widget.window if self.tool['name'] == 'stamp': wr, hr = self.stamp_dimentions - widget.window.draw_rectangle(self.gc_brush, False, - self.x_cursor - wr / 2, self.y_cursor - hr / 2, - wr, hr) + context.rectangle(self.x_cursor - wr / 2, + self.y_cursor - hr / 2, wr, hr) + context.stroke() # draw shape of the brush, square or circle elif self.tool['line shape'] == 'circle': size = self.tool['line size'] - widget.window.draw_arc(self.gc_brush, False, - self.x_cursor - size / 2, self.y_cursor - size / 2, - size, size, 0, 360 * 64) + context.arc(self.x_cursor, + self.y_cursor, size / 2, 0., + 2 * math.pi) + context.stroke() else: size = self.tool['line size'] - widget.window.draw_rectangle(self.gc_brush, False, - self.x_cursor - size / 2, self.y_cursor - size / 2, - size, size) + context.move_to(self.x_cursor - size / 2, + self.y_cursor - size / 2) + context.rectangle(self.x_cursor - size / 2, + self.y_cursor - size / 2, size, size) + context.stroke() + self.last_x_cursor = self.x_cursor + self.last_y_cursor = self.y_cursor def mousedown(self, widget, event): """Make the Area object (GtkDrawingArea) recognize @@ -325,23 +355,26 @@ class Area(gtk.DrawingArea): @param event -- GdkEvent """ - width, height = self.window.get_size() + width, height = self.get_size() coords = int(event.x), int(event.y) # text + design_mode = True if self.tool['name'] == 'text': self.d.text(widget, event) + design_mode = False # This fixes a bug that made the text viewer get stuck in the canvas elif self.text_in_progress: + design_mode = False try: - # This works for a gtk.Entry + # This works for a Gtk.Entry text = self.activity.textview.get_text() except AttributeError: - # This works for a gtk.TextView + # This works for a Gtk.TextView buf = self.activity.textview.get_buffer() start, end = buf.get_bounds() - text = buf.get_text(start, end) + text = buf.get_text(start, end, True) if text is not None: self.d.text(widget, event) @@ -350,68 +383,88 @@ class Area(gtk.DrawingArea): self.oldx, self.oldy = coords - if self.polygon_start is False and self.tool['name'] is not 'freeform': - self.d.polygon(widget, coords, False, self.tool['fill'], "bug") - - x, y, state = event.window.get_pointer() + # TODO: get_pointer is deprecated +# http://developer.gnome.org/gtk3/3.4/GtkWidget.html#gtk-widget-get-pointer + _pointer, x, y, state = event.window.get_pointer() if self.tool['name'] == 'picker': self.pick_color(x, y) - if state & gtk.gdk.BUTTON3_MASK: - #Handle with the right button click event. - if self.tool['name'] == 'marquee-rectangular': - self.sel_get_out = True - elif state & gtk.gdk.BUTTON1_MASK: + if state & Gdk.ModifierType.BUTTON1_MASK: #Handle with the left button click event. if self.tool['name'] == 'eraser': self.last = [] - self.d.eraser(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + self.d.eraser(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'brush': self.last = [] - self.d.brush(widget, coords, self.last, self.tool['line size'], - self.tool['line shape']) + self.d.brush(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'stamp': self.last = [] - self.d.stamp(widget, coords, self.last, - self.tool['stamp size']) + self.d.stamp(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'rainbow': self.last = [] - self.d.rainbow(widget, coords, self.last, - self.rainbow_counter, self.tool['line size'], - self.tool['line shape']) + self.d.rainbow(widget, coords, self.last) self.last = coords self.drawing = True elif self.tool['name'] == 'freeform': self.configure_line(self.tool['line size']) - if self.polygon_start == False: - self.desenha = True - if self.selmove: - #get out of the func selection - if self.tool['name'] != 'marquee-rectangular': - self.getout() - self.selmove = False - else: - size = self.pixmap_sel.get_size() - xi = self.orig_x - yi = self.orig_y - xf = xi + size[0] - yf = yi + size[1] + self.d.freeform(widget, coords, True, + self.tool['fill'], "motion") + if self.tool['name'] == 'marquee-rectangular': + if self.is_selected(): + xi, yi, width, height = self.get_selection_bounds() + xf = xi + width + yf = yi + height + # verify is out of the selected area if (coords[0] < xi) or (coords[0] > xf) or \ (coords[1] < yi) or (coords[1] > yf): - self.sel_get_out = True - else: - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, - 0, 0, 0, 0, width, height) - self.desenha = True - widget.queue_draw() + self.getout() + self._selmove = False + design_mode = False + else: + # if is inside the selected area move the selection + self.d.move_selection(widget, coords) + self._selmove = True + else: + self._selmove = False + + if design_mode: + self.desenha = True + + def calculate_damaged_area(self, points): + min_x = points[0][0] + min_y = points[0][1] + max_x = 0 + max_y = 0 + for point in points: + if point[0] < min_x: + min_x = point[0] + if point[0] > max_x: + max_x = point[0] + if point[1] < min_y: + min_y = point[1] + if point[1] > max_y: + max_y = point[1] + # add the tool size + if self.tool['name'] == 'stamp': + wr, hr = self.stamp_dimentions + min_x = min_x - wr + min_y = min_y - wr + max_x = max_x + hr + max_y = max_y + hr + else: + size = self.tool['line size'] + min_x = min_x - size * 2 + min_y = min_y - size * 2 + max_x = max_x + size * 2 + max_y = max_y + size * 2 + return (min_x, min_y, max_x - min_x, max_y - min_y) def mousemove(self, widget, event): """Make the Area object (GtkDrawingArea) @@ -423,33 +476,26 @@ class Area(gtk.DrawingArea): """ x = event.x y = event.y - state = event.state + state = event.get_state() self.x_cursor, self.y_cursor = int(x), int(y) coords = int(x), int(y) if self.tool['name'] in ['rectangle', 'ellipse', 'line']: - if (state & gtk.gdk.SHIFT_MASK) or \ + if (state & Gdk.ModifierType.SHIFT_MASK) or \ self.keep_shape_ratio: if self.tool['name'] in ['rectangle', 'ellipse']: coords = self._keep_selection_ratio(coords) elif self.tool['name'] == 'line': coords = self._keep_line_ratio(coords) - if state & gtk.gdk.BUTTON1_MASK and self.pixmap != None: - if self.tool['name'] == 'pencil': - self.d.brush(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) - self.last = coords - - elif self.tool['name'] == 'eraser': - self.d.eraser(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + if state & Gdk.ModifierType.BUTTON1_MASK: + if self.tool['name'] == 'eraser': + self.d.eraser(widget, coords, self.last) self.last = coords elif self.tool['name'] == 'brush': - self.d.brush(widget, coords, self.last, - self.tool['line size'], self.tool['line shape']) + self.d.brush(widget, coords, self.last) self.last = coords elif self.tool['name'] == 'stamp': @@ -458,98 +504,85 @@ class Area(gtk.DrawingArea): self.last = coords elif self.tool['name'] == 'rainbow': - self.d.rainbow(widget, coords, self.last, - self.rainbow_counter, self.tool['line size'], - self.tool['line shape']) - self.rainbow_counter += 1 - if self.rainbow_counter > 11: - self.rainbow_counter = 0 + self.d.rainbow(widget, coords, self.last) self.last = coords if self.desenha: if self.tool['name'] == 'line': - self.configure_line(self.tool['line size']) - self.d.line(widget, coords) + self.d.line(widget, coords, True) elif self.tool['name'] == 'ellipse': - self.configure_line(self.tool['line size']) self.d.circle(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'rectangle': - self.configure_line(self.tool['line size']) self.d.square(widget, event, coords, True, self.tool['fill']) - elif self.tool['name'] == 'marquee-rectangular' and \ - not self.selmove: - if (state & gtk.gdk.CONTROL_MASK) or \ - self.keep_aspect_ratio: - coords = self._keep_selection_ratio(coords) - self.d.selection(widget, coords) - # selected - elif self.tool['name'] == 'marquee-rectangular' and \ - self.selmove: - if not self.sel_get_out: - self.d.moveSelection(widget, coords) + elif self.tool['name'] == 'marquee-rectangular': + if self._selmove: + # is inside a selected area, move it + self.d.move_selection(widget, coords) + else: + # create a selected area + if (state & Gdk.ModifierType.CONTROL_MASK) or \ + self.keep_aspect_ratio: + coords = self._keep_selection_ratio(coords) + self.d.selection(widget, coords) elif self.tool['name'] == 'freeform': self.configure_line(self.tool['line size']) - self.d.polygon(widget, coords, True, + self.d.freeform(widget, coords, True, self.tool['fill'], "motion") elif self.tool['name'] == 'triangle': - self.configure_line(self.tool['line size']) self.d.triangle(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'trapezoid': - self.configure_line(self.tool['line size']) self.d.trapezoid(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'arrow': - self.configure_line(self.tool['line size']) self.d.arrow(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'parallelogram': - self.configure_line(self.tool['line size']) self.d.parallelogram(widget, coords, True, self.tool['fill']) elif self.tool['name'] == 'star': - self.configure_line(self.tool['line size']) self.d.star(widget, coords, self.tool['vertices'], True, self.tool['fill']) elif self.tool['name'] == 'polygon_regular': - self.configure_line(self.tool['line size']) self.d.polygon_regular(widget, coords, self.tool['vertices'], True, self.tool['fill']) elif self.tool['name'] == 'heart': - self.configure_line(self.tool['line size']) self.d.heart(widget, coords, True, self.tool['fill']) else: if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil', 'stamp']: - widget.queue_draw() - if self.tool['name'] == 'marquee-rectangular' and self.selmove: - size = self.pixmap_sel.get_size() - xi = self.orig_x - yi = self.orig_y - xf = xi + size[0] - yf = yi + size[1] - if (coords[0] < xi) or (coords[0] > xf) or \ - (coords[1] < yi) or (coords[1] > yf): - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) + # define area to update + last_coords = (self.last_x_cursor, self.last_y_cursor) + area = self.calculate_damaged_area([last_coords, coords]) + widget.queue_draw_area(*area) + if self.tool['name'] == 'marquee-rectangular': + sel_x, sel_y, sel_width, sel_height = \ + self.get_selection_bounds() + # show appropiate cursor + if (coords[0] < sel_x) or (coords[0] > sel_x + sel_width) or \ + (coords[1] < sel_y) or (coords[1] > sel_y + sel_height): + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.CROSS)) else: - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.FLEUR)) - elif self.tool['name'] == 'freeform' and not self.selmove: + elif self.tool['name'] == 'freeform': self.desenha = True self.configure_line(self.tool['line size']) - self.d.polygon(widget, coords, True, + self.d.freeform(widget, coords, True, self.tool['fill'], "moving") - gtk.gdk.event_request_motions(event) + Gdk.event_request_motions(event) def mouseup(self, widget, event): """Make the Area object (GtkDrawingArea) @@ -561,21 +594,19 @@ class Area(gtk.DrawingArea): """ coords = int(event.x), int(event.y) if self.tool['name'] in ['rectangle', 'ellipse', 'line']: - if (event.state & gtk.gdk.SHIFT_MASK) or \ + if (event.get_state() & Gdk.ModifierType.SHIFT_MASK) or \ self.keep_shape_ratio: if self.tool['name'] in ['rectangle', 'ellipse']: coords = self._keep_selection_ratio(coords) if self.tool['name'] == 'line': coords = self._keep_line_ratio(coords) - width, height = self.window.get_size() + width, height = self.get_size() private_undo = False - if self.desenha or self.sel_get_out: + if self.desenha: if self.tool['name'] == 'line': - self.pixmap.draw_line(self.gc_line, self.oldx, self.oldy, - coords[0], coords[1]) - widget.queue_draw() + self.d.line(widget, coords, False) elif self.tool['name'] == 'ellipse': self.d.circle(widget, coords, False, self.tool['fill']) @@ -584,54 +615,26 @@ class Area(gtk.DrawingArea): self.d.square(widget, event, coords, False, self.tool['fill']) elif self.tool['name'] == 'marquee-rectangular': - if self.selmove == False: - if (event.state & gtk.gdk.CONTROL_MASK) or \ - self.keep_aspect_ratio: - coords = self._keep_selection_ratio(coords) - self.d.selection(widget, coords, False) - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) - self.selmove = True - self.sel_get_out = False + if self.is_selected() and not self._selmove: + self.create_selection_surface() self.emit('select') - elif self.sel_get_out: # get out of the func selection - self.getout() - try: - del(self.d.pixbuf_resize) - except: - pass - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) - widget.queue_draw() - self.enableUndo(widget) - elif self.selmove: - self.orig_x = self.orig_x + coords[0] - self.oldx - self.orig_y = self.orig_y + coords[1] - self.oldy - self.emit('select') - private_undo = True + private_undo = True elif self.tool['name'] == 'freeform': - self.d.polygon(widget, coords, False, + self.d.freeform(widget, coords, False, self.tool['fill'], 'release') private_undo = True elif self.tool['name'] == 'bucket': if FALLBACK_FILL: - if self.tool['stroke color'] is not None: - stroke_color = self.tool['stroke color'] - color_r = int(stroke_color.red_float * 65535) - color_g = int(stroke_color.green_float * 65535) - color_b = int(stroke_color.blue_float * 65535) - else: - color_r, color_g, color_b = 0, 0, 0 - cmap = self.pixmap.get_colormap() - fill_color = \ - cmap.alloc_color(color_r, color_g, color_b).pixel - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) - gobject.idle_add(self.flood_fill, coords[0], coords[1], - fill_color) + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.WATCH)) + GObject.idle_add(self.flood_fill, coords[0], coords[1]) else: - width, height = self.window.get_size() - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) - gobject.idle_add(self.fast_flood_fill, widget, coords[0], + width, height = self.get_size() + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.WATCH)) + GObject.idle_add(self.fast_flood_fill, widget, coords[0], coords[1], width, height) elif self.tool['name'] == 'triangle': @@ -660,29 +663,50 @@ class Area(gtk.DrawingArea): if self.tool['name'] in ['brush', 'eraser', 'rainbow', 'pencil', 'stamp']: self.last = [] - widget.queue_draw() + self.d.finish_trace(self) self.drawing = False self.desenha = False if not private_undo and self.tool['name'] != 'bucket': # We have to avoid saving an undo state if the bucket tool # is selected because this undo state is called before the - # gobject.idle_add (with the fill_flood function) finishes + # GObject.idle_add (with the fill_flood function) finishes # and an unconsistent undo state is saved - self.enableUndo(widget) + self.enable_undo() + widget.queue_draw() + self.d.clear_control_points() def fast_flood_fill(self, widget, x, y, width, height): fill(self.pixmap, self.gc, x, y, width, height, self.gc_line.foreground.pixel) widget.queue_draw() - self.enableUndo(widget) - display = gtk.gdk.display_get_default() - cursor = gtk.gdk.cursor_new_from_name(display, 'paint-bucket') - self.window.set_cursor(cursor) - - def flood_fill(self, x, y, fill_color): - width, height = self.window.get_size() + self.enable_undo() + display = Gdk.Display.get_default() + cursor = Gdk.Cursor.new_from_name(display, 'paint-bucket') + self.get_window().set_cursor(cursor) + + def flood_fill(self, x, y): + stroke_color = self.tool['cairo_stroke_color'] + r, g, b = stroke_color[0], stroke_color[1], stroke_color[2] + + # pack the color in a int as 0xAARRGGBB + fill_color = 0xff000000 + \ + (int(r * 255 * 65536) + \ + int(g * 255 * 256) + \ + int(b * 255)) + logging.error('fill_color %d', fill_color) + + # load a array with the surface data + for array_type in ['H', 'I', 'L']: + pixels = array.array(array_type) + if pixels.itemsize == 4: + break + else: + raise AssertionError() + pixels.fromstring(self.drawing_canvas.get_data()) - gdk_image = self.pixmap.get_image(0, 0, width, height) + # process the pixels in the array + width = self.drawing_canvas.get_width() + height = self.drawing_canvas.get_height() def within(x, y): if x < 0 or x >= width: @@ -695,44 +719,57 @@ class Area(gtk.DrawingArea): return edge = [(x, y)] - old_color = gdk_image.get_pixel(x, y) + old_color = pixels[x + y * width] if old_color == fill_color: logging.debug('Already filled') return - gdk_image.put_pixel(x, y, fill_color) + pixels[x + y * width] = fill_color while len(edge) > 0: newedge = [] for (x, y) in edge: for (s, t) in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): if within(s, t) and \ - gdk_image.get_pixel(s, t) == old_color: - gdk_image.put_pixel(s, t, fill_color) + pixels[s + t * width] == old_color: + pixels[s + t * width] = fill_color newedge.append((s, t)) edge = newedge - self.pixmap.draw_image(self.gc, gdk_image, 0, 0, 0, 0, width, height) + # create a updated drawing_canvas + self.drawing_canvas = cairo.ImageSurface.create_for_data(pixels, + cairo.FORMAT_ARGB32, width, height) + self.setup(width, height) + self.queue_draw() - self.enableUndo(self) + self.enable_undo() - display = gtk.gdk.display_get_default() - cursor = gtk.gdk.cursor_new_from_name(display, 'paint-bucket') - self.window.set_cursor(cursor) + display = Gdk.Display.get_default() + cursor = Gdk.Cursor.new_from_name(display, 'paint-bucket') + self.get_window().set_cursor(cursor) def pick_color(self, x, y): - gdk_image = self.pixmap.get_image(x, y, 1, 1) - pixel = gdk_image.get_pixel(0, 0) - cmap = gdk_image.get_colormap() - gdk_color = cmap.query_color(pixel) + # create a new 1x1 cairo surface + cairo_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, 1, 1) + cairo_context = cairo.Context(cairo_surface) + # translate xlib_surface so that target pixel is at 0, 0 + cairo_context.set_source_surface(self.drawing_canvas, -x, -y) + cairo_context.rectangle(0, 0, 1, 1) + cairo_context.set_operator(cairo.OPERATOR_SOURCE) + cairo_context.fill() + cairo_surface.flush() + # Read the pixel + pixels = cairo_surface.get_data() + red = ord(pixels[2]) * 256 + green = ord(pixels[1]) * 256 + blue = ord(pixels[0]) * 256 + + stroke_color = Gdk.Color(red, green, blue) # set in the area - pixmap_cmap = self.pixmap.get_colormap() - stroke_color = pixmap_cmap.alloc_color(gdk_color) - self.tool['stroke color'] = stroke_color - self.set_stroke_color(self.tool['stroke color']) + self.set_stroke_color(stroke_color) - # update the stroke color button + # update the stroke_color in the button self.activity.get_toolbar_box().brush_button.set_color(stroke_color) self.activity.get_toolbar_box().brush_button.stop_stamping() @@ -759,11 +796,12 @@ class Area(gtk.DrawingArea): """ if self.is_selected(): # Change stamp, get it from selection: - width, height = self.pixmap_sel.get_size() - self.pixbuf_stamp = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, - 8, width, height) - self.pixbuf_stamp.get_from_drawable(self.pixmap_sel, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, width, height) + pixbuf_data = StringIO.StringIO() + self.get_selection().write_to_png(pixbuf_data) + pxb_loader = GdkPixbuf.PixbufLoader.new_with_type('png') + pxb_loader.write(pixbuf_data.getvalue()) + + self.pixbuf_stamp = pxb_loader.get_pixbuf() self.stamp_size = 0 # Set white color as transparent: stamp_alpha = self.pixbuf_stamp.add_alpha(True, 255, 255, 255) @@ -790,7 +828,7 @@ class Area(gtk.DrawingArea): wr, hr = int(stamp_size * w * 1.0 / h), stamp_size self.stamp_dimentions = wr, hr self.resized_stamp = self.pixbuf_stamp.scale_simple(wr, hr, - gtk.gdk.INTERP_HYPER) + GdkPixbuf.InterpType.HYPER) # Remove selected area self.getout() @@ -803,9 +841,8 @@ class Area(gtk.DrawingArea): @param self -- the Area object (GtkDrawingArea) """ logging.debug('Area.undo(self)') - width, height = self.window.get_size() - if self.selmove: + if self.is_selected(): self.getout(undo=True) if self.text_in_progress: @@ -815,14 +852,12 @@ class Area(gtk.DrawingArea): if self._undo_index > 0: self._undo_index -= 1 - undo_pix = self._undo_list[self._undo_index] - self.pixmap.draw_drawable(self.gc, undo_pix, 0, 0, 0, 0, -1, -1) + undo_surface = self._undo_list[self._undo_index] + self.drawing_ctx.set_source_surface(undo_surface, 0, 0) + self.drawing_ctx.set_operator(cairo.OPERATOR_SOURCE) + self.drawing_ctx.paint() self.queue_draw() - #special case for func polygon - if self.tool['name'] == 'freeform': - self.polygon_start = True # start the polygon again - self.emit('undo') def redo(self): @@ -831,41 +866,36 @@ class Area(gtk.DrawingArea): @param self -- the Area object (GtkDrawingArea) """ logging.debug('Area.redo(self)') - width, height = self.window.get_size() - if self.selmove: + if self.is_selected(): self.getout() if self._undo_index < len(self._undo_list) - 1: self._undo_index += 1 - undo_pix = self._undo_list[self._undo_index] - self.pixmap.draw_drawable(self.gc, undo_pix, 0, 0, 0, 0, -1, -1) + undo_surface = self._undo_list[self._undo_index] + self.drawing_ctx.set_source_surface(undo_surface, 0, 0) + self.drawing_ctx.set_operator(cairo.OPERATOR_SOURCE) + self.drawing_ctx.paint() self.queue_draw() self.emit('redo') - def enableUndo(self, widget, size=None, overrite=False): + def enable_undo(self, overrite=False): """Keep the last change in a list for Undo/Redo commands. - @param self -- the Area object (GtkDrawingArea) - @param widget -- the Area object (GtkDrawingArea) - """ - #logging.debug('Area.enableUndo(self,widget)') - - width, height = self.window.get_size() - # We need to pass explicitly the size on set up, because the - # window size is not allocated yet. - if size is not None: - width, height = size - if len(self._undo_list) == 0: # first undo pix, start index: self._undo_index = 0 elif len(self._undo_list) == MAX_UNDO_STEPS: # drop the oldest undo pix: self._undo_list.pop(0) + + # it could be at the middle of the list (clicked many + # times undo) and after that draw anything, so we should + # discart the next redos because they are obsolete now. + self._undo_list = self._undo_list[:self._undo_index] else: self._undo_index += 1 # Forget the redos after this one: @@ -877,11 +907,17 @@ class Area(gtk.DrawingArea): if overrite and self._undo_index != 0: self._undo_index -= 1 - undo_pix = gtk.gdk.Pixmap(widget.window, width, height, -1) - undo_pix.draw_drawable(self.gc, self.pixmap, - 0, 0, 0, 0, width, height) + # copy the drawing surface in a new surface + width = self.drawing_canvas.get_width() + height = self.drawing_canvas.get_height() + undo_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + undo_ctx = cairo.Context(undo_surface) + undo_ctx.set_source_surface(self.drawing_canvas, 0, 0) + undo_ctx.set_operator(cairo.OPERATOR_SOURCE) + undo_ctx.paint() + undo_surface.flush() - self._undo_list.append(undo_pix) + self._undo_list.append(undo_surface) self.emit('action-saved') @@ -892,7 +928,7 @@ class Area(gtk.DrawingArea): @param self -- the Area object (GtkDrawingArea) """ - clipBoard = gtk.Clipboard() + clipBoard = Gtk.Clipboard() if 'SUGAR_ACTIVITY_ROOT' in os.environ: temp_dir = os.path.join(os.environ.get('SUGAR_ACTIVITY_ROOT'), 'instance') @@ -902,25 +938,21 @@ class Area(gtk.DrawingArea): f, tempPath = tempfile.mkstemp(suffix='.png', dir=temp_dir) del f - if self.selmove: - size = self.pixmap_sel.get_size() - pixbuf_copy = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, - size[0], size[1]) - pixbuf_copy.get_from_drawable(self.pixmap_sel, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, size[0], size[1]) - pixbuf_copy.save(tempPath, 'png') - os.chmod(tempPath, 0604) - - clipBoard.set_with_data([('text/uri-list', 0, 0)], - self._copyGetFunc, self._copyClearFunc, tempPath) - else: - logging.debug('Area.copy(self): Please select some area first') + selection_surface = self.get_selection() + if selection_surface is None: + selection_surface = self.drawing_canvas + logging.error('Saving file %s', tempPath) + selection_surface.write_to_png(tempPath) + os.chmod(tempPath, 0604) - def _copyGetFunc(self, clipboard, selection_data, info, data): + clipBoard.set_with_data([('text/uri-list', 0, 0)], + self._copy_get_func, self._copy_clear_func, tempPath) + + def _copy_get_func(self, clipboard, selection_data, info, data): """ Determine type data to put in clipboard @param self -- the Area object (GtkDrawingArea) - @param clipboard -- a gtk.Clipboard object + @param clipboard -- a Gtk.Clipboard object @param selection_data -- data of selection @param info -- the app assigned integer associated with a target @param data -- user data (tempPath) @@ -930,11 +962,11 @@ class Area(gtk.DrawingArea): if selection_data.target == "text/uri-list": selection_data.set_uris(['file://' + tempPath]) - def _copyClearFunc(self, clipboard, data): + def _copy_clear_func(self, clipboard, data): """ Clear the clipboard @param self -- the Area object (GtkDrawingArea) - @param clipboard -- a gtk.Clipboard object + @param clipboard -- a Gtk.Clipboard object @param data -- user data (tempPath) """ if (data != None): @@ -944,58 +976,46 @@ class Area(gtk.DrawingArea): def drag_data_received(self, w, context, x, y, data, info, time): if data and data.format == 8: - self.loadImage(urlparse(data.data).path, self) + self.load_image(urlparse(data.data).path, self) context.finish(True, False, time) else: context.finish(False, False, time) - def past(self, widget): - """ Past image. - Past image that is in pixmap + def paste(self, widget): + """ Paste image. + Paste image that is in canvas @param self -- the Area object (GtkDrawingArea) """ - width, height = self.window.get_size() + width, height = self.get_size() tempPath = os.path.join("/tmp", "tempFile") tempPath = os.path.abspath(tempPath) - clipBoard = gtk.Clipboard() + clipBoard = Gtk.Clipboard() if clipBoard.wait_is_image_available(): - self.getout(True, widget) + logging.debug('Area.paste(self): Wait is image available') + self.getout(True) pixbuf_sel = clipBoard.wait_for_image() - size = int(pixbuf_sel.get_width()), int(pixbuf_sel.get_height()) - self.pixmap_sel = gtk.gdk.Pixmap(self.window, size[0], size[1], -1) - self.pixmap_sel.draw_pixbuf(self.gc, pixbuf_sel, - 0, 0, 0, 0, size[0], size[1], - dither=gtk.gdk.RGB_DITHER_NORMAL, x_dither=0, y_dither=0) + width, height = pixbuf_sel.get_width(), pixbuf_sel.get_height() - self.selmove = True + self.temp_ctx.rectangle(0, 0, width, height) + Gdk.cairo_set_source_pixbuf(self.temp_ctx, pixbuf_sel, 0, 0) + self.temp_ctx.paint() + self.set_selection_bounds(0, 0, width, height) self.desenha = True - self.sel_get_out = False - self.orig_x, self.orig_y = 0, 0 - - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, - 0, 0, 0, 0, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, 0, 0, - size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, -1, -1, - size[0] + 2, size[1] + 2) - - self.tool['name'] = 'marquee-rectangular' - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) + self.tool['name'] = 'marquee-rectangular' # TODO: change toolbar? self.emit('select') elif clipBoard.wait_is_uris_available(): + logging.debug('Area.paste(self): is uris available') selection = clipBoard.wait_for_contents('text/uri-list') if selection != None: for uri in selection.get_uris(): - self.loadImage(urlparse(uri).path, self) + self.load_image(urlparse(uri).path, self) else: - self.loadImage(tempPath, self) - logging.debug('Area.past(self): Load from clipboard fails') + self.load_image(tempPath, self) + logging.debug('Area.paste(self): Load from clipboard fails') logging.debug('loading from tempPath') self.queue_draw() @@ -1004,23 +1024,42 @@ class Area(gtk.DrawingArea): """Set fill color. @param self -- the Area object (GtkDrawingArea) - @param color -- a gdk.Color object + @param color -- a Gdk.Color object """ - self.gc.set_foreground(color) + alpha = self.tool['alpha'] + red = color.red / 65535.0 + green = color.green / 65535.0 + blue = color.blue / 65535.0 + self.tool['cairo_fill_color'] = (red, green, blue, alpha) def set_stroke_color(self, color): - """Set stroke color. + """Set cairo_stroke_color. @param self -- the Area object (GtkDrawingArea) - @param color -- a gdk.Color object + @param color -- a Gdk.Color object """ - self.gc_line.set_foreground(color) - self.gc_line.set_line_attributes(1, gtk.gdk.LINE_ON_OFF_DASH, - gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_ROUND) - self.gc_brush.set_foreground(color) - self.activity.textview.modify_text(gtk.STATE_NORMAL, color) + alpha = self.tool['alpha'] + red = color.red / 65535.0 + green = color.green / 65535.0 + blue = color.blue / 65535.0 + self.tool['cairo_stroke_color'] = (red, green, blue, alpha) + self.activity.textview.modify_text(Gtk.StateType.NORMAL, color) + + def set_alpha(self, alpha): + """ + Set the alpha value used to draw + @ param alpha -- float between 0.0 and 1.0 + """ + self.tool['alpha'] = alpha + stroke_color = self.tool['cairo_stroke_color'] + self.tool['cairo_stroke_color'] = (stroke_color[0], stroke_color[1], + stroke_color[2], alpha) + + fill_color = self.tool['cairo_fill_color'] + self.tool['cairo_fill_color'] = (fill_color[0], fill_color[1], + fill_color[2], alpha) def grayscale(self, widget): """Apply grayscale effect. @@ -1044,41 +1083,35 @@ class Area(gtk.DrawingArea): """ - def proc_invert_color(temp_pix): - try: - import numpy - # HACK: This numpy version has a bug and breaks the - # 'invert_color' function - # http://bugs.sugarlabs.org/ticket/3509 - if numpy.__version__ == '1.6.1': - logging.warning('You have installed a version of numpy ' - '(1.6.1) that has a bug and can\'t be ' - 'used. Using string module instead ' - '(slower)') - raise ImportWarning - pix_manip2 = temp_pix.get_pixels_array() - pix_manip = numpy.ones(pix_manip2.shape, dtype=numpy.uint8) \ - * 255 - pix_manip2 = pix_manip - pix_manip2 - temp_pix = gtk.gdk.pixbuf_new_from_array(pix_manip2, - gtk.gdk.COLORSPACE_RGB, 8) - except (ImportError, ImportWarning): - import string - a = temp_pix.get_pixels() - b = len(a) * ['\0'] - for i in range(len(a)): - b[i] = chr(255 - ord(a[i])) - buff = string.join(b, '') - temp_pix = gtk.gdk.pixbuf_new_from_data(buff, - temp_pix.get_colorspace(), - temp_pix.get_has_alpha(), - temp_pix.get_bits_per_sample(), - temp_pix.get_width(), - temp_pix.get_height(), - temp_pix.get_rowstride()) - return temp_pix + def internal_invert(self, old_cursor): + # load a array with the surface data + for array_type in ['H', 'I', 'L']: + pixels = array.array(array_type) + if pixels.itemsize == 4: + break + else: + raise AssertionError() + pixels.fromstring(self.drawing_canvas.get_data()) - self._do_process(widget, proc_invert_color) + # process the pixels in the array + new_array = array.array(pixels.typecode, len(pixels) * [0]) + for i in range(len(pixels)): + new_array[i] = 0xffffffff - pixels[i] | 0xff000000 + + # create a updated drawing_canvas + width = self.drawing_canvas.get_width() + height = self.drawing_canvas.get_height() + self.drawing_canvas = cairo.ImageSurface.create_for_data(new_array, + cairo.FORMAT_ARGB32, width, height) + self.setup(width, height) + + self.queue_draw() + self.enable_undo() + self.get_window().set_cursor(old_cursor) + + old_cursor = self.get_window().get_cursor() + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) + GObject.idle_add(internal_invert, self, old_cursor) def mirror(self, widget, horizontal=True): """Apply mirror horizontal/vertical effect. @@ -1096,95 +1129,57 @@ class Area(gtk.DrawingArea): self._do_process(widget, proc_mirror) def _do_process(self, widget, apply_process): - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) - gobject.idle_add(self._do_process_internal, widget, apply_process) + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) + GObject.idle_add(self._do_process_internal, widget, apply_process) + + def _surface_to_pixbuf(self, surface): + # copy from the surface to the pixbuf + pixbuf_data = StringIO.StringIO() + surface.write_to_png(pixbuf_data) + pxb_loader = GdkPixbuf.PixbufLoader.new_with_type('png') + pxb_loader.write(pixbuf_data.getvalue()) + return pxb_loader.get_pixbuf() + + def _pixbuf_to_context(self, pixbuf, context, x=0, y=0): + # copy from the pixbuf to the drawing context + context.save() + context.translate(x, y) + Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) + context.paint() + context.restore() def _do_process_internal(self, widget, apply_process): - width, height = self.window.get_size() - - if self.selmove: - size = self.pixmap_sel.get_size() - temp_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - size[0], size[1]) - temp_pix.get_from_drawable(self.pixmap_sel, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, size[0], size[1]) + if self.is_selected(): + x, y, _width, _height = self.get_selection_bounds() + surface = self.get_selection() else: - temp_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - width, height) - temp_pix.get_from_drawable(self.pixmap, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, width, height) + x, y = 0, 0 + surface = self.drawing_canvas - temp_pix = apply_process(temp_pix) + temp_pix = self._surface_to_pixbuf(surface) - if self.selmove: - self.pixmap_sel.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, - size[0], size[1], dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, - 0, 0, self.orig_x, self.orig_y, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - self.orig_x, self.orig_y, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - self.orig_x - 1, self.orig_y - 1, size[0] + 2, size[1] + 2) + # process the pixbuf + temp_pix = apply_process(temp_pix) - else: - self.pixmap.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, - width, height, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) + self._pixbuf_to_context(temp_pix, self.drawing_ctx, x, y) + self.create_selection_surface() del temp_pix self.queue_draw() - if not self.selmove: - self.enableUndo(widget) + if not self.is_selected(): + self.enable_undo() self.set_tool_cursor() - def drain_events(self, block=gtk.FALSE): - while gtk.events_pending(): - gtk.mainiteration(block) - - def _pixbuf2Image(self, pb): - """change a pixbuf to RGB image - - @param self -- the Area object (GtkDrawingArea) - @param pb -- the pixbuf object (gtk.gdk.Pixbuf) - - @return RGB Image - - """ - width, height = pb.get_width(), pb.get_height() - return Image.fromstring("RGB", (width, height), pb.get_pixels()) - - def _image2pixbuf(self, im): - """change a RGB image to a pixbuf - - @param self -- the Area object (GtkDrawingArea) - @param im -- a RGB image - - @return pixbuf - - """ - file1 = StringIO.StringIO() - im.save(file1, "ppm") - contents = file1.getvalue() - file1.close() - loader = gtk.gdk.PixbufLoader("pnm") - loader.write(contents, len(contents)) - pixbuf = loader.get_pixbuf() - loader.close() - return pixbuf - def rotate_left(self, widget): """Rotate the image. @param self -- the Area object (GtkDrawingArea) @param widget -- the Area object (GtkDrawingArea) """ - self._rotate(widget, 90) + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) + GObject.idle_add(self._rotate, widget, 90) def rotate_right(self, widget): """Rotate the image. @@ -1192,7 +1187,8 @@ class Area(gtk.DrawingArea): @param self -- the Area object (GtkDrawingArea) @param widget -- the Area object (GtkDrawingArea) """ - self._rotate(widget, 270) + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) + GObject.idle_add(self._rotate, widget, 270) def _rotate(self, widget, angle): """Rotate the image. @@ -1200,71 +1196,36 @@ class Area(gtk.DrawingArea): @param self -- the Area object (GtkDrawingArea) @param widget -- the Area object (GtkDrawingArea) """ - width, height = self.window.get_size() - - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) - self.drain_events() + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) - if self.selmove: - size = self.pixmap_sel.get_size() - temp_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - size[0], size[1]) - temp_pix.get_from_drawable(self.pixmap_sel, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, size[0], size[1]) + if self.is_selected(): + x, y, width, height = self.get_selection_bounds() + surface = self.get_selection() else: - temp_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - width, height) - temp_pix.get_from_drawable(self.pixmap, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, width, height) + x, y = 0, 0 + width, height = self.get_size() + surface = self.drawing_canvas - temp_pix = temp_pix.rotate_simple(angle) - - if self.selmove: - try: - self.d.pixbuf_resize = \ - self.d.pixbuf_resize.rotate_simple(angle) - except: - pass - - try: - del(self.pixmap_sel) - except: - pass + temp_pix = self._surface_to_pixbuf(surface) - self.pixmap_sel = gtk.gdk.Pixmap(widget.window, - size[1], size[0], -1) - self.pixmap_sel.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, - width=-1, height=-1, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) + temp_pix = temp_pix.rotate_simple(angle) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, - 0, 0, self.orig_x, self.orig_y, size[1], size[0]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - self.orig_x, self.orig_y, size[1], size[0]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - self.orig_x - 1, self.orig_y - 1, size[1] + 2, size[0] + 2) + # copy from the pixbuf to the drawing context + if self.is_selected(): + self.set_selection_bounds(x, y, height, width) else: - win = self.window - self.set_size_request(height, width) - self.pixmap = gtk.gdk.Pixmap(win, height, width, -1) - self.pixmap.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, - height, width, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - self.pixmap_temp = gtk.gdk.Pixmap(win, height, width, -1) - self.pixmap_temp.draw_pixbuf(self.gc, temp_pix, 0, 0, 0, 0, - height, width, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - - self.activity.center_area() + # create a new canvas with permuted dimensions + self.drawing_canvas = None + self.setup(height, width) + + self._pixbuf_to_context(temp_pix, self.drawing_ctx, x, y) del temp_pix self.queue_draw() - if not self.selmove: - self.enableUndo(widget) + if not self.is_selected(): + self.enable_undo() self.set_tool_cursor() def can_undo(self): @@ -1284,45 +1245,81 @@ class Area(gtk.DrawingArea): def is_selected(self): """ Return True if there is some thing selected - - @param self -- the Area object (GtkDrawingArea) """ - if self.selmove: - return True - else: - return False + return self.get_selection_bounds() != (0, 0, 0, 0) + + def clear_selection(self): + self.set_selection_bounds(0, 0, 0, 0) + self._selection_horizontal_scale = 1.0 + self._selection_vertical_scale = 1.0 - def _set_selection_bounds(self, x1, y1, x2, y2): + def set_selection_bounds(self, x, y, width, height): """ Set selection bounds - - @param self -- the Area object (GtkDrawingArea) - @param x1,y1,x2,y2 -- the coords of limit points + @param x, y, width, height - the rectangle to define the area """ - self._selection_corners = (x1, y1, x2, y2) + self._selection_bounds = (x, y, width, height) + + def set_selection_start(self, x, y): + self._selection_bounds = (x, y, self._selection_bounds[2], + self._selection_bounds[3]) def get_selection_bounds(self): """ - Get points of selection - - @param self -- the Area object (GtkDrawingArea) - - @return selection_corners - + @return x1, y1, width, height - the rectangle to define the area """ - return self._selection_corners[0], self._selection_corners[1], \ - self._selection_corners[2], self._selection_corners[3] - - def loadImageFromJournal(self, pixbuf): - width, height = pixbuf.get_width(), pixbuf.get_height() - - self.pixmap.draw_pixbuf(self.gc, pixbuf, 0, 0, 0, 0, - width, height, dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - self.enableUndo(self, size=(width, height)) - self.queue_draw() + x, y = self._selection_bounds[0], self._selection_bounds[1] + width, height = self._selection_bounds[2], self._selection_bounds[3] + width = width * self._selection_horizontal_scale + height = height * self._selection_vertical_scale + return (x, y, int(width), int(height)) + + def create_selection_surface(self, clear_background=True): + x, y, width, height = self.get_selection_bounds() + logging.error('create_selection_surface %s', (x, y, width, height)) + self.selection_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + selection_ctx = cairo.Context(self.selection_surface) + selection_ctx.translate(-x, -y) + selection_ctx.set_source_surface(self.drawing_canvas) + selection_ctx.paint() + self.selection_resized_surface = None + if clear_background: + self.pending_clean_selection_background = True + + def resize_selection_surface(self, horizontal_scale, vertical_scale): + x, y = self._selection_bounds[0], self._selection_bounds[1] + new_width = int(self .selection_surface.get_width() * horizontal_scale) + new_height = int(self.selection_surface.get_height() * vertical_scale) + + # create a surface with the selection scaled to the new size + self.selection_resized_surface = cairo.ImageSurface( + cairo.FORMAT_ARGB32, new_width, new_height) + temp_ctx = cairo.Context(self.selection_resized_surface) + temp_ctx.scale(horizontal_scale, vertical_scale) + temp_ctx.set_source_surface(self.selection_surface) + temp_ctx.paint() + + # draw over temp canvas + self.temp_ctx.save() + self.temp_ctx.translate(x, y) + self.temp_ctx.set_source_surface(self.selection_resized_surface) + self.temp_ctx.rectangle(0, 0, new_width, new_height) + self.temp_ctx.paint() + self.temp_ctx.restore() + + self._selection_horizontal_scale = horizontal_scale + self._selection_vertical_scale = vertical_scale + + def get_selection(self): + if self.selection_resized_surface is not None: + return self.selection_resized_surface + if self.selection_surface is not None: + return self.selection_surface + else: + return None - def loadImage(self, name, widget=None): + def load_image(self, name, widget=None): """Load an image. @param self -- Area instance @@ -1330,36 +1327,31 @@ class Area(gtk.DrawingArea): @param widget -- GtkDrawingArea """ - logging.debug('Area.loadImage Loading file %s', name) - - pixbuf = gtk.gdk.pixbuf_new_from_file(name) - size = (int)(pixbuf.get_width()), (int)(pixbuf.get_height()) - #self.getout(True,widget) - self.getout(True) - #self.pixmap_sel = gtk.gdk.Pixmap(widget.window,size[0],size[1],-1) - self.pixmap_sel = gtk.gdk.Pixmap(self.window, size[0], size[1], -1) - self.pixmap_sel.draw_pixbuf(self.gc, pixbuf, 0, 0, 0, 0, - size[0], size[1], dither=gtk.gdk.RGB_DITHER_NORMAL, - x_dither=0, y_dither=0) - - self.sel_get_out = False - self.selmove = True + logging.debug('Area.load_image Loading file %s', name) + + pixbuf = GdkPixbuf.Pixbuf.new_from_file(name) + width, height = (int)(pixbuf.get_width()), (int)(pixbuf.get_height()) + + logging.debug('image size %d x %d', width, height) + + # load in the selection surface + self.selection_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + width, height) + selection_ctx = cairo.Context(self.selection_surface) + self._pixbuf_to_context(pixbuf, selection_ctx) + + # show in the temp context too + self.temp_ctx.save() + self.temp_ctx.translate(0, 0) + self.temp_ctx.set_source_surface(self.selection_surface) + self.temp_ctx.paint() + self.temp_ctx.restore() + + self.set_selection_bounds(0, 0, width, height) self.desenha = True - self.orig_x, self.orig_y = 0, 0 - #width, height = widget.window.get_size() - width, height = self.window.get_size() - - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap_sel, 0, 0, 0, 0, - size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - 0, 0, size[0], size[1]) - self.pixmap_temp.draw_rectangle(self.gc_selection1, False, - -1, -1, size[0] + 2, size[1] + 2) + self._selmove = True self.tool['name'] = 'marquee-rectangular' - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) self.emit('select') self.queue_draw() @@ -1374,20 +1366,13 @@ class Area(gtk.DrawingArea): # If something is selected, the action will be saved # after it is unselected if not self.is_selected(): - self.enableUndo(self) + self.enable_undo() def set_tool(self, tool): ''' Method to configure all tools. - @param - tool: a dictionary with the following keys: - 'name': a string - 'line size': a integer - 'fill color': a gtk.gdk.Color object - 'stroke color': a gtk.gdk.Color object - 'line shape': a string - 'circle' or 'square', for now - 'fill': a Boolean value - 'vertices': a integer + @param - tool: a dictionary with the tool keys ''' # logging.debug('Area.set_tool %s', tool) self.tool = tool @@ -1395,17 +1380,17 @@ class Area(gtk.DrawingArea): if self.tool['line size'] is not None: self.configure_line(self.tool['line size']) - if self.tool['fill color'] is not None: - self.set_fill_color(self.tool['fill color']) - else: - # use black - self.set_fill_color(self.black) +# if self.tool['fill color'] is not None: +# self.set_fill_color(self.tool['fill color']) +# else: +# # use black +# self.set_fill_color(self.black) - if self.tool['stroke color'] is not None: - self.set_stroke_color(self.tool['stroke color']) - else: - # use black - self.set_stroke_color(self.black) +# if self.tool['stroke color'] is not None: +# self.set_stroke_color(self.tool['stroke color']) +# else: +# # use black +# self.set_stroke_color(self.black) except AttributeError: pass @@ -1420,49 +1405,56 @@ class Area(gtk.DrawingArea): 'eraser': 'eraser', 'bucket': 'paint-bucket'} - display = gtk.gdk.display_get_default() + display = Gdk.Display.get_default() if self.tool['name'] in cursors: name = cursors[self.tool['name']] - cursor = gtk.gdk.cursor_new_from_name(display, name) + cursor = Gdk.Cursor.new_from_name(display, name) elif self.tool['name'] == 'marquee-rectangular': - cursor = gtk.gdk.Cursor(gtk.gdk.CROSS) + cursor = Gdk.Cursor.new(Gdk.CursorType.CROSS) else: filename = os.path.join('images', self.tool['name'] + '.png') - pixbuf = gtk.gdk.pixbuf_new_from_file(filename) + pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename) # Decide which is the cursor hot spot offset: if self.tool['name'] == 'stamp': - hotspot_x, hotspot_y = 20, 38 # horizontal + hotspot_x, hotspot_y = 20, 38 # horizontal # center and # bottom elif self.tool['name'] == 'picker': - hotspot_x, hotspot_y = 1, 38 # bottom left + hotspot_x, hotspot_y = 1, 38 # bottom left # corner else: - hotspot_x, hotspot_y = 0, 0 + hotspot_x, hotspot_y = 0, 0 - cursor = gtk.gdk.Cursor(display, pixbuf, hotspot_x, hotspot_y) - except gobject.GError: + cursor = Gdk.Cursor.new_from_pixbuf(display, pixbuf, + hotspot_x, hotspot_y) + except GObject.GError: cursor = None - self.window.set_cursor(cursor) + self.get_window().set_cursor(cursor) - def getout(self, undo=False, widget=None): - logging.debug('Area.getout') + def getout(self, undo=False): + """ + Apply the selected area in the canvas. - try: - self.pixmap_sel - size = self.pixmap_sel.get_size() - self.pixmap.draw_drawable(self.gc, self.pixmap_sel, 0, 0, - self.orig_x, self.orig_y, size[0], size[1]) - self.selmove = False - if undo: - if widget is not None: - self.enableUndo(widget) - else: - self.enableUndo(self) + @param - undo: enable undo + """ - del(self.pixmap_sel) - del(self.pixbuf_resize) + try: + # apply selection over canvas + if self.is_selected(): + x, y, width, height = self.get_selection_bounds() + selection_surface = self.get_selection() + self.drawing_ctx.save() + self.drawing_ctx.translate(x, y) + self.drawing_ctx.set_source_surface(selection_surface) + self.drawing_ctx.rectangle(0, 0, width, height) + self.drawing_ctx.paint() + self.drawing_ctx.restore() + self.desenha = False + + self.clear_selection() + if undo: + self.enable_undo() except NameError, message: logging.debug(message) @@ -1470,50 +1462,38 @@ class Area(gtk.DrawingArea): logging.debug('Unexpected error: %s', message) def key_press(self, widget, event): - if event.keyval == gtk.keysyms.BackSpace: - if self.selmove: - self.selmove = False - try: - del(self.pixmap_sel) - del(self.pixbuf_resize) - except: - pass + if event.keyval == Gdk.KEY_BackSpace: + if self.is_selected(): + # Remove selection + # TODO if self.tool['name'] == 'marquee-rectangular': - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.CROSS)) widget.queue_draw() - self.enableUndo(widget) - elif event.keyval == gtk.keysyms.a and gtk.gdk.CONTROL_MASK: - if self.selmove: + self.enable_undo() + elif event.keyval == Gdk.KEY_a and Gdk.ModifierType.CONTROL_MASK: + if self.is_selected(): self.getout() - width, height = self.window.get_size() + width, height = self.get_size() if self.tool['name'] == 'marquee-rectangular': - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR)) - self.pixmap_sel = gtk.gdk.Pixmap(self.window, width, height, -1) - self.pixmap_sel.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap_temp.draw_drawable(self.gc, self.pixmap, 0, 0, 0, 0, - width, height) - self.pixmap.draw_rectangle(self.get_style().white_gc, True, 0, 0, - width, height) - self.orig_x = 0 - self.orig_y = 0 - self.pixmap_temp.draw_rectangle(self.gc_selection, False, - 0, 0, width - 1, height - 1) - self.selmove = True - self.sel_get_out = False + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorTypeFLEUR)) + self.set_selection_bounds(0, 0, width - 1, height - 1) self.emit('select') widget.queue_draw() - elif event.keyval == gtk.keysyms.d and gtk.gdk.CONTROL_MASK: - if self.selmove: - self.getout(True, widget) + elif event.keyval == Gdk.KEY_d and Gdk.ModifierType.CONTROL_MASK: + if self.is_selected(): + self.getout(True) if self.tool['name'] == 'marquee-rectangular': - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.CROSS)) widget.queue_draw() - elif event.keyval == gtk.keysyms.Return: - self.getout(True, widget) + elif event.keyval == Gdk.KEY_Return: + self.getout(True) if self.tool['name'] == 'marquee-rectangular': - self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSS)) + self.get_window().set_cursor(Gdk.Cursor.new( + Gdk.CursorType.CROSS)) widget.queue_draw() def change_line_size(self, delta): @@ -1524,9 +1504,11 @@ class Area(gtk.DrawingArea): size = 1 self.tool['line size'] = size self.configure_line(size) + # TODO: clip self.queue_draw() if self.tool['name'] == 'stamp': self.resize_stamp(self.stamp_size + delta) + # TODO: clip self.queue_draw() def _keep_selection_ratio(self, coords): diff --git a/Desenho.py b/Desenho.py index d4572c9..e110aa2 100644 --- a/Desenho.py +++ b/Desenho.py @@ -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 diff --git a/OficinaActivity.py b/OficinaActivity.py index 64dace4..29cb2d3 100644 --- a/OficinaActivity.py +++ b/OficinaActivity.py @@ -61,11 +61,11 @@ Walter Bender (walter@laptop.org) """ -import gtk +from gi.repository import Gtk import logging -from sugar.activity import activity -from sugar.graphics import style +from sugar3.activity import activity +from sugar3.graphics import style from Area import Area from toolbox import DrawToolbarBox @@ -85,23 +85,23 @@ class OficinaActivity(activity.Activity): logging.debug('Starting Paint activity (Oficina)') - self.fixed = gtk.Fixed() + self.fixed = Gtk.Fixed() self.fixed.show() - self.fixed.modify_bg(gtk.STATE_NORMAL, + self.fixed.modify_bg(Gtk.StateType.NORMAL, style.COLOR_WHITE.get_gdk_color()) + self.textview = Gtk.TextView() + self.fixed.put(self.textview, 0, 0) + # These attributes are used in other classes, so they should be public self.area = Area(self) self.area.show() self.fixed.put(self.area, 0, 0) - self.textview = gtk.TextView() - self.fixed.put(self.textview, 0, 0) - - sw = gtk.ScrolledWindow() - sw.show() - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.set_canvas(sw) + self._sw = Gtk.ScrolledWindow() + self._sw.show() + self._sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + self.set_canvas(self._sw) toolbar_box = DrawToolbarBox(self) @@ -121,9 +121,9 @@ class OficinaActivity(activity.Activity): self.canvas.add_with_viewport(self.fixed) # to remove the border, we need set the shadowtype # in the viewport child of the scrolledwindow - self.canvas.get_children()[0].set_shadow_type(gtk.SHADOW_NONE) + self.canvas.get_children()[0].set_shadow_type(Gtk.ShadowType.NONE) self.disconnect(self._setup_handle) - self._setup_handle = self.fixed.connect('size_allocate', + self._setup_handle = self._sw.connect('size_allocate', size_allocate_cb) self._setup_handle = self.connect('map', map_cp) @@ -140,27 +140,30 @@ class OficinaActivity(activity.Activity): logging.debug('reading file %s, mimetype: %s, title: %s', file_path, self.metadata['mime_type'], self.metadata['title']) - pixbuf = gtk.gdk.pixbuf_new_from_file(file_path) + self.area.load_from_file(file_path) def size_allocate_cb(widget, allocation): + logging.error('read file size allocate') self.fixed.disconnect(self._setup_handle) - self.area.setup(pixbuf.get_width(), pixbuf.get_height()) + width = self.area.drawing_canvas.get_width() + height = self.area.drawing_canvas.get_height() + self.area.setup(width, height) # The scrolled window is confused with a image of the same size # of the canvas when the toolbars popup and the scrolls # keep visible. - if pixbuf.get_height() > allocation.height or \ - pixbuf.get_width() > allocation.width: - self.canvas.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + if height > allocation.height or width > allocation.width: + self.canvas.set_policy(Gtk.PolicyType.AUTOMATIC, + Gtk.PolicyType.AUTOMATIC) else: - self.canvas.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + self.canvas.set_policy(Gtk.PolicyType.NEVER, + Gtk.PolicyType.AUTOMATIC) - self.area.loadImageFromJournal(pixbuf) self.center_area() self.canvas.add_with_viewport(self.fixed) # to remove the border, we need set the shadowtype # in the viewport child of the scrolledwindow - self.canvas.get_children()[0].set_shadow_type(gtk.SHADOW_NONE) + self.canvas.get_children()[0].set_shadow_type(Gtk.ShadowType.NONE) self.canvas.get_children()[0].set_border_width(0) self.disconnect(self._setup_handle) @@ -186,20 +189,16 @@ class OficinaActivity(activity.Activity): self.area.d.text(self.area, event=None) self.area.getout() - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, - width, height) - pixbuf.get_from_drawable(self.area.pixmap, - gtk.gdk.colormap_get_system(), 0, 0, 0, 0, -1, -1) + self.area.drawing_canvas.write_to_png(file_path) self.metadata['mime_type'] = 'image/png' - pixbuf.save(file_path, 'png', {}) def _get_area_displacement(self): """Return the point to use as top left corner in order to move the drawing area and center it on the canvas. """ - canvas_width = self.canvas.allocation.width - canvas_height = self.canvas.allocation.height + canvas_width = self.canvas.get_allocation().width + canvas_height = self.canvas.get_allocation().height area_width, area_height = self.area.get_size_request() # Avoid 'x' and 'y' to be outside the screen diff --git a/fontcombobox.py b/fontcombobox.py index 58f9140..9de5e34 100644 --- a/fontcombobox.py +++ b/fontcombobox.py @@ -15,21 +15,22 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import gtk +from gi.repository import Gtk +from gi.repository import GObject FONT_BLACKLIST = ['cmex10', 'cmmi10', 'cmr10', 'cmsy10', 'esint10', 'eufm10', 'msam10', 'msbm10', 'rsfs10', 'wasy10'] -class FontComboBox(gtk.ComboBox): +class FontComboBox(Gtk.ComboBox): def __init__(self): - gtk.ComboBox.__init__(self) - font_renderer = gtk.CellRendererText() - self.pack_start(font_renderer) + GObject.GObject.__init__(self) + font_renderer = Gtk.CellRendererText() + self.pack_start(font_renderer, True) self.add_attribute(font_renderer, 'text', 0) self.add_attribute(font_renderer, 'font', 0) - font_model = gtk.ListStore(str) + font_model = Gtk.ListStore(str) context = self.get_pango_context() font_index = 0 @@ -39,15 +40,15 @@ class FontComboBox(gtk.ComboBox): name = family.get_name() if name not in FONT_BLACKLIST: font_model.append([name]) - font_faces = [] - for face in family.list_faces(): - face_name = face.get_face_name() - font_faces.append(face_name) - self.faces[name] = font_faces + # TODO gtk3 +# font_faces = [] +# for face in family.list_faces(): +# face_name = face.get_face_name() +# font_faces.append(face_name) +# self.faces[name] = font_faces - sorter = gtk.TreeModelSort(font_model) - sorter.set_sort_column_id(0, gtk.SORT_ASCENDING) - self.set_model(sorter) + font_model.set_sort_column_id(0, Gtk.SortType.ASCENDING) + self.set_model(font_model) self.show() def set_font_name(self, font_name): diff --git a/setup.py b/setup.py index d46c873..5b17d28 100755 --- a/setup.py +++ b/setup.py @@ -16,6 +16,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from sugar.activity import bundlebuilder +from sugar3.activity import bundlebuilder if __name__ == "__main__": bundlebuilder.start() diff --git a/toolbox.py b/toolbox.py index 8d58306..3d44877 100644 --- a/toolbox.py +++ b/toolbox.py @@ -63,24 +63,26 @@ Walter Bender (walter@laptop.org) from gettext import gettext as _ -import gtk -import pango +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GObject +from gi.repository import Pango import logging -from sugar.activity.activity import EditToolbar -from sugar.graphics.toolcombobox import ToolComboBox -from sugar.graphics.toolbutton import ToolButton -from sugar.graphics.radiotoolbutton import RadioToolButton -from sugar.graphics.toggletoolbutton import ToggleToolButton -from sugar.graphics.objectchooser import ObjectChooser +from sugar3.activity.widgets import EditToolbar +from sugar3.graphics.toolcombobox import ToolComboBox +from sugar3.graphics.toolbutton import ToolButton +from sugar3.graphics.radiotoolbutton import RadioToolButton +from sugar3.graphics.toggletoolbutton import ToggleToolButton +from sugar3.graphics.objectchooser import ObjectChooser from widgets import ButtonStrokeColor -from sugar.graphics.colorbutton import ColorToolButton +from sugar3.graphics.colorbutton import ColorToolButton -from sugar.graphics import style +from sugar3.graphics import style -from sugar.activity.widgets import ActivityToolbarButton -from sugar.graphics.toolbarbox import ToolbarButton, ToolbarBox -from sugar.activity.widgets import StopButton +from sugar3.activity.widgets import ActivityToolbarButton +from sugar3.graphics.toolbarbox import ToolbarButton, ToolbarBox +from sugar3.activity.widgets import StopButton from fontcombobox import FontComboBox @@ -125,7 +127,7 @@ class DrawToolbarBox(ToolbarBox): image_button.props.label = _('Image') self.toolbar.insert(image_button, -1) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_size_request(0, -1) separator.set_expand(True) @@ -142,8 +144,15 @@ class DrawToolbarBox(ToolbarBox): self.brush_button.set_brush_shape(area.tool['line shape']) self.brush_button.set_brush_size(area.tool['line size']) self.brush_button.set_stamp_size(area.tool['stamp size']) - if self._activity.area.tool['stroke color'] is not None: - self.brush_button.set_color(area.tool['stroke color']) + + # init the color + cairo_stroke_color = area.tool['cairo_stroke_color'] + red = cairo_stroke_color[0] * 65535 + green = cairo_stroke_color[1] * 65535 + blue = cairo_stroke_color[2] * 65535 + + stroke_color = Gdk.Color(red, green, blue) + self.brush_button.set_color(stroke_color) ##Make the Edit Toolbar @@ -159,7 +168,7 @@ class DrawEditToolbar(EditToolbar): self.copy.set_tooltip(_('Copy')) self.paste.set_tooltip(_('Paste')) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) @@ -191,7 +200,7 @@ class DrawEditToolbar(EditToolbar): self._activity.area.copy() def _paste_cb(self, widget, data=None): - self._activity.area.past(self._activity.area) + self._activity.area.paste(self._activity.area) def _on_signal_undo_cb(self, widget, data=None): self._verify_sensitive_buttons() @@ -248,11 +257,11 @@ class ToolsToolbarBuilder(): #self._stroke_color.set_icon_name('icon-stroke') self._stroke_color.set_title(_('Brush properties')) self._stroke_color.connect('notify::color', self._color_button_cb) - item = gtk.ToolItem() + item = Gtk.ToolItem() item.add(self._stroke_color) toolbar.insert(item, -1) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) toolbar.insert(separator, -1) @@ -290,7 +299,7 @@ class ToolsToolbarBuilder(): activity.tool_group, _('Select Area')) toolbar.insert(self._tool_marquee_rectangular, -1) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) toolbar.insert(separator, -1) @@ -315,7 +324,7 @@ class ToolsToolbarBuilder(): """ Set tool to the Area object. Configures tool's color and size. - @param self -- gtk.Toolbar + @param self -- Gtk.Toolbar @param widget -- The connected widget, if any; necessary in case this method is used in a connect() @param tool_name --The name of the selected tool @@ -332,9 +341,8 @@ class ToolsToolbarBuilder(): def _color_button_cb(self, widget, pspec): logging.error('ToolsToolbarBuilder._color_button_cb') - new_color = widget.alloc_color(widget.get_color()) + new_color = widget.get_color() self._activity.area.set_stroke_color(new_color) - self.properties['stroke color'] = new_color def _on_signal_undo_cb(self, widget, data=None): self._verify_sensitive_buttons() @@ -369,61 +377,55 @@ class ButtonFillColor(ColorToolButton): color = self.get_color() self.set_fill_color(color) - def alloc_color(self, color): - colormap = self._activity.area.get_colormap() - return colormap.alloc_color(color.red, color.green, color.blue) - def set_fill_color(self, color): - new_color = self.alloc_color(color) - self._activity.area.set_fill_color(new_color) - self.properties['fill color'] = new_color + self._activity.area.set_fill_color(color) def create_palette(self): self._palette = self.get_child().create_palette() color_palette_hbox = self._palette._picker_hbox - content_box = gtk.VBox() + content_box = Gtk.VBox() # Fill option - fill_checkbutton = gtk.CheckButton(_('Fill')) + fill_checkbutton = Gtk.CheckButton(_('Fill')) fill_checkbutton.set_active(self.properties['fill']) fill_checkbutton.connect('toggled', self._on_fill_checkbutton_toggled) - content_box.pack_start(fill_checkbutton) + content_box.pack_start(fill_checkbutton, True, True, 0) - keep_aspect_checkbutton = gtk.CheckButton(_('Keep aspect')) + keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect')) logging.error('Create palette : tool name %s', self.properties['name']) ratio = self._activity.area.keep_shape_ratio keep_aspect_checkbutton.set_active(ratio) keep_aspect_checkbutton.connect('toggled', self._on_keep_aspect_checkbutton_toggled) - content_box.pack_start(keep_aspect_checkbutton) + content_box.pack_start(keep_aspect_checkbutton, True, True, 0) # We want choose the number of sides to our polygon - spin = gtk.SpinButton() + spin = Gtk.SpinButton() # This is where we set restrictions for sides in Regular Polygon: # Initial value, minimum value, maximum value, step - adj = gtk.Adjustment(self.properties['vertices'], 3.0, 50.0, 1.0) + adj = Gtk.Adjustment(self.properties['vertices'], 3.0, 50.0, 1.0) spin.set_adjustment(adj) spin.set_numeric(True) - label = gtk.Label(_('Sides: ')) + label = Gtk.Label(label=_('Sides: ')) #For stars - #label = gtk.Label(_('Points: ')) + #label = Gtk.Label(label=_('Points: ')) - hbox = gtk.HBox() + hbox = Gtk.HBox() hbox.show_all() - hbox.pack_start(label) - hbox.pack_start(spin) + hbox.pack_start(label, True, True, 0) + hbox.pack_start(spin, True, True, 0) - content_box.pack_start(hbox) + content_box.pack_start(hbox, True, True, 0) hbox.show_all() spin.connect('value-changed', self._on_vertices_value_changed) - color_palette_hbox.pack_start(gtk.VSeparator(), + color_palette_hbox.pack_start(Gtk.VSeparator(), True, True, padding=style.DEFAULT_SPACING) - color_palette_hbox.pack_start(content_box) + color_palette_hbox.pack_start(content_box, True, True, 0) color_palette_hbox.show_all() return self._palette @@ -439,7 +441,7 @@ class ButtonFillColor(ColorToolButton): ##Make the Shapes Toolbar -class ShapesToolbar(gtk.Toolbar): +class ShapesToolbar(Gtk.Toolbar): _SHAPE_ARROW_NAME = 'arrow' _SHAPE_CURVE_NAME = 'curve' @@ -456,7 +458,7 @@ class ShapesToolbar(gtk.Toolbar): ##The Constructor def __init__(self, activity): - gtk.Toolbar.__init__(self) + GObject.GObject.__init__(self) self._activity = activity self.properties = self._activity.area.tool @@ -464,11 +466,11 @@ class ShapesToolbar(gtk.Toolbar): self._fill_color = ButtonFillColor(activity) self._fill_color.set_icon_name('icon-fill') self._fill_color.set_title(_('Shapes properties')) - item = gtk.ToolItem() + item = Gtk.ToolItem() item.add(self._fill_color) self.insert(item, -1) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) @@ -544,20 +546,16 @@ class ShapesToolbar(gtk.Toolbar): def set_tool(self, widget, tool, tool_name): tool['name'] = tool_name - - fill_color = self._fill_color.get_color() - tool['fill color'] = self._fill_color.alloc_color(fill_color) - self._activity.area.set_tool(tool) ##Make the Text Toolbar -class TextToolbar(gtk.Toolbar): +class TextToolbar(Gtk.Toolbar): _ACTION_TEXT_NAME = 'text' def __init__(self, activity): - gtk.Toolbar.__init__(self) + GObject.GObject.__init__(self) self._activity = activity self.properties = self._activity.area.tool @@ -566,7 +564,7 @@ class TextToolbar(gtk.Toolbar): self.insert(self._text, -1) self._text.connect('clicked', self.set_tool, self._ACTION_TEXT_NAME) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) @@ -580,33 +578,20 @@ class TextToolbar(gtk.Toolbar): self._italic.show() self._italic.connect('clicked', self.__italic_bt_cb) - """ - self._text_color = ButtonFillColor(activity) - item = gtk.ToolItem() - item.add(self._text_color) - self.insert(item, -1) - """ - - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) - """ - self._font_size_icon = Icon(icon_name="format-text-size", - icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR) - tool_item = gtk.ToolItem() - tool_item.add(self._font_size_icon) - self.insert(tool_item, -1) - """ + fd = activity.area.get_font_description() - self._font_size_combo = gtk.combo_box_new_text() + self._font_size_combo = Gtk.ComboBoxText() self._font_sizes = ['8', '10', '12', '14', '16', '20', '22', '24', '26', '28', '36', '48', '72'] self._font_size_changed_id = self._font_size_combo.connect('changed', self.__font_size_changed_cb) for i, s in enumerate(self._font_sizes): self._font_size_combo.append_text(s) - if int(s) == activity.area.font_description.get_size(): + if int(s) == (fd.get_size() /Pango.SCALE): self._font_size_combo.set_active(i) tool_item = ToolComboBox(self._font_size_combo) @@ -615,39 +600,39 @@ class TextToolbar(gtk.Toolbar): self._font_combo = FontComboBox() self._fonts_changed_id = self._font_combo.connect('changed', self.__font_changed_cb) - font_name = activity.area.font_description.get_family() + font_name = fd.get_family() self._font_combo.set_font_name(font_name) tool_item = ToolComboBox(self._font_combo) self.insert(tool_item, -1) self.show_all() def __bold_bt_cb(self, button): - activity = self._activity + fd = self._activity.area.get_font_description() if button.get_active(): - activity.area.font_description.set_weight(pango.WEIGHT_BOLD) + fd.set_weight(Pango.Weight.BOLD) else: - activity.area.font_description.set_weight(pango.WEIGHT_NORMAL) - activity.textview.modify_font(activity.area.font_description) + fd.set_weight(Pango.Weight.NORMAL) + self._activity.area.set_font_description(fd) def __italic_bt_cb(self, button): - activity = self._activity + fd = self._activity.area.get_font_description() if button.get_active(): - activity.area.font_description.set_style(pango.STYLE_ITALIC) + fd.set_style(Pango.Style.ITALIC) else: - activity.area.font_description.set_style(pango.STYLE_NORMAL) - activity.textview.modify_font(activity.area.font_description) + fd.set_style(Pango.Style.NORMAL) + self._activity.area.set_font_description(fd) def __font_size_changed_cb(self, combo): - activity = self._activity + fd = self._activity.area.get_font_description() value = self.get_active_text(combo) - activity.area.font_description.set_size(int(value) * pango.SCALE) - activity.textview.modify_font(activity.area.font_description) + fd.set_size(int(value) * Pango.SCALE) + self._activity.area.set_font_description(fd) def __font_changed_cb(self, combo): - activity = self._activity + fd = self._activity.area.get_font_description() font_name = combo.get_font_name() - activity.area.font_description.set_family(font_name) - activity.textview.modify_font(activity.area.font_description) + fd.set_family(font_name) + self._activity.area.set_font_description(fd) def get_active_text(self, combobox): model = combobox.get_model() @@ -662,12 +647,12 @@ class TextToolbar(gtk.Toolbar): ##Make the Images Toolbar -class ImageToolbar(gtk.Toolbar): +class ImageToolbar(Gtk.Toolbar): _EFFECT_RAINBOW_NAME = 'rainbow' def __init__(self, activity): - gtk.Toolbar.__init__(self) + GObject.GObject.__init__(self) self._activity = activity self.properties = self._activity.area.tool @@ -675,7 +660,7 @@ class ImageToolbar(gtk.Toolbar): self.insert(self._object_insert, -1) self._object_insert.set_tooltip(_('Insert Image')) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) @@ -709,7 +694,7 @@ class ImageToolbar(gtk.Toolbar): self.height_spinButton = self._create_spinButton(self._object_height, 'object-height', activity) - item = gtk.ToolItem() + item = Gtk.ToolItem() item.add(self.height_spinButton) self.insert(item, -1) @@ -720,11 +705,11 @@ class ImageToolbar(gtk.Toolbar): self.width_spinButton = self._create_spinButton(self._object_width, 'object-width', activity) - item = gtk.ToolItem() + item = Gtk.ToolItem() item.add(self.width_spinButton) self.insert(item, -1) - separator = gtk.SeparatorToolItem() + separator = Gtk.SeparatorToolItem() separator.set_draw(True) self.insert(separator, -1) @@ -774,7 +759,7 @@ class ImageToolbar(gtk.Toolbar): def resize(self, spinButton, tool, activity): if activity.area.tool['name'] == 'marquee-rectangular' and \ - activity.area.selmove: + activity.area.is_selected(): if tool == "object-height": self.height_percent = spinButton.get_value_as_int() / 100. activity.area.d.resizeSelection(activity.area, @@ -787,7 +772,7 @@ class ImageToolbar(gtk.Toolbar): def _create_spinButton(self, widget, tool, activity): """Set palette for a tool - width or height - @param self -- gtk.Toolbar + @param self -- Gtk.Toolbar @param widget - the widget which Palette will be set, a ToolButton object @param tool @@ -795,13 +780,13 @@ class ImageToolbar(gtk.Toolbar): """ logging.debug('setting a spinButton for %s', tool) - spin = gtk.SpinButton() + spin = Gtk.SpinButton() spin.show() # This is where we set restrictions for Resizing: # Initial value, minimum value, maximum value, step initial = float(100) - adj = gtk.Adjustment(initial, 10.0, 500.0, 1.0) + adj = Gtk.Adjustment(initial, 10.0, 500.0, 1.0) spin.set_adjustment(adj) spin.set_numeric(True) @@ -814,20 +799,20 @@ class ImageToolbar(gtk.Toolbar): def insertImage(self, widget, activity): try: chooser = ObjectChooser(_('Choose image'), - self._activity, gtk.DIALOG_MODAL | - gtk.DIALOG_DESTROY_WITH_PARENT, what_filter='Image') + self._activity, Gtk.DialogFlags.MODAL | + Gtk.DialogFlags.DESTROY_WITH_PARENT, what_filter='Image') except: chooser = ObjectChooser(_('Choose image'), - self._activity, gtk.DIALOG_MODAL | - gtk.DIALOG_DESTROY_WITH_PARENT) + self._activity, Gtk.DialogFlags.MODAL | + Gtk.DialogFlags.DESTROY_WITH_PARENT) try: result = chooser.run() - if result == gtk.RESPONSE_ACCEPT: + if result == Gtk.ResponseType.ACCEPT: logging.debug('ObjectChooser: %r', chooser.get_selected_object()) jobject = chooser.get_selected_object() if jobject and jobject.file_path: - self._activity.area.loadImage(jobject.file_path) + self._activity.area.load_image(jobject.file_path) finally: chooser.destroy() del chooser diff --git a/widgets.py b/widgets.py index c1833fa..46c8db2 100644 --- a/widgets.py +++ b/widgets.py @@ -2,12 +2,19 @@ from gettext import gettext as _ -import gtk -import gobject +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GObject +import math -from sugar.graphics import style -from sugar.graphics.palette import ToolInvoker -from sugar.graphics.colorbutton import _ColorButton +from sugar3.graphics import style +from sugar3.graphics.palette import ToolInvoker +from sugar3.graphics.colorbutton import _ColorButton +from sugar3.graphics.radiotoolbutton import RadioToolButton + +# this strings are here only to enable pootle to translate them +# and do not broke the old versions +_old_strings = [_('Size: '), _('Opacity: '), _('Circle'), _('Square')] class BrushButton(_ColorButton): @@ -22,7 +29,7 @@ class BrushButton(_ColorButton): def __init__(self, **kwargs): self._title = _('Choose brush properties') - self._color = gtk.gdk.Color(0, 0, 0) + self._color = Gdk.Color(0, 0, 0) self._has_palette = True self._has_invoker = True self._palette = None @@ -30,35 +37,30 @@ class BrushButton(_ColorButton): self._brush_size = 2 self._stamp_size = 20 self._brush_shape = 'circle' + self._alpha = 1.0 self._resized_stamp = None - self._preview = gtk.DrawingArea() + self._preview = Gtk.DrawingArea() self._preview.set_size_request(style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE) + self._ctx = None - gobject.GObject.__init__(self, **kwargs) - self._preview.set_events(gtk.gdk.BUTTON_PRESS_MASK) + GObject.GObject.__init__(self, **kwargs) + self._preview.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._preview.connect('button_press_event', self.__mouse_down_cb) - self._preview.connect("expose_event", self.expose) + self._preview.connect("draw", self.draw) self.set_image(self._preview) - self._gc = None - if self._has_palette and self._has_invoker: self._invoker = WidgetInvoker(self) # FIXME: This is a hack. self._invoker.has_rectangle_gap = lambda: False self._invoker.palette = self._palette - def _setup(self): - if self.get_window() is not None: - self._preview.fill_color = '' - self._preview.show() - self.pixmap = gtk.gdk.Pixmap(self.get_window(), - style.STANDARD_ICON_SIZE, - style.STANDARD_ICON_SIZE, -1) - self._gc = self.get_window().new_gc() - self.show_all() +# def _setup(self): +# if self.get_window() is not None: +# self._preview.show() +# self.show_all() def get_brush_size(self): return self._brush_size @@ -67,7 +69,7 @@ class BrushButton(_ColorButton): self._brush_size = brush_size self._preview.queue_draw() - brush_size = gobject.property(type=int, getter=get_brush_size, + brush_size = GObject.property(type=int, getter=get_brush_size, setter=set_brush_size) def get_brush_shape(self): @@ -77,11 +79,13 @@ class BrushButton(_ColorButton): self._brush_shape = brush_shape self._preview.queue_draw() - brush_shape = gobject.property(type=str, getter=get_brush_shape, + brush_shape = GObject.property(type=str, getter=get_brush_shape, setter=set_brush_shape) def set_color(self, color): - # receive gtk.gdk.Color + """ + @ param color Gdk.Color + """ self._color = color self._preview.queue_draw() @@ -92,7 +96,7 @@ class BrushButton(_ColorButton): self._stamp_size = stamp_size self._preview.queue_draw() - stamp_size = gobject.property(type=int, getter=get_stamp_size, + stamp_size = GObject.property(type=int, getter=get_stamp_size, setter=set_stamp_size) def set_resized_stamp(self, resized_stamp): @@ -105,40 +109,47 @@ class BrushButton(_ColorButton): def is_stamping(self): return self._resized_stamp != None - def expose(self, widget, event): - if self._gc is None: - self._setup() + def set_alpha(self, alpha): + self._alpha = alpha + self._preview.queue_draw() + + def draw(self, widget, ctx): + #if self._ctx is None: + # self._setup() if self.get_window() is not None: center = style.STANDARD_ICON_SIZE / 2 - self.pixmap.draw_rectangle(self._preview.get_style().white_gc, - True, 0, 0, style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE) - self._gc.set_foreground(self._color) + ctx.rectangle(0, 0, style.STANDARD_ICON_SIZE, + style.STANDARD_ICON_SIZE) + ctx.set_source_rgb(1.0, 1.0, 1.0) + ctx.fill() if self.is_stamping(): width = self._resized_stamp.get_width() height = self._resized_stamp.get_height() dx = center - width / 2 dy = center - height / 2 - self.pixmap.draw_pixbuf(self._gc, self._resized_stamp, - 0, 0, dx, dy, width, height) + + ctx.rectangle(dx, dy, width, height) + Gdk.cairo_set_source_pixbuf(ctx, self._resized_stamp, 0, 0) + ctx.paint() else: + red = float(self._color.red) / 65535.0 + green = float(self._color.green) / 65535.0 + blue = float(self._color.blue) / 65535.0 + ctx.set_source_rgba(red, green, blue, self._alpha) if self._brush_shape == 'circle': - self.pixmap.draw_arc(self._gc, True, - center - self._brush_size / 2, - center - self._brush_size / 2, - self._brush_size, self._brush_size, 0, 360 * 64) + ctx.arc(center, center, self._brush_size / 2, 0., + 2 * math.pi) + ctx.fill() elif self._brush_shape == 'square': - self.pixmap.draw_rectangle(self._gc, True, - center - self._brush_size / 2, - center - self._brush_size / 2, - self._brush_size, self._brush_size) - - area = event.area - widget.window.draw_drawable(self._gc, self.pixmap, - area[0], area[1], area[0], area[1], area[2], area[3]) + ctx.rectangle(center - self._brush_size / 2, + center - self._brush_size / 2, self._brush_size, + self._brush_size) + ctx.fill() + return False def do_style_set(self, previous_style): @@ -166,11 +177,11 @@ class BrushButton(_ColorButton): return True -class ButtonStrokeColor(gtk.ToolItem): +class ButtonStrokeColor(Gtk.ToolItem): """Class to manage the Stroke Color of a Button""" __gtype_name__ = 'BrushColorToolButton' - __gsignals__ = {'color-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + __gsignals__ = {'color-set': (GObject.SignalFlags.RUN_FIRST, None, tuple())} def __init__(self, activity, **kwargs): @@ -181,9 +192,9 @@ class ButtonStrokeColor(gtk.ToolItem): self._palette_invoker = ToolInvoker() self._palette = None - gobject.GObject.__init__(self, **kwargs) + GObject.GObject.__init__(self, **kwargs) - # The gtk.ToolButton has already added a normal button. + # The Gtk.ToolButton has already added a normal button. # Replace it with a ColorButton self.color_button = BrushButton(has_invoker=False) self.add(self.color_button) @@ -192,7 +203,7 @@ class ButtonStrokeColor(gtk.ToolItem): self.color_button.set_stamp_size(20) # The following is so that the behaviour on the toolbar is correct. - self.color_button.set_relief(gtk.RELIEF_NONE) + self.color_button.set_relief(Gtk.ReliefStyle.NONE) self._palette_invoker.attach_tool(self) @@ -211,79 +222,92 @@ class ButtonStrokeColor(gtk.ToolItem): return True def __notify_change(self, widget, pspec): - new_color = self.alloc_color(self.get_color()) - self.color_button.set_color(new_color) + #new_color = self.alloc_color(self.get_color()) + #self.color_button.set_color(new_color) + self.color_button.set_color(self.get_color()) self.notify(pspec.name) def _color_button_cb(self, widget, pspec): color = self.get_color() self.set_stroke_color(color) - def alloc_color(self, color): - colormap = self._activity.area.get_colormap() - return colormap.alloc_color(color.red, color.green, color.blue) +# def alloc_color(self, color): +# colormap = self._activity.area.get_colormap() +# return colormap.alloc_color(color.red, color.green, color.blue) def create_palette(self): self._palette = self.get_child().create_palette() color_palette_hbox = self._palette._picker_hbox - content_box = gtk.VBox() - self.size_spinbutton = gtk.SpinButton() - # This is where we set restrictions for size: - # Initial value, minimum value, maximum value, step - adj = gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0) - self.size_spinbutton.set_adjustment(adj) - self.size_spinbutton.set_numeric(True) + content_box = Gtk.VBox() - label = gtk.Label(_('Size: ')) - hbox_size = gtk.HBox() - self.vbox_brush_options = gtk.VBox() - content_box.pack_start(hbox_size) - content_box.pack_start(self.vbox_brush_options) + self._brush_table = Gtk.Table(2, 2) + self._brush_table.set_col_spacing(0, style.DEFAULT_PADDING) - hbox_size.pack_start(label) - hbox_size.pack_start(self.size_spinbutton) - self.size_spinbutton.connect('value-changed', self._on_value_changed) + # This is where we set restrictions for size: + # Initial value, minimum value, maximum value, step + adj = Gtk.Adjustment(self.properties['line size'], 1.0, 100.0, 1.0) + self.size_scale = Gtk.HScale() + self.size_scale.set_adjustment(adj) + self.size_scale.set_value_pos(Gtk.PositionType.RIGHT) + self.size_scale.set_digits(0) + self.size_scale.set_size_request(style.zoom(150), -1) + label = Gtk.Label(label=_('Size')) + row = 0 + self._brush_table.attach(label, 0, 1, row, row + 1) + self._brush_table.attach(self.size_scale, 1, 2, row, row + 1) + + content_box.pack_start(self._brush_table, True, True, 0) + + self.size_scale.connect('value-changed', self._on_value_changed) + + # Control alpha + alpha = self.properties['alpha'] * 100 + adj_alpha = Gtk.Adjustment(alpha, 10.0, 100.0, 1.0) + self.alpha_scale = Gtk.HScale() + self.alpha_scale.set_adjustment(adj_alpha) + self.alpha_scale.set_value_pos(Gtk.PositionType.RIGHT) + self.alpha_scale.set_digits(0) + self.alpha_scale.set_size_request(style.zoom(150), -1) + self.alpha_label = Gtk.Label(label=_('Opacity')) + row = row + 1 + self._brush_table.attach(self.alpha_label, 0, 1, row, row + 1) + self._brush_table.attach(self.alpha_scale, 1, 2, row, row + 1) + + self.alpha_scale.connect('value-changed', self._on_alpha_changed) # User is able to choose Shapes for 'Brush' and 'Eraser' - item1 = gtk.RadioButton(None, _('Circle')) + self.vbox_brush_options = Gtk.VBox() + shape_box = Gtk.HBox() + content_box.pack_start(self.vbox_brush_options, True, True, 0) + item1 = RadioToolButton() + item1.set_icon_name('tool-shape-ellipse') item1.set_active(True) - image1 = gtk.Image() - pixbuf1 = gtk.gdk.pixbuf_new_from_file_at_size( - './icons/tool-shape-ellipse.svg', - style.SMALL_ICON_SIZE, - style.SMALL_ICON_SIZE) - image1.set_from_pixbuf(pixbuf1) - item1.set_image(image1) - - item2 = gtk.RadioButton(item1, _('Square')) - image2 = gtk.Image() - pixbuf2 = gtk.gdk.pixbuf_new_from_file_at_size( - './icons/tool-shape-rectangle.svg', - style.SMALL_ICON_SIZE, - style.SMALL_ICON_SIZE) - image2.set_from_pixbuf(pixbuf2) - item2.set_image(image2) + + item2 = RadioToolButton() + item2.set_icon_name('tool-shape-rectangle') + item2.props.group = item1 item1.connect('toggled', self._on_toggled, self.properties, 'circle') item2.connect('toggled', self._on_toggled, self.properties, 'square') - label = gtk.Label(_('Shape')) + shape_box.pack_start(Gtk.Label(_('Shape')), True, True, 0) + shape_box.pack_start(item1, True, True, 0) + shape_box.pack_start(item2, True, True, 0) - self.vbox_brush_options.pack_start(label) - self.vbox_brush_options.pack_start(item1) - self.vbox_brush_options.pack_start(item2) + self.vbox_brush_options.pack_start(shape_box, True, True, 0) - keep_aspect_checkbutton = gtk.CheckButton(_('Keep aspect')) + keep_aspect_checkbutton = Gtk.CheckButton(_('Keep aspect')) ratio = self._activity.area.keep_aspect_ratio keep_aspect_checkbutton.set_active(ratio) keep_aspect_checkbutton.connect('toggled', self._keep_aspect_checkbutton_toggled) - self.vbox_brush_options.pack_start(keep_aspect_checkbutton) + self.vbox_brush_options.pack_start(keep_aspect_checkbutton, True, True, + 0) - color_palette_hbox.pack_start(gtk.VSeparator(), + color_palette_hbox.pack_start(Gtk.VSeparator(), True, True, padding=style.DEFAULT_SPACING) - color_palette_hbox.pack_start(content_box) + color_palette_hbox.pack_start(content_box, True, True, 0) color_palette_hbox.show_all() self._update_palette() return self._palette @@ -296,9 +320,11 @@ class ButtonStrokeColor(gtk.ToolItem): if self.color_button.is_stamping(): # Hide palette color widgets: for ch in palette_children[:4]: - ch.hide_all() + ch.hide() # Hide brush options: - self.vbox_brush_options.hide_all() + self.vbox_brush_options.hide() + self.alpha_label.hide() + self.alpha_scale.hide() # Change title: self.set_title(_('Stamp properties')) else: @@ -307,6 +333,8 @@ class ButtonStrokeColor(gtk.ToolItem): ch.show_all() # Show brush options: self.vbox_brush_options.show_all() + self.alpha_label.show() + self.alpha_scale.show() # Change title: self.set_title(_('Brush properties')) @@ -315,13 +343,18 @@ class ButtonStrokeColor(gtk.ToolItem): def update_stamping(self): if self.color_button.is_stamping(): - self.size_spinbutton.set_value(self.color_button.stamp_size) + self.size_scale.set_value(self.color_button.stamp_size) else: - self.size_spinbutton.set_value(self.color_button.brush_size) + self.size_scale.set_value(self.color_button.brush_size) self._update_palette() - def _on_value_changed(self, spinbutton): - size = spinbutton.get_value_as_int() + def _on_alpha_changed(self, scale): + alpha = scale.get_value() / 100.0 + self._activity.area.set_alpha(alpha) + self.color_button.set_alpha(alpha) + + def _on_value_changed(self, scale): + size = int(scale.get_value()) if self.color_button.is_stamping(): self.properties['stamp size'] = size resized_stamp = self._activity.area.resize_stamp(size) @@ -345,7 +378,7 @@ class ButtonStrokeColor(gtk.ToolItem): self._palette_invoker.detach() self._palette_invoker = palette_invoker - palette_invoker = gobject.property( + palette_invoker = GObject.property( type=object, setter=set_palette_invoker, getter=get_palette_invoker) def set_color(self, color): @@ -354,7 +387,7 @@ class ButtonStrokeColor(gtk.ToolItem): def get_color(self): return self.get_child().props.color - color = gobject.property(type=object, getter=get_color, setter=set_color) + color = GObject.property(type=object, getter=get_color, setter=set_color) def set_title(self, title): self.get_child().props.title = title @@ -362,7 +395,7 @@ class ButtonStrokeColor(gtk.ToolItem): def get_title(self): return self.get_child().props.title - title = gobject.property(type=str, getter=get_title, setter=set_title) + title = GObject.property(type=str, getter=get_title, setter=set_title) def do_expose_event(self, event): child = self.get_child() @@ -370,11 +403,11 @@ class ButtonStrokeColor(gtk.ToolItem): if self._palette and self._palette.is_up(): invoker = self._palette.props.invoker invoker.draw_rectangle(event, self._palette) - elif child.state == gtk.STATE_PRELIGHT: - child.style.paint_box(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_NONE, event.area, + elif child.state == Gtk.StateType.PRELIGHT: + child.style.paint_box(event.window, Gtk.StateType.PRELIGHT, + Gtk.ShadowType.NONE, event.area, child, 'toolbutton-prelight', allocation.x, allocation.y, allocation.width, allocation.height) - gtk.ToolButton.do_expose_event(self, event) + Gtk.ToolButton.do_expose_event(self, event) -- cgit v0.9.1