commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r8331 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/w


From: jcater
Subject: [gnue] r8331 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/wx/uihelpers/documentcanvas
Date: Mon, 3 Apr 2006 18:24:41 -0500 (CDT)

Author: jcater
Date: 2006-03-31 17:46:55 -0600 (Fri, 31 Mar 2006)
New Revision: 8331

Modified:
   trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
   trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
   trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
   trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
Log:
More work on the new document painter canvas; added rubberband selection 
support and reworked the mouse functions completely. Also implemented resizer 
grippers.


Modified: trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
===================================================================
--- trunk/gnue-designer/src/forms/PagePainter/PagePainter.py    2006-03-31 
22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/forms/PagePainter/PagePainter.py    2006-03-31 
23:46:55 UTC (rev 8331)
@@ -53,7 +53,7 @@
 
   def init(self, object):
 
-    canvas = self.canvas = DocumentCanvas(self)
+    canvas = self.canvas = PainterCanvas(self)
     self.document.app.ui.autoSizer(self, canvas)
 
     self.object = object
@@ -79,7 +79,11 @@
       xscale = max(xscale, w)
       yscale = max(yscale, h)
 
-    canvas.set_grid_size(xscale, yscale)
+    # Make the scale slightly larger, so that input widgets
+    # are bigger than text labels.
+    xscale += 4
+    yscale += 4
+    canvas.set_grid_scale(xscale, yscale)
 
     # Draw initial objects
     self.object.walk(self.inventoryObject)
@@ -87,25 +91,28 @@
 
   def inventoryObject(self, object):
 
-    canvas = self.canvas
+    # Right now, we assume if an object has an x,y component, it is drawable.
+    # This won't be true when we support layout management.
     try:
       x = object['Char:x']
       y = object['Char:y']
     except:
+      print "Not inventorying %s" % object
       return
 
-    try:
-      w = object['Char:width']
-      h = object['Char:height']
-    except:
-      w = object['Char:width'] = 10
-      h = object['Char:height'] = 1
+    canvas = self.canvas
 
-    widget = MyObject(object, canvas)
+    cls = getWidgetSkinClass(object)
+    
+    widget = cls(object, canvas)
     canvas.add(widget)
-    widget.SetSize(w*xscale,h*yscale)
-    widget.MoveTo(x*xscale,y*yscale)
 
+    # Debugging...
+    widget.set_selected(True)
+    
+    print "Added %s" % object._type
+
+
   def __objectSelected(self, object):
     pass
 
@@ -134,67 +141,139 @@
 from gnue.designer.uidrivers.wx.uihelpers.documentcanvas.colors import 
colorIndex
 
 #--------------------------------------------------------------------------
-# MyObject
+# form widgets
 #--------------------------------------------------------------------------
 
-class MyObject(DocumentWidget):
-  def __init__(self, object, canvas):
-    self.__object = object
-    DocumentWidget.__init__(self, canvas)
-
-  def draw(self, dc):
-    dc.BeginDrawing()
-    dc.SetPen(wx.Pen(colorIndex['text']))
-    object = self.__object
-    x,y,w,h = self.bounds
-    dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
-    if object._type == 'GFLabel':
-      x = 0
-      for char in object.text:
-        dc.DrawText(char, x,0)
-        x += xscale
-    elif object._type == 'GFEntry':
-      dc.DrawRoundedRectangle(0,0,w,h, 1)
+def getWidgetSkinClass(object): 
+    if object._type == 'GFLabel': 
+        cls = LabelWidget    
+    elif object._type == 'GFEntry': 
+           cls = EntryWidget    
     else:
-      dc.DrawRoundedRectangle(0,0,w,h, 4)
+        cls = UnknownWidget
 
-  def DragHitTest (self, x, y):
-    if self.bounds.x <= x <= self.bounds.x + self.bounds.width and \
-       self.bounds.y <= y <= self.bounds.y + self.bounds.height:
-      return True
-    else:
-      return False
+    return cls
 
-  def ConvertDrawableObjectToDataObject (self, x, y):
-    object = self
 
-    data = [ { "Type" : "selectedWidgets",
-               "Attributes": {
-                   'startingX': x,
-                   'startingY': y,
-                   }
-               }]
-#        "name": dname,
-#        "table": object.name,
-#        "database": self.connectionName } }
-#
-#
-    do = 
wx.CustomDataObject(wx.CustomDataFormat("application/x-gnue-designer"))
-    do.SetData(cPickle.dumps(data,1))
+class FormWidget(DocumentWidget): 
+    """
+    A DocumentWidget specific to the forms document type
+    """
 
-    return do
+    # If Char:height or Char:width aren't specified, assume...
+    char_default_height = 1
+    char_default_width = 10
 
-  def SelectedHitTest(self, x, y):
-    if self.bounds.x <= x <= self.bounds.x + self.bounds.width and \
-       self.bounds.y <= y <= self.bounds.y + self.bounds.height:
-      return True
-    else:
-      return False
+    # Specific widgets can redefine these if they can't go smaller or larger
+    # than a certain size (e.g., labels are always 1 char high)
+    char_min_height = 1
+    char_min_width = 1
+    char_max_height = 99999999
+    char_max_width = 99999999
+    
+    # Compensation for the border drawn around objects if selected
+    border_compensation = 4 
+    
+    
+    def calc_metrics(self):
+    
+        canvas = self.canvas
+        
+        object = self.gobject
 
