#!/usr/bin/python import goocanvas import gtk import cairo import random class Stack(goocanvas.Group): def __init__(self, canvas, x = 0, y = 0): self.dragging = False self.cards = [] self.x = x self.y = y self.canvas = canvas goocanvas.Group.__init__(self) self.set_canvas(self.canvas) self.props.parent = canvas.get_root_item() self.connect ("leave_notify_event", self.on_leave_notify) self.connect ("enter_notify_event", self.on_enter_notify) self.connect ("motion_notify_event", self.on_motion_notify) self.connect ("button_press_event", self.on_button_press) self.connect ("button_release_event", self.on_button_release) self.stack = self self.parent_stack = False self.top = False def resetBorder(self): self.top.border.props.line_width = self.top.border_width def on_leave_notify(self, item, target, event): try: self.resetBorder() pass except: pass def on_enter_notify(self, item, target, event): self.top.border.props.line_width = 4 def addCard(self, card, adjust_position=True): card.set_transform(cairo.Matrix()) card.props.parent = self try: top_card_bounds = self.top.get_bounds() stack_bounds = self.get_bounds() card.translate(top_card_bounds.x1 - stack_bounds.x1, top_card_bounds.y1 - stack_bounds.y1) except: pass if len(self.cards) < 5: offset = 3 else: offset = (random.randint(0,5)==0) * (random.randint(0,len(self.cards)/30) == 0) * 3 if adjust_position is True: self.translate((-1 * offset)+1, (-1 * offset)+1) for x in self.cards: x.translate(offset,offset) self.cards.append(card) self.top = card def on_motion_notify (self, item, target, event): if self.dragging and event.state and gtk.gdk.BUTTON1_MASK: self.translate (event.x - self.drag_x, event.y - self.drag_y) area = self.get_bounds() below = self.canvas.get_items_in_area(area, True, True, True) if below is not None: drop_candidates = [] for stack in below: if stack is not self and hasattr(stack, "addCard"): drop_candidates.append(stack) current_selection = self.canvas.drag_selection # TODO select closest based on distance if len(drop_candidates) > 0: drop_target = drop_candidates[0] if drop_target is not current_selection: if current_selection: current_selection.on_drag_unselect(drop_target) drop_target.on_drag_select(self) else: drop_target = False if current_selection: current_selection.on_drag_unselect(drop_target) self.canvas.drag_selection = drop_target return True def on_drag_select(self, dragging): self.top.border.props.line_width = 3 def on_drag_unselect(self, dragging): self.resetBorder() def on_button_press (self, item, target, event): if event.button == 1: # are we dragging a stack or a single card if event.state & gtk.gdk.SHIFT_MASK: drug = self else: # create a new stack for the single card to go into drug = Stack(self.canvas, self.x, self.y) moved_card = self.cards[len(self.cards)-1] bounds = goocanvas.Bounds() bounds = moved_card.get_bounds() moved_card.props.parent = drug drug.parent_stack = self drug.addCard(moved_card) self.cards.pop() drug.parent_stack = self drug.translate(bounds.x1 + moved_card.border_width, bounds.y1 + moved_card.border_width) try: self.top = self.cards[len(self.cards) - 1] self.top.border.props.line_width = 4 except: self.top = False drug.raise_(None) fleur = gtk.gdk.Cursor (gtk.gdk.FLEUR) self.canvas.pointer_grab (drug, gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_RELEASE_MASK, fleur, event.time) drug.dragging = True drug.drag_x = event.x drug.drag_y = event.y def on_button_release (self, item, target, event): self.canvas.pointer_ungrab (self, event.time) self.dragging = False if self.canvas.drag_selection: selection = self.canvas.drag_selection selection.on_drag_unselect(self) self.resetBorder() for card in self.cards: selection.addCard(card) print "moved to stack" else: print "new stack" self.canvas.drag_selection = False class Card(goocanvas.Group): width = 200 height = 300 rim_width = 12 border_width = 2 border_color = "gray" background_color = "tan" backside_color = "brown" flipped = False title = None def __init__(self, parent, title = None): self.title = title goocanvas.Group.__init__(self, parent=parent) self.border = goocanvas.Rect (parent = self, x = 0, y = 0, width = self.width, height = self.height, fill_color = self.border_color, radius_x = 10, radius_y = 10) if self.flipped: goocanvas.Rect (parent = self, x = self.rim_width, y = self.rim_width, width = self.width - (2 * self.rim_width), height = self.height - (2 * self.rim_width), fill_color = self.backside_color) else: goocanvas.Rect (parent = self, x = self.rim_width, y = self.rim_width, width = self.width - (2 * self.rim_width), height = self.height - (2 * self.rim_width), fill_color = self.background_color) if self.title is not None: text = goocanvas.Text(parent = self, text = self.title, x = self.rim_width * 1.5, y = self.rim_width * 1.5) def setup_canvas (canvas): s = Stack(canvas) s.translate(100,100) for i in range(60): s.addCard(Card(parent=s, title = "TEST " + str(i)), adjust_position=False) #s = Stack(canvas, 500, 500) #for i in range(10): # s.addCard(Card(parent=s, title = "TEST " + str(i))) def create_card_page (): canvas = PlaySurface() canvas.set_size_request (600, 450) canvas.set_bounds (0, 0, 1000, 1000) vbox = gtk.VBox (False, 4) vbox.set_border_width (4) hbox = gtk.HBox (False, 4) vbox.pack_start (hbox, False, False, 0) w = gtk.Label ("Zoom:") hbox.pack_start (w, False, False, 0) adj = gtk.Adjustment (1.00, 0.05, 1.00, 0.05, 0.50, 0.50) adj.connect("value_changed", canvas.zoom_changed, canvas) w = gtk.SpinButton (adj, 0.0, 2) w.set_size_request (50, -1) hbox.pack_start (w, False, False, 0) scrolled_win = gtk.ScrolledWindow () scrolled_win.set_shadow_type (gtk.SHADOW_IN) vbox.add (scrolled_win) canvas.scrolled_win = scrolled_win scrolled_win.add (canvas) return vbox, canvas class PlaySurface(goocanvas.Canvas): dragging = False drag_selection = False def __init__(self): gtk.Widget.__init__(self) #self.connect ("motion_notify_event", self.on_motion_notify) #self.connect ("button_press_event", self.on_button_press) #self.connect ("button_release_event", self.on_button_release) self.root = self.get_root_item() self.scroll_speed = 1 def zoom_changed (self, adj, canvas): canvas.set_scale (adj.get_value()) def on_motion_notify (self, item, event): if self.dragging and event.state and gtk.gdk.BUTTON1_MASK: self.scrolled_win.get_hadjustment().set_value(self.scrolled_win.get_hadjustment().value + ((event.x - self.drag_x) * self.scroll_speed)) self.scrolled_win.get_vadjustment().set_value(self.scrolled_win.get_vadjustment().value + ((event.y - self.drag_y) * self.scroll_speed)) self.drag_x = event.x self.drag_y = event.y def on_button_press (self, item, event): if event.button == 1: fleur = gtk.gdk.Cursor (gtk.gdk.FLEUR) self.pointer_grab (self.root, gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_RELEASE_MASK, fleur, event.time) self.dragging = True self.drag_x = event.x self.drag_y = event.y def on_button_release (self, item, event): self.pointer_ungrab (self.root, event.time) self.dragging = False def main (): v, canvas = create_card_page () w = gtk.Window() w.connect("destroy", gtk.main_quit) w.add(v) w.show_all() setup_canvas(canvas) gtk.main() if __name__ == "__main__": main()