Logo Search packages:      
Sourcecode: whyteboard version File versions  Download package

canvas.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (c) 2009, 2010 by Steven Sproat
#
# GNU General Public Licence (GPL)
#
# Whyteboard is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
# Whyteboard is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
# You should have received a copy of the GNU General Public License along with
# Whyteboard; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA  02111-1307  USA


"""
This module contains the Canvas class, a window that can be drawn upon. Each
Canvas panel gets added to a tab in the GUI, and each Canvas maintains
a list of undo/redo actions for itself; thus each Canvas tab on the GUI has
its own undo/redo.

The canvas to be drawn on is managed by a buffer bitmap, and the rest of the
area is flooded with a grey, to indicate it is the background. This background
can be grabbed with the mouse to resize the canvas' size. If the canvas is
larger than the client size, then scrollbars are displayed, and a slight
"border" is shown around the canvas - this can be grabbed to resize.
"""

import os
import copy

import wx
#import wx.lib.wxcairo as wxcairo

from whyteboard.lib import DragScroller, pub

from whyteboard.misc import get_image_path
from whyteboard.tools import (Highlighter, Image, Line, Media, Note, OverlayShape, Polygon,
                   Select, Text, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT,
                   BOTTOM_RIGHT, CENTER_TOP, CENTER_RIGHT, CENTER_BOTTOM,
                   CENTER_LEFT, HANDLE_ROTATE, EDGE_TOP, EDGE_RIGHT, EDGE_LEFT,
                   EDGE_BOTTOM)

EDGE = 15    # distance from canvas edge before shape will scroll canvas
TO_MOVE = 5  # pixels shape will cause canvas to scroll

# area user clicked on canvas to resize
RIGHT = 1
DIAGONAL = 2
BOTTOM = 3

DRAG_LEFT = 1
DRAG_RIGHT = 2
DRAG_UP = 3
DRAG_DOWN = 4

_ = wx.GetTranslation

#----------------------------------------------------------------------

00066 class Canvas(wx.ScrolledWindow):
    """
    The drawing frame of the application. References to self.shape.drawing are
    for the Polygon tool, mainly avoiding isinstance() checks
    """
    CANVAS_BORDER = 15  # border pixels in size (overridable by user)

00073     def __init__(self, tab, gui, area):
        """
        Initalise the window, class variables and bind mouse/paint events
        """
        wx.ScrolledWindow.__init__(self, tab, style=wx.NO_FULL_REPAINT_ON_RESIZE | wx.CLIP_CHILDREN)
        self.SetVirtualSizeHints(2, 2)
        self.SetScrollRate(1, 1)
        self.SetBackgroundColour('Grey')
        self.SetDropTarget(CanvasDropTarget())

        if os.name == "nt":
            self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)  # no flicking on Win!
        else:
            self.ClearBackground()

        self.area = area
        self.scroller = DragScroller(self)
        self.overlay = wx.Overlay()
        self.buffer = wx.EmptyBitmap(*self.area)

        self.gui = gui
        self.scale = (1.0, 1.0)
        self.shapes = []  # list of shapes for re-drawing/saving
        self.shape = None  # currently selected shape *to draw with*
        self.medias = []  # list of Media panels
        self.selected = None  # selected shape *with Select tool*
        self.text = None  # current Text object for redraw all
        self.copy = None  # BitmapSelect instance
        self.resizing = False
        self.cursor_control = False  # toggle resize canvas cursor on/off
        self.resize_direction = None
        self.undo_list = []
        self.redo_list = []
        self.drawing = False
        self.prev_drag = (0, 0)
        self.SetScrollRate(3, 3)

        img = wx.Image(get_image_path(u"cursors", u"rotate"))
        self.rotate_cursor = wx.CursorFromImage(img)
        self.gui.change_tool(canvas=self)
        self.redraw_all()

        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.EVT_LEFT_DOWN, self.left_down)
        self.Bind(wx.EVT_LEFT_UP, self.left_up)
        self.Bind(wx.EVT_RIGHT_UP, self.right_up)
        self.Bind(wx.EVT_LEFT_DCLICK, self.left_double)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.middle_down)
        self.Bind(wx.EVT_MIDDLE_UP, self.middle_up)
        self.Bind(wx.EVT_MOTION, self.motion)
        self.Bind(wx.EVT_PAINT, self.on_paint)

        pub.subscribe(self.set_border, 'canvas.set_border')



