Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/canvas.py
diff options
context:
space:
mode:
Diffstat (limited to 'canvas.py')
-rw-r--r--canvas.py187
1 files changed, 187 insertions, 0 deletions
diff --git a/canvas.py b/canvas.py
new file mode 100644
index 0000000..d137f78
--- /dev/null
+++ b/canvas.py
@@ -0,0 +1,187 @@
+# Copyright (C) 2009, Tomeu Vizoso
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+
+import gobject
+import gtk
+
+from sugar import dispatch
+
+class Canvas(gtk.DrawingArea):
+
+ __gtype_name__ = 'MindMapCanvas'
+
+ def __init__(self):
+ self._elements = []
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._last_clicked_element = None
+ self._dragging = False
+
+ gobject.GObject.__init__(self)
+
+ self.dragging_started = dispatch.Signal()
+ self.dragging_finished = dispatch.Signal()
+
+ self.add_events(gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.BUTTON_RELEASE_MASK |
+ gtk.gdk.POINTER_MOTION_MASK |
+ gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect('expose-event', self.__expose_event_cb)
+ self.connect('button-press-event', self.__button_press_event_cb)
+ self.connect('button-release-event', self.__button_release_event_cb)
+ self.connect('motion-notify-event', self.__motion_notify_event_cb)
+
+ def __expose_event_cb(self, widget, event):
+ context = self.window.cairo_create()
+
+ rect = self.get_allocation()
+ rect.x = 0
+ rect.y = 0
+
+ context.rectangle(event.area.x, event.area.y,
+ event.area.width, event.area.height)
+ context.clip()
+
+ context.set_source_rgb(1.0, 1.0, 1.0)
+ context.paint()
+
+ for element in self._elements:
+ rect = element.get_rect()
+ if rect.intersect(event.area):
+ context.save()
+
+ context.rectangle(rect.x, rect.y, rect.width, rect.height)
+ context.clip()
+
+ element.draw(context)
+
+ context.restore()
+
+ return False
+
+ def get_element_at(self, x, y):
+ for element in self._elements:
+ rect = element.get_rect()
+ if (x >= rect.x and x <= rect.x + rect.width) and \
+ (y >= rect.y and y <= rect.y + rect.height):
+ return element
+ return None
+
+ def _invalidate(self, x, y, width, height):
+ if self.window is None:
+ return
+ rect = gtk.gdk.Rectangle(x, y, width, height)
+ self.window.invalidate_rect(rect, True)
+ self.window.process_updates(True)
+
+ def add_element(self, element):
+ element.invalidated.connect(self.__element_invalidated_cb)
+ self._elements.append(element)
+ rect = element.get_rect()
+ self._invalidate(rect.x, rect.y, rect.width, rect.height)
+
+ def remove_all_elements(self):
+ rect = gtk.gdk.Rectangle(0, 0, 0, 0)
+ for element in self._elements:
+ rect.union(element.get_rect())
+
+ self._invalidate(rect.x, rect.y, rect.width, rect.height)
+
+ self._elements = []
+
+ def __element_invalidated_cb(self, **kwargs):
+ element = kwargs['sender']
+
+ rect = element.previous_rect
+ self._invalidate(rect.x, rect.y, rect.width, rect.height)
+
+ rect = element.get_rect()
+ self._invalidate(rect.x, rect.y, rect.width, rect.height)
+
+ def __button_press_event_cb(self, widget, event):
+ logging.debug('button_press_event_cb')
+
+ if event.button == 1 and event.type == gtk.gdk.BUTTON_PRESS:
+ self._last_clicked_element = self.get_element_at(event.x, event.y)
+ logging.debug('button_press_event_cb %r' % self._last_clicked_element)
+ if self._last_clicked_element is not None:
+ self._pressed_button = event.button
+ self._press_start_x = event.x
+ self._press_start_y = event.y
+
+ return False
+
+ def __button_release_event_cb(self, widget, event):
+ logging.debug('button_release_event_cb')
+ if self._dragging:
+ self.dragging_finished.send(self, position=(event.x, event.y),
+ element=self._last_clicked_element)
+ self._cleanup_drag()
+
+ return False
+
+ def _cleanup_drag(self):
+ self._last_clicked_element = None
+ self._pressed_button = None
+ self._press_start_x = None
+ self._press_start_y = None
+ self._dragging = False
+
+ def __motion_notify_event_cb(self, widget, event):
+ if self._dragging or self._last_clicked_element is None:
+ return True
+
+ # if the mouse button is not pressed, no drag should occurr
+ if not event.state & gtk.gdk.BUTTON1_MASK:
+ self._cleanup_drag()
+ return True
+
+ logging.debug('motion_notify_event_cb')
+
+ if event.is_hint:
+ x, y, state_ = event.window.get_pointer()
+ else:
+ x = event.x
+ y = event.y
+
+ if self.drag_check_threshold(int(self._press_start_x),
+ int(self._press_start_y),
+ int(x),
+ int(y)):
+ self._dragging = True
+ self.dragging_started.send(self, position=(x, y),
+ element=self._last_clicked_element)
+
+ return True
+
+class CanvasElement(object):
+ def __init__(self):
+ self.invalidated = dispatch.Signal()
+ self.previous_rect = (0, 0, 0, 0)
+
+ def draw(self, context):
+ self.previous_rect = self.get_rect()
+
+ def invalidate(self):
+ self.invalidated.send(self)
+
+ def get_rect(self):
+ pass
+