Exemple #1
0
    def __init__(self, presenter, parent):
        self.presenter = presenter
        self.eventloop = self.presenter.eventloop
        self.app = presenter.app
        self.doc = self.presenter.model
        self.renderer = PDRenderer(self)
        style = wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS
        wx.Panel.__init__(self, parent, style=style)
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.hit_surface = HitSurface(self)
        self.zoom_stack = []

        self.ctx_menu = ContextMenu(self.app, self)

        self.timer = CanvasTimer(self)
        self.Bind(wx.EVT_TIMER, self._on_timer)

        self.ctrls = self.init_controllers()
        self.Bind(wx.EVT_PAINT, self.on_paint, self)
        self.Bind(wx.EVT_ENTER_WINDOW, self.mouse_enter, self)
        # ----- Mouse binding
        self.Bind(wx.EVT_LEFT_DOWN, self.mouse_left_down)
        self.Bind(wx.EVT_LEFT_UP, self.mouse_left_up)
        self.Bind(wx.EVT_LEFT_DCLICK, self.mouse_left_dclick)
        self.Bind(wx.EVT_RIGHT_DOWN, self.mouse_right_down)
        self.Bind(wx.EVT_RIGHT_UP, self.mouse_right_up)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.mouse_middle_down)
        self.Bind(wx.EVT_MIDDLE_UP, self.mouse_middle_up)
        self.Bind(wx.EVT_MOUSEWHEEL, self.mouse_wheel)
        self.Bind(wx.EVT_MOTION, self.mouse_move)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.capture_lost)
        # ----- Keyboard binding
        self.kbproc = Kbd_Processor(self)
        self.Bind(wx.EVT_KEY_DOWN, self.kbproc.on_key_down)
        self.Bind(wx.EVT_CHAR, self.kbproc.on_char)
        # 		self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        # ----- Application eventloop bindings
        self.eventloop.connect(self.eventloop.DOC_MODIFIED, self.doc_modified)
        self.eventloop.connect(self.eventloop.PAGE_CHANGED, self.doc_modified)
        self.eventloop.connect(self.eventloop.SELECTION_CHANGED,
                               self.selection_redraw)
Exemple #2
0
    def __init__(self, presenter, parent):
        self.presenter = presenter
        self.eventloop = self.presenter.eventloop
        self.app = presenter.app
        self.doc = self.presenter.model
        self.renderer = PDRenderer(self)
        style = wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS
        wx.Panel.__init__(self, parent, style=style)
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.hit_surface = HitSurface(self)
        self.zoom_stack = []

        self.ctx_menu = ContextMenu(self.app, self)

        self.timer = CanvasTimer(self)
        self.Bind(wx.EVT_TIMER, self._on_timer)

        self.ctrls = self.init_controllers()
        self.Bind(wx.EVT_PAINT, self.on_paint, self)
        self.Bind(wx.EVT_ENTER_WINDOW, self.mouse_enter, self)
        # ----- Mouse binding
        self.Bind(wx.EVT_LEFT_DOWN, self.mouse_left_down)
        self.Bind(wx.EVT_LEFT_UP, self.mouse_left_up)
        self.Bind(wx.EVT_LEFT_DCLICK, self.mouse_left_dclick)
        self.Bind(wx.EVT_RIGHT_DOWN, self.mouse_right_down)
        self.Bind(wx.EVT_RIGHT_UP, self.mouse_right_up)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.mouse_middle_down)
        self.Bind(wx.EVT_MIDDLE_UP, self.mouse_middle_up)
        self.Bind(wx.EVT_MOUSEWHEEL, self.mouse_wheel)
        self.Bind(wx.EVT_MOTION, self.mouse_move)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.capture_lost)
        # ----- Keyboard binding
        self.kbproc = Kbd_Processor(self)
        self.Bind(wx.EVT_KEY_DOWN, self.kbproc.on_key_down)
        self.Bind(wx.EVT_CHAR, self.kbproc.on_char)
        # 		self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        # ----- Application eventloop bindings
        self.eventloop.connect(self.eventloop.DOC_MODIFIED, self.doc_modified)
        self.eventloop.connect(self.eventloop.PAGE_CHANGED, self.doc_modified)
        self.eventloop.connect(self.eventloop.SELECTION_CHANGED, self.selection_redraw)