00129     def left_down(self, event):
        """Starts drawing"""
        x, y = self.convert_coords(event)
        if os.name == "nt" and not isinstance(self.shape, Media) and not self.shape.drawing:
            self.CaptureMouse()
        if not self.shape.drawing and self.check_resize_direction(x, y):  # don't draw outside canvas
            self.resizing = True
            return

        if not isinstance(self.shape, Select) and self.selected:
            self.deselect_shape()

        self.shape.left_down(x, y)
        #  Crashes without the Text check
        if not isinstance(self.shape, (Text, Media)):
            self.drawing = True


00147     def motion(self, event):
        """
        Checks if the canvas can be updated, changes the cursor to show it can
        Updates the shape if the user is drawing. Indicate shape may be changed
        when using Select tool by changing the cursor
        """
        x, y = self.convert_coords(event)
        if self.resizing:
            self.resize((x, y), self.resize_direction)
            return
        else:
            if not self.check_mouse_for_resize(x, y):
                return

        self.gui.SetStatusText(u" %s, %s" % (x, y))

        if self.drawing or self.shape.drawing:
            self.shape.motion(x, y)
            if not self.shape.drawing:  # polygon
                self.draw_shape(self.shape)
        elif isinstance(self.shape, Select):  # change cursor to indicate action
            self.select_tool_cursor(x, y)


00171     def left_up(self, event):
        """
        Called when the left mouse button is released.
        """
        if os.name == "nt" and not self.shape.drawing:
            if self.HasCapture():
                self.ReleaseMouse()

        if self.resizing:
            self.resizing = False
            self.redraw_all(True)  # update thumb for new canvas size
            self.Layout()
            if self.copy:
                self.draw_shape(self.copy)  # draw back the GCDC
            return
        if self.drawing or isinstance(self.shape, Text):
            before = len(self.shapes)
            self.shape.left_up(*self.convert_coords(event))
            if not isinstance(self.shape, Media):
                if len(self.shapes) - before:
                    pub.sendMessage('canvas.change_tool')
                    pub.sendMessage('thumbs.update_current')
            self.drawing = False


00196     def right_up(self, event):
        """Called when the right mouse button is released - used for zoom"""
        self.shape.right_up(*self.convert_coords(event))


00201     def left_double(self, event):
        """Double click for the Select tool - edit text"""
        self.shape.double_click(*self.convert_coords(event))


    def set_border(self, border_size):
        self.CANVAS_BORDER = border_size
        self.resize(self.area)


    def select_tool_cursor(self, x, y):
        if self.selected:
            if self.select_tool_cursor_change(self.selected, x, y):
                return

        for shape in reversed(self.shapes):
            if self.select_tool_cursor_change(shape, x, y):
                break
        else:
            self.change_cursor()


    def select_tool_cursor_change(self, shape, x, y):
        res = shape.handle_hit_test(x, y)
        ret = True
        if res and isinstance(shape, (Line, Polygon)):
            if wx.GetKeyState(wx.WXK_SHIFT):
                self.set_cursor(wx.CURSOR_SIZENWSE)
            elif wx.GetKeyState(wx.WXK_CONTROL):
                self.SetCursor(self.rotate_cursor)
            else:
                self.set_cursor(wx.CURSOR_SIZING)

        elif res == HANDLE_ROTATE:
            self.SetCursor(self.rotate_cursor)
        elif res in [TOP_LEFT, BOTTOM_RIGHT]:
            self.set_cursor(wx.CURSOR_SIZENWSE)
        elif res in [TOP_RIGHT, BOTTOM_LEFT]:
            self.set_cursor(wx.CURSOR_SIZENESW)
        elif res in [CENTER_TOP, CENTER_BOTTOM]:
            self.set_cursor(wx.CURSOR_SIZENS)
        elif res in [CENTER_LEFT, CENTER_RIGHT]:
            self.set_cursor(wx.CURSOR_SIZEWE)
        elif shape.hit_test(x, y):
            self.set_cursor(wx.CURSOR_HAND)
        else:
            ret = False
        return ret

    def set_cursor(self, cursor):
        self.SetCursor(wx.StockCursor(cursor))


00254     def check_mouse_for_resize(self, x, y):
        """
        Sees if the user's mouse is outside of the canvas, and updates the
        cursor if it's in a different resizable area than it previously was
        Returns the cursor to its normal state if it's moved back in
        """
        direction = self.check_resize_direction(x, y)
        if not self.drawing and direction and not self.shape.drawing:
            if not self.resize_direction:
                self.resize_direction = direction
            if not self.cursor_control or direction != self.resize_direction:
                self.resize_direction = direction
                self.cursor_control = True
                self.set_resize_cursor(direction) # change cursor
            return False
        else:
            if self.cursor_control:
                self.change_cursor()
                self.cursor_control = False
                return False
        return True


