Title: [commits] (jeffrey) [16118] [APP] Add lozenge dragging
Revision
16118
Author
jeffrey
Date
2007-12-12 17:54:49 -0800 (Wed, 12 Dec 2007)

Log Message

[APP] Add lozenge dragging
- Drag lozenges smoothly in the x-axis, add a shadow where the event will
actually end up
- Enable dragging between canvases
- Create large transparent bitmaps in a more rational way

Modified Paths

Diff

Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py (16117 => 16118)

--- branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py	2007-12-13 01:52:16 UTC (rev 16117)
+++ branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py	2007-12-13 01:54:49 UTC (rev 16118)
@@ -75,6 +75,10 @@
 DefaultColors = get_lozonge_colors('Blue', 'UnSelected')
 DefaultSelectedColors = get_lozonge_colors('Blue', 'Selected')
 
+# arbitrarily chosen different constants that evaluate false
+DRAG_NO_POSITION = None
+DRAG_OTHER_CANVAS = False
+
 class InitializeSlots(object):
     __slots__ = ()
     def __init__(self, **kw):
@@ -335,7 +339,6 @@
             combined_dc.DrawBitmap(self._background_buffer, 0, 0)
 
         self.ShadeToday(combined_dc)
-        self.DrawPhantom(combined_dc)
 
         if self.draw_lozenges_from_cache:
             combined_dc.DrawBitmap(self._lozenge_buffer, 0, 0)
@@ -347,6 +350,8 @@
             combined_dc.DrawBitmap(self._lozenge_buffer, 0, 0)
             self.draw_lozenges_from_cache = True
 
+        self.DrawPhantom(combined_dc)
+
         self.DrawDraggedLozenge(combined_dc)
 
         dc = wx.BufferedPaintDC(self)
@@ -368,35 +373,62 @@
                         self.DrawLozenge(gc, event, rect, truncate, draw_text)
 
     def DrawDraggedLozenge(self, dc):
-        if not self.drag_id:
+        dragged_from = self.GetDraggedFromWidget()
+        if dragged_from and dragged_from != self:
+            if not self.drag_position:
+                return
+            depth, max_depth = 0, 0
+            real_event = dragged_from.events.get(dragged_from.drag_id)
+            delta = timedelta(0)
+        elif not self.drag_id or self.drag_position == DRAG_OTHER_CANVAS:
             return
+        else:
+            depth, max_depth = self.EventDepth(self.drag_id)
+            real_event = self.events.get(self.drag_id)
+            delta = self.DragEndAdjust()
+
+        offset = wx.Point2D()        
+        gc = wx.GraphicsContext.Create(dc)
         
-        gc = wx.GraphicsContext.Create(dc)
-        real_event = self.events.get(self.drag_id)
-        drag_event = Event(id=None)
-        for slot in drag_event.__slots__:
-            if slot != 'id':
-                setattr(drag_event, slot, getattr(real_event, slot))
-        delta = self.DragEndAdjust()
-        drag_time = (self.TimeFromPosition(self.drag_position)
-                     if self.drag_position else None)
-        if self.drag_edge in (wx.TOP, wx.LEFT):
-            drag_start = drag_time if drag_time else real_event.start
-            drag_event.duration = max(delta, drag_event.end - drag_start)
-            max_start = real_event.end - delta
-            drag_event.start = min(drag_start, max_start)
-        elif self.drag_edge in (wx.BOTTOM, wx.RIGHT):
-            drag_end = drag_time if drag_time else real_event.end
-            duration = drag_end - drag_event.start + delta
-            drag_event.duration = max(delta, duration)
-        depth, max_depth = self.EventDepth(self.drag_id)
+        if self.drag_type == 'move' and self.drag_position == DRAG_NO_POSITION:
+            drag_event = real_event
+        else:
+            drag_event = Event(id=None)
+            for slot in drag_event.__slots__:
+                if slot != 'id':
+                    setattr(drag_event, slot, getattr(real_event, slot))
+            if self.drag_type == 'resize':
+                drag_time = (self.TimeFromPosition(self.drag_position)
+                             if self.drag_position else None)
+                if self.drag_edge in (wx.TOP, wx.LEFT):
+                    drag_start = drag_time if drag_time else real_event.start
+                    drag_event.duration = max(delta, drag_event.end-drag_start)
+                    max_start = real_event.end - delta
+                    drag_event.start = min(drag_start, max_start)
+                elif self.drag_edge in (wx.BOTTOM, wx.RIGHT):
+                    drag_end = drag_time if drag_time else real_event.end
+                    duration = drag_end - drag_event.start + delta
+                    drag_event.duration = max(delta, duration)
+            elif self.drag_position:
+                center = wx.Point2D(*self.drag_position)
+                center += self.drag_offset
+                drag_time = self.TimeFromPosition(center)
+                if drag_time:
+                    depth, max_depth = 0, 0
+                    drag_event.start = drag_time - self.drag_time_offset
+                    self.DrawPhantom(dc, drag_event.start, drag_event.end - delta)
+                    offset = (center.x - self.DayColumnMiddle(center).x,
+                              self.VerticalDragOffset())
+
         for interval in self.intervals:
             if drag_event.overlaps(interval):
                 args = drag_event, interval, depth, max_depth
                 rect, truncate, draw_text = self.LozengeGeometry(*args)
