--- /dev/null
+"""
+
+Module that holds the GUI modes used by FloatCanvas
+
+
+Note that this can only be imported after a wx.App() has been created.
+
+This approach was inpired by Christian Blouin, who also wrote the initial
+version of the code.
+
+"""
+
+import wx
+## fixme: events should live in their own module, so all of FloatCanvas
+## wouldn't have to be imported here.
+import FloatCanvas, Resources
+import numpy as N
+
+## create all the Cursors, so they don't need to be created each time.
+if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
+ HandCursor = wx.CursorFromImage(Resources.getHand16Image())
+ GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
+
+ img = Resources.getMagPlus16Image()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+ MagPlusCursor = wx.CursorFromImage(img)
+
+ img = Resources.getMagMinus16Image()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+ MagMinusCursor = wx.CursorFromImage(img)
+else: # use 24X24 cursors for GTK and Windows
+ HandCursor = wx.CursorFromImage(Resources.getHandImage())
+ GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
+
+ img = Resources.getMagPlusImage()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+ MagPlusCursor = wx.CursorFromImage(img)
+
+ img = Resources.getMagMinusImage()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+ MagMinusCursor = wx.CursorFromImage(img)
+
+
+class GUIBase:
+ """
+ Basic Mouse mode and baseclass for other GUImode.
+
+ This one does nothing with any event
+
+ """
+ def __init__(self, parent):
+ self.parent = parent
+
+ Cursor = wx.NullCursor
+
+ # Handlers
+ def OnLeftDown(self, event):
+ pass
+ def OnLeftUp(self, event):
+ pass
+ def OnLeftDouble(self, event):
+ pass
+ def OnRightDown(self, event):
+ pass
+ def OnRightUp(self, event):
+ pass
+ def OnRightDouble(self, event):
+ pass
+ def OnMiddleDown(self, event):
+ pass
+ def OnMiddleUp(self, event):
+ pass
+ def OnMiddleDouble(self, event):
+ pass
+ def OnWheel(self, event):
+ pass
+ def OnMove(self, event):
+ pass
+
+ def UpdateScreen(self):
+ """
+ Update gets called if the screen has been repainted in the middle of a zoom in
+ so the Rubber Band Box can get updated
+ """
+ pass
+
+class GUIMouse(GUIBase):
+ """
+
+ Mouse mode checks for a hit test, and if nothing is hit,
+ raises a FloatCanvas mouse event for each event.
+
+ """
+
+ Cursor = wx.NullCursor
+
+ # Handlers
+ def OnLeftDown(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnLeftUp(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnLeftDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleDown(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleUp(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightDown(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightUp(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnWheel(self, event):
+ EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMove(self, event):
+ ## The Move event always gets raised, even if there is a hit-test
+ self.parent.MouseOverTest(event)
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+
+
+class GUIMove(GUIBase):
+
+ Cursor = HandCursor
+ GrabCursor = GrabHandCursor
+ def __init__(self, parent):
+ GUIBase.__init__(self, parent)
+ self.StartMove = None
+ self.PrevMoveXY = None
+
+ def OnLeftDown(self, event):
+ self.parent.SetCursor(self.GrabCursor)
+ self.parent.CaptureMouse()
+ self.StartMove = N.array( event.GetPosition() )
+ self.PrevMoveXY = (0,0)
+
+ def OnLeftUp(self, event):
+ if self.StartMove is not None:
+ StartMove = self.StartMove
+ EndMove = N.array(event.GetPosition())
+ DiffMove = StartMove-EndMove
+ if N.sum(DiffMove**2) > 16:
+ self.parent.MoveImage(DiffMove, 'Pixel')
+ self.StartMove = None
+ self.parent.SetCursor(self.Cursor)
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+ if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
+ xy1 = N.array( event.GetPosition() )
+ wh = self.parent.PanelSize
+ xy_tl = xy1 - self.StartMove
+ dc = wx.ClientDC(self.parent)
+ dc.BeginDrawing()
+ x1,y1 = self.PrevMoveXY
+ x2,y2 = xy_tl
+ w,h = self.parent.PanelSize
+ ##fixme: This sure could be cleaner!
+ if x2 > x1 and y2 > y1:
+ xa = xb = x1
+ ya = yb = y1
+ wa = w
+ ha = y2 - y1
+ wb = x2- x1
+ hb = h
+ elif x2 > x1 and y2 <= y1:
+ xa = x1
+ ya = y1
+ wa = x2 - x1
+ ha = h
+ xb = x1
+ yb = y2 + h
+ wb = w
+ hb = y1 - y2
+ elif x2 <= x1 and y2 > y1:
+ xa = x1
+ ya = y1
+ wa = w
+ ha = y2 - y1
+ xb = x2 + w
+ yb = y1
+ wb = x1 - x2
+ hb = h - y2 + y1
+ elif x2 <= x1 and y2 <= y1:
+ xa = x2 + w
+ ya = y1
+ wa = x1 - x2
+ ha = h
+ xb = x1
+ yb = y2 + h
+ wb = w
+ hb = y1 - y2
+
+ dc.SetPen(wx.TRANSPARENT_PEN)
+ dc.SetBrush(self.parent.BackgroundBrush)
+ dc.DrawRectangle(xa, ya, wa, ha)
+ dc.DrawRectangle(xb, yb, wb, hb)
+ self.PrevMoveXY = xy_tl
+ if self.parent._ForeDrawList:
+ dc.DrawBitmapPoint(self.parent._ForegroundBuffer,xy_tl)
+ else:
+ dc.DrawBitmapPoint(self.parent._Buffer,xy_tl)
+ dc.EndDrawing()
+
+ def OnWheel(self, event):
+ """
+ By default, zoom in/out by a 0.1 factor per Wheel event.
+ """
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+class GUIZoomIn(GUIBase):
+
+ Cursor = MagPlusCursor
+
+ def __init__(self, parent):
+ GUIBase.__init__(self, parent)
+ self.StartRBBox = None
+ self.PrevRBBox = None
+
+ def OnLeftDown(self, event):
+ self.StartRBBox = N.array( event.GetPosition() )
+ self.PrevRBBox = None
+ self.parent.CaptureMouse()
+
+ def OnLeftUp(self, event):
+ #if self.parent.HasCapture():
+ # self.parent.ReleaseMouse()
+ if event.LeftUp() and not self.StartRBBox is None:
+ self.PrevRBBox = None
+ EndRBBox = event.GetPosition()
+ StartRBBox = self.StartRBBox
+ # if mouse has moved less that ten pixels, don't use the box.
+ if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
+ and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
+ EndRBBox = self.parent.PixelToWorld(EndRBBox)
+ StartRBBox = self.parent.PixelToWorld(StartRBBox)
+ BB = N.array(((min(EndRBBox[0],StartRBBox[0]),
+ min(EndRBBox[1],StartRBBox[1])),
+ (max(EndRBBox[0],StartRBBox[0]),
+ max(EndRBBox[1],StartRBBox[1]))),N.float_)
+ self.parent.ZoomToBB(BB)
+ else:
+ Center = self.parent.PixelToWorld(StartRBBox)
+ self.parent.Zoom(1.5,Center)
+ self.StartRBBox = None
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+ if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
+ xy0 = self.StartRBBox
+ xy1 = N.array( event.GetPosition() )
+ wh = abs(xy1 - xy0)
+ wh[0] = max(wh[0], int(wh[1]*self.parent.AspectRatio))
+ wh[1] = int(wh[0] / self.parent.AspectRatio)
+ xy_c = (xy0 + xy1) / 2
+ dc = wx.ClientDC(self.parent)
+ dc.BeginDrawing()
+ dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+ if self.PrevRBBox:
+ dc.DrawRectanglePointSize(*self.PrevRBBox)
+ self.PrevRBBox = ( xy_c - wh/2, wh )
+ dc.DrawRectanglePointSize( *self.PrevRBBox )
+ dc.EndDrawing()
+
+ def UpdateScreen(self):
+ """
+ Update gets called if the screen has been repainted in the middle of a zoom in
+ so the Rubber Band Box can get updated
+ """
+ if self.PrevRBBox is not None:
+ dc = wx.ClientDC(self.parent)
+ dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+ dc.DrawRectanglePointSize(*self.PrevRBBox)
+
+ def OnRightDown(self, event):
+ self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnWheel(self, event):
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+class GUIZoomOut(GUIBase):
+
+ Cursor = MagMinusCursor
+
+ def OnLeftDown(self, event):
+ self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnRightDown(self, event):
+ self.parent.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnWheel(self, event):
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+