00277     def check_resize_direction(self, x, y):
        """Which direction the canvas can be resized in, if any"""
        if x > self.area[0] and y > self.area[1]:
            return DIAGONAL
        elif x > self.area[0]:
            return RIGHT
        elif y > self.area[1]:
            return BOTTOM
        return False


    def set_resize_cursor(self, direction):
        cursors = {DIAGONAL: wx.CURSOR_SIZENWSE, RIGHT: wx.CURSOR_SIZEWE,
                   BOTTOM: wx.CURSOR_SIZENS}
        self.SetCursor(wx.StockCursor(cursors.get(direction)))



00295     def resize(self, size, direction=None):
        """ Performs the canvas resizing. Size = (w, h) tuple """
        if size[0] < 1 or size[1] < 1:
            return

        if direction == RIGHT:
            size = (size[0], self.area[1])
            self.Scroll(self.GetVirtualSizeTuple()[0], -1)
        elif direction == BOTTOM:
            size = (self.area[0], size[1])
            self.Scroll(-1, size[1])
        elif direction is not None:
            self.Scroll(*size)

        self.buffer = wx.EmptyBitmap(*size)
        self.area = size
        size = (size[0] + self.CANVAS_BORDER, size[1] + self.CANVAS_BORDER)
        self.SetVirtualSize(size)
        self.redraw_all(resizing=True)


00316     def redraw_dirty(self, dc):
        """ Figure out what part of the window to refresh. """
        x1, y1, x2, y2 = dc.GetBoundingBox()
        rect = wx.Rect()
        rect.SetTopLeft(self.CalcScrolledPosition(x1, y1))
        rect.SetBottomRight(self.CalcScrolledPosition(x2, y2))
        self.RefreshRect(rect.Inflate(2, 2))


00325     def redraw_all(self, update_thumb=False, dc=None, resizing=False):
        """
        Redraws all shapes that have been drawn. self.text is used to show text
        characters as they're being typed, as new Text/Note objects have not
        been added to self.shapes at this point.
        dc is used as the DC for printing.
        """
        if not dc:
            dc = wx.BufferedDC(None, self.buffer)
            dc.Clear()

        for s in self.shapes:
            if not resizing:
                s.draw(dc, True)
            else:
                if not isinstance(s, Highlighter):
                    s.draw(dc, True)

        if self.text:
            self.text.draw(dc, True)
        if self.copy:
            self.copy.draw(dc, True)
        self.Refresh()
        if update_thumb:
            pub.sendMessage('thumbs.update_current')


    def change_tool(self):
        if self.HasCapture():
            self.ReleaseMouse()
        self.change_cursor()


    def change_cursor(self):
        if isinstance(self.shape.cursor, wx.Cursor):
            self.SetCursor(self.shape.cursor)
        else:
            self.SetCursor(wx.StockCursor(self.shape.cursor))


00365     def add_shape(self, shape):
        """ Adds a shape to the "to-draw" list. """
        self.add_undo()
        self.shapes.append(shape)

        if self.selected:
            self.deselect_shape()
            self.redraw_all()
        if self.text:
            self.text = None
        if self.copy:
            self.copy = None
            self.redraw_all()
        pub.sendMessage('update_shape_viewer')


00381     def add_undo(self):
        """Creates an undo point. NEED to change this for memory improvements"""
        l = [copy.copy(x) for x in self.shapes]
        self.undo_list.append(l)

        if self.redo_list:
            self.redo_list = []
        pub.sendMessage('gui.mark_unsaved')


00391     def undo(self):
        """ Undoes an action, and adds it to the redo list. """
        self.perform(self.undo_list, self.redo_list)

00395     def redo(self):
        """ Redoes an action, and adds it to the undo list. """
        self.perform(self.redo_list, self.undo_list)


00400     def perform(self, list_a, list_b):
        """ perform undo/redo. list_a: to remove from / list b: append to """
        if not list_a:
            return
        list_b.append(list(self.shapes))
        self.shapes = list_a.pop()
        self.deselect_shape()
        self.redraw_all(True)

        pub.sendMessage('note.delete_sheet_items')  # lazy way of doing things...
        for x in self.shapes:
            if isinstance(x, Note):
                pub.sendMessage('note.add', note=x)
        pub.sendMessage('gui.mark_unsaved')
        pub.sendMessage('update_shape_viewer')