+        # This gives us an offset onto the current drawing canvas
+        # accounting for any workspace margins.
+        documentX = canvas.document_origin_x
+        documentY = canvas.document_origin_y
 
+        # Right now, only Char positioning is supported.
+        x = object['Char:x']
+        y = object['Char:y']
+
+        try:
+            w = object['Char:width']
+        except KeyError: 
+            w =  self.char_default_width          
+
+        try: 
+            h = object['Char:height']
+        except KeyError: 
+            h =  self.char_default_height
+       
+        # Convert from Char positions into wx positions
+        x *= xscale
+        y *= yscale
+        h *= xscale
+        w *= xscale
+        
+        # Used to figure in compensation needed for selection box
+        # decorations and borders
+        compensation = self.border_compensation
+        
+        # Save the bounds, relative to the canvas, of the area
+        # that needs to be refreshed when this widget changes. 
+        # (Includes all generic decoration, like selection borders)
+        self.refresh_bounds = wx.Rect(x + documentX  - compensation, 
+                                      y + documentY - compensation,
+                                      w + compensation * 2, 
+                                      h  + compensation * 2)
+        
+        # Save the bounds, relative to the canvas, of the area
+        # that the widget sans decorations occupy on the 
+        self.hit_test_bounds = wx.Rect(x  + documentX, 
+                                       y + documentY, 
+                                       w, h)
+
+        # Save the bounds, relative to being drawn in a context at (0,0)
+        self.draw_bounds = wx.Rect(compensation, compensation, w, h)
+        
+        
+        
+  #def move(self, x, y): 
+
+
+class LabelWidget(FormWidget): 
+  
+    char_max_height = 1
+  
+    def draw_widget(self, dc):
+        dc.SetPen(wx.Pen(colorIndex['text']))
+        object = self.gobject
+        x, y, w, h = self.draw_bounds.Get()
+        dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
+        # Center each character in its cell
+        for char in object.text:
+            cw, ch = dc.GetTextExtent(char)
+            dc.DrawText(char, x + int((xscale - cw)/2), y)
+            x += xscale
+
+
+class EntryWidget(FormWidget): 
+    def draw_widget(self, dc):
+        dc.SetPen(wx.Pen(colorIndex['widget']))
+        x,y,w,h = self.draw_bounds.Get()
+        dc.DrawRoundedRectangle(x,y,w,h, 1)
+
+
+class UnknownWidget(FormWidget): 
+    def draw_widget(self, dc):
+        dc.SetPen(wx.Pen(colorIndex['widget']))
+        object = self.gobject
+        x,y,w,h = self.draw_bounds.Get()
+        dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
+        dc.DrawRoundedRectangle(x,y,w,h, 4)
+
+
 #==========================================================================
 # Canvas implementation
 #==========================================================================
-class PaintCanvas(DocumentCanvas):
-  def draw_document_frame(self, dc):
-    object = self._object
+class PainterCanvas(DocumentCanvas):
+
+    def __init__(self, *args, **parms): 
+        DocumentCanvas.__init__(self, *args, **parms)
+        
+        self.document_origin_x = xscale * 2
+        self.document_origin_y = xscale * 2
+    
+
+

Modified: 
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py     
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py     
2006-03-31 23:46:55 UTC (rev 8331)
@@ -20,10 +20,6 @@
 #
 # $Id: TriggerEditor.py 8210 2006-03-07 00:04:31Z jcater $
 
-#
-# These classes were based on SimpleCanvas from OSA's Chandler
-# application, but heavily commented, debugged, and extended.
-#
 
 import time
 
@@ -31,6 +27,17 @@
 
 from colors import colorIndex, buildColorIndex
 
+cursor_map = {'resize-top': wx.CURSOR_SIZENS, 
+              'resize-bottom': wx.CURSOR_SIZENS, 
+              'resize-left': wx.CURSOR_SIZEWE, 
+              'resize-right': wx.CURSOR_SIZEWE, 
+              'resize-ul': wx.CURSOR_SIZENWSE, 
+              'resize-lr': wx.CURSOR_SIZENWSE,
+              'resize-ll': wx.CURSOR_SIZENESW, 
+              'resize-ur': wx.CURSOR_SIZENESW, 
+              'move': wx.CURSOR_SIZING, 
+             }
+
 # ===================================================================
 # The canvas to draw on
 # ===================================================================
@@ -46,20 +53,56 @@
         if not colorIndex:
           buildColorIndex()
 
+        self.ordered_widget_list = []
 
-        self.autoCreateDistance = 0
-        self.zOrderedDrawableObjects = []
-        self.internalDnDItem = None
         self.Bind(wx.EVT_PAINT, self.__wx_on_paint)
         self.Bind(wx.EVT_ERASE_BACKGROUND, self.__wx_on_erase_background)
         self.Bind(wx.EVT_MOUSE_EVENTS, self.__wx_on_mouse_event)
 
+        
+        # Mouse events
+        self.__mouse_hot_spots = []
+        self.__current_cursor = None
+        self.__mouse_mode = 'select'
+        self.__mouse_click_function = None
+        self.__mouse_current_action = ""                        
 
+
     def __del__(self):
-        for item in self.zOrderedDrawableObjects:
+        for item in self.ordered_widget_list:
             item.Destroy()
+        self.ordered_widget_list = None
 
