DiagramFrame.py :  » UML » Python-UML-Tool » pyut-1.4.0 » src » MiniOgl » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » UML » Python UML Tool 
Python UML Tool » pyut 1.4.0 » src » MiniOgl » DiagramFrame.py
#!/usr/bin/env python
#
# Copyright 2002, Laurent Burgbacher, Eivd.
# Visit http://www.eivd.ch
#
# This file is part of MiniOgl.
#
# MiniOgl is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# MiniOgl is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MiniOgl; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

__author__    = "Laurent Burgbacher, lb@alawa.ch, Eivd"
__copyright__ = "Copyright 2002, Laurent Burgbacher, Eivd"
__license__   = "Released under the terms of the GNU General Public Licence V2"
__date__      = "2002-10-15"
__version__   = "$Id: DiagramFrame.py,v 1.16 2006/02/04 22:01:01 dutoitc Exp $"

from __future__ import division
#import wxPython
#from wxPython.wx               import *
from Diagram import Diagram
from ShapeEventHandler import ShapeEventHandler
from SizerShape import SizerShape
from ControlPoint import ControlPoint
from Constants import *
from RectangleShape import RectangleShape
import wx
#import wx.lib.scrolledpanel as scrolled

__all__ = ["DiagramFrame"]

DEBUG = 0 # set to 1 to have some debug info in the terminal

LEFT_MARGIN = 0
RIGHT_MARGIN = 1
TOP_MARGIN = 2
BOTTOM_MARGIN = 3