00417     def restore_sheet(self, shapes, undo_list, redo_list, size, medias, viewport):
        """
        Restores itself (e.g. from undoing closing a sheet.)
        """
        self.shapes = shapes
        self.undo_list = undo_list
        self.redo_list = redo_list
        self.medias = medias

        for media in medias:
            media.canvas = self
            media.make_panel()

        for shape in shapes:
            shape.canvas = self
            if isinstance(shape, Note):
                pub.sendMessage('note.add', note=shape)

        wx.Yield()
        self.resize(size)
        self.Scroll(viewport[0], viewport[1])
        pub.sendMessage('thumbs.update_current')


00441     def toggle_transparent(self):
        """Toggles the selected item's transparency"""
        if not self.selected or isinstance(self.selected, (Media, Image, Text)):
            return
        self.add_undo()
        val = wx.TRANSPARENT

        if self.selected.background == wx.TRANSPARENT:
            val = self.gui.get_background_colour()

        self.selected.background = val
        self.selected.make_pen()
        self.redraw_all(True)


00456     def delete_selected(self):
        """Deletes the selected shape"""
        if not self.selected:
            return

        if isinstance(self.selected, Media):
            self.selected.remove_panel()
        else:
            if isinstance(self.selected, Note):
                self.gui.notes.tree.Delete(self.selected.tree_id)
            self.add_undo()
            self.shapes.remove(self.selected)
        pub.sendMessage('update_shape_viewer')
        self.selected = None
        self.redraw_all(True)


00473     def clear(self, keep_images=False):
        """ Removes all shapes from the 'to-draw' list. """
        if not self.medias and not self.shapes:
            return

        for m in self.medias:
            m.remove_panel()
        self.medias = []
        images = []

        if self.shapes:
            self.add_undo()
            if keep_images:
                for x in self.shapes:
                    if isinstance(x, Image):
                        images.append(x)

        self.shapes = images
        pub.sendMessage('update_shape_viewer')
        self.redraw_all(update_thumb=True)


00495     def drag_direction(self, x, y):
        """
        Work out the direction a shape's being moved in so that we don't scroll
        the canvas as a shape is being dragged away from a canvas edge.
        """
        direction = []

        if self.prev_drag[0] > x:
            direction.append(DRAG_LEFT)
        elif self.prev_drag[0] < x:
            direction.append(DRAG_RIGHT)
        if self.prev_drag[1] < y:
            direction.append(DRAG_DOWN)
        elif self.prev_drag[1] > y:
            direction.append(DRAG_UP)

        self.prev_drag = (x, y)
        return direction


00515     def shape_near_canvas_edge(self, x, y, direction, moving=False):
        """
        Check that the x/y coords is within X pixels from the edge of the canvas
        and scroll the canvas accordingly. If the shape is being moved, we need
        to check specific edges of the shape (e.g. left/right side of rectangle)
        """
        size = self.GetClientSizeTuple()
        if not self.area > size:  # canvas is too small to need to scroll
            return

        # no point continuing if we're not near the canvas' border
        start = self.GetViewStart()
        end_x = self.GetClientSize()[0] + start[0]
        end_y = self.GetClientSize()[1] + start[1]

        rect = wx.Rect(start[0] + EDGE, start[1] + EDGE, end_x - EDGE, end_y - EDGE)

        if rect.ContainsXY(x, y):
            return

        scroll = (-1, -1)

        if moving:
            if self.selected.edges[EDGE_RIGHT] > start[0] + size[0] - EDGE and DRAG_RIGHT in direction:
                scroll = (start[0] + TO_MOVE, -1)
            if self.selected.edges[EDGE_BOTTOM] > start[1] + size[1] - EDGE and DRAG_DOWN in direction:
                scroll = (-1, start[1] + TO_MOVE)

            if self.selected.edges[EDGE_LEFT] < start[0] + EDGE and DRAG_LEFT in direction:
                scroll = (start[0] - TO_MOVE, -1)
            if self.selected.edges[EDGE_TOP] > start[1] + EDGE and DRAG_RIGHT in direction:
                scroll = (-1, start[1] - TO_MOVE)
        else:
            if x > start[0] + size[0] - EDGE:
                scroll = (start[0] + TO_MOVE, -1)
            if y > start[1] + size[1] - EDGE:
                scroll = (-1, start[1] + TO_MOVE)

            if x < start[0] + EDGE:  # x left
                scroll = start[0] - TO_MOVE, -1
            if y < start[1] + EDGE:  # y top
                scroll = (-1, start[1] - TO_MOVE)

        self.Scroll(*scroll)