Exemple #3
0
class AppCanvas(wx.Panel):
    presenter = None
    app = None
    eventloop = None
    renderer = None
    hscroll = None
    vscroll = None
    timer = None
    ctx_menu = None
    hit_surface = None

    mode = None
    previous_mode = None
    controller = None
    ctrls = {}
    current_cursor = None

    workspace = (WORKSPACE_WIDTH, WORKSPACE_HEIGHT)
    matrix = None
    trafo = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]
    zoom = 1.0
    zoom_stack = []
    width = 0
    height = 0
    orig_cursor = None
    current_cursor = None
    resize_marker = 0

    stroke_view = False
    draft_view = False
    soft_repaint = False
    full_repaint = False
    selection_repaint = True
    mouse_captured = False
    show_snapping = config.show_snap
    dragged_guide = ()

    my_changes = False
    redraw_flag = False
    request_redraw_flag = False

    def __init__(self, presenter, parent):
        self.presenter = presenter
        self.eventloop = self.presenter.eventloop
        self.app = presenter.app
        self.doc = self.presenter.model
        self.renderer = PDRenderer(self)
        style = wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS
        wx.Panel.__init__(self, parent, style=style)
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.hit_surface = HitSurface(self)
        self.zoom_stack = []

        self.ctx_menu = ContextMenu(self.app, self)

        self.timer = CanvasTimer(self)
        self.Bind(wx.EVT_TIMER, self._on_timer)

        self.ctrls = self.init_controllers()
        self.Bind(wx.EVT_PAINT, self.on_paint, self)
        self.Bind(wx.EVT_ENTER_WINDOW, self.mouse_enter, self)
        # ----- Mouse binding
        self.Bind(wx.EVT_LEFT_DOWN, self.mouse_left_down)
        self.Bind(wx.EVT_LEFT_UP, self.mouse_left_up)
        self.Bind(wx.EVT_LEFT_DCLICK, self.mouse_left_dclick)
        self.Bind(wx.EVT_RIGHT_DOWN, self.mouse_right_down)
        self.Bind(wx.EVT_RIGHT_UP, self.mouse_right_up)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.mouse_middle_down)
        self.Bind(wx.EVT_MIDDLE_UP, self.mouse_middle_up)
        self.Bind(wx.EVT_MOUSEWHEEL, self.mouse_wheel)
        self.Bind(wx.EVT_MOTION, self.mouse_move)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.capture_lost)
        # ----- Keyboard binding
        self.kbproc = Kbd_Processor(self)
        self.Bind(wx.EVT_KEY_DOWN, self.kbproc.on_key_down)
        self.Bind(wx.EVT_CHAR, self.kbproc.on_char)
        # 		self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        # ----- Application eventloop bindings
        self.eventloop.connect(self.eventloop.DOC_MODIFIED, self.doc_modified)
        self.eventloop.connect(self.eventloop.PAGE_CHANGED, self.doc_modified)
        self.eventloop.connect(self.eventloop.SELECTION_CHANGED,
                               self.selection_redraw)

    def destroy(self):
        self.timer.stop()
        self.ctx_menu.destroy()
        self.renderer.destroy()
        self.hit_surface.destroy()
        items = self.ctrls.keys()
        for item in items:
            if not inspect.isclass(self.ctrls[item]):
                self.ctrls[item].destroy()
        items = self.__dict__.keys()
        for item in items:
            self.__dict__[item] = None

    def set_focus(self):
        self.SetFocus()

    # ----- SCROLLING

    def _set_scrolls(self, hscroll, vscroll):
        self.hscroll = hscroll
        self.vscroll = vscroll
        self.hscroll.SetScrollbar(500, 100, 1100, 100, refresh=True)
        self.vscroll.SetScrollbar(500, 100, 1100, 100, refresh=True)
        self.hscroll.Bind(wx.EVT_SCROLL, self._scrolling, self.hscroll)
        self.vscroll.Bind(wx.EVT_SCROLL, self._scrolling, self.vscroll)

    def _scrolling(self, *args):
        if self.my_changes: return
        xpos = self.hscroll.GetThumbPosition() / 1000.0
        ypos = (1000 - self.vscroll.GetThumbPosition()) / 1000.0
        x = (xpos - 0.5) * self.workspace[0]
        y = (ypos - 0.5) * self.workspace[1]
        center = self.doc_to_win((x, y))
        self._set_center(center)
        self.force_redraw()

    def update_scrolls(self):
        self.my_changes = True
        center = self._get_center()
        x = (center[0] + self.workspace[0] / 2.0) / self.workspace[0]
        y = (center[1] + self.workspace[1] / 2.0) / self.workspace[1]
        hscroll = int(1000 * x)
        vscroll = int(1000 - 1000 * y)
        self.hscroll.SetScrollbar(hscroll, 100, 1100, 100, refresh=True)
        self.vscroll.SetScrollbar(vscroll, 100, 1100, 100, refresh=True)
        self.my_changes = False

    # ----- CONTROLLERS

    def init_controllers(self):
        dummy = controllers.AbstractController(self, self.presenter)
        ctrls = {
            modes.SELECT_MODE:
            controllers.SelectController(self, self.presenter),
            modes.SHAPER_MODE: controllers.EditorChooser,
            modes.BEZIER_EDITOR_MODE: controllers.BezierEditor,
            modes.RECT_EDITOR_MODE: controllers.RectEditor,
            modes.ELLIPSE_EDITOR_MODE: controllers.EllipseEditor,
            modes.POLYGON_EDITOR_MODE: controllers.PolygonEditor,
            modes.TEXT_EDITOR_MODE: controllers.TextEditor,
            modes.ZOOM_MODE: controllers.ZoomController,
            modes.FLEUR_MODE: controllers.FleurController,
            modes.TEMP_FLEUR_MODE: controllers.TempFleurController,
            modes.PICK_MODE: controllers.PickController,
            modes.LINE_MODE: controllers.PolyLineCreator,
            modes.CURVE_MODE: controllers.PathsCreator,
            modes.RECT_MODE: controllers.RectangleCreator,
            modes.ELLIPSE_MODE: controllers.EllipseCreator,
            modes.TEXT_MODE: controllers.TextCreator,
            modes.TEXT_EDIT_MODE: controllers.TextEditController,
            modes.POLYGON_MODE: controllers.PolygonCreator,
            modes.MOVE_MODE: controllers.MoveController,
            modes.RESIZE_MODE: controllers.TransformController,
            modes.GUIDE_MODE: controllers.GuideController,
            modes.WAIT_MODE: controllers.WaitController,
            modes.GR_SELECT_MODE: controllers.GradientChooser,
            modes.GR_CREATE_MODE: controllers.GradientCreator,
            modes.GR_EDIT_MODE: controllers.GradientEditor,
        }
        return ctrls

    def get_controller(self, mode):
        ctrl = self.ctrls[mode]
        if inspect.isclass(ctrl):
            self.ctrls[mode] = ctrl(self, self.presenter)
        return self.ctrls[mode]

    def set_mode(self, mode=modes.SELECT_MODE):
        if not mode == self.mode:
            if not self.previous_mode is None:
                self.restore_mode()
            if not self.controller is None:
                self.controller.stop_()
            self.mode = mode
            self.controller = self.get_controller(mode)
            self.controller.set_cursor()
            self.controller.start_()
            events.emit(events.MODE_CHANGED, mode)
            if self.presenter.selection and self.presenter.selection.objs:
                self.presenter.selection.update()

    def set_canvas_cursor(self, mode):
        self.current_cursor = self.app.cursors[mode]
        self.SetCursor(self.current_cursor)

    def set_temp_mode(self, mode=modes.SELECT_MODE, callback=None):
        if not mode == self.mode:
            self.previous_mode = self.mode
            self.ctrls[self.mode].standby()
            self.mode = mode
            self.controller = self.get_controller(mode)
            self.controller.callback = callback
            self.controller.start_()
            self.controller.set_cursor()

    def restore_mode(self):
        if not self.previous_mode is None:
            if not self.controller is None:
                self.controller.stop_()
            self.mode = self.previous_mode
            self.controller = self.get_controller(self.mode)
            self.controller.set_cursor()
            self.controller.restore()
            events.emit(events.MODE_CHANGED, self.mode)
            self.previous_mode = None
        else:
            self.set_mode()

    def set_temp_cursor(self, cursor):
        self.orig_cursor = self.app.cursors[self.mode]
        self.current_cursor = cursor
        self.SetCursor(cursor)

    def restore_cursor(self):
        if not self.orig_cursor is None:
            self.SetCursor(self.orig_cursor)
            self.current_cursor = self.orig_cursor
            self.orig_cursor = None

    def show_context_menu(self):
        self.ctx_menu.rebuild()
        self.PopupMenu(self.ctx_menu)

    # ----- CANVAS MATH

    def _keep_center(self):
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        if not w == self.width or not h == self.height:
            _dx = (w - self.width) / 2.0
            _dy = (h - self.height) / 2.0
            m11, m12, m21, m22, dx, dy = self.trafo
            dx += _dx
            dy += _dy
            self.trafo = [m11, m12, m21, m22, dx, dy]
            self.matrix = cairo.Matrix(m11, m12, m21, m22, dx, dy)
            self.width = w
            self.height = h
            self.update_scrolls()

    def _set_center(self, center):
        x, y = center
        _dx = self.width / 2.0 - x
        _dy = self.height / 2.0 - y
        m11, m12, m21, m22, dx, dy = self.trafo
        dx += _dx
        dy += _dy
        self.trafo = [m11, m12, m21, m22, dx, dy]
        self.matrix = cairo.Matrix(m11, m12, m21, m22, dx, dy)
        self.update_scrolls()

    def _get_center(self):
        x = self.width / 2.0
        y = self.height / 2.0
        return self.win_to_doc((x, y))

    def doc_to_win(self, point=[0.0, 0.0]):
        x, y = point
        m11 = self.trafo[0]
        m22, dx, dy = self.trafo[3:]
        x_new = m11 * x + dx
        y_new = m22 * y + dy
        return [x_new, y_new]

    def point_doc_to_win(self, point=[0.0, 0.0]):
        if not point: return []
        if len(point) == 2:
            return self.doc_to_win(point)
        else:
            return [
                self.doc_to_win(point[0]),
                self.doc_to_win(point[1]),
                self.doc_to_win(point[2]), point[3]
            ]

    def win_to_doc(self, point=[0, 0]):
        x, y = point
        x = float(x)
        y = float(y)
        m11 = self.trafo[0]
        m22, dx, dy = self.trafo[3:]
        x_new = (x - dx) / m11
        y_new = (y - dy) / m22
        return [x_new, y_new]

    def win_to_doc_coords(self, point=[0, 0]):
        x, y = self.win_to_doc(point)
        origin = self.presenter.model.doc_origin
        w, h = self.presenter.get_page_size()
        if origin == DOC_ORIGIN_LL:
            return [w / 2.0 + x, h / 2.0 + y]
        elif origin == DOC_ORIGIN_LU:
            return [w / 2.0 + x, h / 2.0 - y]
        else:
            return [x, y]

    def point_win_to_doc(self, point=[0.0, 0.0]):
        if not point: return []
        if len(point) == 2:
            return self.win_to_doc(point)
        else:
            return [
                self.win_to_doc(point[0]),
                self.win_to_doc(point[1]),
                self.win_to_doc(point[2]), point[3]
            ]

    def paths_doc_to_win(self, paths):
        result = []
        for path in paths:
            new_path = []
            new_points = []
            new_path.append(self.doc_to_win(path[0]))
            for point in path[1]:
                new_points.append(self.point_doc_to_win(point))
            new_path.append(new_points)
            new_path.append(path[2])
            result.append(new_path)
        return result

    def bbox_win_to_doc(self, bbox):
        new_bbox = self.win_to_doc(bbox[:2]) + self.win_to_doc(bbox[2:])
        return normalize_bbox(new_bbox)

    def bbox_doc_to_win(self, bbox):
        new_bbox = self.doc_to_win(bbox[:2]) + self.doc_to_win(bbox[2:])
        return normalize_bbox(new_bbox)

    def scroll(self, cdx, cdy):
        m11, m12, m21, m22, dx, dy = self.trafo
        dx += cdx
        dy += cdy
        self.trafo = [m11, m12, m21, m22, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.update_scrolls()
        self.force_redraw()

    # ----- ZOOMING

    def _fit_to_page(self):
        width, height = self.presenter.get_page_size()
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        self.width = w
        self.height = h
        zoom = min(w / width, h / height) * PAGEFIT
        dx = w / 2.0
        dy = h / 2.0
        self.trafo = [zoom, 0, 0, -zoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(zoom, 0, 0, -zoom, dx, dy)
        self.zoom = zoom
        self.update_scrolls()

    def zoom_fit_to_page(self):
        self._fit_to_page()
        self.force_redraw()

    def _zoom(self, dzoom=1.0):
        m11, m12, m21, m22, dx, dy = self.trafo
        _dx = (self.width * dzoom - self.width) / 2.0
        _dy = (self.height * dzoom - self.height) / 2.0
        dx = dx * dzoom - _dx
        dy = dy * dzoom - _dy
        self.trafo = [m11 * dzoom, m12, m21, m22 * dzoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.zoom = m11 * dzoom
        self.update_scrolls()
        self.force_redraw()

    def zoom_in(self):
        self._zoom(ZOOM_IN)

    def zoom_out(self):
        self._zoom(ZOOM_OUT)

    def zoom_100(self):
        self._zoom(1.0 / self.zoom)

    def zoom_at_point(self, point, zoom):
        x, y = point
        m11, m12, m21, m22, dx, dy = self.trafo
        dx = dx * zoom - x * zoom + x
        dy = dy * zoom - y * zoom + y
        self.trafo = [m11 * zoom, m12, m21, m22 * zoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.zoom = m11 * zoom
        self.update_scrolls()
        self.force_redraw()

    def zoom_to_rectangle(self, start, end):
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        self.width = w
        self.height = h
        width = abs(end[0] - start[0])
        height = abs(end[1] - start[1])
        zoom = min(w / width, h / height) * 0.95
        center = [
            start[0] + (end[0] - start[0]) / 2,
            start[1] + (end[1] - start[1]) / 2
        ]
        self._set_center(center)
        self._zoom(zoom)

    def zoom_selected(self):
        x0, y0, x1, y1 = self.presenter.selection.frame
        start = self.doc_to_win([x0, y0])
        end = self.doc_to_win([x1, y1])
        self.zoom_to_rectangle(start, end)

    def zoom_previous(self):
        if len(self.zoom_stack) > 1:
            self.zoom_stack = self.zoom_stack[:-1]
            self.trafo = [] + self.zoom_stack[-1]
            self.zoom = self.trafo[0]
            self.matrix = cairo.Matrix(*self.trafo)
            self.update_scrolls()
            self.force_redraw()

    # ----- SELECTION STUFF

    def select_at_point(self, point, add_flag=False):
        point = self.win_to_doc(point)
        self.presenter.selection.select_at_point(point, add_flag)

    def pick_at_point(self, point):
        point = self.win_to_doc(point)
        return self.presenter.selection.pick_at_point(point)

    def select_by_rect(self, start, end, flag=False):
        start = self.win_to_doc(start)
        end = self.win_to_doc(end)
        rect = start + end
        rect = normalize_bbox(rect)
        self.presenter.selection.select_by_rect(rect, flag)

    # ----- RENDERING -----
    def selection_redraw(self, *args):
        if not self.full_repaint:
            self.soft_repaint = True
        self.force_redraw()

    def doc_modified(self, *args):
        self.full_repaint = True
        self.force_redraw()

    def force_redraw(self, *args):
        if self.redraw_flag:
            self.request_redraw_flag = True
        else:
            w, h = self.GetSize()
            self.redraw_flag = True
            self.Refresh(rect=wx.Rect(0, 0, w, h), eraseBackground=False)

    def on_paint(self, event):
        if self.matrix is None:
            self.zoom_fit_to_page()
            self.set_mode(modes.SELECT_MODE)
        self._keep_center()

        try:
            if self.soft_repaint and not self.full_repaint:
                if self.selection_repaint:
                    if self.mode in modes.EDIT_MODES and \
                            self.presenter.selection.objs:
                        pass
                    else:
                        self.renderer.paint_selection()
                self.soft_repaint = False
            else:
                self.renderer.paint_document()
                if self.selection_repaint:
                    if self.mode in modes.EDIT_MODES and \
                            self.presenter.selection.objs:
                        pass
                    else:
                        self.renderer.paint_selection()
                self.eventloop.emit(self.eventloop.VIEW_CHANGED)
                self.full_repaint = False
                self.soft_repaint = False
            if not self.controller is None: self.controller.repaint()
            if self.dragged_guide:
                self.renderer.paint_guide_dragging(*self.dragged_guide)
                if not self.mode == modes.GUIDE_MODE: self.dragged_guide = ()
            self.renderer.finalize()
        except:
            self.app.print_stacktrace()

        self.redraw_flag = False
        if self.request_redraw_flag:
            self.request_redraw_flag = False
            self.force_redraw()

        # ==============EVENT CONTROLLING==========================

    def mouse_enter(self, enent):
        if wal.IS_MSW:
            self.SetFocus()

    def capture_mouse(self):
        if wal.IS_MSW:
            self.CaptureMouse()
            self.mouse_captured = True

    def release_mouse(self):
        if self.mouse_captured:
            try:
                self.ReleaseMouse()
            except:
                pass
            self.mouse_captured = False

    def capture_lost(self, event):
        self.release_mouse()

    def _on_timer(self, event):
        self.controller.on_timer()

    def mouse_left_down(self, event):
        self.capture_mouse()
        self.controller.set_cursor()
        self.controller.mouse_down(CanvasEvent(event))
        event.Skip()

    def mouse_left_up(self, event):
        self.release_mouse()
        self.controller.mouse_up(CanvasEvent(event))

    def mouse_left_dclick(self, event):
        self.controller.set_cursor()
        self.controller.mouse_double_click(CanvasEvent(event))

    def mouse_move(self, event):
        x, y = self.win_to_doc_coords(list(event.GetPositionTuple()))
        unit = self.presenter.model.doc_units
        tr_unit = uc2const.unit_short_names[unit]
        msg = '  %i x %i' % (x * point_dict[unit], y * point_dict[unit])
        events.emit(events.MOUSE_STATUS, '%s %s' % (msg, tr_unit))
        self.controller.mouse_move(CanvasEvent(event))

    def mouse_right_down(self, event):
        self.controller.mouse_right_down(CanvasEvent(event))

    def mouse_right_up(self, event):
        self.controller.mouse_right_up(CanvasEvent(event))

    def mouse_middle_down(self, event):
        self.controller.mouse_middle_down(CanvasEvent(event))

    def mouse_middle_up(self, event):
        self.controller.mouse_middle_up(CanvasEvent(event))

    def mouse_wheel(self, event):
        self.controller.wheel(CanvasEvent(event))
Exemple #4
0
class AppCanvas(wx.Panel):

    presenter = None
    app = None
    eventloop = None
    renderer = None
    hscroll = None
    vscroll = None
    timer = None
    ctx_menu = None
    hit_surface = None

    mode = None
    previous_mode = None
    controller = None
    ctrls = {}
    current_cursor = None

    workspace = (WORKSPACE_WIDTH, WORKSPACE_HEIGHT)
    matrix = None
    trafo = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]
    zoom = 1.0
    zoom_stack = []
    width = 0
    height = 0
    orig_cursor = None
    current_cursor = None
    resize_marker = 0

    stroke_view = False
    draft_view = False
    soft_repaint = False
    full_repaint = False
    selection_repaint = True
    mouse_captured = False
    show_snapping = config.show_snap
    dragged_guide = ()

    my_changes = False
    redraw_flag = False
    request_redraw_flag = False

    def __init__(self, presenter, parent):
        self.presenter = presenter
        self.eventloop = self.presenter.eventloop
        self.app = presenter.app
        self.doc = self.presenter.model
        self.renderer = PDRenderer(self)
        style = wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS
        wx.Panel.__init__(self, parent, style=style)
        self.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.hit_surface = HitSurface(self)
        self.zoom_stack = []

        self.ctx_menu = ContextMenu(self.app, self)

        self.timer = CanvasTimer(self)
        self.Bind(wx.EVT_TIMER, self._on_timer)

        self.ctrls = self.init_controllers()
        self.Bind(wx.EVT_PAINT, self.on_paint, self)
        self.Bind(wx.EVT_ENTER_WINDOW, self.mouse_enter, self)
        # ----- Mouse binding
        self.Bind(wx.EVT_LEFT_DOWN, self.mouse_left_down)
        self.Bind(wx.EVT_LEFT_UP, self.mouse_left_up)
        self.Bind(wx.EVT_LEFT_DCLICK, self.mouse_left_dclick)
        self.Bind(wx.EVT_RIGHT_DOWN, self.mouse_right_down)
        self.Bind(wx.EVT_RIGHT_UP, self.mouse_right_up)
        self.Bind(wx.EVT_MIDDLE_DOWN, self.mouse_middle_down)
        self.Bind(wx.EVT_MIDDLE_UP, self.mouse_middle_up)
        self.Bind(wx.EVT_MOUSEWHEEL, self.mouse_wheel)
        self.Bind(wx.EVT_MOTION, self.mouse_move)
        self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self.capture_lost)
        # ----- Keyboard binding
        self.kbproc = Kbd_Processor(self)
        self.Bind(wx.EVT_KEY_DOWN, self.kbproc.on_key_down)
        self.Bind(wx.EVT_CHAR, self.kbproc.on_char)
        # 		self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        # ----- Application eventloop bindings
        self.eventloop.connect(self.eventloop.DOC_MODIFIED, self.doc_modified)
        self.eventloop.connect(self.eventloop.PAGE_CHANGED, self.doc_modified)
        self.eventloop.connect(self.eventloop.SELECTION_CHANGED, self.selection_redraw)

    def destroy(self):
        self.timer.stop()
        self.ctx_menu.destroy()
        self.renderer.destroy()
        self.hit_surface.destroy()
        items = self.ctrls.keys()
        for item in items:
            if not inspect.isclass(self.ctrls[item]):
                self.ctrls[item].destroy()
        items = self.__dict__.keys()
        for item in items:
            self.__dict__[item] = None

    def set_focus(self):
        self.SetFocus()

        # ----- SCROLLING

    def _set_scrolls(self, hscroll, vscroll):
        self.hscroll = hscroll
        self.vscroll = vscroll
        self.hscroll.SetScrollbar(500, 100, 1100, 100, refresh=True)
        self.vscroll.SetScrollbar(500, 100, 1100, 100, refresh=True)
        self.hscroll.Bind(wx.EVT_SCROLL, self._scrolling, self.hscroll)
        self.vscroll.Bind(wx.EVT_SCROLL, self._scrolling, self.vscroll)

    def _scrolling(self, *args):
        if self.my_changes:
            return
        xpos = self.hscroll.GetThumbPosition() / 1000.0
        ypos = (1000 - self.vscroll.GetThumbPosition()) / 1000.0
        x = (xpos - 0.5) * self.workspace[0]
        y = (ypos - 0.5) * self.workspace[1]
        center = self.doc_to_win((x, y))
        self._set_center(center)
        self.force_redraw()

    def update_scrolls(self):
        self.my_changes = True
        center = self._get_center()
        x = (center[0] + self.workspace[0] / 2.0) / self.workspace[0]
        y = (center[1] + self.workspace[1] / 2.0) / self.workspace[1]
        hscroll = int(1000 * x)
        vscroll = int(1000 - 1000 * y)
        self.hscroll.SetScrollbar(hscroll, 100, 1100, 100, refresh=True)
        self.vscroll.SetScrollbar(vscroll, 100, 1100, 100, refresh=True)
        self.my_changes = False

        # ----- CONTROLLERS

    def init_controllers(self):
        dummy = controllers.AbstractController(self, self.presenter)
        ctrls = {
            modes.SELECT_MODE: controllers.SelectController(self, self.presenter),
            modes.SHAPER_MODE: controllers.EditorChooser,
            modes.BEZIER_EDITOR_MODE: controllers.BezierEditor,
            modes.RECT_EDITOR_MODE: controllers.RectEditor,
            modes.ELLIPSE_EDITOR_MODE: controllers.EllipseEditor,
            modes.POLYGON_EDITOR_MODE: controllers.PolygonEditor,
            modes.TEXT_EDITOR_MODE: controllers.TextEditor,
            modes.ZOOM_MODE: controllers.ZoomController,
            modes.FLEUR_MODE: controllers.FleurController,
            modes.TEMP_FLEUR_MODE: controllers.TempFleurController,
            modes.PICK_MODE: controllers.PickController,
            modes.LINE_MODE: controllers.PolyLineCreator,
            modes.CURVE_MODE: controllers.PathsCreator,
            modes.RECT_MODE: controllers.RectangleCreator,
            modes.ELLIPSE_MODE: controllers.EllipseCreator,
            modes.TEXT_MODE: controllers.TextCreator,
            modes.TEXT_EDIT_MODE: controllers.TextEditController,
            modes.POLYGON_MODE: controllers.PolygonCreator,
            modes.MOVE_MODE: controllers.MoveController,
            modes.RESIZE_MODE: controllers.TransformController,
            modes.GUIDE_MODE: controllers.GuideController,
            modes.WAIT_MODE: controllers.WaitController,
            modes.GR_SELECT_MODE: controllers.GradientChooser,
            modes.GR_CREATE_MODE: controllers.GradientCreator,
            modes.GR_EDIT_MODE: controllers.GradientEditor,
        }
        return ctrls

    def get_controller(self, mode):
        ctrl = self.ctrls[mode]
        if inspect.isclass(ctrl):
            self.ctrls[mode] = ctrl(self, self.presenter)
        return self.ctrls[mode]

    def set_mode(self, mode=modes.SELECT_MODE):
        if not mode == self.mode:
            if not self.previous_mode is None:
                self.restore_mode()
            if not self.controller is None:
                self.controller.stop_()
            self.mode = mode
            self.controller = self.get_controller(mode)
            self.controller.set_cursor()
            self.controller.start_()
            events.emit(events.MODE_CHANGED, mode)
            if self.presenter.selection and self.presenter.selection.objs:
                self.presenter.selection.update()

    def set_canvas_cursor(self, mode):
        self.current_cursor = self.app.cursors[mode]
        self.SetCursor(self.current_cursor)

    def set_temp_mode(self, mode=modes.SELECT_MODE, callback=None):
        if not mode == self.mode:
            self.previous_mode = self.mode
            self.ctrls[self.mode].standby()
            self.mode = mode
            self.controller = self.get_controller(mode)
            self.controller.callback = callback
            self.controller.start_()
            self.controller.set_cursor()

    def restore_mode(self):
        if not self.previous_mode is None:
            if not self.controller is None:
                self.controller.stop_()
            self.mode = self.previous_mode
            self.controller = self.get_controller(self.mode)
            self.controller.set_cursor()
            self.controller.restore()
            events.emit(events.MODE_CHANGED, self.mode)
            self.previous_mode = None
        else:
            self.set_mode()

    def set_temp_cursor(self, cursor):
        self.orig_cursor = self.app.cursors[self.mode]
        self.current_cursor = cursor
        self.SetCursor(cursor)

    def restore_cursor(self):
        if not self.orig_cursor is None:
            self.SetCursor(self.orig_cursor)
            self.current_cursor = self.orig_cursor
            self.orig_cursor = None

    def show_context_menu(self):
        self.ctx_menu.rebuild()
        self.PopupMenu(self.ctx_menu)

        # ----- CANVAS MATH

    def _keep_center(self):
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        if not w == self.width or not h == self.height:
            _dx = (w - self.width) / 2.0
            _dy = (h - self.height) / 2.0
            m11, m12, m21, m22, dx, dy = self.trafo
            dx += _dx
            dy += _dy
            self.trafo = [m11, m12, m21, m22, dx, dy]
            self.matrix = cairo.Matrix(m11, m12, m21, m22, dx, dy)
            self.width = w
            self.height = h
            self.update_scrolls()

    def _set_center(self, center):
        x, y = center
        _dx = self.width / 2.0 - x
        _dy = self.height / 2.0 - y
        m11, m12, m21, m22, dx, dy = self.trafo
        dx += _dx
        dy += _dy
        self.trafo = [m11, m12, m21, m22, dx, dy]
        self.matrix = cairo.Matrix(m11, m12, m21, m22, dx, dy)
        self.update_scrolls()

    def _get_center(self):
        x = self.width / 2.0
        y = self.height / 2.0
        return self.win_to_doc((x, y))

    def doc_to_win(self, point=[0.0, 0.0]):
        x, y = point
        m11 = self.trafo[0]
        m22, dx, dy = self.trafo[3:]
        x_new = m11 * x + dx
        y_new = m22 * y + dy
        return [x_new, y_new]

    def point_doc_to_win(self, point=[0.0, 0.0]):
        if not point:
            return []
        if len(point) == 2:
            return self.doc_to_win(point)
        else:
            return [self.doc_to_win(point[0]), self.doc_to_win(point[1]), self.doc_to_win(point[2]), point[3]]

    def win_to_doc(self, point=[0, 0]):
        x, y = point
        x = float(x)
        y = float(y)
        m11 = self.trafo[0]
        m22, dx, dy = self.trafo[3:]
        x_new = (x - dx) / m11
        y_new = (y - dy) / m22
        return [x_new, y_new]

    def win_to_doc_coords(self, point=[0, 0]):
        x, y = self.win_to_doc(point)
        origin = self.presenter.model.doc_origin
        w, h = self.presenter.get_page_size()
        if origin == DOC_ORIGIN_LL:
            return [w / 2.0 + x, h / 2.0 + y]
        elif origin == DOC_ORIGIN_LU:
            return [w / 2.0 + x, h / 2.0 - y]
        else:
            return [x, y]

    def point_win_to_doc(self, point=[0.0, 0.0]):
        if not point:
            return []
        if len(point) == 2:
            return self.win_to_doc(point)
        else:
            return [self.win_to_doc(point[0]), self.win_to_doc(point[1]), self.win_to_doc(point[2]), point[3]]

    def paths_doc_to_win(self, paths):
        result = []
        for path in paths:
            new_path = []
            new_points = []
            new_path.append(self.doc_to_win(path[0]))
            for point in path[1]:
                new_points.append(self.point_doc_to_win(point))
            new_path.append(new_points)
            new_path.append(path[2])
            result.append(new_path)
        return result

    def bbox_win_to_doc(self, bbox):
        new_bbox = self.win_to_doc(bbox[:2]) + self.win_to_doc(bbox[2:])
        return normalize_bbox(new_bbox)

    def bbox_doc_to_win(self, bbox):
        new_bbox = self.doc_to_win(bbox[:2]) + self.doc_to_win(bbox[2:])
        return normalize_bbox(new_bbox)

    def scroll(self, cdx, cdy):
        m11, m12, m21, m22, dx, dy = self.trafo
        dx += cdx
        dy += cdy
        self.trafo = [m11, m12, m21, m22, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.update_scrolls()
        self.force_redraw()

        # ----- ZOOMING

    def _fit_to_page(self):
        width, height = self.presenter.get_page_size()
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        self.width = w
        self.height = h
        zoom = min(w / width, h / height) * PAGEFIT
        dx = w / 2.0
        dy = h / 2.0
        self.trafo = [zoom, 0, 0, -zoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(zoom, 0, 0, -zoom, dx, dy)
        self.zoom = zoom
        self.update_scrolls()

    def zoom_fit_to_page(self):
        self._fit_to_page()
        self.force_redraw()

    def _zoom(self, dzoom=1.0):
        m11, m12, m21, m22, dx, dy = self.trafo
        _dx = (self.width * dzoom - self.width) / 2.0
        _dy = (self.height * dzoom - self.height) / 2.0
        dx = dx * dzoom - _dx
        dy = dy * dzoom - _dy
        self.trafo = [m11 * dzoom, m12, m21, m22 * dzoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.zoom = m11 * dzoom
        self.update_scrolls()
        self.force_redraw()

    def zoom_in(self):
        self._zoom(ZOOM_IN)

    def zoom_out(self):
        self._zoom(ZOOM_OUT)

    def zoom_100(self):
        self._zoom(1.0 / self.zoom)

    def zoom_at_point(self, point, zoom):
        x, y = point
        m11, m12, m21, m22, dx, dy = self.trafo
        dx = dx * zoom - x * zoom + x
        dy = dy * zoom - y * zoom + y
        self.trafo = [m11 * zoom, m12, m21, m22 * zoom, dx, dy]
        self.zoom_stack.append([] + self.trafo)
        self.matrix = cairo.Matrix(*self.trafo)
        self.zoom = m11 * zoom
        self.update_scrolls()
        self.force_redraw()

    def zoom_to_rectangle(self, start, end):
        w, h = self.GetSize()
        w = float(w)
        h = float(h)
        self.width = w
        self.height = h
        width = abs(end[0] - start[0])
        height = abs(end[1] - start[1])
        zoom = min(w / width, h / height) * 0.95
        center = [start[0] + (end[0] - start[0]) / 2, start[1] + (end[1] - start[1]) / 2]
        self._set_center(center)
        self._zoom(zoom)

    def zoom_selected(self):
        x0, y0, x1, y1 = self.presenter.selection.frame
        start = self.doc_to_win([x0, y0])
        end = self.doc_to_win([x1, y1])
        self.zoom_to_rectangle(start, end)

    def zoom_previous(self):
        if len(self.zoom_stack) > 1:
            self.zoom_stack = self.zoom_stack[:-1]
            self.trafo = [] + self.zoom_stack[-1]
            self.zoom = self.trafo[0]
            self.matrix = cairo.Matrix(*self.trafo)
            self.update_scrolls()
            self.force_redraw()

            # ----- SELECTION STUFF

    def select_at_point(self, point, add_flag=False):
        point = self.win_to_doc(point)
        self.presenter.selection.select_at_point(point, add_flag)

    def pick_at_point(self, point):
        point = self.win_to_doc(point)
        return self.presenter.selection.pick_at_point(point)

    def select_by_rect(self, start, end, flag=False):
        start = self.win_to_doc(start)
        end = self.win_to_doc(end)
        rect = start + end
        rect = normalize_bbox(rect)
        self.presenter.selection.select_by_rect(rect, flag)

        # ----- RENDERING -----

    def selection_redraw(self, *args):
        if not self.full_repaint:
            self.soft_repaint = True
        self.force_redraw()

    def doc_modified(self, *args):
        self.full_repaint = True
        self.force_redraw()

    def force_redraw(self, *args):
        if self.redraw_flag:
            self.request_redraw_flag = True
        else:
            w, h = self.GetSize()
            self.redraw_flag = True
            self.Refresh(rect=wx.Rect(0, 0, w, h), eraseBackground=False)

    def on_paint(self, event):
        if self.matrix is None:
            self.zoom_fit_to_page()
            self.set_mode(modes.SELECT_MODE)
        self._keep_center()

        try:
            if self.soft_repaint and not self.full_repaint:
                if self.selection_repaint:
                    if self.mode in modes.EDIT_MODES and self.presenter.selection.objs:
                        pass
                    else:
                        self.renderer.paint_selection()
                self.soft_repaint = False
            else:
                self.renderer.paint_document()
                if self.selection_repaint:
                    if self.mode in modes.EDIT_MODES and self.presenter.selection.objs:
                        pass
                    else:
                        self.renderer.paint_selection()
                self.eventloop.emit(self.eventloop.VIEW_CHANGED)
                self.full_repaint = False
                self.soft_repaint = False
            if not self.controller is None:
                self.controller.repaint()
            if self.dragged_guide:
                self.renderer.paint_guide_dragging(*self.dragged_guide)
                if not self.mode == modes.GUIDE_MODE:
                    self.dragged_guide = ()
            self.renderer.finalize()
        except:
            self.app.print_stacktrace()

        self.redraw_flag = False
        if self.request_redraw_flag:
            self.request_redraw_flag = False
            self.force_redraw()

    # ==============EVENT CONTROLLING==========================

    def mouse_enter(self, enent):
        if wal.is_msw():
            self.SetFocus()

    def capture_mouse(self):
        if wal.is_msw():
            self.CaptureMouse()
            self.mouse_captured = True

    def release_mouse(self):
        if self.mouse_captured:
            try:
                self.ReleaseMouse()
            except:
                pass
            self.mouse_captured = False

    def capture_lost(self, event):
        if self.mouse_captured:
            try:
                self.ReleaseMouse()
            except:
                pass
            self.mouse_captured = False

    def _on_timer(self, event):
        self.controller.on_timer()

    def mouse_left_down(self, event):
        self.capture_mouse()
        self.controller.set_cursor()
        self.controller.mouse_down(CanvasEvent(event))
        event.Skip()

    def mouse_left_up(self, event):
        self.release_mouse()
        self.controller.mouse_up(CanvasEvent(event))

    def mouse_left_dclick(self, event):
        self.controller.set_cursor()
        self.controller.mouse_double_click(CanvasEvent(event))

    def mouse_move(self, event):
        x, y = self.win_to_doc_coords(list(event.GetPositionTuple()))
        unit = self.presenter.model.doc_units
        tr_unit = uc2const.unit_short_names[unit]
        msg = "  %i x %i" % (x * point_dict[unit], y * point_dict[unit])
        events.emit(events.MOUSE_STATUS, "%s %s" % (msg, tr_unit))
        self.controller.mouse_move(CanvasEvent(event))

    def mouse_right_down(self, event):
        self.controller.mouse_right_down(CanvasEvent(event))

    def mouse_right_up(self, event):
        self.controller.mouse_right_up(CanvasEvent(event))

    def mouse_middle_down(self, event):
        self.controller.mouse_middle_down(CanvasEvent(event))

    def mouse_middle_up(self, event):
        self.controller.mouse_middle_up(CanvasEvent(event))

    def mouse_wheel(self, event):
        self.controller.wheel(CanvasEvent(event))