-    def set_grid_size(self, x, y):
+    def set_mouse_mode(self, mode='select', function=None): 
+        """
+        mode       String that is one of: 
+        
+          select:  The default mouse behavior, in which existing objects
+                   can be selected, resized, moved, etc.
+                   
+          edit:    Clicking any object "edits" that object. (Mainly, 
+                   used to change text on labels).  Clicking the canvas
+                   has no action.
+                   
+          create:  Clicking the canvas creates a new object. This mode 
+                   doesn't actually do anything... your "method" passed in 
+                   should handle the actual creation. 
+                   
+          delete:  Clicking an object "deletes" the object.  This mode
+                   doesn't do anything ... your method should handle it. 
+                   
+
+        method     A python method to call once the target action happens. 
+        
+        """
+        self.__mouse_mode = mode
+        self.__mouse_click_function = method
+        
+
+    def set_grid_scale(self, x, y):
+      """
+      Sets the grid x,y spacing
+      """
       self.grid_x_spacing = x
       self.grid_y_spacing = y
 
@@ -70,20 +113,19 @@
 
     def __wx_on_paint(self, event):
         """
-          wx.BufferedDC doesn't work here since it doesn't handle scrolled 
windows
-        and always allocates a buffer the size of the client area. So instead 
we'll
-        allocate a wx.MemoryDC, draw into it then blit it to our paintDC.
-          Eventually, if we're printing, we should bypass the wx.MemoryDC.
-          The updateRegion is not in scrolled coordinates.
+        Catch the wx OnPaint event and draw the canvas and any widgets
         """
+        
+        # The updateRegion is not in scrolled coordinates.
         scrollWindowOriginX, scrollWindowOriginY = 
self.CalcUnscrolledPosition(0, 0)
 
+        # wx.BufferedDC doesn't work here since it doesn't handle scrolled 
windows
+        # and always allocates a buffer the size of the client area. So 
instead we'll
+        # allocate a wx.MemoryDC, draw into it then blit it to our paintDC.
         paintDC = wx.PaintDC(self)
         self.PrepareDC(paintDC)
 
-        """
-          Calculate the rectangle that needs updating in scrolled coordinates
-        """
+        # Calculate the rectangle that needs updating in scrolled coordinates
         updateRect = self.GetUpdateRegion().GetBox()
         bufferX = updateRect.GetLeft() + scrollWindowOriginX
         bufferY = updateRect.GetTop() + scrollWindowOriginY
@@ -95,9 +137,7 @@
         memoryDC.SelectObject(offscreenBuffer)
         memoryDC.SetDeviceOrigin(-bufferX, -bufferY)
 