00561     def check_move(self, pos):
        """
        Checks whether a selected shape can be moved in the shape order
        """
        if not self.selected or isinstance(self.selected, Media):
            return False
        if not self.selected in self.shapes:
            return False
        if pos in [u"top", u"up"]:
            length = len(self.shapes) - 1
            if length < 0:
                length = 0
            if self.shapes.index(self.selected) != length:
                return True
        elif pos in [u"down", u"bottom"]:
            if self.shapes.index(self.selected) != 0:
                return True
        return False


00581     def do_move(self, shape):
        """ Performs the move, by popping the item to be moved """
        self.add_undo()
        x = self.shapes.index(shape)
        return (x, self.shapes.pop(x))


    def move_shape(fn):
        def wrapper(self, shape, x=None, item=None):
            x, item = self.do_move(shape)
            fn(self, shape, x, item)
            pub.sendMessage('update_shape_viewer')
            self.redraw_all(True)
        return wrapper

    @move_shape
    def move_up(self, shape, x=None, item=None):
        self.shapes.insert(x + 1, item)

    @move_shape
    def move_down(self, shape, x=None, item=None):
        self.shapes.insert(x - 1, item)

    @move_shape
    def move_top(self, shape, x=None, item=None):
        self.shapes.append(item)

    @move_shape
    def move_bottom(self, shape, x=None, item=None):
        self.shapes.insert(0, item)


00613     def convert_coords(self, event):
        """ Translate mouse x/y coords to virtual scroll ones. """
        return self.CalcUnscrolledPosition(event.GetX(), event.GetY())

00617     def middle_down(self, event):
        """ Begin dragging the scroller to move around the panel """
        self.set_cursor(wx.CURSOR_SIZENESW)
        self.scroller.Start(event.GetPosition())

00622     def middle_up(self, event):
        """ Stop dragging the scroller. """
        self.scroller.Stop()
        self.change_cursor()  # bugfix with custom cursor

00627     def on_paint(self, event=None):
        """
        Called when the window is exposed. Paint the buffer, and then create a
        region, remove the buffer rectangle then clear it with grey.
        """
        wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)

        if os.name == "nt":
            relbuf = self.CalcScrolledPosition(self.area)
            cli = self.GetClientSize()

            if cli.x > relbuf[0] or cli.y > relbuf[1]:
                bkgregion = wx.Region(0, 0, cli.x, cli.y)
                bkgregion.SubtractRect(wx.Rect(0, 0, relbuf[0], relbuf[1]))
                dc = wx.ClientDC(self)
                dc.SetClippingRegionAsRegion(bkgregion)
                dc.SetBrush(wx.GREY_BRUSH)
                dc.Clear()
                dc.DestroyClippingRegion()


    def paste_image(self, bitmap, x, y, ignore=False):
        shape = Image(self, bitmap, None)
        shape.left_down(x, y)
        wx.Yield()
        if ignore:
            self.resize((bitmap.GetWidth(), bitmap.GetHeight()))
        self.redraw_all(True)


    def paste_text(self, text, x, y, colour):
        self.shape = Text(self, colour, 1)
        self.shape.text = text
        self.shape.left_down(x, y)
        self.shape.left_up(x, y)
        self.text = None
        pub.sendMessage('canvas.change_tool')
        self.redraw_all(True)


00667     def get_selection_bitmap(self):
        """
        If a rectangle selection is made, copy the selection as a bitmap.
        NOTE: The bitmap selection can be larger than the actual canvas bitmap,
        so we must only selection the region of the selection that is visible
        on the canvas
        """
        self.copy.update_rect()  # ensure w, h are correct
        bmp = self.copy
        area = self.area

        if bmp.x + bmp.width > area[0]:
            bmp.rect.SetWidth(area[0] - bmp.x)

        if bmp.y + bmp.height > area[1]:
            bmp.rect.SetHeight(area[1] - bmp.y)

        self.copy = None
        self.redraw_all()

        return self.buffer.GetSubBitmap(bmp.rect)


00690     def deselect_shape(self):
        """Deselects the currently selected shape"""
        for x in self.shapes:
            if isinstance(x, OverlayShape):
                x.selected = False
        if self.selected:
            self.selected = None
            self.redraw_all()