+                rect = wx.Rect2D(*rect)
+                rect.Offset(offset)
                 self.DrawLozenge(gc, drag_event, rect, truncate,
-                                 draw_text, dragging=self.dragging)
-
+                                 draw_text, True)
+    
     def EventDepth(self, event_id):
         """Return the depth, max_depth tuple for the event's first lozenge."""
         event = self.events.get(event_id)
@@ -451,6 +483,12 @@
             day += timedelta(1) * int(x / BASE_DAY_WIDTH)
         return day + timedelta(hours=(y / self.hour_height))
 
+    def DayColumnMiddle(self, position):
+        """Given a position in a day column, return the column's middle."""
+        dummy_time = self.TimeFromPosition(position)
+        day_rect = self.RectFromTime(dummy_time, dummy_time)
+        return day_rect.GetCentre()
+
     def SidesToTruncate(self, real_start, real_end, lozenge_start, lozenge_end):
         truncate = 0 if real_start >= lozenge_start else wx.TOP
         return truncate | (0 if real_end <= lozenge_end else wx.BOTTOM)
@@ -591,14 +629,14 @@
     def PhantomDepth(self, dt):
         return 0
 
-    def DrawPhantom(self, dc):
+    def DrawPhantom(self, dc, start=None, end=None):
         """Highlight a rectangle for a not-yet-created event."""
-        if not self.phantom_start:
+        start = start if start else self.phantom_start
+        end = end if end else self.phantom_end
+        if not start:
             return
-        start = self.phantom_start
-        end = self.phantom_end
-        if not end:
-            end = self.phantom_start + timedelta(hours=1)
+        
+        end = end if end else start + timedelta(hours=1)
         gc = wx.GraphicsContext.Create(dc)
         start, end = min(start, end), max(start, end)
         end += self.DragEndAdjust()
@@ -607,8 +645,7 @@
             if phantom.overlaps(interval):
                 start = max(phantom.start, interval.start)
                 end = min(phantom.end, interval.end)
-                rect = self.RectFromTime(start, end,
-                                         self.PhantomDepth(self.phantom_start))
+                rect = self.RectFromTime(start, end, self.PhantomDepth(start))
                 self.ShadeRect(gc, rect, PHANTOM_COLOR)
 
 
@@ -848,7 +885,9 @@
         self.drag_edge = None
         self.dragging = False
         self.click_position = None
-        self.drag_position = None
+        self.drag_position = DRAG_NO_POSITION
+        self.drag_time_offset = timedelta(0)
+        self.drag_offset = wx.Point2D(0, 0)
 
     def OnMouse(self, event):
         threshold = 5
@@ -859,15 +898,20 @@
             info = self.TopInfoFromPosition(position)
             self.phantom_end = None
             if info:
-                selected_id = info[0].id
-                self.drag_id = selected_id
+                ev, depth, max_depth, range, rect = info
+                selected_id = ev.id
+                self.drag_id = ev.id
+                self.drag_time_offset = self.TimeFromPosition(position) - ev.start
+                self.drag_offset.Set(self.DayColumnMiddle(position).x -
+                                     position.x, 0)
                 self.phantom_start = None
             else:
                 selected_id = None
-                self.phantom_start = roundTime(self.TimeFromPosition(position))
+                start = self.TimeFromPosition(position)
+                if start:
+                    self.phantom_start = roundTime(start, round_down=True)
             self.Refresh()
             wx.PostEvent(self, SelectItemsEvent(selected_ids=[selected_id]))