-        """
-          Debugging code that makes it easy to see which areas are updating.
-        """
+        ## Debugging code that makes it easy to see which areas are updating.
         if 0:
           success = paintDC.Blit(bufferX, bufferY,
                                   bufferWidth, bufferHeight,
@@ -116,8 +156,8 @@
 
         self.draw_background(memoryDC)
         self.draw_widgets(memoryDC)
-        self.draw_selection(memoryDC)
 
+        # Move the offscreen bitmap onto the scrolled window
         paintDC.Blit(bufferX, bufferY,
                       bufferWidth, bufferHeight,
                       memoryDC,
@@ -126,29 +166,110 @@
         memoryDC.EndDrawing()
 
     def draw_widgets(self, dc):
+      """
+      Draw the individual widgets
+      """
+      
       updateRegion = self.GetUpdateRegion()
-      scrollWindowOriginX, scrollWindowOriginY = 
self.CalcUnscrolledPosition(0, 0)
+      
+      (scrollWindowOriginX, 
+       scrollWindowOriginY) = self.CalcUnscrolledPosition(0, 0)
+       
       dcOriginX, dcOriginY = dc.GetDeviceOrigin()
-      index = len(self.zOrderedDrawableObjects) - 1
+      
+      selection_bounds = None
+
+      index = len(self.ordered_widget_list) - 1
+      
+      
       while index >= 0:
-        drawableObject = self.zOrderedDrawableObjects [index]
-        objectX = drawableObject.bounds.GetLeft()
-        objectY = drawableObject.bounds.GetTop()
-        objectWidth = drawableObject.bounds.GetWidth()
-        objectHeight = drawableObject.bounds.GetHeight()
+        drawableObject = self.ordered_widget_list [index]
+        refresh_bounds = drawableObject.refresh_bounds 
+        
+        (objectX, objectY, 
+         objectWidth, objectHeight) = refresh_bounds.Get()
 
+        # Calculate the bounds, offset by the scrollwindow's origin
         bounds = wx.Rect(objectX - scrollWindowOriginX,
-                          objectY - scrollWindowOriginY,
-                          objectWidth,
-                          objectHeight)
+                         objectY - scrollWindowOriginY, 
+                         objectWidth, objectHeight)
 
-        if updateRegion.ContainsRect(bounds) != wx.OutRegion and 
drawableObject.visible:
-          dc.SetDeviceOrigin(objectX + dcOriginX, objectY + dcOriginY)
-          drawableObject.draw(dc)
-          dc.SetDeviceOrigin(dcOriginX, dcOriginY)
+        
+
+        # Keep track of the overall bounds of all the selected 
+        # widgets, so we can draw a selection box later
+        if drawableObject.selected: 
+          if selection_bounds is None: 
+             selection_bounds = bounds
+          else: 
+             selection_bounds = selection_bounds.Union(bounds)
+
+        if (updateRegion.ContainsRect(bounds) != wx.OutRegion and 
+            drawableObject.visible):
+            
+            dc.SetDeviceOrigin(objectX + dcOriginX, objectY + dcOriginY)
+            drawableObject.draw(dc)
+            dc.SetDeviceOrigin(dcOriginX, dcOriginY)
+
         index -= 1
+      
+      # TODO: Somehow move most of this logic out of draw...
+      
+      # Draw a box around all the selected widgets  
+      if selection_bounds is not None:
+          self.__mouse_hot_spots = []
+          add_hotspot = self.__mouse_hot_spots.append
+          
+          x, y, w, h = selection_bounds.Get()
+          
+          # Not sure why we have to do this, but it makes the 
+          # box draw correctly.
+          x += 2
+          y += 2
+          w -= 4
+          h -= 4
+          
+          # TODO: quick fix for enforcing a min size (fix!)
+          w = max(w, 17)
+          h = max(h, 17)
 
+          selection_bounds = self.__selection_bounds = wx.Rect(x,y,w,h)
+
+          # Draw the main border
+          dc.SetBrush(wx.Brush(wx.BLACK, wx.TRANSPARENT))
+          dc.SetPen(wx.Pen(colorIndex['selectionframe']))
+          dc.DrawRectangleRect(selection_bounds)          
+          
+          # Draw the sizer grippers
+          dc.SetBrush(wx.Brush(wx.WHITE))
+          for bx, by, position in ( 
+             (x - 2, y - 2, 'resize-ul'),                  # upper left
+             (x + w - 3, y - 2, 'resize-ur'),              # upper right
+             (x - 2, y + h - 3, 'resize-ll'),              # lower left
+             (x + w - 3, y + h - 3, 'resize-lr'),          # lower right
+             (x - 2 + w / 2, y - 2, 'resize-top'),         # top middle
+             (x - 2, y - 2 + h / 2,'resize-left'),         # left middle
+             (x + w - 3, y - 2 + h / 2, 'resize-right'),   # right middle
+             (x - 2 + w / 2 , y + h - 3, 'resize-bottom'), # bottom middle
+             ):
+
+             rect = wx.Rect(bx,by,5,5)
+             dc.DrawRectangleRect(rect)
+             add_hotspot((rect,position))
+
+          # Add hotspot markers for the selection box        
+          add_hotspot((wx.Rect(x, y, 1, h), 'resize-left'))
+          add_hotspot((wx.Rect(x, y, w, 1), 'resize-top'))
+          add_hotspot((wx.Rect(x + w - 1, y, 1, h), 'resize-right'))
+          add_hotspot((wx.Rect(x, y + h - 1, w, 1), 'resize-bottom'))
+          add_hotspot((wx.Rect(x + 1, y + 1, w - 1, h - 1), 'move'))
+  
+
+
     def draw_background(self, dc):
+      """
+      Draw a background (with grid if appropriate)
+      """
       dc.SetBackgroundMode(wx.TRANSPARENT)
       dc.Clear()
       #
@@ -170,6 +291,7 @@
           dc.DrawLine(x,0,x,h-1)
           if switchToMainPen:
             dc.SetPen(pen1)
+            switchToMainPen = False
 
 
         # Draw horizontal grid lines
@@ -181,6 +303,7 @@
           dc.DrawLine(0, y, w, y)
           if switchToMainPen:
             dc.SetPen(pen1)
+            switchToMainPen = False
 
       elif self.gridStyle == 2:
         SOLIDSPACING = 4
@@ -205,174 +328,279 @@
           dc.DrawLine(0, y, w, y)
 
 
-    def draw_selection(self, dc):
-      print "Draw da box, fewl"
 
-
     def __wx_on_erase_background(self, event):
       """
-        Override __wx_on_erase_background to avoid erasing background. Instead
+      Override __wx_on_erase_background to avoid erasing background. Instead
       implement OnDrawBackground to draw/erase the background. This
       design alternative will eliminate flicker
       """
       pass
 
+
+    # ---------------------------------------------------------------
+    # Mouse events
+    # ---------------------------------------------------------------
     def __wx_on_mouse_event(self, event):
       x, y = event.GetPositionTuple()
       x, y = self.CalcUnscrolledPosition(x, y)
-      for drawableObject in self.zOrderedDrawableObjects:
-        if drawableObject.bounds.Inside((x, y)):
-          event.m_x = x - drawableObject.bounds.GetX()
-          event.m_y = y - drawableObject.bounds.GetY()
-          if drawableObject.ProcessEvent(event):
-            return True
 
-      if self.autoCreateDistance != 0:
-        if event.ButtonDown() and self.CreateHitTest(x, y):
-            self.dragStart = wx.Point(x, y)
-            self.CaptureMouse()
-            return True
-        elif hasattr(self, 'dragStart'):
-          if event.Dragging():
-            """
-              Clip mouse position to the scrolling window's bounds
-            """
-            boundsX, boundsY = self.GetVirtualSizeTuple()
-            if x < 0:
-              x = 0
-            if x > boundsX:
-              x = boundsX
-            if y < 0:
-              y = 0
-            if y > boundsY:
-              y = boundsY
+      mode = self.__mouse_mode
+      current_action = self.__mouse_current_action
+      
+      if event.Dragging(): 
+          if mode == 'select':
+              if current_action == 'rubberband': 
+                  self.__update_rubberband(x,y) 
+                  
+              elif current_action == 'move': 
+                  self.__update_moving(x,y)
+                  
+              elif current_action == 'resize':
+                  self.__update_resizing(x,y)
+              
+          
+      elif event.Moving():
+          cursor = None
+          if  mode == 'select':
+              if not current_action:
+                  # No previous action. Should we change the cursor?
+                  for rect, position in self.__mouse_hot_spots:
+                      if rect.InsideXY(x, y):
+                          cursor = cursor_map[position]
+                          break
+                  if not cursor: 
+                      cursor = wx.CURSOR_ARROW
 
-            deltaX =  x - self.dragStart.x
-            deltaY =  y - self.dragStart.y
+                  if cursor != self.__current_cursor:
+                      self.SetCursor(wx.StockCursor(cursor))
+                      
+                  return True
 
-            if deltaX >= 0:
-              left = self.dragStart.x
-              width = deltaX
-            else:
-              left = x
-              width = -deltaX
+          
+      elif event.LeftDown(): 
 
-            if deltaY >= 0:
-              top = self.dragStart.y
-              height = deltaY
-            else:
-              top = y
-              height = -deltaY
-            dragRect = wx.Rect(left, top, width, height)
+          if mode == 'select': 
 
-            if not hasattr(self, 'dragCreateDrawableObject'):
-              if(deltaX * deltaX) +(deltaY * deltaY) >(self.autoCreateDistance 
* self.autoCreateDistance):
-                """
-                Create a new drawable object if we've dragged 
autoCreateDistance
-                pixels
-                """
-                object = self.dragCreateDrawableObject = 
self.CreateNewDrawableObject(dragRect,
-                                                                              
self.dragStart,
-                                                                              
wx.Point(x, y))
-                # if we weren't allowed to create one, give up
-                if object is None:
-                    return True
+              if not current_action: 
+                  # No previous action. 
+                  new_action = None
+                  
+                  # Check to see if a currently selected area has been 
+                  # clicked, and whether it will be moved or resized:
+                  for rect, action in self.__mouse_hot_spots:
+                      if rect.InsideXY(x, y):
+                          new_action = action
+                          break
 
-                self.DeSelectAll()
-                object.selected = True
+                  # Check to see if any widgets were clicked                   
       
+                  if not new_action: 
+                      for widget in self.ordered_widget_list:
+                          if widget.select_hit_test(x,y): 
+                              new_action = 'move'
+                              break
+              
+                  # Finally, assume we're creating a rubberband selector
+                  if not new_action: 
+                      new_action = 'rubberband'
+                      self.__start_rubberband(x, y)
+                      
+                  elif new_action == 'move': 
+                      self.__start_moving(x,y)
 
+                  elif new_action.startswith('resize-'): 
+                      self.__start_resizing(x, y,
+                                      new_action.split('-')[1])
+                      new_action = 'resize'
 
-                #self.zOrderedDrawableObjects.insert(0, 
self.dragCreateDrawableObject)
-                
#self.refresh_scrolled_rect(self.dragCreateDrawableObject.bounds);
-                self.add(object)
+                  self.__mouse_current_action = new_action
+                  
+                  return True
+                      
 
