Laurent@814: #!/usr/bin/env python
Laurent@814: # -*- coding: utf-8 -*-
Laurent@814: 
andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for
andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
Laurent@814: #
andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
Laurent@814: #
andrej@1571: # See COPYING file for copyrights details.
Laurent@814: #
andrej@1571: # This program is free software; you can redistribute it and/or
andrej@1571: # modify it under the terms of the GNU General Public License
andrej@1571: # as published by the Free Software Foundation; either version 2
andrej@1571: # of the License, or (at your option) any later version.
Laurent@814: #
andrej@1571: # This program is distributed in the hope that it will be useful,
andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of
andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
andrej@1571: # GNU General Public License for more details.
Laurent@814: #
andrej@1571: # You should have received a copy of the GNU General Public License
andrej@1571: # along with this program; if not, write to the Free Software
andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Laurent@814: 
andrej@1881: 
andrej@1881: from __future__ import absolute_import
Laurent@814: import wx
Laurent@814: 
Laurent@1173: from graphics.GraphicCommons import GetScaledEventPosition
Laurent@1169: 
andrej@1782: 
andrej@1782: # -------------------------------------------------------------------------------
Laurent@1173: #                               Viewer RubberBand
andrej@1782: # -------------------------------------------------------------------------------
Laurent@814: 
Laurent@814: 
andrej@1831: class RubberBand(object):
andrej@1736:     """
andrej@1736:     Class that implements a rubberband for graphic Viewers
andrej@1736:     """
andrej@1730: 
Laurent@814:     def __init__(self, viewer):
Laurent@1173:         """
Laurent@1173:         Constructor
Laurent@1173:         @param viewer: Viewer on which rubberband must be drawn
Laurent@1173:         """
Laurent@814:         self.Viewer = viewer
andrej@1730: 
Laurent@1173:         # wx.Panel on which rubberband will be drawn
Laurent@1173:         self.DrawingSurface = viewer.Editor
andrej@1730: 
Laurent@814:         self.Reset()
andrej@1730: 
Laurent@814:     def Reset(self):
Laurent@1173:         """
Laurent@1173:         Initialize internal attributes of rubberband
Laurent@1173:         """
Laurent@1173:         self.StartPoint = None
Laurent@1173:         self.CurrentBBox = None
Laurent@1173:         self.LastBBox = None
andrej@1730: 
Laurent@814:     def IsShown(self):
Laurent@1173:         """
Laurent@1173:         Indicate if rubberband is drawn on viewer
Laurent@1173:         @return: True if rubberband is drawn
Laurent@1173:         """
andrej@1743:         return self.CurrentBBox is not None
andrej@1730: 
Laurent@814:     def GetCurrentExtent(self):
Laurent@1173:         """
Laurent@1173:         Return the rubberband bounding box
Laurent@1173:         @return: Rubberband bounding box (wx.Rect object)
Laurent@1173:         """
Laurent@1173:         # In case of rubberband not shown, return the last rubberband
Laurent@1173:         # bounding box
Laurent@1173:         if self.IsShown():
Laurent@1173:             return self.CurrentBBox
Laurent@1173:         return self.LastBBox
andrej@1730: 
Laurent@814:     def OnLeftDown(self, event, dc, scaling):
Laurent@1173:         """
Laurent@1173:         Called when left mouse is pressed on Viewer. Starts to edit a new
Laurent@1173:         rubberband bounding box
Laurent@1173:         @param event: Mouse event
Laurent@1173:         @param dc: Device Context of Viewer
Laurent@1173:         @param scaling: PLCOpen scaling applied on Viewer
Laurent@1173:         """
Laurent@1173:         # Save the point where mouse was pressed in Viewer unit, position may
Laurent@1173:         # be modified by scroll and zoom applied on viewer
Laurent@1173:         self.StartPoint = GetScaledEventPosition(event, dc, scaling)
andrej@1730: 
Laurent@1173:         # Initialize rubberband bounding box
Laurent@1173:         self.CurrentBBox = wx.Rect(self.StartPoint.x, self.StartPoint.y, 0, 0)
andrej@1730: 
Laurent@1173:         # Change viewer mouse cursor to reflect a rubberband bounding box is
Laurent@1173:         # edited
Laurent@1173:         self.DrawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
andrej@1730: 
Laurent@814:         self.Redraw()
andrej@1730: 
Laurent@814:     def OnMotion(self, event, dc, scaling):
Laurent@1173:         """
Laurent@1173:         Called when mouse is dragging over Viewer. Update the current edited
Laurent@1173:         rubberband bounding box
Laurent@1173:         @param event: Mouse event
Laurent@1173:         @param dc: Device Context of Viewer
Laurent@1173:         @param scaling: PLCOpen scaling applied on Viewer
Laurent@1173:         """
Laurent@1173:         # Get mouse position in Viewer unit, position may be modified by scroll
Laurent@1173:         # and zoom applied on viewer
Laurent@814:         pos = GetScaledEventPosition(event, dc, scaling)
andrej@1730: 
Laurent@1173:         # Save the last bounding box drawn for erasing it later
Laurent@1173:         self.LastBBox = wx.Rect(0, 0, 0, 0)
Laurent@1173:         self.LastBBox.Union(self.CurrentBBox)
andrej@1730: 
andrej@1730:         # Calculate new position and size of the box
Laurent@1173:         self.CurrentBBox.x = min(pos.x, self.StartPoint.x)
Laurent@1173:         self.CurrentBBox.y = min(pos.y, self.StartPoint.y)
Laurent@1173:         self.CurrentBBox.width = abs(pos.x - self.StartPoint.x) + 1
Laurent@1173:         self.CurrentBBox.height = abs(pos.y - self.StartPoint.y) + 1
andrej@1730: 
Laurent@814:         self.Redraw()
andrej@1730: 
Laurent@814:     def OnLeftUp(self, event, dc, scaling):
Laurent@1173:         """
Laurent@1173:         Called when mouse is release from Viewer. Erase the current edited
Laurent@1173:         rubberband bounding box
Laurent@1173:         @param event: Mouse event
Laurent@1173:         @param dc: Device Context of Viewer
Laurent@1173:         @param scaling: PLCOpen scaling applied on Viewer
Laurent@1173:         """
Laurent@1173:         # Change viewer mouse cursor to default
Laurent@1173:         self.DrawingSurface.SetCursor(wx.NullCursor)
andrej@1730: 
Laurent@1173:         # Save the last edited bounding box
Laurent@1173:         self.LastBBox = self.CurrentBBox
Laurent@1173:         self.CurrentBBox = None
andrej@1730: 
Laurent@814:         self.Redraw()
andrej@1730: 
Laurent@1173:     def DrawBoundingBoxes(self, bboxes, dc=None):
Laurent@1173:         """
andrej@1730:         Draw a list of bounding box on Viewer in the order given using XOR
Laurent@1173:         logical function
Laurent@1173:         @param bboxes: List of bounding boxes to draw on viewer
Laurent@1173:         @param dc: Device Context of Viewer (default None)
Laurent@1173:         """
Laurent@1173:         # Get viewer Device Context if not given
Laurent@814:         if dc is None:
Laurent@814:             dc = self.Viewer.GetLogicalDC()
andrej@1730: 
Laurent@1173:         # Save current viewer scale factors before resetting them in order to
Laurent@1173:         # avoid rubberband pen to be scaled
Laurent@814:         scalex, scaley = dc.GetUserScale()
Laurent@814:         dc.SetUserScale(1, 1)
andrej@1730: 
Laurent@1173:         # Set DC drawing style
Laurent@1173:         dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT))
Laurent@814:         dc.SetBrush(wx.TRANSPARENT_BRUSH)
Laurent@814:         dc.SetLogicalFunction(wx.XOR)
andrej@1730: 
Laurent@1173:         # Draw the bounding boxes using viewer scale factor
Laurent@1173:         for bbox in bboxes:
Laurent@1173:             if bbox is not None:
Laurent@1173:                 dc.DrawRectangle(
andrej@1730:                     bbox.x * scalex, bbox.y * scaley,
Laurent@1173:                     bbox.width * scalex, bbox.height * scaley)
andrej@1730: 
Laurent@1273:         dc.SetLogicalFunction(wx.COPY)
andrej@1730: 
Laurent@1173:         # Restore Viewer scale factor
Laurent@814:         dc.SetUserScale(scalex, scaley)
andrej@1730: 
andrej@1744:     def Redraw(self, dc=None):
Laurent@1173:         """
Laurent@1173:         Redraw rubberband on Viewer
Laurent@1173:         @param dc: Device Context of Viewer (default None)
Laurent@1173:         """
Laurent@1173:         # Erase last bbox and draw current bbox
Laurent@1173:         self.DrawBoundingBoxes([self.LastBBox, self.CurrentBBox], dc)
andrej@1730: 
andrej@1744:     def Erase(self, dc=None):
Laurent@1173:         """
Laurent@1173:         Erase rubberband from Viewer
Laurent@1173:         @param dc: Device Context of Viewer (default None)
Laurent@1173:         """
Laurent@1173:         # Erase last bbox
Laurent@1173:         self.DrawBoundingBoxes([self.LastBBox], dc)
Laurent@814: 
andrej@1744:     def Draw(self, dc=None):
Laurent@1173:         """
Laurent@1173:         Draw rubberband on Viewer
Laurent@1173:         @param dc: Device Context of Viewer (default None)
Laurent@1173:         """
Laurent@1173:         # Erase last bbox and draw current bbox
Laurent@1173:         self.DrawBoundingBoxes([self.CurrentBBox], dc)