-            print "left down", self.TimeFromPosition(position)
             
         elif event.LeftUp():
             if self.phantom_start:
@@ -878,52 +922,66 @@
             self.ResetDrag()
             self.Refresh()
         elif event.Entering() or event.Leaving():
-            self.ResetDrag()
-            self.Refresh()
-        if event.Dragging():
+            if self.drag_type != 'move':
+                self.ResetDrag()
+                self.Refresh()
+        elif event.Dragging():
             if not self.click_position:
-                print "dragging, but nothing to do"
+                pass
             elif (not self.dragging and
                   self.click_position.GetDistance(position) < threshold):
-                print "dragging within threshold"
+                pass
             elif not self.drag_type:
+                self.dragging = True
                 edge = self.DragEdge(self.click_position)
                 if edge:
                     self.drag_type = 'resize'
-                    ev, depth, max_depth, range, rect = self.TopInfoFromPosition(self.click_position)
-                    self.drag_id = ev.id
                     self.drag_edge = edge
-                    self.dragging = True
                 elif self.phantom_start:
                     self.drag_type = 'phantom'
-                    self.dragging = True
-            else:    
-                if self.drag_type == 'resize':
-                    self.drag_position = position
-                elif self.drag_type == 'phantom':
-                    self.phantom_end = self.TimeFromPosition(position)
                 else:
-                    pass
-                    #drag_and_drop.DoDragAndDrop(self, 'phantom_lozenge')
-                    #print "done with DragAndDrop"
-            self.Refresh(keep_cache=True)
+                    self.drag_type = 'move'
+            else:
+                if self.drag_type == 'move':
+                    drag_and_drop.DoDragAndDrop(self)
+                    self.ResetDrag()
+                    self.Refresh()
+                else:
+                    if self.drag_type == 'resize':
+                        self.drag_position = position
+                    elif self.drag_type == 'phantom':
+                        self.phantom_end = self.TimeFromPosition(position)                   
+                    self.Refresh(keep_cache=True)
                 
         elif event.Moving():
             self.UpdateCursor(position)
         event.Skip()
 
+    def VerticalDragOffset(self):
+        """Adjust lozenge position based on drag_position (used in all-day)."""
+        return 0
+
     def OnHover(self, x, y, drag_result):
-        if self.CurrentDragFormat() == 'phantom_lozenge':
-            if self.GetDraggedFromWidget() == self:
-                print "hovering in the right window"
-                return wx.DragMove
-            else:
-                print "hovering in the wrong window"
-                return wx.DragNone
-        return drag_result
+        """Hovering while drag and drop is in progress."""
+        drag_position = wx.Point2D(*self.CalcUnscrolledPosition(x, y))
+        if (not drag_position or not self.drag_position or 
+            drag_position.GetRounded() != self.drag_position.GetRounded()):
+            self.drag_position = drag_position
+            self.Refresh(keep_cache=True)
+            dragged_from = self.GetDraggedFromWidget()
+            if dragged_from and dragged_from is not self:
+                if dragged_from.drag_position != DRAG_OTHER_CANVAS:
+                    dragged_from.drag_position = DRAG_OTHER_CANVAS
+                    dragged_from.Refresh(keep_cache=True)
+                    wx.GetApp().Yield(True)
+        return wx.DragMove
 
