import cairo
from gettext import gettext as _
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
from gi.repository import GdkPixbuf
from sugar3.graphics import style
from sugar3.graphics.toolbutton import ToolButton
HANDLE_SIZE = 18
MIN_IMAGE_SIZE = 50
class BaseWindow(Gtk.Window):
def __init__(self, width=-1, height=-1):
GObject.GObject.__init__(self)
self.set_border_width(style.LINE_WIDTH)
self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
self.set_decorated(False)
self.set_resizable(False)
if width == -1:
width = Gdk.Screen.width() - style.GRID_CELL_SIZE * 2
if height == -1:
height = Gdk.Screen.height() - style.GRID_CELL_SIZE * 2
self.set_size_request(width, height)
class BasicToolbar(Gtk.Toolbar):
def __init__(self, icon_name, title=''):
GObject.GObject.__init__(self)
icon = ToolButton(icon_name)
self.insert(icon, -1)
label = Gtk.Label()
label.set_markup('%s' % title)
label.set_alignment(0, 0.5)
tool_item = Gtk.ToolItem()
tool_item.set_expand(True)
tool_item.add(label)
tool_item.show_all()
self.insert(tool_item, -1)
self.separator = Gtk.SeparatorToolItem()
self.separator.props.draw = False
self.separator.set_expand(True)
self.insert(self.separator, -1)
self.stop = ToolButton(icon_name='dialog-cancel')
self.stop.set_tooltip(_('Cancel'))
self.insert(self.stop, -1)
self.stop.show()
self.confirm = ToolButton(icon_name='dialog-ok')
self.confirm.set_tooltip(_('Done'))
self.insert(self.confirm, -1)
self.confirm.show()
class ReorderView(BaseWindow):
def __init__(self, activity):
BaseWindow.__init__(self)
self.toolbar = BasicToolbar('thumbs-view')
self.toolbar.stop.connect('clicked', self.__stop_clicked_cb)
self.toolbar.confirm.connect('clicked', self.__ok_clicked_cb)
self.scrollwin = ReorderObjects(activity)
title = _('Drag the images to reorder')
label = Gtk.Label('')
label.set_markup('%s' % title)
self.vbox = Gtk.VBox()
self.vbox.pack_start(self.toolbar, False, False, 0)
self.vbox.pack_start(label, False, False, style.DEFAULT_SPACING)
self.vbox.pack_start(self.scrollwin, True, True, 0)
self.add(self.vbox)
self.scrollwin.show()
self.modify_bg(Gtk.StateType.NORMAL,
style.COLOR_WHITE.get_gdk_color())
def __stop_clicked_cb(self, button):
self.destroy()
def __ok_clicked_cb(self, button):
self.scrollwin.reorder_comicboxs()
self.scrollwin.display_comicboxs()
self.destroy()
class ReorderObjects(Gtk.ScrolledWindow):
def __init__(self, activity):
GObject.GObject.__init__(self)
self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
self.activity = activity
self.comicboxes = self.activity.page.boxs
self.liststore = Gtk.ListStore(GdkPixbuf.Pixbuf)
self.iconview = Gtk.IconView.new()
self.iconview.set_property('item-width', 200)
self.iconview.set_model(self.liststore)
self.iconview.set_pixbuf_column(0)
self.iconview.set_reorderable(True)
for comicbox in self.comicboxes[1:]:
self.liststore.append([comicbox.get_thumbnail()])
self.add(self.iconview)
def on_item_activated(self):
model = self.iconview.get_model()
pixbuf = model[self.iconview.get_selected_items()[0]][0]
for comicbox in self.comicboxes[1:]:
if pixbuf is comicbox.thumbnail:
self.editor = ImageEditorView(comicbox)
self.editor.show_all()
break
def reorder_comicboxs(self):
sorted_list = []
for row in self.liststore:
for comicbox in self.comicboxes[1:]:
if row[0] is comicbox.thumbnail:
self.activity.page.table.remove(comicbox)
sorted_list.append(comicbox)
break
sorted_list.insert(0, self.comicboxes[0])
self.comicboxes = sorted_list
self.activity.page.boxs = self.comicboxes
def display_comicboxs(self):
for i in range(0, len(self.comicboxes[1:])):
reng = int(i / 2)
column = i - (reng * 2)
self.activity.page.table.attach(
self.comicboxes[i+1], column, column + 1, reng, reng + 1)
class ImageElement:
def __init__(self, pixbuf, box, x, y, w, h):
self.box = box
self.pixbuf = pixbuf
self.pixbuf_original = self.pixbuf.scale_simple(
self.pixbuf.get_width(), self.pixbuf.get_height(),
GdkPixbuf.InterpType.BILINEAR)
self.x = x
self.y = y
self.width = w
self.height = h
self.calculate_boundaries()
self.calculate_points()
self.margin_x = 0
self.margin_y = 0
self.box_width = 0
self.box_height = 0
def reset(self):
self.x = 0
self.y = 0
self.width = self.box_width
self.height = self.box_height
self.calculate_boundaries()
self.calculate_points()
def calculate_boundaries(self):
self.boundaries = {}
self.boundaries['min_x'] = self.x
self.boundaries['max_x'] = self.x + self.width
self.boundaries['min_y'] = self.y
self.boundaries['max_y'] = self.y + self.height
def calculate_points(self):
self.points = {}
self.points["upper_left"] = [self.x, self.y]
self.points["upper_right"] = [self.x + self.width - HANDLE_SIZE,
self.y]
self.points["lower_left"] = [self.x,
self.y + self.height - HANDLE_SIZE]
self.points["lower_right"] = [self.x + self.width - HANDLE_SIZE,
self.y + self.height - HANDLE_SIZE]
def is_selected(self, x, y):
# substract the margin values
x = x - self.margin_x
y = y - self.margin_y
if (x >= self.boundaries['min_x'] and
x <= self.boundaries['max_x']) and \
(y >= self.boundaries['min_y'] and
y <= self.boundaries['max_y']):
return True
else:
return False
def is_resize(self, x, y):
if self.is_in_point(x, y):
return True
else:
return False
def is_in_point(self, x, y, point=None):
if point is not None:
# substract the margin values
x = x - self.margin_x
y = y - self.margin_y
if (x >= point[0] and x <= (point[0] + HANDLE_SIZE)) \
and (y >= point[1] and y <= (point[1] + HANDLE_SIZE)):
return True
else:
return False
else:
if self.is_in_point(x, y, self.points["upper_left"]) or \
self.is_in_point(x, y, self.points["upper_right"]) or \
self.is_in_point(x, y, self.points["lower_left"]) or \
self.is_in_point(x, y, self.points["lower_right"]):
return True
else:
return False
def draw(self, ctx):
self.image = ctx.get_target().create_similar(
cairo.CONTENT_COLOR_ALPHA, self.box.width,
self.box.height)
pixb_scaled = self.pixbuf_original.scale_simple(
self.width, self.height, GdkPixbuf.InterpType.BILINEAR)
ct = cairo.Context(self.image)
Gdk.cairo_set_source_pixbuf(ct, pixb_scaled, self.x, self.y)
ct.paint()
self.pixbuf = pixb_scaled
ctx.save()
ctx.translate(self.margin_x, self.margin_y)
ctx.rectangle(0, 0, self.box.width, self.box.height)
ctx.clip()
ctx.set_source_surface(self.image, 0, 0)
ctx.paint()
ctx.restore()
# draw the box border
ctx.save()
ctx.rectangle(self.margin_x, self.margin_y, self.box_width,
self.box_height)
ctx.set_source_rgb(0, 0, 0)
ctx.stroke()
ctx.restore()
# draw the image border
ctx.save()
ctx.translate(self.margin_x, self.margin_y)
ctx.set_line_width(2)
ctx.set_source_rgb(1, 1, 1)
ctx.rectangle(self.x, self.y, self.width, self.height)
ctx.stroke_preserve()
ctx.set_source_rgb(0, 0, 0)
ctx.set_dash([2])
ctx.stroke()
ctx.restore()
# draw hadles
self._draw_handle(ctx, self.x, self.y)
self._draw_handle(ctx, self.x + self.width - HANDLE_SIZE, self.y)
self._draw_handle(ctx, self.x, self.y + self.height - HANDLE_SIZE)
self._draw_handle(ctx, self.x + self.width - HANDLE_SIZE,
self.y + self.height - HANDLE_SIZE)
def _draw_handle(self, ctx, x, y):
ctx.save()
ctx.translate(self.margin_x, self.margin_y)
ctx.set_line_width(2)
ctx.set_source_rgb(1, 1, 1)
ctx.rectangle(x, y, HANDLE_SIZE, HANDLE_SIZE)
ctx.stroke_preserve()
ctx.set_source_rgb(0, 0, 0)
ctx.set_dash([2])
ctx.stroke()
ctx.restore()
def move(self, x_movement, y_movement, allocation):
self.x = self.x + x_movement
self.y = self.y + y_movement
if self.x + self.width > allocation.width:
self.x -= (self.x + self.width) - (allocation.width)
if self.y + self.height > allocation.height:
self.y -= (self.y + self.height) - (allocation.height)
self.calculate_boundaries()
self.calculate_points()
def resize(self, x_movement, y_movement, allocation, start_x, start_y):
if self.is_in_point(start_x, start_y, self.points["lower_left"]):
self.x += x_movement
self.width -= x_movement
self.height += y_movement
elif self.is_in_point(start_x, start_y, self.points["upper_right"]):
self.y += y_movement
self.height -= y_movement
self.width += x_movement
elif self.is_in_point(start_x, start_y, self.points["upper_left"]):
self.y += y_movement
self.x += x_movement
self.width -= x_movement
self.height -= y_movement
else:
self.height += y_movement
self.width += x_movement
if self.width < MIN_IMAGE_SIZE:
self.width = MIN_IMAGE_SIZE
if self.height < MIN_IMAGE_SIZE:
self.height = MIN_IMAGE_SIZE
if self.x + self.width > allocation.width:
self.width -= (self.x + self.width) - (allocation.width)
if self.y + self.height > allocation.height:
self.height -= (self.y + self.height) - (allocation.height)
self.calculate_boundaries()
self.calculate_points()
class CanvasEditor(Gtk.EventBox):
def __init__(self, comicbox, width, height, window):
Gtk.EventBox.__init__(self)
self.width = width
self.height = height
self.is_resize = False
self.is_move = False
self.parentw = window
self.modify_bg(Gtk.StateType.NORMAL,
style.COLOR_WHITE.get_gdk_color())
self._drawingarea = Gtk.DrawingArea()
self.add(self._drawingarea)
self._drawingarea.add_events(
Gdk.EventMask.POINTER_MOTION_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK |
Gdk.EventMask.BUTTON_MOTION_MASK)
self.image = ImageElement(
comicbox.pixbuf, self,
comicbox.img_x, comicbox.img_y,
comicbox.img_w, comicbox.img_h)
self._drawingarea.connect("draw", self.draw_cb)
self.connect("button_press_event", self.pressing)
self.connect("motion_notify_event", self.mouse_move)
self.connect("motion_notify_event", self.moving)
self.connect("button_release_event", self.releassing)
self.redraw()
def size_allocate(widget, allocation):
self._drawingarea.set_size_request(allocation.width,
allocation.height)
self.image.margin_x = (allocation.width - self.width) / 2
self.image.margin_y = (allocation.height - self.height) / 2
self.image.box_width = self.width
self.image.box_height = self.height
self.connect('size_allocate', size_allocate)
self.set_size_request(self.width, self.height)
self._drawingarea.set_size_request(self.width, self.height)
self.show_all()
def redraw(self):
self._drawingarea.queue_draw()
def draw_cb(self, widget, context):
self.image.draw(context)
def pressing(self, widget, event):
if self.image.is_selected(event.x, event.y):
if self.image.is_in_point(event.x, event.y):
self.is_resize = True
self.is_move = False
else:
self.is_resize = False
self.is_move = True
self.start_x = event.x
self.start_y = event.y
def mouse_move(self, widget, event):
cursor = None
if self.image.is_in_point(event.x, event.y,
self.image.points["upper_left"]) or \
self.image.is_in_point(event.x, event.y,
self.image.points["lower_right"]):
cursor = Gdk.Cursor(Gdk.CursorType.BOTTOM_RIGHT_CORNER)
elif self.image.is_in_point(event.x, event.y,
self.image.points["upper_right"]) or \
self.image.is_in_point(event.x, event.y,
self.image.points["lower_left"]):
cursor = Gdk.Cursor(Gdk.CursorType.BOTTOM_LEFT_CORNER)
elif self.image.is_selected(event.x, event.y):
cursor = Gdk.Cursor(Gdk.CursorType.FLEUR)
self.get_window().set_cursor(cursor)
def moving(self, widget, event):
if self.is_move:
x_movement = event.x - self.start_x
y_movement = event.y - self.start_y
self.image.move(x_movement, y_movement, self.get_allocation())
self.start_x = event.x
self.start_y = event.y
self.redraw()
elif self.is_resize:
x_movement = event.x - self.start_x
y_movement = event.y - self.start_y
self.image.resize(x_movement, y_movement, self.get_allocation(),
self.start_x, self.start_y)
self.start_x = event.x
self.start_y = event.y
self.redraw()
def releassing(self, widget, event):
self.is_resize = False
self.is_move = False
self.start_x = -1
self.start_y = -1
def reset(self):
self.image.reset()
self.redraw()
class ImageEditorView(BaseWindow):
def __init__(self, comicbox):
BaseWindow.__init__(self)
self.toolbar = BasicToolbar('contract-coordinates')
self.toolbar.stop.connect('clicked', self.__stop_clicked_cb)
self.toolbar.confirm.connect('clicked', self.__ok_clicked_cb)
reset_size = ToolButton(icon_name='box-size')
reset_size.set_tooltip(_('Reset to box size'))
self.toolbar.insert(reset_size, 3)
reset_size.show()
reset_size.connect('clicked', self.__reset_size_cb)
self.comicbox = comicbox
self.canvas = CanvasEditor(
self.comicbox, self.comicbox.width,
self.comicbox.height, self)
label = Gtk.Label('')
title = _('Drag to move or resize using the marked corners')
label.set_markup('%s' % title)
self.vbox = Gtk.VBox()
self.vbox.pack_start(self.toolbar, False, False, 0)
self.vbox.pack_start(label, False, False, style.DEFAULT_SPACING)
self.vbox.pack_start(self.canvas, True, True, 0)
self.add(self.vbox)
self.modify_bg(Gtk.StateType.NORMAL,
style.COLOR_WHITE.get_gdk_color())
def __reset_size_cb(self, button):
self.canvas.reset()
def __stop_clicked_cb(self, button):
self.destroy()
def __ok_clicked_cb(self, button):
self.comicbox.img_x = self.canvas.image.x
self.comicbox.img_y = self.canvas.image.y
self.comicbox.img_w = self.canvas.image.width
self.comicbox.img_h = self.canvas.image.height
self.comicbox.redraw()
self.destroy()