#class DiagramFrame(scrolled.ScrolledPanel):
class DiagramFrame(wx.ScrolledWindow):
    """
    A frame to draw simulation diagrams.
    This frame also manage all mouse events.
    It has a Diagram automatically associated.

    Exported methods:
    -----------------

    __init__(self, parent)
        Constructor.
    getEventPosition(self, event)
        Return the position of a click in the diagram.
    GenericHandler(self, event, methodName)
        This handler finds the shape at event coords and dispatch the event.
    OnLeftDown(self, event)
        Callback for left down events on the diagram.
    OnLeftUp(self, event)
        Callback for left up events.
    OnDrag(self, event)
        Callback to drag the selected shapes.
    OnMove(self, event)
        Callback for mouse movements.
    OnLeftDClick(self, event)
        Callback for left double clicks.
    OnMiddleDown(self, event)
        Callback.
    OnMiddleUp(self, event)
        Callback.
    OnMiddleDClick(self, event)
        Callback.
    OnRightDown(self, event)
        Callback.
    OnRightUp(self, event)
        Callback.
    OnRightDClick(self, event)
        Callback.
    GetDiagram(self)
        Return the diagram associated with this panel.
    SetDiagram(self, diagram)
        Set a new diagram for this panel.
    FindShape(self, x, y)
        Return the shape at (x, y).
    DeselectAllShapes(self)
        Deselect all shapes in the frame.
    GetSelectedShapes(self)
        Get the selected shapes.
    SetSelectedShapes(self, shapes)
        Set the list of selected shapes.
    KeepMoving(self, keep)
        Tell the frame to continue capturing the mouse movements.
    Refresh(self, eraseBackground=True, rect=None)
        This refresh is done imediately, not through an event.
    SaveBackground(self, dc)
        Save the given dc as the new background image.
    LoadBackground(self, dc, w, h)
        Load the background image in the given dc.
    ClearBackground(self)
        Clear the background image.
    CreateDC(self, loadBackground, w, h)
        Create a DC, load the background on demand.
    PrepareBackground(self)
        Redraw the screen without movable shapes, store it as the background.
    RedrawWithBackground(self)
        Redraw the screen using the background.
    Redraw(self, dc=None, full=True, saveBackground=False
        No description.
    OnPaint(self, event)
        Callback.

    @author Laurent Burgbacher <lb@alawa.ch>
    """
    def __init__(self, parent):
        """
        Constructor.

        @param wxObject parent : parent window
        """
        #print ">>>MiniOGL-DiagramFrame-1", parent
        wx.ScrolledWindow.__init__(self, parent)
        #scrolled.ScrolledPanel.__init__(self, parent, -1, 
        #        style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER )
        #print "---MiniOGL-DiagramFrame-2"
        self._diagram = Diagram(self)
        #print "---MiniOGL-DiagramFrame-3"

        self.__keepMoving = False
        self._selectedShapes = [] # list of the shapes that are selected
        self._lastMousePosition = None
        self._selector = None # rectangle selector shape
        self._clickedShape = None # last clicked shape
        self._moving = False # a drag has been initiated

        #zoom related
        #added by P. Dabrowski <przemek.dabrowski@destroy-display.com>
        self._xOffset = 0.0 # abscissa offset between the view and the model
        self._yOffset = 0.0 # ordinate offset between the view and the model
        self._zoomStack = [] #store all zoom factors applied
        #self._zoomStack.append(1) #the view is 100% at the begining
        self._zoomLevel = 0 #number of zoom factors applied
        self._maxZoomFactor = 6 #can zoom in beyond 600%
        self._minZoomFactor = 0.2 #can zoom out beyond 20%
        self._defaultZoomFactor = 1.5 #used when only a point is selected

        #infinite work area related
        #added by P. Dabrowski <przemek.dabrowski@destroy-display.com>
        # margins define a perimeter around the work area that must remains
        # blank and hidden. if we scroll beyond the limits, the diagram is
        # resized.
        DEFAULT_MARGIN_VALUE = 100
        self._leftMargin = DEFAULT_MARGIN_VALUE   
        self._rightMargin = DEFAULT_MARGIN_VALUE   
        self._topMargin = DEFAULT_MARGIN_VALUE
        self._bottomMargin = DEFAULT_MARGIN_VALUE
        self._isInfinite = False # to know if the frame is infinite or not
        
        # paint related
        w, h = self.GetSize()
        self.__workingBitmap = wx.EmptyBitmap(w, h) # double buffering
        self.__backgroundBitmap = wx.EmptyBitmap(w, h)

        DEFAULT_FONT_SIZE = 12
        self._defaultFont = wx.Font(DEFAULT_FONT_SIZE, wx.DEFAULT, \
                wx.NORMAL, wx.NORMAL)

        self.SetBackgroundColour(wx.WHITE)
        #print "---MiniOGL-DiagramFrame-4"

        # Mouse events
        #self.Bind(wx.EVT_LEFT_DOWN,     self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DOWN,     self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP,       self.OnLeftUp)
        self.Bind(wx.EVT_LEFT_DCLICK,   self.OnLeftDClick)
        self.Bind(wx.EVT_MIDDLE_DOWN,   self.OnMiddleDown)
        self.Bind(wx.EVT_MIDDLE_UP,     self.OnMiddleUp)
        self.Bind(wx.EVT_MIDDLE_DCLICK, self.OnMiddleDClick)
        self.Bind(wx.EVT_RIGHT_DOWN,    self.OnRightDown)
        self.Bind(wx.EVT_RIGHT_UP,      self.OnRightUp)
        self.Bind(wx.EVT_RIGHT_DCLICK,  self.OnRightDClick)

        #added by P. Dabrowski <przemek.dabrowski@destroy-display.com>
        #to manage the fact that we scroll beyond the margins (in the
        #case where the work area is infinite).
        #self.Bind(wx.EVT_SCROLLWIN,     self.OnScroll) #PRZ removed cause of an unsolvable error
        
        
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        #print "---MiniOGL-DiagramFrame-5"
        
    #>------------------------------------------------------------------ 

    def getEventPosition(self, event):
        """
        Return the position of a click in the diagram.

        @param wx.Event event : mouse event
        @return (double, double) : x, y
        """
        #print event.GetButton(), event.GetPosition(),
        #x, y = self.CalcUnscrolledPosition(event.GetX(), event.GetY())
        x, y = self._ConvertEventCoords(event)  # Updated by CD, 20041005
        #print "getEventPosition, event=(%s, %s); p=(%s, %s)" % \
        #        (event.GetX(), event.GetY(), x, y)
        #print dir(event)
        return x, y

    #>------------------------------------------------------------------ 

    def GenericHandler(self, event, methodName):
        """
        This handler finds the shape at event coords and dispatch the event.
        The handler will receive an event with coords already unscrolled.

        @param wx.Event event : original event
        @param string methodName : name of the method to invoke in the
            event handler of the shape
        @return Shape : the clicked shape
        """
        if DEBUG:
            print "Generic for", methodName
        x, y = self.getEventPosition(event)
        shape = self.FindShape(x, y)
        event.m_x, event.m_y = x, y
        # if the shape found is a ShapeEventHandler
        if shape and isinstance(shape, ShapeEventHandler):
            # dispatch it the event
            getattr(shape, methodName)(event)
        else:
            # the event has not been treated
            event.Skip()
        return shape

    #>------------------------------------------------------------------------

    def OnLeftDown(self, event):
        """
        Callback for left down events on the diagram.

        @param wx.Event event
        """
        if DEBUG:
            print "DiagramFrame.OnLeftDown"

        # First, call the generic handler for OnLeftDown
        shape = self.GenericHandler(event, "OnLeftDown")
        self._clickedShape = shape # store the last clicked shape
        if not event.GetSkipped():
            return
        if shape is None:
            self._BeginSelect(event)
            return

        # manage click and drag
        x, y = event.GetX(), event.GetY()
        #x, y = self._ConvertEventCoords(event)  # Updated by CD, 20041005
        #print "OnLeftDown, event=(%s, %s); %s; (%s, %s))" % \
        #        (event.GetX(), event.GetY(), event.GetPosition(), x, y)
        self._lastMousePosition = (x, y)

        if not event.ControlDown() and not shape.IsSelected():
            shapes = self._diagram.GetShapes()
            shapes.remove(shape)
            if isinstance(shape, SizerShape):
                # don't deselect the parent of a sizer
                # or its sizers would be detached
                shapes.remove(shape.GetParent())
            #elif isinstance(self, ControlPoint): CD
            elif isinstance(shape, ControlPoint):
                # don't deselect the line of a control point
                for line in shape.GetLines():
                    shapes.remove(line)

            # don't call DeselectAllShapes, because we must ensure that
            # sizers won't be deselected (because they're detached when they're
            # deselected)
            # deselect all other shapes
            for s in shapes:
                s.SetSelected(False)
                s.SetMoving(False)

            self._selectedShapes = [shape]
            shape.SetSelected(True)
            shape.SetMoving(True)
            self._clickedShape = None
            self.Refresh()

        self.Bind(wx.EVT_MOTION, self.OnMove)

    #>------------------------------------------------------------------------

    def _BeginSelect(self, event):
        """
        Create a selector box and manage it.

        @param wx.Event event
        """
        if not event.ControlDown():
            self.DeselectAllShapes()
        x, y = event.GetX(), event.GetY() # event position has been modified
        self._selector = rect = RectangleShape(x, y, 0, 0)
        rect.SetDrawFrame(True)
        rect.SetBrush(wx.TRANSPARENT_BRUSH)
        rect.SetMoving(True)
        self._diagram.AddShape(rect)
        self.PrepareBackground()
        self.Bind(wx.EVT_MOTION, self._OnMoveSelector)

    #>------------------------------------------------------------------------

    def _OnMoveSelector(self, event):
        """
        Callback for the selector box.

        @param wx.Event event
        """
        if self._selector is not None:
            x, y = self.getEventPosition(event)
            x0, y0 = self._selector.GetPosition()
            self._selector.SetSize(x - x0, y - y0)
            self.Refresh(False)

    #>------------------------------------------------------------------------

    def OnLeftUp(self, event):
        """
        Callback for left up events.

        @param wx.Event event
        """
        #print "OnLeftUp, event=(%s, %s); %s)" % \
        #        (event.GetX(), event.GetY(), event.GetPosition())

        # manage the selector box
        if self._selector is not None:
            self.Bind(wx.EVT_MOTION, self._NullCallback)
            rect = self._selector
            x, y = rect.GetPosition()
            w, h = rect.GetSize()
            for shape in self._diagram.GetShapes():
                x0, y0 = shape.GetTopLeft()
                w0, h0 = shape.GetSize()
                if shape.GetParent() is None and \
                   rect.Inside(x0, y0) and \
                   rect.Inside(x0 + w0, y0) and \
                   rect.Inside(x0, y0 + h0) and \
                   rect.Inside(x0 + w0, y0 + h0):
                    shape.SetSelected(True)
                    shape.SetMoving(True)
                    self._selectedShapes.append(shape)
            rect.Detach()
            self._selector = None
        if not self._moving and self._clickedShape:
            clicked = self._clickedShape
            if not event.ControlDown():
                self.DeselectAllShapes()
                self._selectedShapes = [clicked]
                clicked.SetSelected(True)
                clicked.SetMoving(True)
            else:
                sel = not clicked.IsSelected()
                clicked.SetSelected(sel)
                clicked.SetMoving(sel)
                if sel and clicked not in self._selectedShapes:
                    self._selectedShapes.append(clicked)
                elif not sel and clicked in self._selectedShapes:
                    self._selectedShapes.remove(clicked)
            self._clickedShape = None
            self.Refresh()

        self._moving = False

        # normal event management
        self.GenericHandler(event, "OnLeftUp")
        if not self.__keepMoving:
            self.Bind(wx.EVT_MOTION, self._NullCallback)
            self.Refresh()

    #>------------------------------------------------------------------------

    def OnDrag(self, event):
        """
        Callback to drag the selected shapes.

        @param wx.Event event
        """
        x, y = event.GetX(), event.GetY()
        if not self._moving:
            self.PrepareBackground()
        self._moving = True
        clicked = self._clickedShape
        if clicked and not clicked.IsSelected():
            self._selectedShapes.append(clicked)
            clicked.SetSelected(True)
            clicked.SetMoving(True)
        self._clickedShape = None
        for shape in self._selectedShapes:
            parent = shape.GetParent()
            if parent is not None and parent.IsSelected() and \
                not isinstance(shape, SizerShape):
                continue
            ox, oy = self._lastMousePosition
            dx, dy = x - ox, y - oy
            sx, sy = shape.GetPosition()
            shape.SetPosition(sx + dx, sy + dy)
                 
        self.Refresh(False)
        self._lastMousePosition = (x, y)

    #>------------------------------------------------------------------------

    def OnMove(self, event):
        """
        Callback for mouse movements.

        @param wx.Event event
        """
        event.m_x, event.m_y = self.getEventPosition(event)
        self.OnDrag(event)

    #>------------------------------------------------------------------ 

    def OnLeftDClick(self, event):
        """
        Callback for left double clicks.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnLeftDClick")
        self._clickedShape = None
        if not self.__keepMoving:
            self.Bind(wx.EVT_MOTION, self._NullCallback)

    #>------------------------------------------------------------------------

    def OnMiddleDown(self, event):
        """
        Callback.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnMiddleDown")

    #>------------------------------------------------------------------------

    def OnMiddleUp(self, event):
        """
        Callback.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnMiddleUp")

    #>------------------------------------------------------------------------

    def OnMiddleDClick(self, event):
        """
        Callback.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnMiddleDClick")

    #>------------------------------------------------------------------------

    def OnRightDown(self, event):
        """
        Callback.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnRightDown")

    #>------------------------------------------------------------------------

    def OnRightUp(self, event):
        """
        Callback.

        @param wx.Event event
        """
        self.GenericHandler(event, "OnRightUp")

    #>------------------------------------------------------------------------

    def OnRightDClick(self, event):
        """
        Callback.

        @param wx.Event event
        """
        # DEBUG
        import wx
        import wx.py as py
        crustWin = wx.Dialog(self, -1, "PyCrust", (0,0), (640,480))
        win = py.crust.Crust(crustWin)
        crustWin.Show()
        self.GenericHandler(event, "OnRightDClick")

    #>------------------------------------------------------------------------

    def GetDiagram(self):
        """
        Return the diagram associated with this panel.

        @return Diagram
        """
        return self._diagram

    #>------------------------------------------------------------------ 

    def SetDiagram(self, diagram):
        """
        Set a new diagram for this panel.

        @param Diagram diagram
        """
        self._diagram = diagram

    #>------------------------------------------------------------------------

    def FindShape(self, x, y):
        """
        Return the shape at (x, y).

        @param double x, y : coord
        @return Shape : found shape or None
        """
        found = None
        shapes = self._diagram.GetShapes()
        shapes.reverse() # to select the one at the top
        for shape in shapes:
            if shape.Inside(x, y):
                if DEBUG:
                    print "Inside", shape
                found = shape
                break # only select the first one
        return found

    #>------------------------------------------------------------------------

    def DeselectAllShapes(self):
        """
        Deselect all shapes in the frame.
        """
        for shape in self._diagram.GetShapes():
            shape.SetSelected(False)
            shape.SetMoving(False)
        self._selectedShapes = []

    #>------------------------------------------------------------------------

    def GetSelectedShapes(self):
        """
        Get the selected shapes.
        Beware, this is the list of the frame, but other shapes could be
        selected and not declared to the frame.

        @return Shape []
        """
        return self._selectedShapes

    #>------------------------------------------------------------------------

    def SetSelectedShapes(self, shapes):
        """
        Set the list of selected shapes.

        @param Shape [] shapes
        """
        self._selectedShapes = shapes

    #>------------------------------------------------------------------------

    def KeepMoving(self, keep):
        """
        Tell the frame to continue capturing the mouse movements.
        Even after a mouse up event.

        @param bool keep : True to continue capturing mouse move events
        """
        self.__keepMoving = keep
        if not keep:
            self.Bind(wx.EVT_MOTION, self._NullCallback)

    #>------------------------------------------------------------------ 

    def Refresh(self, eraseBackground=True, rect=None):
        """
        This refresh is done imediately, not through an event.

        @param bool eraseBackground : if False, the stored background is used
        @param wx.Rect rect : not used
        """
        if eraseBackground:
            self.Redraw()
        else:
            self.RedrawWithBackground()

    #>------------------------------------------------------------------------

    def SaveBackground(self, dc):
        """
        Save the given dc as the new background image.

        @param wx.DC dc : the dc to save
        """
        w, h = self.GetSize()
        bb = self.__backgroundBitmap
        if (bb.GetWidth(), bb.GetHeight()) != (w, h):
            bb = self.__backgroundBitmap = wx.EmptyBitmap(w, h)
        mem = wx.MemoryDC()
        mem.SelectObject(bb)
        # ADDED BY C.DUTOIT - wx.Python version test
        #from wx.Python.wx. import wx.CHECK_VERSION
        #if wx.CHECK_VERSION(2, 3, 2):
        if wx.__version__>"2.3.2":
            x, y = self.CalcUnscrolledPosition(0, 0)
            mem.Blit(0, 0, w, h, dc, x, y)
        else:
            mem.Blit(0, 0, w, h, dc, 0, 0)
        mem.SelectObject(wx.NullBitmap)

    #>------------------------------------------------------------------------

    def LoadBackground(self, dc, w, h):
        """
        Load the background image in the given dc.

        @param wx.DC dc
        """
        mem = wx.MemoryDC()
        mem.SelectObject(self.__backgroundBitmap)
        dc.Blit(0, 0, w, h, mem, 0, 0)
        mem.SelectObject(wx.NullBitmap)

    #>------------------------------------------------------------------------

    def ClearBackground(self):
        """
        Clear the background image.
        """
        dc = wx.MemoryDC()
        bb = self.__backgroundBitmap
        w, h = self.GetSize()
        if (bb.GetWidth(), bb.GetHeight()) != (w, h):
            bb = self.__backgroundBitmap = wx.EmptyBitmap(w, h)
        dc.SelectObject(bb)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.Clear()
        dc.SelectObject(wx.NullBitmap)

    #>------------------------------------------------------------------------

    def CreateDC(self, loadBackground, w, h):
        """
        Create a DC, load the background on demand.

        @param boolean loadBackground
        @param int w, h : width and height of the frame.
        @return wx.DC
        """
        dc = wx.MemoryDC()
        bm = self.__workingBitmap
        # cache the bitmap, to avoid creating a new at each refresh.
        # only recreate it if the size of the window has changed
        if (bm.GetWidth(), bm.GetHeight()) != (w, h):
            bm = self.__workingBitmap = wx.EmptyBitmap(w, h)
        dc.SelectObject(bm)
        if loadBackground:
            self.LoadBackground(dc, w, h)
        else:
            dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
            dc.Clear()
        self.PrepareDC(dc)
        return dc

    #>------------------------------------------------------------------------

    def PrepareBackground(self):
        """
        Redraw the screen without movable shapes, store it as the background.
        """
        self.Redraw(None, True, True, False)

    #>------------------------------------------------------------------------

    def RedrawWithBackground(self):
        """
        Redraw the screen using the background.
        """
        self.Redraw(None, True, False, True)

    #>------------------------------------------------------------------------

    def Redraw(self, dc=None, full=True, saveBackground=False,
               useBackground=False):
        """
        Refresh the diagram graphically.
        If a dc is given, use it. Otherwise, a double buffered dc is used.

        @param wx.DC dc : if None, a default dc will be created
        @param bool full : if 0, only draws the borders of shapes
        @param bool saveBackground : if True, the background will be saved
        @param bool useBackground : if True, the background will be used
        """
        needBlit = False
        w, h = self.GetSize()

        if dc is None:
            dc = self.CreateDC(useBackground, w, h)
            needBlit = True

        dc.SetFont(self._defaultFont)

        #dc.SetUserScale(2, 2)
        #shapes = self._diagram.GetParentShapes()
        shapes = self._diagram.GetShapes()
        
        if full:
            # first time, need to create the background
            if saveBackground:
                # first, draw every non moving shapes
                for shape in shapes:
                    if not shape.IsMoving():
                        #C.Dutoit
                        #shape.Draw(dc, True)
                        shape.Draw(dc)

                # save the background
                self.SaveBackground(dc)

                # draw every moving shapes
                for shape in shapes:
                    if shape.IsMoving():
                        shape.Draw(dc)
                        #C.Dutoit
                        #shape.Draw(dc, True)

            if useBackground:
                # draw every moving shapes
                for shape in shapes:
                    if shape.IsMoving():
                        shape.Draw(dc)
                        #C.Dutoit
                        #shape.Draw(dc, True)

            else: # don't use background
                # draw all shapes
                for shape in shapes:
                    shape.Draw(dc)
                    #C.Dutoit
                    #shape.Draw(dc, True)

        else: # not full
            for shape in shapes:
                shape.DrawBorder(dc)
                shape.DrawAnchors(dc)

        if needBlit:
            #  MODIFIED BY C.DUTOIT : Added wx.Python test
            client = wx.ClientDC(self)
            #from wx.Python.wx. import wx.CHECK_VERSION
            #if wx.CHECK_VERSION(2, 3, 2):
            if wx.__version__>"2.3.2":
                x, y = self.CalcUnscrolledPosition(0, 0)
                client.Blit(0, 0, w, h, dc, x, y)
            else:
                client.Blit(0, 0, w, h, dc, 0, 0)

    #>------------------------------------------------------------------------

    def OnPaint(self, event):
        """
        Callback.
        Refresh the screen when a paint event is issued by the system.

        @param wx.Event event
        """
        dc = wx.PaintDC(self)
        w, h = self.GetSize()
        mem = self.CreateDC(False, w, h)
        mem.SetBackground(wx.Brush(self.GetBackgroundColour()))
        mem.Clear()
        self.Redraw(mem)
        dc.BeginDrawing()
        #  MODIFIED BY C.DUTOIT : Added wx.Python test
        #from wx.Python.wx. import wx.CHECK_VERSION
        #if wx.CHECK_VERSION(2, 3, 2):
        #import wx.Python
        if wx.__version__>"2.3.2":
            x, y = self.CalcUnscrolledPosition(0, 0)
            dc.Blit(0, 0, w, h, mem, x, y)
        else:
            dc.Blit(0, 0, w, h, mem, 0, 0)
        dc.EndDrawing()

    #>------------------------------------------------------------------------

    def _NullCallback(self, evt):
        #print "None"
        pass
    
    #>------------------------------------------------------------------------

    def _ConvertEventCoords(self, event):
        xView, yView = self.GetViewStart()
        xDelta, yDelta = self.GetScrollPixelsPerUnit()
        return (event.GetX() + (xView * xDelta),
                event.GetY() + (yView * yDelta))

    #>------------------------------------------------------------------------

    def GetCurrentZoom(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the global current zoom factor.
        """
        zoom = 1.0
        for z in self._zoomStack:
            zoom *= z
        return zoom
        
    #>------------------------------------------------------------------------
    
    def GetXOffset(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the x offset between the model an the view of the shapes (MVC)
        """
        return self._xOffset

    #>------------------------------------------------------------------------
    
    def GetYOffset(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the y offset between the model an the view of the shapes (MVC)
        """
        return self._yOffset

    #>------------------------------------------------------------------------
    
    def SetXOffset(self, offset):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        Set the x offset between the model an the view of the shapes (MVC)
        """
        self._xOffset = offset
        
    #>------------------------------------------------------------------------
    
    def SetYOffset(self, offset):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        Set the y offset between the model an the view of the shapes (MVC)
        """
        self._yOffset = offset
        
    #>------------------------------------------------------------------------
    
    def SetDefaultZoomFactor(self, factor):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        Set the default zoom factor (1 = 100%)
        """
        self._defaultZoomFactor = factor

    #>------------------------------------------------------------------------

    def SetMaxZoomFactor(self, factor):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        Set the maximal zoom factor that can be reached (1 = 100%)
        """
        self._maxZoomFactor = factor

    #>------------------------------------------------------------------------
        
    def GetDefaultZoomFactor(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the default zoom factor. (1 = 100%)
        """
        return self._defaultZoomFactor

    #>------------------------------------------------------------------------
    #PRZ
    def GetMaxZoomFactor(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the maximal zoom factor that can be reached. (1 = 100%)
        """
        return self._maxZoomFactor

    #>------------------------------------------------------------------------

    def SetMinZoomFactor(self, factor):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        Set the minimal zoom factor that can be reached. (1 = 100%)
        """
        self._minLevelZoom = factor

    #>------------------------------------------------------------------------

    def GetMinZoomFactor(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)
        @return the minimal zoom factor that can be reached. (1 = 100%)
        """
        return self._minZoomFactor
    
    #>------------------------------------------------------------------------    

    def DoZoomIn(self, ax, ay, width = 0, height = 0):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005) 
        
        Do the "zoom in" fitted on the selected area or with a default factor
        and the clicked point as central point of the zoom.
        The maximal zoom that can be reached is :
        
            self.GetMaxLevelZoom() * self.GetDefaultZoomFactor()

        If the maximal zoom level is reached, then the shapes are just centered
        on the selected area or on the clicked point.

        @param ax        :  abscissa of the upper left corner of the selected
                            area or abscissa of the central point of the zoom

        @param ay        :  ordinate of the upper left corner of the selected
                            area or ordinate of the central point of the zoom

        @param width    :   width of the selected area for the zoom

        @param height   :   height of the selected area for the zoom
        """

        #number of pixels per unit of scrolling
        xUnit, yUnit = self.GetScrollPixelsPerUnit()

        #position of the upper left corner of the client area
        #(work area that is visible) in scroll units.
        viewStartX, viewStartY = self.GetViewStart()

        #Get the client and virtual size of the work area, where
        #the client size is the size of the work area that is
        #visible and the virtual is the whole work area's size.
        clientWidth, clientHeight = self.GetClientSize()
        virtualWidth, virtualHeight = self.GetVirtualSize()

        #maximal zoom factor that can be applied
        #maxZoomFactor = self.GetMaxLevelZoom() * self.GetDefaultZoomFactor()
        maxZoomFactor = self.GetMaxZoomFactor()
        
        #transform event coords to get them relative to the upper left corner of
        #the virual screen (avoid the case where that corner is on a shape and
        #get its coords relative to the client view).
        if ax >= viewStartX * xUnit and ay >= viewStartY * yUnit :
            x = ax
            y = ay
        else :
            x = ax + viewStartX * xUnit
            y = ay + viewStartY * yUnit

        #to get the upper left corner of the zoom selected area in the
        #case where we select first the bottom right corner.
        if width < 0 :
            x = x - width
        if height < 0 :
            y = y - height

        #init the zoom's offsets and factor
        zoomFactor = 1
        dx = 0
        dy = 0
            
        #if there is no selected area but a clicked point, a default
        #zoom is performed with the clicked point as center.
        if width == 0 or height == 0:
            zoomFactor = self.GetDefaultZoomFactor()

            #check if the zoom factor that we are to apply combined with the
            #previous ones won't be beyond the maximal zoom. If it's the case,
            #we proceed to the calculation of the zoom factor that allows to 
            #exactly reach the maximal zoom.
            maxZoomReached = maxZoomFactor <= (self.GetCurrentZoom() * zoomFactor)
            if maxZoomReached:
                zoomFactor = maxZoomFactor/self.GetCurrentZoom()

            
            
            #if the view is reduced, we just eliminate the
            #last zoom out performed
            if self._zoomLevel < 0:
                self._zoomStack.pop()
                self._zoomLevel += 1
            else:
                if zoomFactor > 1.0:
                    self._zoomStack.append(zoomFactor)
                    self._zoomLevel += 1
                    
                    
            #calculation of the upper-left corner of a zoom area whose
            #size is the half of the diagram frame and which is centred
            #on the clicked point. This calculation is done in the way to
            #get the zoom area centred in the middle of the virtual screen.
            dx = virtualWidth/2 - x
            dy = virtualHeight/2 - y
                
        #if there is a selected area...   
        else:

            #to be sure to get all the shapes in the selected zoom area
            if width > height:
                zoomFactor = clientWidth / abs(width)
            else:
                zoomFactor = clientHeight / abs(height)

            #check if the zoom factor that we are to apply combined with the
            #previous ones won't be beyond the maximal zoom. If it's the case,
            #we proceed to the calculation of the zoom factor that allows to 
            #exactly reach the maximal zoom.
            maxZoomReached = maxZoomFactor <= self.GetCurrentZoom() * zoomFactor
            if maxZoomReached:   
                zoomFactor = maxZoomFactor/self.GetCurrentZoom()
                
            #calculation of the upper-left corner of a zoom area whose
            #size is the half of the diagram frame and which is centred
            #on the clicked point. This calculation is done in the way to
            #get the zoom area centred in the middle of the virtual screen.
            dx = virtualWidth/2 - x - (clientWidth / zoomFactor / 2.0)
            dy = virtualHeight/2 - y - (clientHeight / zoomFactor / 2.0)

            #we have to check if the "zoom in" on a reduced view produce
            #an other less reduced view or an elarged view. For this, we
            #get the global current zoom, multiply by the zoom factor to
            #obtain only one zoom factor.
            if self._zoomLevel < 0 :

                globalFactor = zoomFactor * self.GetCurrentZoom()
                self._zoomStack = []
                self._zoomStack.append(globalFactor)
                    
                if globalFactor < 1.0 :
                    self._zoomLevel = -1    #the view is still reduced
                elif globalFactor > 1.0 :
                    self._zoomLevel = 1     #the view is elarged
                else:
                    self._zoomLevel = 0     #the zoom in is just equal
                                            #to all the zoom out
                                            #previously applied
            else:
                if zoomFactor > 1.0:
                    self._zoomStack.append(zoomFactor)
                    self._zoomLevel += 1


        # set the offsets between the model and the view
        self.SetXOffset((self.GetXOffset() + dx) * zoomFactor)
        self.SetYOffset((self.GetYOffset() + dy) * zoomFactor)
            
        # updates the shapes (view) position and dimensions from
        # their models in the light of the new zoom factor and offsets.
        for shape in self.GetDiagram().GetShapes():
            shape.UpdateFromModel()

        #resize the virutal screen in order to match with the zoom
        virtualWidth = (virtualWidth) * zoomFactor
        virtualHeight = (virtualHeight) * zoomFactor
        virtualSize = wx.Size(virtualWidth, virtualHeight)
        self.SetVirtualSize(virtualSize)

        #perform the scrolling in the way to have the zoom area visible
        #and centred on the virutal screen.
        scrollX = (virtualWidth - clientWidth) /2 / xUnit
        scrollY = (virtualHeight - clientHeight) /2 /yUnit
        self.Scroll(scrollX, scrollY)
        
    #>------------------------------------------------------------------------
    
    def DoZoomOut(self, ax, ay) :

        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005)

        Do the 'zoom out' in the way to have the clicked point (ax, ay) as
        the central point of new view.
        If one or many 'zoom in' where performed before, then we just suppress the
        last one from the zoom stack. Else, we add the default zoom factor inversed
        to the stack.

        @param ax int   : abscissa of the clicked point
        @param ay int   : ordinate of the clicked point
        """
        
        #number of pixels per unit of scrolling
        xUnit, yUnit = self.GetScrollPixelsPerUnit()

        #position of the upper left corner of the client area
        #(work area that is visible) in scroll units.
        viewStartX, viewStartY = self.GetViewStart()

        #Get the client and virtual size of the work area, where
        #the client size is the size of the work area that is
        #visible and the virtual is the whole work area's size.
        clientWidth, clientHeight = self.GetClientSize()
        virtualWidth, virtualHeight = self.GetVirtualSize()
        
        #transform event coords to get them relative to the upper left corner of
        #the virual screen (avoid the case where that corner is on a shape and
        #get its coords relative to the shape).
        if ax >= viewStartX * xUnit and ay >= viewStartY * yUnit :
            x = ax
            y = ay
        else :
            x = ax + viewStartX * xUnit
            y = ay + viewStartY * yUnit

        #calculation of the upper-left corner of a zoom area whose
        #size is the half of the diagram frame and which is centred
        #on the clicked point. This calculation is done in the way to
        #get the zoom area centred in the middle of the virtual screen.
        dx = virtualWidth/2 - x
        dy = virtualHeight/2 - y

        minZoomFactor = self.GetMinZoomFactor()
        minZoomReached = False
        
        #if the view is elarged, then we just remove the last
        #zoom in factor that has been applied. Else, we apply
        #the default one inversed.
        if self._zoomLevel > 0:
            zoomFactor = 1/self._zoomStack.pop()
            self._zoomLevel -= 1
        else:
            zoomFactor = 1/self.GetDefaultZoomFactor()
            
            #check if minimal zoom has been reached
            minZoomReached = minZoomFactor >= (self.GetCurrentZoom() * zoomFactor)
            if not minZoomReached:
                self._zoomStack.append(zoomFactor)
                self._zoomLevel -= 1
            else:
                zoomFactor = minZoomFactor / self.GetCurrentZoom()
                if zoomFactor <> 1:
                    self._zoomStack.append(zoomFactor)
                    self._zoomLevel -=1
      
        # set the offsets between the view and the model of the
        # each shape on this diagram frame.
        self.SetXOffset((self.GetXOffset() + dx) * zoomFactor)
        self.SetYOffset((self.GetYOffset() + dy) * zoomFactor)

        # updates the shapes (view) position and dimensions from
        # their model in the light of the new zoom factor and offsets.
        for shape in self.GetDiagram().GetShapes():
            shape.UpdateFromModel()

        #resize the virutal screen in order to match with the zoom
        virtualWidth = (virtualWidth) * zoomFactor
        virtualHeight = (virtualHeight) * zoomFactor
        virtualSize = wx.Size(virtualWidth, virtualHeight)
        self.SetVirtualSize(virtualSize)

        #perform the scrolling in the way to have the zoom area visible
        #and centred on the virutal screen.
        scrollX = (virtualWidth - clientWidth) /2 / xUnit
        scrollY = (virtualHeight - clientHeight) /2 /yUnit
        self.Scroll(scrollX, scrollY)
     

    def SetMargins(self, left, right, top, bottom):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005
        set the size of the margins that can't be reached by the
        scrollbars if the frame is infinite.
        """
        self._leftMargin = left
        self._topMargin = top
        self._bottomMargin = bottom
        self._rightMargin = right

    #>------------------------------------------------------------------------

    def GetMargins(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005
        @return the size of the margins that can't be reached by the
        scrollbars if the frame is infinite.
        """
        return self._leftMargin, self._rightMargin, self._topMargin, self._bottomMargin

    #>------------------------------------------------------------------------
    
    def SetInfinite(self, infinite=False):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005
        Set this diagram frame as infinite work area. The result is that the
        virtual size is elarged when the scrollbar reached the specified
        margins (see SetMargins). When we set this as true, the scrollbars
        are moved in the middle of their scale.

        @param infinite Bool    : shows if the work area is infinite or not. 
        """
        
        self._isInfinite = infinite

        if infinite:

            #place all the shape in an area centred
            #on the infinite work area
            #self.FitOnShapes()

            vWidth, vHeight = self.GetVirtualSize()
            cWidth, cHeight = self.GetClientSize()
            # get the number of pixels per scroll unit
            xUnit, yUnit = self.GetScrollPixelsPerUnit()
            
            # get the number of scroll unit
            noUnitX = (vWidth-cWidth) / xUnit
            noUnitY = (vHeight-cHeight) / yUnit
            
            # set the scrollbars position in the middle of their scale
            self.Scroll(noUnitX / 2, noUnitY / 2)
            
    #>------------------------------------------------------------------------                       

    def IsInfinite(self):
        """
        added by P. Dabrowski <przemek.dabrowski@destroy-display.com> (11.11.2005
        @return this frame is infinite.
        """
        return self._isInfinite

    #>------------------------------------------------------------------------


        
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.