+
+      elif event.LeftUp(): 
+
+          if mode == 'select': 
+
+              if current_action == 'rubberband': 
+                  self.__stop_rubberband()
+                                
+              elif current_action == 'move': 
+                  self.__stop_moving()
+                  
+              elif current_action == 'resize': 
+                  self.__stop_resizing()
+
+              self.__mouse_current_action = ''
+
               return True
-            else:
-              if self.dragCreateDrawableObject != None:
-                self.dragCreateDrawableObject.SizeDrag(dragRect,
-                                                        self.dragStart,
-                                                        wx.Point(x, y))
 
-          elif event.ButtonUp():
-            del self.dragStart
-            if hasattr(self, 'dragCreateDrawableObject'):
-                del self.dragCreateDrawableObject
-            self.ReleaseMouse()
-            return True
+      
       return False
 
+
+
+
+    # ---------------------------------------------------------------
+    # Get all selected widgets
+    # ---------------------------------------------------------------
+    def get_selected_widgets(self): 
+      """
+      Return a list of the currently selected widgets
+      """
+      results = []
+      append = results.append
+      for widget in self.ordered_widget_list:
+          if widget.selected:
+             append(widget)
+            
+      return results
+
+
+    # ---------------------------------------------------------------
+    # Support for visual moving hints
+    # ---------------------------------------------------------------
+    def __start_moving(self, x, y): 
+        self.__mouse_move_items = widgets = self.get_selected_widgets()
+        self.CaptureMouse()
+        for widget in widgets: 
+            widget.selected = False
+            widget.show(False)
+    
+    def __update_moving(self, x, y): 
+        pass
+    
+    def __stop_moving(self): 
+        self.ReleaseMouse()
+        for widget in self.__mouse_move_items: 
+            widget.selected = True
+            widget.show(True)
+        self.__mouse_move_items = None
+
+    # ---------------------------------------------------------------
+    # Support for visual resizing hints
+    # ---------------------------------------------------------------
+    def __start_resizing(self, x, y, orientation): 
+        self.__mouse_resize_orientation = orientation
+        self.CaptureMouse()
+    
+    def __update_resizing(self, x, y): 
+        orientation = self.__mouse_resize_orientation
+    
+    def __stop_resizing(self): 
+        self.ReleaseMouse()
+    
+    
+    # ---------------------------------------------------------------
+    # Support for click and drag rubberband boxes
+    # ---------------------------------------------------------------
+    def __start_rubberband(self, x, y): 
+        """ 
+        Start a rubberband box
+        """
+        self.__mouse_rubberband_orig_cursor = self.GetCursor()
+        self.__mouse_rubberband_start = (x, y)
+        self.__mouse_rubberband_last = None
+        self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
+        self.CaptureMouse()
+    
+    def __update_rubberband(self, x=None, y=None): 
+        """
+        Draw one box shape and possibly erase another.
+        """
+        dc = wx.ClientDC(self)
+        dc.BeginDrawing()
+        dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
+        dc.SetBrush(wx.TRANSPARENT_BRUSH)
+        dc.SetLogicalFunction(wx.XOR)
+
+        start = self.__mouse_rubberband_start
+        last = self.__mouse_rubberband_last
+        
+        # Erase the old box
+        if last: 
+            r = wx.RectPP(start, last)
+            dc.DrawRectangleRect(r)
+
+        # Draw the new box
+        if x is not None: 
+            r = wx.RectPP(start, (x,y))
+            dc.DrawRectangleRect(r)
+            self.__mouse_rubberband_last = (x,y)
+          
+        dc.EndDrawing()
+        
+
+    def __stop_rubberband(self): 
+        """ 
+        Reset the cursor 
+        """
+        self.SetCursor(self.__mouse_rubberband_orig_cursor)
+        self.__update_rubberband(None)
+        self.ReleaseMouse()
+    
+
+    # ---------------------------------------------------------------
+    # Drag and Drop support
+    # ---------------------------------------------------------------
     def OnData(self, dataObject, x, y, result):
       """
         Handle default behavior of copy and move
       """
       if result == wx.DragMove or result == wx.DragCopy:
           if(self.internalDnDItem != None) and(result == wx.DragMove):