00700     def select_shape(self, shape):
        """Selects the selected shape"""
        self.overlay = wx.Overlay()
        if self.selected:
            self.deselect_shape()

        self.selected = shape
        shape.selected = True
        x = self.shapes.index(shape)
        self.shapes.pop(x)
        self.redraw_all()  # hide 'original'
        self.shapes.insert(x, shape)
        shape.draw(self.get_dc(), False)  # draw 'new'


    def change_colour(self):
        x = self.colour_data(self.selected.colour)
        if x:
            self.selected.colour = x
            self.redraw_all(True)

    def change_background(self,):
        x = self.colour_data(self.selected.background)
        if x:
            self.selected.background = x
            self.redraw_all(True)

00727     def colour_data(self, colour):
        """Shows a colour info box"""
        data = wx.ColourData()
        data.SetChooseFull(True)
        data.SetColour(colour)

        dlg = wx.ColourDialog(self.gui, data)
        if dlg.ShowModal() == wx.ID_OK:
            x = dlg.GetColourData()
            self.add_undo()
            return x.GetColour().Get()
        return False


    def get_mouse_position(self):
        x, y = self.ScreenToClient(wx.GetMousePosition())
        if x < 0 or y < 0 or x > self.area[0] or y > self.area[1]:
            x, y = 0, 0

        return self.CalcUnscrolledPosition(x, y)


    def get_dc(self):
        cdc = wx.ClientDC(self)
        self.PrepareDC(cdc)
        return wx.BufferedDC(cdc, self.buffer, wx.BUFFER_VIRTUAL_AREA)


00755     def draw_shape(self, shape, replay=False):
        """ Redraws a single shape efficiently"""
        dc = self.get_dc()
        #dc.SetUserScale(self.scale[0], self.scale[1])
        if replay:
            shape.draw(dc, replay)
        else:
            shape.draw(dc)
        self.redraw_dirty(dc)

    def can_swap_transparency(self):
        return self.selected and not isinstance(self.selected, (Media, Image, Text))

    def can_swap_colours(self):
        return self.can_swap_transparency() and not self.is_transparent()

    def is_transparent(self):
        return self.selected.background == wx.TRANSPARENT

    def swap_colours(self):
        self.selected.colour, self.selected.background = self.selected.background, self.selected.colour
        self.redraw_all()

    def show_text_edit_dialog(self, text_shape):
        return self.gui.show_text_edit_dialog(text_shape)

    def capture_mouse(self):
        if not self.HasCapture():
            self.CaptureMouse()

    def release_mouse(self):
        if self.HasCapture():
            self.ReleaseMouse()

00789     def resize_if_large_image(self, size):
        """ Check whether the canvas should be resized (for large images) """
        if size[0] > self.area[0] or size[1] > self.area[1]:
            self.resize(size)


00795     def on_size(self, event):
        """ Updates the scrollbars when the window is resized. """
        size = self.GetClientSize()

        if size[0] < self.area[0] or size[1] < self.area[1]:
            self.SetVirtualSize((self.area[0] + 20, self.area[1] + 20))


#----------------------------------------------------------------------

00805 class CanvasDropTarget(wx.PyDropTarget):
    """Implements drop target functionality to receive files and text"""
    def __init__(self):
        wx.PyDropTarget.__init__(self)
        self.do = wx.DataObjectComposite()
        self.filedo = wx.FileDataObject()
        self.textdo = wx.TextDataObject()
        self.bmpdo = wx.BitmapDataObject()
        self.do.Add(self.filedo)
        self.do.Add(self.bmpdo)
        self.do.Add(self.textdo)
        self.SetDataObject(self.do)


00819     def OnData(self, x, y, d):
        """
        Handles drag/dropping files/text or a bitmap
        """
        if self.GetData():
            df = self.do.GetReceivedFormat().GetType()

            if df in [wx.DF_UNICODETEXT, wx.DF_TEXT]:
                pub.sendMessage('canvas.paste_text', text=self.textdo.GetText(),
                                x=x, y=y)

            elif df == wx.DF_FILENAME:
                for x, name in enumerate(self.filedo.GetFilenames()):
                    pub.sendMessage('gui.open_file', filename=name)

            elif df == wx.DF_BITMAP:
                pub.sendMessage('canvas.paste_image', image=self.bmpdo.GetBitmap(),
                                x=x, y=y, ignore=True)
        return d

Generated by  Doxygen 1.6.0   Back to index