-    def OnDragResize(self, x, y):
-        if not self.dragging: return
+    def OnLeave(self):
+        """Leaving while drag and drop is in progress."""
+        self.drag_position = DRAG_NO_POSITION
+        if not self.dragging:
+            self.ResetDrag()
+        self.Refresh(keep_cache=True)
         
     def DragEndAdjust(self):
         """
@@ -991,7 +1049,7 @@
 class AllDayCalendar(Calendar):
     draggable_edges = wx.LEFT, wx.RIGHT
     scroll=False
-
+    
     def DrawBackground(self, gc):
         gc.SetPen(wx.Pen(MINOR_LINE_COLOR))
         gc.StrokePath(self.GetDayLinePath(gc))
@@ -1031,17 +1089,19 @@
         # to allow a rect to be calculated for the whole day, leave height
         # large if no depth is passed in
         if depth >= 0:
-            height = 2*TEXT_Y_MARGIN + self.title_font_height + 2*HALO_WIDTH
-            rect.y = height*depth
-            rect.height = height
+            rect.y = self.line_height*depth
+            rect.height = self.line_height
         return rect
 
+    @property
+    def line_height(self):
+        return 2*TEXT_Y_MARGIN + self.title_font_height + 2*HALO_WIDTH
+
     def TimeFromPosition(self, position):
         dt = super(AllDayCalendar, self).TimeFromPosition(position)
         if dt:
             return dt.replace(hour=0, minute=0, second=0, microsecond=0)
 
-
     def SidesToTruncate(self, real_start, real_end, lozenge_start, lozenge_end):
         truncate = 0 if real_start >= lozenge_start else wx.LEFT
         return truncate | (0 if real_end <= lozenge_end else wx.RIGHT)
@@ -1062,6 +1122,15 @@
                 return len([e for e in cluster.event_list if event.overlaps(e)])
         return 0
 
+    def VerticalDragOffset(self):
+        """Adjust lozenge position based on drag_position (used in all-day)."""
+        if not self.drag_position:
+            return 0
+        if not self.click_position:
+            return self.drag_position.y - self.line_height / 2
+        return self.drag_position.y - (self.click_position.y % self.line_height)
+
+
 class CalendarPanel(wx.Panel):
     def __init__(self, parent, **kw):
         super(CalendarPanel, self).__init__(parent, -1, style=wx.NO_BORDER, **kw)
@@ -1331,17 +1400,19 @@
     gc.SetFont(font)
     return gc.GetTextExtent('M')[1]
 
-empty = '\x00' * 1200**2
+empty = '\x00' * 500**2
 
 def transparent_bitmap(w, h):
     image = wx.EmptyImage(w, h)
     image.InitAlpha()
     alpha = image.GetAlphaBuffer()
-    if len(alpha) < len(empty):
-        alpha[:] = empty[:len(alpha)]
-    else:
-        for i in xrange(len(alpha)):
-            alpha[i] = '\x00'
+    position = 0
+    window = len(empty)
+    total = len(alpha)
+    while position < total:
+        end = min(total, position + window)
+        alpha[position:end] = empty[:end-position]
+        position += window
 
     return wx.BitmapFromImage(image)
 
@@ -1350,11 +1421,12 @@
     rect.Inset(-outset,-outset)
     return rect
 
-def roundTime(dt, nearest_minutes=15):
+def roundTime(dt, nearest_minutes=15, round_down=False):
     day_start = dt.combine(dt, time(0))
     delta = dt - day_start
     nearest_seconds = 60.0 * nearest_minutes
-    seconds = round(delta.seconds / nearest_seconds) * nearest_seconds
+    round_func = int if round_down else round
+    seconds = round_func(delta.seconds / nearest_seconds) * nearest_seconds
     return day_start + timedelta(seconds=seconds)
 
 def shortTZ(dt, tzinfo=None):

Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py (16117 => 16118)

--- branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py	2007-12-13 01:52:16 UTC (rev 16117)
+++ branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py	2007-12-13 01:54:49 UTC (rev 16118)
@@ -6,7 +6,8 @@
 # what the drag source window was.
 DraggedFromWidget = None
 
-def DoDragAndDrop(source_window, format_label, data_object=None):
+def DoDragAndDrop(source_window, format_label='chandler_item_ids',
+                  data_object=None):
     """
     Do a Drag And Drop operation, given the data in the selection.
 
@@ -42,7 +43,6 @@
         """
         Override this to decide whether or not to accept a dropped item.
         """
-        print "OnRequestDrop", x, y, self.GetDraggedFromWidget()
         # default - don't allow drop onto ourself
         return self.GetDraggedFromWidget() is not self
         
@@ -66,7 +66,6 @@
         @return: A wxDragResult other than dragResult if you want to change
                  the drag operation
         """
-        #print "OnHover", dragResult, x, y, self.GetDraggedFromWidget()
         # default - don't allow dragging to ourself
         if self.GetDraggedFromWidget() is self:
             return wx.DragNone
@@ -84,7 +83,10 @@
         return DraggedFromWidget
 
     def CurrentDragFormat(self):
-        return self.GetDraggedFromWidget()._drag_format_label
+        if DraggedFromWidget:
+            return DraggedFromWidget._drag_format_label
+        else:
+            return ""
 
 class CustomDropTarget(wx.DropTarget):
     """




_______________________________________________
Commits mailing list
[email protected]
http://lists.osafoundation.org/mailman/listinfo/commits

Reply via email to