-              assert(self.zOrderedDrawableObjects.count(self.internalDnDItem) 
== 1)
+              assert(self.ordered_widget_list.count(self.internalDnDItem) == 1)
 
               drawableObject = 
self.ConvertDataObjectToDrawableObject(dataObject, x, y, True)
-              x = drawableObject.bounds.GetLeft()
-              y = drawableObject.bounds.GetTop()
+              x = drawableObject.refresh_bounds.GetLeft()
+              y = drawableObject.refresh_bounds.GetTop()
 
-              self.zOrderedDrawableObjects.remove(self.internalDnDItem)
-              self.zOrderedDrawableObjects.insert(0, self.internalDnDItem)
+              self.ordered_widget_list.remove(self.internalDnDItem)
+              self.ordered_widget_list.insert(0, self.internalDnDItem)
               self.internalDnDItem.MoveTo(x, y)
               self.internalDnDItem.Show(True)
           else:
 
               drawableObject = 
self.ConvertDataObjectToDrawableObject(dataObject, x, y, False)
-              x = drawableObject.bounds.GetLeft()
-              y = drawableObject.bounds.GetTop()
+              x = drawableObject.refresh_bounds.GetLeft()
+              y = drawableObject.refresh_bounds.GetTop()
 
-              self.zOrderedDrawableObjects.insert(0, drawableObject)
-              self.refresh_scrolled_rect(drawableObject.bounds);
+              self.ordered_widget_list.insert(0, drawableObject)
+              drawableObject.force_canvas_refresh()
       return result
 
-    def CreateHitTest(self, x, y):
-      """
-        Set self.autoCreateDistance to some value other than zero to enable
-      dragging autoCreateDistance pixels to automatically create new drawable
-      objects.
-        By default, drawable objects are created if you click and drag in the
-      canvas anwhere there isn't a drawable object. You can restrict this 
location
-      that drawable objects are created by overriding this routine
-      """
-      return True
-    """
-      You must implement the following functions
-    """
-    def ConvertDataObjectToDrawableObject(self, dataObject, x, y, move):
-      """
-        You must implement this routine to convert a dataobject, used in
-      drag and drop into a drawable object.
-      """
-      assert(False)
 
-    def CreateNewDrawableObject(self, dragRect, startDrag, endDrag):
-      """
-        You must implement this routine to create new drawable objects by
-      dragging on the blank canvas.
-      """
-      assert(False)
+    # ---------------------------------------------------------------
+    #
+    # ---------------------------------------------------------------
 
-    def DeSelectAll(self):
+    def deselect_all(self):
       """
       Mark all selected objects as not selected. Only Refresh the objects
       that change, don't call Update.
       """
-      for drawableObject in self.zOrderedDrawableObjects:
-          if(drawableObject.selected):
+      for drawableObject in self.get_selected_widgets():
+          if (drawableObject.selected):
               drawableObject.selected = False
-              self.refresh_scrolled_rect(drawableObject.bounds)
+              drawableObject.force_canvas_refresh()
 
-    def add(self, object):
+    def add(self, widget):
       """
-      Add a DrawableObject to the canvas
+      Add a DocumentWidget to the canvas
       """
-      self.zOrderedDrawableObjects.insert(0, object)
-      self.refresh_scrolled_rect(object.bounds);
+      self.ordered_widget_list.insert(0, widget)
+      widget.force_canvas_refresh()
 
-    def remove(self, object):
-      self.zOrderedDrawableObjects.remove(object)
-      bounds = object.bounds
-      self.refresh_scrolled_rect(bounds)
-      object.Destroy()
+
+    def remove(self, widget):
+      """
+      Remove a DocumentWidget from the canvas
+      """
+      self.ordered_widget_list.remove(widget)
+      bounds = widget.refresh_bounds
+      self.refresh_scrolled_rect(refresh_bounds)
+      widget.Destroy()
+
+
+

Modified: 
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py     
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py     
2006-03-31 23:46:55 UTC (rev 8331)
@@ -29,8 +29,8 @@
 
 colorIndex = {}
 def buildColorIndex():
-  colorIndex['selectionframe'] = 
wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
-  colorIndex['selectedframe'] = 
wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+  colorIndex['selectionframe'] = wx.BLUE # 
wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
+  colorIndex['selectedframe'] = wx.BLUE # 
wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
   colorIndex['workspace'] = 
wx.SystemSettings_GetColour(wx.SYS_COLOUR_APPWORKSPACE)
   colorIndex['workspaceGrid'] = wx.Colour(240,240,240) # TODO: ???
   colorIndex['panel'] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND)

Modified: 
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py     
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py     
2006-03-31 23:46:55 UTC (rev 8331)
@@ -20,10 +20,7 @@
 #
 # $Id: TriggerEditor.py 8210 2006-03-07 00:04:31Z jcater $
 
-#
-# These classes were based on SimpleCanvas from OSA's Chandler
-# application, but heavily commented, debugged, and extended.
-#
+import cPickle
 
 import wx
 
@@ -34,33 +31,44 @@
 # A Drawable canvas widget
 # ===================================================================
 class DocumentWidget(wx.EvtHandler):
-    def __init__(self, canvas):
+    def __init__(self, gobject, canvas):
         super(DocumentWidget, self).__init__()
 
-        self.bounds = wx.Rect()
+        self.gobject = gobject
+
         self.canvas = canvas
         self.visible = True
         self.selected = False
-        self.dragStartPos = None
 
-        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
+        # Coordinates (relative to canvas) that should register hit tests 
+        self.hit_test_bounds = wx.Rect()
+        self.hit_test_exclusions = [] # Can be a list of wx.Rect()
 
-    def MoveTo(self, x, y):
-        self.canvas.refresh_scrolled_rect(self.bounds);
-        self.bounds.SetLeft(x)
-        self.bounds.SetTop(y)
-        self.canvas.refresh_scrolled_rect(self.bounds);
+        # Coordinates (relative to canvas) to refresh on widget updates
+        self.refresh_bounds = wx.Rect()
 
-    def SetSize(self, width, height):
-        self.canvas.refresh_scrolled_rect(self.bounds);
-        self.bounds.SetWidth(width)
-        self.bounds.SetHeight(height)
-        self.canvas.refresh_scrolled_rect(self.bounds);
+        # Calculate defaults for the above
+        self.recalc_metrics()
 
-    def OnMouseEvent(self, event):
+        self.Bind(wx.EVT_MOUSE_EVENTS, self.__wx_mouse_event)
+
+
+    def drag_move(self, x, y):
+        self.force_canvas_refresh()
+        self.recalc_metrics()
+        self.force_canvas_refresh()
+        
+
+    def drag_resize(self, width, height):
+        self.force_canvas_refresh()
+        # TODO
+        self.force_canvas_refresh()
+
+
+    def __wx_mouse_event(self, event):
         x, y = event.GetPositionTuple()
         if event.ButtonDown(1):
-            if self.SelectedHitTest(x, y) :
+            if self.select_hit_test(x, y) :
                 self.canvas.DeSelectAll()
                 self.SetSelected()
                 self.canvas.Update()
@@ -79,23 +87,18 @@
         event.Skip()
         return False
 
-    def SetBounds(self, bounds):
-        self.canvas.refresh_scrolled_rect(self.bounds);
-        self.bounds = bounds
-        self.canvas.refresh_scrolled_rect(self.bounds);
-
-    def Show(self, show):
+    def show(self, show):
       # Make sure show is a Boolean
       show = bool(show)
 
       if(show ^ self.visible):
         self.visible = show
-        self.canvas.refresh_scrolled_rect(self.bounds);
+        self.force_canvas_refresh()
         self.canvas.Update()
 
     def ConvertToCanvasDeviceCoordinates(self, x, y):
-      return self.canvas.CalcScrolledPosition(self.bounds.GetLeft() + x,
-                                                self.bounds.GetTop() + y)
+      return self.canvas.CalcScrolledPosition(self.refresh_bounds.GetLeft() + 
x,
+                                                self.refresh_bounds.GetTop() + 
y)
 
     def DoDrag(self, x, y):
       """
@@ -105,7 +108,7 @@
       """
         Create the dragImage and begin dragging over the full screen
       """
-      offscreenBuffer = wx.EmptyBitmap(self.bounds.GetWidth(), 
self.bounds.GetHeight())
+      offscreenBuffer = wx.EmptyBitmap(self.refresh_bounds.GetWidth(), 
self.refresh_bounds.GetHeight())
       memoryDC = wx.MemoryDC()
       memoryDC.SelectObject(offscreenBuffer)
 
@@ -113,7 +116,7 @@
       memoryDC.Clear()
       self.draw(memoryDC)
 
-      maskBitmap = wx.EmptyBitmap(self.bounds.GetWidth(), 
self.bounds.GetHeight(), 1)
+      maskBitmap = wx.EmptyBitmap(self.refresh_bounds.GetWidth(), 
self.refresh_bounds.GetHeight(), 1)
       memoryDC.SelectObject(maskBitmap)
 
       memoryDC.SetBackground(wx.BLACK_BRUSH)
@@ -143,7 +146,7 @@
       self.dragImage.Show()
 
       """
-        We need to keep a reference to the dataObject, rather than create
+      We need to keep a reference to the dataObject, rather than create
       it in the construction because wxCanvasDropSource doesn't own the
       data so the garbage collector will delete it.
       """
@@ -157,56 +160,141 @@
       self.dragImage.EndDrag()
       del self.dragImage
 
-    def draw_mask(self, dc):
+    def SizeDrag(self, dragRect, startDrag, endDrag):
+      self.force_canvas_refresh()
+      #self.bounds = dragRect
+      self.force_canvas_refresh()
+
+    def set_selected(self, selected=True):
+        """
+        Sets the selected bit for this object. Does a canvas refresh on 
+        the object if it has changed state. Does not call Update on the canvas.
+        """
+        selected = bool(selected)
+
+        # Only do a refresh if we've changed state.
+        if (selected ^ self.selected):
+            self.selected = selected
+            self.force_canvas_refresh()
+
+
+    def recalc_metrics(self): 
       """
-        optionally implement this routine to draw a mask
+      Force a recalculation of all metric information
       """
-      pass
+      self.calc_metrics()
+      
 
-    def SizeDrag(self, dragRect, startDrag, endDrag):
-      self.canvas.refresh_scrolled_rect(self.bounds)
-      self.bounds = dragRect
-      self.canvas.refresh_scrolled_rect(self.bounds)
+    def force_canvas_refresh(self): 
+        """
+        Signal to the canvas that are area changed
+        """
+        self.canvas.refresh_scrolled_rect(self.refresh_bounds)
+       
 
-    """
-      You must implement the following functions
-    """
     def draw(self, dc):
-      """
-        You must implement this routine to do draw
-      """
-      assert(False)
+        """
+        Draw the widget and any decorations. 
+        """
+        self.draw_widget(dc)
+        if self.selected: 
+          self.draw_border(dc)
 
-    def DragHitTest(self, x, y):
-      """
+
+    # ====================================================================
+    # You can implement the following functions, but usually won't need to
+    # ====================================================================
+
+    def draw_border(self, dc):
+        """
+        Draw a border for selected widgets
+        """
+        x, y, w, h = self.hit_test_bounds.Get()
+        dc.SetBrush(wx.Brush(wx.BLACK, wx.TRANSPARENT))
+        dc.SetPen(wx.Pen(colorIndex['selectedframe']))
+        dc.DrawRectangle(3, 3, w  + 2, h + 2)
+
+       
+    def select_hit_test(self, x, y):
+        """
+        You must implement this routine to do hit testing to identify 
+        the region for selecting the object.
+        
+        By default, if not subclassed, we just use the bounds defined for the 
+        object.
+        """
+        self.hit_test_bounds.InsideXY(x,y)
+
+
+    def drag_hit_test (self, x1, y1, x2, y2):
+        """
         You must implement this routine to do hit testing for dragable region
-      of drawable object
-      """
-      assert(False)
+        of drawable object
+        
+        By default, we use the bounds of the object.
+        """
+        # Should also take into account self.hit_test_exclusions
+        dragRect = wx.Rect((x1, y1), (x2, y2))
+        result = self.hit_test_bounds.Intersects(dragRect) 
+        
+        if result: 
+          # If the entire dragRect is in an exclusion area, return false
+          for exclusion in self.hit_test_exclusions: 
+            if exclusion.Intersect(dragRect) == dragRect:
+              return False
+              
+        return result
 
-    def ConvertDrawableObjectToDataObject(self, x, y):
-      """
+
+    def add_to_clipboard (self, x, y):
+        """
         You must implement this routine to create data object for drag and drop
-      """
-      assert(False)
+        """
+        object = self
 
-    def SelectedHitTest(self, x, y):
+        # TODO: This is a placeholder only! Not implemented yet.
+        data = [ { "Type" : "selectedWidgets",
+                   "Attributes": {
+                       'startingX': x,
+                       'startingY': y,
+                       }
+                   }]
+    #        "name": dname,
+    #        "table": object.name,
+    #        "database": self.connectionName } }
+    #
+    #
+        do = 
wx.CustomDataObject(wx.CustomDataFormat("application/x-gnue-designer"))
+        do.SetData(cPickle.dumps(data,1))
+
+        return do
+
+
+    def draw_mask(self, dc):
       """
-      You must implement this routine to do hit testing to identify the region
-      for selecting the object.
+      optionally implement this routine to draw a mask
+      (Not sure what this means... inherited from SimpleCanvas)
       """
-      assert(False)
+      pass
 
-    def SetSelected(self, selected=True):
+      
+
+    # ====================================================================
+    # You *must* implement the following functions
+    # ====================================================================
+
+    def calc_metrics(self): 
+        """
+        Calculate hit_test_bounds, refresh_bounds, and any 
+        class-specific bounds your draw_widget methods might need. 
+        """
+        assert(False)
+
+
+    def draw_widget(self, dc):
       """
-      Sets the selected bit for this object. Does a canvas refresh on the 
object
-      if it has changed state. Does not call Update on the canvas.
+      You must implement this routine to do draw the actual widget
       """
-      # Use the same trick as Show()
-      selected = bool(selected)
+      assert(False)
 
-      # Only do a refresh if we've changed state.
-      if(selected ^ self.selected):
-        self.selected = selected
-        self.canvas.refresh_scrolled_rect(self.bounds);
 





reply via email to

[Prev in Thread] Current Thread [Next in Thread]