graphics/GraphicCommons.py
branch1.1 Korean release
changeset 968 eee7625de1f7
parent 945 c1159acb0886
child 993 7fbde4a19ec3
equal deleted inserted replaced
808:6e205c1f05a0 968:eee7625de1f7
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
       
    26 from time import time as gettime
       
    27 from math import *
       
    28 from types import *
       
    29 import datetime
       
    30 from threading import Lock,Timer
       
    31 
       
    32 #-------------------------------------------------------------------------------
       
    33 #                               Common constants
       
    34 #-------------------------------------------------------------------------------
       
    35 
       
    36 """
       
    37 Definition of constants for dimensions of graphic elements
       
    38 """
       
    39 
       
    40 # FBD and SFC constants
       
    41 MIN_MOVE = 5                            # Minimum move before starting a element dragging
       
    42 CONNECTOR_SIZE = 8                      # Size of connectors
       
    43 BLOCK_LINE_SIZE = 20                    # Minimum size of each line in a block
       
    44 HANDLE_SIZE = 6                         # Size of the squares for handles
       
    45 ANCHOR_DISTANCE = 5                     # Distance where wire is automativally attached to a connector
       
    46 POINT_RADIUS = 2                        # Radius of the point of wire ends
       
    47 MIN_SEGMENT_SIZE = 2                    # Minimum size of the endling segments of a wire
       
    48 
       
    49 # LD constants
       
    50 LD_LINE_SIZE = 40                       # Distance between two lines in a ladder rung
       
    51 LD_ELEMENT_SIZE = (21, 15)              # Size (width, height) of a ladder element (contact or coil)
       
    52 LD_WIRE_SIZE = 30                       # Size of a wire between two contact
       
    53 LD_WIRECOIL_SIZE = 70                   # Size of a wire between a coil and a contact
       
    54 LD_POWERRAIL_WIDTH = 3                  # Width of a Powerrail
       
    55 LD_OFFSET = (10, 10)                    # Distance (x, y) between each comment and rung of the ladder
       
    56 LD_COMMENT_DEFAULTSIZE = (600, 40)      # Size (width, height) of a comment box
       
    57 
       
    58 # SFC constants
       
    59 SFC_STEP_DEFAULT_SIZE = (40, 30)        # Default size of a SFC step
       
    60 SFC_TRANSITION_SIZE = (20, 2)           # Size of a SFC transition
       
    61 SFC_DEFAULT_SEQUENCE_INTERVAL = 40      # Default size of the interval between two divergence branches
       
    62 SFC_SIMULTANEOUS_SEQUENCE_EXTRA = 20    # Size of extra lines for simultaneous divergence and convergence
       
    63 SFC_JUMP_SIZE = (12, 13)                # Size of a SFC jump to step
       
    64 SFC_WIRE_MIN_SIZE = 25                  # Size of a wire between two elements
       
    65 SFC_ACTION_MIN_SIZE = (100, 30)         # Minimum size of an action block line
       
    66 
       
    67 # Type definition constants for graphic elements
       
    68 [INPUT, OUTPUT, INOUT] = range(3)
       
    69 [CONNECTOR, CONTINUATION] = range(2)
       
    70 [LEFTRAIL, RIGHTRAIL] = range(2)
       
    71 [CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] = range(4)
       
    72 [COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING] = range(6)
       
    73 [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] = range(4)
       
    74 
       
    75 # Constants for defining the type of dragging that has been selected
       
    76 [HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = range(5)
       
    77 
       
    78 # List of value for resize handle that are valid
       
    79 VALID_HANDLES = [(1,1), (1,2), (1,3), (2,3), (3,3), (3,2), (3,1), (2,1)]
       
    80 
       
    81 # Contants for defining the direction of a connector
       
    82 [EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)]
       
    83 
       
    84 # Contants for defining which mode is selected for each view 
       
    85 [MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, 
       
    86  MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, 
       
    87  MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15)
       
    88 
       
    89 # Contants for defining alignment types for graphic group 
       
    90 [ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6)
       
    91 
       
    92 # Contants for defining which drawing mode is selected for app
       
    93 [FREEDRAWING_MODE, DRIVENDRAWING_MODE] = [1, 2]
       
    94 
       
    95 # Color for Highlighting
       
    96 HIGHLIGHTCOLOR = wx.CYAN
       
    97 
       
    98 # Define highlight types
       
    99 ERROR_HIGHLIGHT = (wx.Colour(255, 255, 0), wx.RED)
       
   100 SEARCH_RESULT_HIGHLIGHT = (wx.Colour(255, 165, 0), wx.WHITE)
       
   101 
       
   102 # Define highlight refresh inhibition period in second
       
   103 REFRESH_HIGHLIGHT_PERIOD = 0.1
       
   104 
       
   105 # Define tooltip wait for displaying period in second
       
   106 TOOLTIP_WAIT_PERIOD = 0.5
       
   107 
       
   108 HANDLE_CURSORS = {
       
   109     (1, 1) : 2,
       
   110     (3, 3) : 2,
       
   111     (1, 3) : 3,
       
   112     (3, 1) : 3,
       
   113     (1, 2) : 4,
       
   114     (3, 2) : 4,
       
   115     (2, 1) : 5,
       
   116     (2, 3) : 5
       
   117 }
       
   118 
       
   119 def round_scaling(x, n, constraint=0):
       
   120     fraction = float(x) / float(n)
       
   121     if constraint == -1:
       
   122         xround = int(fraction)
       
   123     else:
       
   124         xround = round(fraction)
       
   125         if constraint == 1 and xround < fraction:
       
   126             xround += 1 
       
   127     return int(xround * n)
       
   128 
       
   129 """
       
   130 Basic vector operations for calculate wire points
       
   131 """
       
   132 
       
   133 # Create a vector from two points and define if vector must be normal
       
   134 def vector(p1, p2, normal = True):
       
   135     vector = (p2.x - p1.x, p2.y - p1.y)
       
   136     if normal:
       
   137         return normalize(vector)
       
   138     return vector
       
   139 
       
   140 # Calculate the norm of a given vector
       
   141 def norm(v):
       
   142     return sqrt(v[0] * v[0] + v[1] * v[1])
       
   143 
       
   144 # Normalize a given vector
       
   145 def normalize(v):
       
   146     v_norm = norm(v)
       
   147     # Verifie if it is not a null vector
       
   148     if v_norm > 0:
       
   149         return (v[0] / v_norm, v[1] / v_norm)
       
   150     else:
       
   151         return v
       
   152 
       
   153 # Calculate the scalar product of two vectors
       
   154 def is_null_vector(v):
       
   155     return v == (0, 0)
       
   156 
       
   157 # Calculate the scalar product of two vectors
       
   158 def add_vectors(v1, v2):
       
   159     return (v1[0] + v2[0], v1[1] + v2[1])
       
   160 
       
   161 # Calculate the scalar product of two vectors
       
   162 def product(v1, v2):
       
   163     return v1[0] * v2[0] + v1[1] * v2[1]
       
   164 
       
   165 
       
   166 """
       
   167 Function that calculates the nearest point of the grid defined by scaling for the given point
       
   168 """
       
   169 
       
   170 def GetScaledEventPosition(event, dc, scaling):
       
   171     pos = event.GetLogicalPosition(dc)
       
   172     if scaling:
       
   173         pos.x = round(float(pos.x) / float(scaling[0])) * scaling[0]
       
   174         pos.y = round(float(pos.y) / float(scaling[1])) * scaling[1]
       
   175     return pos
       
   176 
       
   177 
       
   178 """
       
   179 Function that choose a direction during the wire points generation
       
   180 """
       
   181 
       
   182 def DirectionChoice(v_base, v_target, dir_target):
       
   183     dir_product = product(v_base, v_target)
       
   184     if dir_product < 0:
       
   185         return (-v_base[0], -v_base[1])
       
   186     elif dir_product == 0 and product(v_base, dir_target) != 0:
       
   187         return dir_target
       
   188     return v_base
       
   189 
       
   190 SECOND = 1000000
       
   191 MINUTE = 60 * SECOND
       
   192 HOUR = 60 * MINUTE
       
   193 DAY = 24 * HOUR
       
   194 
       
   195 def generate_time(value):
       
   196     microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
       
   197     negative = microseconds < 0
       
   198     microseconds = abs(microseconds)
       
   199     data = "T#"
       
   200     not_null = False
       
   201     if negative:
       
   202         data += "-"
       
   203     for val, format in [(int(microseconds) / DAY, "%dd"),
       
   204                         ((int(microseconds) % DAY) / HOUR, "%dh"),
       
   205                         ((int(microseconds) % HOUR) / MINUTE, "%dm"),
       
   206                         ((int(microseconds) % MINUTE) / SECOND, "%ds")]:
       
   207         if val > 0 or not_null:
       
   208             data += format % val
       
   209             not_null = True
       
   210     data += "%gms" % (microseconds % SECOND / 1000.)
       
   211     return data
       
   212 
       
   213 def generate_date(value):
       
   214     base_date = datetime.datetime(1970, 1, 1)
       
   215     date = base_date + value 
       
   216     return date.strftime("DATE#%Y-%m-%d")
       
   217 
       
   218 def generate_datetime(value):
       
   219     base_date = datetime.datetime(1970, 1, 1)
       
   220     date_time = base_date + value 
       
   221     return date_time.strftime("DT#%Y-%m-%d-%H:%M:%S.%f")
       
   222 
       
   223 def generate_timeofday(value):
       
   224     microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
       
   225     negative = microseconds < 0
       
   226     microseconds = abs(microseconds)
       
   227     data = "TOD#"
       
   228     for val, format in [(int(microseconds) / HOUR, "%2.2d:"),
       
   229                         ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"),
       
   230                         ((int(microseconds) % MINUTE) / SECOND, "%2.2d."),
       
   231                         (microseconds % SECOND, "%6.6d")]:
       
   232         data += format % val
       
   233     return data
       
   234 
       
   235 TYPE_TRANSLATOR = {"TIME": generate_time,
       
   236                    "DATE": generate_date,
       
   237                    "DT": generate_datetime,
       
   238                    "TOD": generate_timeofday}
       
   239 
       
   240 def MiterPen(colour, width=1, style=wx.SOLID):
       
   241     pen = wx.Pen(colour, width, style)
       
   242     pen.SetJoin(wx.JOIN_MITER)
       
   243     pen.SetCap(wx.CAP_PROJECTING)
       
   244     return pen
       
   245 
       
   246 #-------------------------------------------------------------------------------
       
   247 #                            Debug Data Consumer Class
       
   248 #-------------------------------------------------------------------------------
       
   249 
       
   250 class DebugDataConsumer:
       
   251     
       
   252     def __init__(self):
       
   253         self.LastValue = None
       
   254         self.Value = None
       
   255         self.DataType = None
       
   256         self.LastForced = False
       
   257         self.Forced = False
       
   258         self.Inhibited = False
       
   259     
       
   260     def Inhibit(self, inhibit):
       
   261         self.Inhibited = inhibit
       
   262         if not inhibit and self.LastValue is not None:
       
   263             self.SetForced(self.LastForced)
       
   264             self.SetValue(self.LastValue)
       
   265             self.LastValue = None
       
   266     
       
   267     def SetDataType(self, data_type):
       
   268         self.DataType = data_type
       
   269     
       
   270     def NewValue(self, tick, value, forced=False):
       
   271         value = TYPE_TRANSLATOR.get(self.DataType, lambda x:x)(value)
       
   272         if self.Inhibited:
       
   273             self.LastValue = value
       
   274             self.LastForced = forced
       
   275         else:
       
   276             self.SetForced(forced)
       
   277             self.SetValue(value)
       
   278     
       
   279     def SetValue(self, value):
       
   280         self.Value = value
       
   281     
       
   282     def GetValue(self):
       
   283         return self.Value
       
   284     
       
   285     def SetForced(self, forced):
       
   286         self.Forced = forced
       
   287     
       
   288     def IsForced(self):
       
   289         return self.Forced
       
   290 
       
   291 #-------------------------------------------------------------------------------
       
   292 #                               Debug Viewer Class
       
   293 #-------------------------------------------------------------------------------
       
   294 
       
   295 REFRESH_PERIOD = 0.1
       
   296 DEBUG_REFRESH_LOCK = Lock()
       
   297 
       
   298 class DebugViewer:
       
   299     
       
   300     def __init__(self, producer, debug, register_tick=True):
       
   301         self.DataProducer = None
       
   302         self.Debug = debug
       
   303         self.RegisterTick = register_tick
       
   304         self.Inhibited = False
       
   305         
       
   306         self.DataConsumers = {}
       
   307         
       
   308         self.LastRefreshTime = gettime()
       
   309         self.HasAcquiredLock = False
       
   310         self.AccessLock = Lock()
       
   311         self.TimerAccessLock = Lock()
       
   312         
       
   313         self.LastRefreshTimer = None
       
   314         
       
   315         self.SetDataProducer(producer)
       
   316         
       
   317     def __del__(self):
       
   318         self.DataProducer = None
       
   319         self.DeleteDataConsumers()
       
   320         if self.LastRefreshTimer is not None:
       
   321             self.LastRefreshTimer.Stop()
       
   322         if self.HasAcquiredLock:
       
   323             DEBUG_REFRESH_LOCK.release()
       
   324     
       
   325     def SetDataProducer(self, producer):
       
   326         if self.RegisterTick and self.Debug:
       
   327             if producer is not None:
       
   328                 producer.SubscribeDebugIECVariable("__tick__", self)
       
   329             if self.DataProducer is not None:
       
   330                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)        
       
   331         self.DataProducer = producer
       
   332     
       
   333     def IsDebugging(self):
       
   334         return self.Debug
       
   335     
       
   336     def Inhibit(self, inhibit):
       
   337         for consumer, iec_path in self.DataConsumers.iteritems():
       
   338             consumer.Inhibit(inhibit)
       
   339         self.Inhibited = inhibit
       
   340     
       
   341     def AddDataConsumer(self, iec_path, consumer):
       
   342         if self.DataProducer is None:
       
   343             return None
       
   344         result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer)
       
   345         if result is not None and consumer != self:
       
   346             self.DataConsumers[consumer] = iec_path
       
   347             consumer.SetDataType(self.GetDataType(iec_path))
       
   348         return result
       
   349     
       
   350     def RemoveDataConsumer(self, consumer):
       
   351         iec_path = self.DataConsumers.pop(consumer, None)
       
   352         if iec_path is not None:
       
   353             self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
       
   354     
       
   355     def RegisterVariables(self):
       
   356         pass
       
   357     
       
   358     def GetDataType(self, iec_path):
       
   359         if self.DataProducer is not None:
       
   360             infos = self.DataProducer.GetInstanceInfos(iec_path)
       
   361             if infos is not None:
       
   362                 return infos["type"]
       
   363             return self.DataProducer.GetDebugIECVariableType(iec_path.upper())
       
   364         return None
       
   365     
       
   366     def IsNumType(self, data_type):
       
   367         return self.DataProducer.IsNumType(data_type)
       
   368     
       
   369     def ForceDataValue(self, iec_path, value):
       
   370         if self.DataProducer is not None:
       
   371             self.DataProducer.ForceDebugIECVariable(iec_path, value)
       
   372     
       
   373     def ReleaseDataValue(self, iec_path):
       
   374         if self.DataProducer is not None:
       
   375             self.DataProducer.ReleaseDebugIECVariable(iec_path)
       
   376     
       
   377     def DeleteDataConsumers(self):
       
   378         if self.DataProducer is not None:
       
   379             for consumer, iec_path in self.DataConsumers.iteritems():
       
   380                 self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
       
   381         self.DataConsumers = {}
       
   382     
       
   383     def ShouldRefresh(self):
       
   384         if self:
       
   385             wx.CallAfter(self._ShouldRefresh)
       
   386         
       
   387     def _ShouldRefresh(self):
       
   388         if self:
       
   389             if DEBUG_REFRESH_LOCK.acquire(False):
       
   390                 self.AccessLock.acquire()
       
   391                 self.HasAcquiredLock = True
       
   392                 self.AccessLock.release()
       
   393                 self.RefreshNewData()
       
   394             else:
       
   395                 self.TimerAccessLock.acquire()
       
   396                 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
       
   397                 self.LastRefreshTimer.start()
       
   398                 self.TimerAccessLock.release()
       
   399     
       
   400     def NewDataAvailable(self, tick, *args, **kwargs):
       
   401         self.TimerAccessLock.acquire()
       
   402         if self.LastRefreshTimer is not None:
       
   403             self.LastRefreshTimer.cancel()
       
   404             self.LastRefreshTimer=None
       
   405         self.TimerAccessLock.release()
       
   406         if self.IsShown() and not self.Inhibited:
       
   407             if gettime() - self.LastRefreshTime > REFRESH_PERIOD and DEBUG_REFRESH_LOCK.acquire(False):
       
   408                 self.AccessLock.acquire()
       
   409                 self.HasAcquiredLock = True
       
   410                 self.AccessLock.release()
       
   411                 self.LastRefreshTime = gettime()
       
   412                 self.Inhibit(True)
       
   413                 wx.CallAfter(self.RefreshViewOnNewData, *args, **kwargs)
       
   414             else:
       
   415                 self.TimerAccessLock.acquire()
       
   416                 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
       
   417                 self.LastRefreshTimer.start()
       
   418                 self.TimerAccessLock.release()
       
   419         elif not self.IsShown() and self.HasAcquiredLock:
       
   420             DebugViewer.RefreshNewData(self)
       
   421             
       
   422     def RefreshViewOnNewData(self, *args, **kwargs):
       
   423         if self:
       
   424             self.RefreshNewData(*args, **kwargs)
       
   425     
       
   426     def RefreshNewData(self, *args, **kwargs):
       
   427         self.Inhibit(False)
       
   428         self.AccessLock.acquire()
       
   429         if self.HasAcquiredLock:
       
   430             DEBUG_REFRESH_LOCK.release()
       
   431             self.HasAcquiredLock = False
       
   432         if gettime() - self.LastRefreshTime > REFRESH_PERIOD:
       
   433             self.LastRefreshTime = gettime()
       
   434         self.AccessLock.release()
       
   435 
       
   436 #-------------------------------------------------------------------------------
       
   437 #                               Viewer Rubberband
       
   438 #-------------------------------------------------------------------------------
       
   439 
       
   440 """
       
   441 Class that implements a rubberband
       
   442 """
       
   443 
       
   444 class RubberBand:
       
   445     
       
   446     # Create a rubberband by indicated on which window it must be drawn
       
   447     def __init__(self, viewer):
       
   448         self.Viewer = viewer
       
   449         self.drawingSurface = viewer.Editor
       
   450         self.Reset()
       
   451     
       
   452     # Method that initializes the internal attributes of the rubberband
       
   453     def Reset(self):
       
   454         self.startPoint = None
       
   455         self.currentBox = None
       
   456         self.lastBox = None
       
   457     
       
   458     # Method that return if a box is currently edited
       
   459     def IsShown(self):
       
   460         return self.currentBox != None
       
   461     
       
   462     # Method that returns the currently edited box
       
   463     def GetCurrentExtent(self):
       
   464         if self.currentBox is None:
       
   465             return self.lastBox
       
   466         return self.currentBox
       
   467     
       
   468     # Method called when a new box starts to be edited
       
   469     def OnLeftDown(self, event, dc, scaling):
       
   470         pos = GetScaledEventPosition(event, dc, scaling)
       
   471         # Save the point for calculate the box position and size
       
   472         self.startPoint = pos
       
   473         self.currentBox = wx.Rect(pos.x, pos.y, 0, 0)
       
   474         self.drawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
       
   475         self.Redraw()
       
   476     
       
   477     # Method called when dragging with a box edited
       
   478     def OnMotion(self, event, dc, scaling):
       
   479         pos = GetScaledEventPosition(event, dc, scaling)
       
   480         # Save the last position and size of the box for erasing it
       
   481         self.lastBox = wx.Rect(self.currentBox.x, self.currentBox.y, self.currentBox.width,
       
   482             self.currentBox.height)
       
   483         # Calculate new position and size of the box 
       
   484         if pos.x >= self.startPoint.x:
       
   485             self.currentBox.x = self.startPoint.x
       
   486             self.currentBox.width = pos.x - self.startPoint.x + 1
       
   487         else:
       
   488             self.currentBox.x = pos.x
       
   489             self.currentBox.width = self.startPoint.x - pos.x + 1
       
   490         if pos.y >= self.startPoint.y:
       
   491             self.currentBox.y = self.startPoint.y
       
   492             self.currentBox.height = pos.y - self.startPoint.y + 1
       
   493         else:
       
   494             self.currentBox.y = pos.y
       
   495             self.currentBox.height = self.startPoint.y - pos.y + 1
       
   496         self.Redraw()
       
   497     
       
   498     # Method called when dragging is stopped
       
   499     def OnLeftUp(self, event, dc, scaling):
       
   500         self.drawingSurface.SetCursor(wx.NullCursor)
       
   501         self.lastBox = self.currentBox
       
   502         self.currentBox = None
       
   503         self.Redraw()
       
   504 
       
   505     # Method that erase the last box and draw the new box
       
   506     def Redraw(self, dc = None):
       
   507         if dc is None:
       
   508             dc = self.Viewer.GetLogicalDC()
       
   509         scalex, scaley = dc.GetUserScale()
       
   510         dc.SetUserScale(1, 1)
       
   511         dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
       
   512         dc.SetBrush(wx.TRANSPARENT_BRUSH)
       
   513         dc.SetLogicalFunction(wx.XOR)
       
   514         if self.lastBox:
       
   515             # Erase last box
       
   516             dc.DrawRectangle(self.lastBox.x * scalex, self.lastBox.y * scaley, 
       
   517                              self.lastBox.width * scalex, self.lastBox.height * scaley)
       
   518         if self.currentBox:
       
   519             # Draw current box
       
   520             dc.DrawRectangle(self.currentBox.x * scalex, self.currentBox.y * scaley, 
       
   521                              self.currentBox.width * scalex, self.currentBox.height * scaley)
       
   522         dc.SetUserScale(scalex, scaley)
       
   523     
       
   524     # Erase last box
       
   525     def Erase(self, dc = None):
       
   526         if dc is None:
       
   527             dc = self.Viewer.GetLogicalDC()
       
   528         scalex, scaley = dc.GetUserScale()
       
   529         dc.SetUserScale(1, 1)
       
   530         dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
       
   531         dc.SetBrush(wx.TRANSPARENT_BRUSH)
       
   532         dc.SetLogicalFunction(wx.XOR)
       
   533         if self.lastBox:
       
   534             dc.DrawRectangle(self.lastBox.x * scalex, self.lastBox.y * scaley, 
       
   535                              self.lastBox.width * scalex, self.lastBox.height * scalex)
       
   536         dc.SetUserScale(scalex, scaley)
       
   537 
       
   538     # Draw current box
       
   539     def Draw(self, dc = None):
       
   540         if dc is None:
       
   541             dc = self.Viewer.GetLogicalDC()
       
   542         scalex, scaley = dc.GetUserScale()
       
   543         dc.SetUserScale(1, 1)
       
   544         dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
       
   545         dc.SetBrush(wx.TRANSPARENT_BRUSH)
       
   546         dc.SetLogicalFunction(wx.XOR)
       
   547         if self.currentBox:
       
   548             # Draw current box
       
   549             dc.DrawRectangle(self.currentBox.x * scalex, self.currentBox.y * scaley, 
       
   550                              self.currentBox.width * scalex, self.currentBox.height * scaley)
       
   551         dc.SetUserScale(scalex, scaley)
       
   552 
       
   553 #-------------------------------------------------------------------------------
       
   554 #                               Viewer ToolTip
       
   555 #-------------------------------------------------------------------------------
       
   556 
       
   557 """
       
   558 Class that implements a custom tool tip
       
   559 """
       
   560 
       
   561 if wx.Platform == '__WXMSW__':
       
   562     faces = { 'times': 'Times New Roman',
       
   563               'mono' : 'Courier New',
       
   564               'helv' : 'Arial',
       
   565               'other': 'Comic Sans MS',
       
   566               'size' : 10,
       
   567              }
       
   568 else:
       
   569     faces = { 'times': 'Times',
       
   570               'mono' : 'Courier',
       
   571               'helv' : 'Helvetica',
       
   572               'other': 'new century schoolbook',
       
   573               'size' : 12,
       
   574              }
       
   575 
       
   576 TOOLTIP_MAX_CHARACTERS = 30
       
   577 TOOLTIP_MAX_LINE = 5
       
   578 
       
   579 class ToolTip(wx.PopupWindow):
       
   580     
       
   581     def __init__(self, parent, tip):
       
   582         wx.PopupWindow.__init__(self, parent)
       
   583         
       
   584         self.CurrentPosition = wx.Point(0, 0)
       
   585         
       
   586         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
       
   587         self.SetTip(tip)
       
   588         
       
   589         self.Bind(wx.EVT_PAINT, self.OnPaint)
       
   590         
       
   591     def SetTip(self, tip):
       
   592         lines = []
       
   593         for line in tip.splitlines():
       
   594             if line != "":
       
   595                 words = line.split()
       
   596                 new_line = words[0]
       
   597                 for word in words[1:]:
       
   598                     if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS:
       
   599                         new_line += " " + word
       
   600                     else:
       
   601                         lines.append(new_line)
       
   602                         new_line = word
       
   603                 lines.append(new_line)
       
   604             else:
       
   605                 lines.append(line)
       
   606         if len(lines) > TOOLTIP_MAX_LINE:
       
   607             self.Tip = lines[:TOOLTIP_MAX_LINE]
       
   608             if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
       
   609                 self.Tip[-1] += "..."
       
   610             else:
       
   611                 self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + "..."
       
   612         else:
       
   613             self.Tip = lines
       
   614         wx.CallAfter(self.RefreshTip)
       
   615     
       
   616     def MoveToolTip(self, pos):
       
   617         self.CurrentPosition = pos
       
   618         self.SetPosition(pos)
       
   619     
       
   620     def GetTipExtent(self):
       
   621         max_width = 0
       
   622         max_height = 0
       
   623         for line in self.Tip:
       
   624             w, h = self.GetTextExtent(line)
       
   625             max_width = max(max_width, w)
       
   626             max_height += h
       
   627         return max_width, max_height
       
   628     
       
   629     def RefreshTip(self):
       
   630         if self:
       
   631             w, h = self.GetTipExtent()
       
   632             self.SetSize(wx.Size(w + 4, h + 4))
       
   633             self.SetPosition(self.CurrentPosition)
       
   634             self.Refresh()
       
   635         
       
   636     def OnPaint(self, event):
       
   637         dc = wx.AutoBufferedPaintDC(self)
       
   638         dc.Clear()
       
   639         dc.SetPen(MiterPen(wx.BLACK))
       
   640         dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
       
   641         dc.SetFont(wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"]))
       
   642         dc.BeginDrawing()
       
   643         w, h = self.GetTipExtent()
       
   644         dc.DrawRectangle(0, 0, w + 4, h + 4)
       
   645         offset = 0
       
   646         for line in self.Tip:
       
   647             dc.DrawText(line, 2, offset + 2)
       
   648             w, h = dc.GetTextExtent(line)
       
   649             offset += h
       
   650         dc.EndDrawing()
       
   651         event.Skip()
       
   652 
       
   653 #-------------------------------------------------------------------------------
       
   654 #                    Helpers for highlighting text
       
   655 #-------------------------------------------------------------------------------
       
   656 
       
   657 def AddHighlight(highlights, infos):
       
   658     RemoveHighlight(highlights, infos)
       
   659     highlights.append(infos)
       
   660 
       
   661 def RemoveHighlight(highlights, infos):
       
   662     if infos in highlights:
       
   663         highlights.remove(infos)
       
   664         return True
       
   665     return False
       
   666 
       
   667 def ClearHighlight(highlights, highlight_type=None):
       
   668     if highlight_type is not None:
       
   669         return [highlight for highlight in highlights if highlight[2] != highlight_type]
       
   670     return []
       
   671 
       
   672 def DrawHighlightedText(dc, text, highlights, x, y):
       
   673     current_pen = dc.GetPen()
       
   674     dc.SetPen(wx.TRANSPARENT_PEN)
       
   675     for start, end, highlight_type in highlights:
       
   676         dc.SetBrush(wx.Brush(highlight_type[0]))
       
   677         offset_width, offset_height = dc.GetTextExtent(text[:start[1]])
       
   678         part = text[start[1]:end[1] + 1]
       
   679         part_width, part_height = dc.GetTextExtent(part)
       
   680         dc.DrawRectangle(x + offset_width, y, part_width, part_height)
       
   681         dc.SetTextForeground(highlight_type[1])
       
   682         dc.DrawText(part, x + offset_width, y)
       
   683     dc.SetPen(current_pen)
       
   684     dc.SetTextForeground(wx.BLACK)
       
   685     
       
   686 #-------------------------------------------------------------------------------
       
   687 #                           Graphic element base class
       
   688 #-------------------------------------------------------------------------------
       
   689 
       
   690 """
       
   691 Class that implements a generic graphic element
       
   692 """
       
   693 
       
   694 class Graphic_Element:
       
   695     
       
   696     # Create a new graphic element
       
   697     def __init__(self, parent, id = None):
       
   698         self.Parent = parent
       
   699         self.Id = id
       
   700         self.oldPos = None
       
   701         self.StartPos = None
       
   702         self.CurrentDrag = None
       
   703         self.Handle = (None,None)
       
   704         self.Dragging = False
       
   705         self.Selected = False
       
   706         self.Highlighted = False
       
   707         self.Pos = wx.Point(0, 0)
       
   708         self.Size = wx.Size(0, 0)
       
   709         self.BoundingBox = wx.Rect(0, 0, 0, 0)
       
   710         self.Visible = False
       
   711         self.ToolTip = None
       
   712         self.ToolTipPos = None
       
   713         self.ToolTipTimer = wx.Timer(self.Parent, -1)
       
   714         self.Parent.Bind(wx.EVT_TIMER, self.OnToolTipTimer, self.ToolTipTimer)
       
   715     
       
   716     def __del__(self):
       
   717         self.ToolTipTimer.Stop()
       
   718     
       
   719     def GetDefinition(self):
       
   720         return [self.Id], []
       
   721     
       
   722     def TestVisible(self, screen):
       
   723         self.Visible = self.Selected or self.GetRedrawRect().Intersects(screen)
       
   724     
       
   725     def IsVisible(self):
       
   726         return self.Visible
       
   727     
       
   728     def SpreadCurrent(self):
       
   729         pass
       
   730     
       
   731     def GetConnectorTranslation(self, element):
       
   732         return {}
       
   733     
       
   734     def FindNearestConnector(self, position, connectors):
       
   735         distances = []
       
   736         for connector in connectors:
       
   737             connector_pos = connector.GetRelPosition()
       
   738             distances.append((sqrt((self.Pos.x + connector_pos.x - position.x) ** 2 +
       
   739                                    (self.Pos.y + connector_pos.y - position.y) ** 2),
       
   740                               connector))
       
   741         distances.sort()
       
   742         if len(distances) > 0:
       
   743             return distances[0][1]
       
   744         return None
       
   745         
       
   746     def IsOfType(self, type, reference):
       
   747         return self.Parent.IsOfType(type, reference)
       
   748     
       
   749     def IsEndType(self, type):
       
   750         return self.Parent.IsEndType(type)
       
   751         
       
   752     def GetDragging(self):
       
   753         return self.Dragging
       
   754     
       
   755     # Make a clone of this element
       
   756     def Clone(self, parent):
       
   757         return Graphic_Element(parent, self.Id)
       
   758     
       
   759     # Changes the block position
       
   760     def SetPosition(self, x, y):
       
   761         self.Pos.x = x
       
   762         self.Pos.y = y
       
   763         self.RefreshConnected()
       
   764         self.RefreshBoundingBox()
       
   765 
       
   766     # Returns the block position
       
   767     def GetPosition(self):
       
   768         return self.Pos.x, self.Pos.y
       
   769     
       
   770     # Changes the element size
       
   771     def SetSize(self, width, height):
       
   772         self.Size.SetWidth(width)
       
   773         self.Size.SetHeight(height)
       
   774         self.RefreshConnectors()
       
   775         self.RefreshBoundingBox()
       
   776 
       
   777     # Returns the element size
       
   778     def GetSize(self):
       
   779         return self.Size.GetWidth(), self.Size.GetHeight()
       
   780     
       
   781     # Returns the minimum element size
       
   782     def GetMinSize(self):
       
   783         return 0, 0
       
   784     
       
   785     # Set size of the element to the minimum size
       
   786     def SetBestSize(self, scaling, x_factor=0.5, y_factor=0.5):
       
   787         width, height = self.GetSize()
       
   788         posx, posy = self.GetPosition()
       
   789         min_width, min_height = self.GetMinSize()
       
   790         if width < min_width:
       
   791             self.Pos.x = max(0, self.Pos.x - (width - min_width) * x_factor)
       
   792             width = min_width
       
   793         if height < min_height:
       
   794             self.Pos.y = max(0, self.Pos.y - (height - min_height) * y_factor)
       
   795             height = min_height
       
   796         if scaling is not None:
       
   797             self.Pos.x = round_scaling(self.Pos.x, scaling[0])
       
   798             self.Pos.y = round_scaling(self.Pos.y, scaling[1])
       
   799             width = round_scaling(width, scaling[0], 1)
       
   800             height = round_scaling(height, scaling[1], 1)
       
   801         self.SetSize(width, height)
       
   802         return self.Pos.x - posx, self.Pos.y - posy
       
   803     
       
   804     # Refresh the element Bounding Box
       
   805     def RefreshBoundingBox(self):
       
   806         self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
       
   807     
       
   808     # Refresh the element connectors position
       
   809     def RefreshConnectors(self):
       
   810         pass
       
   811     
       
   812     # Refresh the position of wires connected to element inputs and outputs
       
   813     def RefreshConnected(self):
       
   814         pass
       
   815     
       
   816     # Change the parent
       
   817     def SetParent(self, parent):
       
   818         self.Parent = parent
       
   819     
       
   820     # Override this method for defining the method to call for deleting this element
       
   821     def Delete(self):
       
   822         pass
       
   823     
       
   824     # Returns the Id
       
   825     def GetId(self):
       
   826         return self.Id
       
   827     
       
   828     # Returns if the point given is in the bounding box
       
   829     def HitTest(self, pt, connectors=True):
       
   830         if connectors:
       
   831             rect = self.BoundingBox
       
   832         else:
       
   833             rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
       
   834         return rect.InsideXY(pt.x, pt.y)
       
   835     
       
   836     # Returns if the point given is in the bounding box
       
   837     def IsInSelection(self, rect):
       
   838         return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height)
       
   839     
       
   840     # Override this method for refreshing the bounding box
       
   841     def RefreshBoundingBox(self):
       
   842         pass
       
   843     
       
   844     # Returns the bounding box
       
   845     def GetBoundingBox(self):
       
   846         return self.BoundingBox
       
   847     
       
   848     # Returns the RedrawRect
       
   849     def GetRedrawRect(self, movex = 0, movey = 0):
       
   850         scalex, scaley = self.Parent.GetViewScale()
       
   851         rect = wx.Rect()
       
   852         rect.x = self.BoundingBox.x - int(HANDLE_SIZE / scalex) - 3 - abs(movex)
       
   853         rect.y = self.BoundingBox.y - int(HANDLE_SIZE / scaley) - 3 - abs(movey)
       
   854         rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4
       
   855         rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4
       
   856         return rect
       
   857     
       
   858     def Refresh(self, rect = None):
       
   859         if self.Visible:
       
   860             if rect is not None:
       
   861                 self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
       
   862             else:
       
   863                 self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False)
       
   864     
       
   865     # Change the variable that indicates if this element is selected
       
   866     def SetSelected(self, selected):
       
   867         self.Selected = selected
       
   868         self.Refresh()
       
   869     
       
   870     # Change the variable that indicates if this element is highlighted
       
   871     def SetHighlighted(self, highlighted):
       
   872         self.Highlighted = highlighted
       
   873         self.Refresh()
       
   874     
       
   875     # Test if the point is on a handle of this element
       
   876     def TestHandle(self, event):
       
   877         dc = self.Parent.GetLogicalDC()
       
   878         scalex, scaley = dc.GetUserScale()
       
   879         pos = event.GetPosition()
       
   880         pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y))
       
   881         
       
   882         left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
       
   883         center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
       
   884         right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
       
   885         
       
   886         top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
       
   887         middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
       
   888         bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
       
   889         
       
   890         extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top)
       
   891         intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE)
       
   892         
       
   893         # Verify that this element is selected
       
   894         if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y):
       
   895             # Find if point is on a handle horizontally
       
   896             if left <= pt.x < left + HANDLE_SIZE:
       
   897                 handle_x = 1
       
   898             elif center <= pt.x < center + HANDLE_SIZE:
       
   899                 handle_x = 2
       
   900             elif right <= pt.x < right + HANDLE_SIZE:
       
   901                 handle_x = 3
       
   902             else:
       
   903                 handle_x = 0
       
   904             # Find if point is on a handle vertically
       
   905             if top <= pt.y < top + HANDLE_SIZE:
       
   906                 handle_y = 1
       
   907             elif middle <= pt.y < middle + HANDLE_SIZE:
       
   908                 handle_y = 2
       
   909             elif bottom <= pt.y < bottom + HANDLE_SIZE:
       
   910                 handle_y = 3
       
   911             else:
       
   912                 handle_y = 0
       
   913             # Verify that the result is valid
       
   914             if (handle_x, handle_y) in VALID_HANDLES:
       
   915                 return handle_x, handle_y
       
   916         return 0, 0
       
   917     
       
   918     # Method called when a LeftDown event have been generated
       
   919     def OnLeftDown(self, event, dc, scaling):
       
   920         pos = event.GetLogicalPosition(dc)
       
   921         # Test if an handle have been clicked
       
   922         handle = self.TestHandle(event)
       
   923         # Find which type of handle have been clicked,
       
   924         # Save a resize event and change the cursor
       
   925         cursor = HANDLE_CURSORS.get(handle, 1)
       
   926         wx.CallAfter(self.Parent.SetCurrentCursor, cursor)
       
   927         if cursor > 1:
       
   928             self.Handle = (HANDLE_RESIZE, handle)
       
   929         else:
       
   930             self.Handle = (HANDLE_MOVE, None)
       
   931             self.SetSelected(False)
       
   932         # Initializes the last position
       
   933         self.oldPos = GetScaledEventPosition(event, dc, scaling)
       
   934         self.StartPos = wx.Point(self.Pos.x, self.Pos.y)
       
   935         self.CurrentDrag = wx.Point(0, 0)
       
   936     
       
   937     # Method called when a LeftUp event have been generated
       
   938     def OnLeftUp(self, event, dc, scaling):
       
   939         # If a dragging have been initiated
       
   940         if self.Dragging and self.oldPos:
       
   941             self.RefreshModel()
       
   942             self.Parent.RefreshBuffer()
       
   943         wx.CallAfter(self.Parent.SetCurrentCursor, 0)
       
   944         self.SetSelected(True)
       
   945         self.oldPos = None
       
   946 
       
   947     # Method called when a RightDown event have been generated
       
   948     def OnRightDown(self, event, dc, scaling):
       
   949         pass
       
   950 
       
   951     # Method called when a RightUp event have been generated
       
   952     def OnRightUp(self, event, dc, scaling):
       
   953         if self.Dragging and self.oldPos:
       
   954             self.RefreshModel()
       
   955             self.Parent.RefreshBuffer()
       
   956         wx.CallAfter(self.Parent.SetCurrentCursor, 0)
       
   957         self.SetSelected(True)
       
   958         self.oldPos = None
       
   959         if self.Parent.Debug:
       
   960             self.Parent.PopupForceMenu()
       
   961 
       
   962     # Method called when a LeftDClick event have been generated
       
   963     def OnLeftDClick(self, event, dc, scaling):
       
   964         pass
       
   965     
       
   966     # Method called when a Motion event have been generated
       
   967     def OnMotion(self, event, dc, scaling):
       
   968         # If the cursor is dragging and the element have been clicked
       
   969         if event.Dragging() and self.oldPos:
       
   970             # Calculate the movement of cursor
       
   971             pos = event.GetLogicalPosition(dc)
       
   972             movex = pos.x - self.oldPos.x
       
   973             movey = pos.y - self.oldPos.y
       
   974             # If movement is greater than MIN_MOVE then a dragging is initiated
       
   975             if not self.Dragging and (abs(movex) > MIN_MOVE or abs(movey) > MIN_MOVE):
       
   976                 self.Dragging = True
       
   977             # If a dragging have been initiated, refreshes the element state
       
   978             if self.Dragging:
       
   979                 dragx, dragy = self.ProcessDragging(movex, movey, event, scaling)
       
   980                 if event.ControlDown() and self.Handle[0] == HANDLE_MOVE:
       
   981                     self.oldPos.x = self.StartPos.x + self.CurrentDrag.x
       
   982                     self.oldPos.y = self.StartPos.y + self.CurrentDrag.y
       
   983                 else:
       
   984                     self.oldPos.x += dragx
       
   985                     self.oldPos.y += dragy
       
   986                 return dragx, dragy
       
   987             return movex, movey
       
   988         # If cursor just pass over the element, changes the cursor if it is on a handle
       
   989         else:
       
   990             pos = event.GetLogicalPosition(dc)
       
   991             handle = self.TestHandle(event)
       
   992             # Find which type of handle have been clicked,
       
   993             # Save a resize event and change the cursor
       
   994             cursor = HANDLE_CURSORS.get(handle, 0)
       
   995             wx.CallAfter(self.Parent.SetCurrentCursor, cursor)
       
   996             return 0, 0
       
   997 
       
   998     # Moves the element
       
   999     def Move(self, dx, dy, exclude = []):
       
  1000         self.Pos.x += max(-self.BoundingBox.x, dx)
       
  1001         self.Pos.y += max(-self.BoundingBox.y, dy)
       
  1002         self.RefreshConnected(exclude)
       
  1003         self.RefreshBoundingBox()
       
  1004     
       
  1005     # Resizes the element from position and size given
       
  1006     def Resize(self, x, y, width, height):
       
  1007         self.Move(x, y)
       
  1008         self.SetSize(width, height)
       
  1009     
       
  1010     # Refreshes the element state according to move defined and handle selected
       
  1011     def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1):
       
  1012         handle_type, handle = self.Handle
       
  1013         # If it is a resize handle, calculate the values from resizing
       
  1014         if handle_type == HANDLE_RESIZE:
       
  1015             if scaling is not None:
       
  1016                 scaling = (scaling[0] * width_fac, scaling[1] * height_fac)
       
  1017             x = y = start_x = start_y = 0
       
  1018             width, height = start_width, start_height = self.GetSize()
       
  1019             if handle[0] == 1:
       
  1020                 movex = max(-self.BoundingBox.x, movex)
       
  1021                 if scaling is not None:
       
  1022                     movex = -(round_scaling(width - movex, scaling[0]) - width)
       
  1023                 x = movex
       
  1024                 if event.ShiftDown():
       
  1025                     width -= 2 * movex
       
  1026                 else:
       
  1027                     width -= movex
       
  1028             elif handle[0] == 3:
       
  1029                 if scaling is not None:
       
  1030                     movex = round_scaling(width + movex, scaling[0]) - width
       
  1031                 if event.ShiftDown():
       
  1032                     movex = min(self.BoundingBox.x, movex)
       
  1033                     x = -movex
       
  1034                     width += 2 * movex
       
  1035                 else:
       
  1036                     width += movex
       
  1037             if handle[1] == 1:
       
  1038                 movey = max(-self.BoundingBox.y, movey)
       
  1039                 if scaling is not None:
       
  1040                     movey = -(round_scaling(height - movey, scaling[1]) - height)
       
  1041                 y = movey
       
  1042                 if event.ShiftDown():
       
  1043                     height -= 2 * movey
       
  1044                 else:
       
  1045                     height -= movey
       
  1046             elif handle[1] == 3:
       
  1047                 if scaling is not None:
       
  1048                     movey = round_scaling(height + movey, scaling[1]) - height
       
  1049                 if event.ShiftDown():
       
  1050                     movey = min(self.BoundingBox.y, movey)
       
  1051                     y = -movey
       
  1052                     height += 2 * movey
       
  1053                 else:
       
  1054                     height += movey
       
  1055             # Verify that new size is not lesser than minimum
       
  1056             min_width, min_height = self.GetMinSize()
       
  1057             if handle[0] != 2 and (width >= min_width or width > self.Size[0]):
       
  1058                 start_x = x
       
  1059                 start_width = width
       
  1060             else:
       
  1061                 movex = 0
       
  1062             if handle[1] != 2 and (height >= min_height or height > self.Size[1]):
       
  1063                 start_y = y
       
  1064                 start_height = height
       
  1065             else:
       
  1066                 movey = 0
       
  1067             if movex != 0 or movey != 0:
       
  1068                 self.Resize(start_x, start_y, start_width, start_height)
       
  1069             return movex, movey
       
  1070         # If it is a move handle, Move this element
       
  1071         elif handle_type == HANDLE_MOVE:
       
  1072             movex = max(-self.BoundingBox.x, movex)
       
  1073             movey = max(-self.BoundingBox.y, movey)
       
  1074             if scaling is not None:
       
  1075                 movex = round_scaling(self.Pos.x + movex, scaling[0]) - self.Pos.x
       
  1076                 movey = round_scaling(self.Pos.y + movey, scaling[1]) - self.Pos.y
       
  1077             if event.ControlDown():
       
  1078                 self.CurrentDrag.x = self.CurrentDrag.x + movex
       
  1079                 self.CurrentDrag.y = self.CurrentDrag.y + movey
       
  1080                 if abs(self.CurrentDrag.x) > abs(self.CurrentDrag.y):
       
  1081                     movex = self.StartPos.x + self.CurrentDrag.x - self.Pos.x
       
  1082                     movey = self.StartPos.y - self.Pos.y
       
  1083                 else:
       
  1084                     movex = self.StartPos.x - self.Pos.x
       
  1085                     movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y   
       
  1086             self.Move(movex, movey)
       
  1087             return movex, movey
       
  1088         return 0, 0
       
  1089     
       
  1090     def OnToolTipTimer(self, event):
       
  1091         value = self.GetToolTipValue()
       
  1092         if value is not None and self.ToolTipPos is not None:
       
  1093             self.ToolTip = ToolTip(self.Parent, value)
       
  1094             self.ToolTip.MoveToolTip(self.ToolTipPos)
       
  1095             self.ToolTip.Show()
       
  1096         
       
  1097     def GetToolTipValue(self):
       
  1098         return None
       
  1099     
       
  1100     def CreateToolTip(self, pos):
       
  1101         value = self.GetToolTipValue()
       
  1102         if value is not None:
       
  1103             self.ToolTipPos = pos
       
  1104             self.ToolTipTimer.Start(int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True)
       
  1105         
       
  1106     def MoveToolTip(self, pos):
       
  1107         if self.ToolTip is not None:
       
  1108             self.ToolTip.MoveToolTip(pos)
       
  1109         elif self.ToolTipPos is not None:
       
  1110             self.ToolTipPos = pos
       
  1111             self.ToolTipTimer.Start(int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True)
       
  1112     
       
  1113     def ClearToolTip(self):
       
  1114         self.ToolTipTimer.Stop()
       
  1115         self.ToolTipPos = None
       
  1116         if self.ToolTip is not None:
       
  1117             self.ToolTip.Destroy()
       
  1118             self.ToolTip = None
       
  1119     
       
  1120     # Override this method for defining the method to call for adding an highlight to this element
       
  1121     def AddHighlight(self, infos, start, end, highlight_type):
       
  1122         pass
       
  1123     
       
  1124     # Override this method for defining the method to call for removing an highlight from this element
       
  1125     def RemoveHighlight(self, infos, start, end, highlight_type):
       
  1126         pass
       
  1127     
       
  1128     # Override this method for defining the method to call for removing all the highlights of one particular type from this element
       
  1129     def ClearHighlight(self, highlight_type=None):
       
  1130         pass
       
  1131     
       
  1132     # Override this method for defining the method to call for refreshing the model of this element
       
  1133     def RefreshModel(self, move=True):
       
  1134         pass
       
  1135     
       
  1136     # Draws the highlightment of this element if it is highlighted (can be overwritten)
       
  1137     def DrawHighlightment(self, dc):
       
  1138         scalex, scaley = dc.GetUserScale()
       
  1139         dc.SetUserScale(1, 1)
       
  1140         dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
       
  1141         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  1142         dc.SetLogicalFunction(wx.AND)
       
  1143         dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, 
       
  1144                          int(round((self.Pos.y - 1) * scaley)) - 2, 
       
  1145                          int(round((self.Size.width + 3) * scalex)) + 5, 
       
  1146                          int(round((self.Size.height + 3) * scaley)) + 5)
       
  1147         dc.SetLogicalFunction(wx.COPY)
       
  1148         dc.SetUserScale(scalex, scaley)
       
  1149     
       
  1150     # Draws the handles of this element if it is selected
       
  1151     def Draw(self, dc):
       
  1152         if not getattr(dc, "printing", False):
       
  1153             if self.Highlighted:
       
  1154                 self.DrawHighlightment(dc)
       
  1155             if self.Selected:
       
  1156                 scalex, scaley = dc.GetUserScale()
       
  1157                 dc.SetUserScale(1, 1)
       
  1158                 dc.SetPen(MiterPen(wx.BLACK))
       
  1159                 dc.SetBrush(wx.BLACK_BRUSH)
       
  1160                 
       
  1161                 left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
       
  1162                 center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
       
  1163                 right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
       
  1164                 
       
  1165                 top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
       
  1166                 middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
       
  1167                 bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
       
  1168                 
       
  1169                 for x, y in [(left, top), (center, top), (right, top),
       
  1170                              (left, middle), (right, middle),
       
  1171                              (left, bottom), (center, bottom), (right, bottom)]:
       
  1172                     dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE)
       
  1173                 
       
  1174                 dc.SetUserScale(scalex, scaley)
       
  1175 
       
  1176 
       
  1177 #-------------------------------------------------------------------------------
       
  1178 #                           Group of graphic elements
       
  1179 #-------------------------------------------------------------------------------
       
  1180 
       
  1181 """
       
  1182 Class that implements a group of graphic elements
       
  1183 """
       
  1184 
       
  1185 class Graphic_Group(Graphic_Element):
       
  1186     
       
  1187     # Create a new group of graphic elements
       
  1188     def __init__(self, parent):
       
  1189         Graphic_Element.__init__(self, parent)
       
  1190         self.Elements = []
       
  1191         self.RefreshWireExclusion()
       
  1192         self.RefreshBoundingBox()
       
  1193     
       
  1194     # Destructor
       
  1195     def __del__(self):
       
  1196         self.Elements = []
       
  1197     
       
  1198     def GetDefinition(self):
       
  1199         blocks = [] 
       
  1200         wires = []
       
  1201         for element in self.Elements:
       
  1202             block, wire = element.GetDefinition()
       
  1203             blocks.extend(block)
       
  1204             wires.extend(wire)
       
  1205         return blocks, wires
       
  1206     
       
  1207     # Make a clone of this element
       
  1208     def Clone(self, parent, pos = None):
       
  1209         group = Graphic_Group(parent)
       
  1210         connectors = {}
       
  1211         exclude_names = {}
       
  1212         wires = []
       
  1213         if pos is not None:
       
  1214             dx, dy = pos.x - self.BoundingBox.x, pos.y - self.BoundingBox.y
       
  1215         for element in self.Elements:
       
  1216             if isinstance(element, Wire):
       
  1217                 wires.append(element)
       
  1218             else:
       
  1219                 if pos is not None:
       
  1220                     x, y = element.GetPosition()
       
  1221                     new_pos = wx.Point(x + dx, y + dy)
       
  1222                     newid = parent.GetNewId()
       
  1223                     if parent.IsNamedElement(element):
       
  1224                         name = parent.GenerateNewName(element, exclude_names)
       
  1225                         exclude_names[name.upper()] = True
       
  1226                         new_element = element.Clone(parent, newid, name, pos = new_pos)
       
  1227                     else:
       
  1228                         new_element = element.Clone(parent, newid, pos = new_pos)
       
  1229                     new_element.SetBestSize(parent.Scaling)
       
  1230                 else:
       
  1231                     new_element = element.Clone(parent)
       
  1232                 connectors.update(element.GetConnectorTranslation(new_element))
       
  1233                 group.SelectElement(new_element)
       
  1234         for element in wires:
       
  1235             if pos is not None:
       
  1236                 new_wire = element.Clone(parent, connectors, dx, dy)
       
  1237             else:
       
  1238                 new_wire = element.Clone(parent, connectors)
       
  1239             if new_wire is not None:
       
  1240                 if pos is not None:
       
  1241                     parent.AddWire(new_wire)
       
  1242                 group.SelectElement(new_wire)
       
  1243         if pos is not None:
       
  1244             for element in group.Elements:
       
  1245                 if not isinstance(element, Wire):
       
  1246                     parent.AddBlockInModel(element)
       
  1247         return group
       
  1248     
       
  1249     def CanAddBlocks(self, parent):
       
  1250         valid = True
       
  1251         for element in self.Elements:
       
  1252             if not isinstance(element, Wire):
       
  1253                 valid &= parent.CanAddElement(element)
       
  1254         return valid
       
  1255     
       
  1256     def IsVisible(self):
       
  1257         for element in self.Elements:
       
  1258             if element.IsVisible():
       
  1259                 return True
       
  1260         return False
       
  1261     
       
  1262     # Refresh the list of wire excluded
       
  1263     def RefreshWireExclusion(self):
       
  1264         self.WireExcluded = []
       
  1265         for element in self.Elements:
       
  1266             if isinstance(element, Wire):
       
  1267                 startblock = element.StartConnected.GetParentBlock()
       
  1268                 endblock = element.EndConnected.GetParentBlock()
       
  1269                 if startblock in self.Elements and endblock in self.Elements:
       
  1270                     self.WireExcluded.append(element)
       
  1271     
       
  1272     # Returns the RedrawRect
       
  1273     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1274         rect = None
       
  1275         for element in self.Elements:
       
  1276             if rect is None:
       
  1277                 rect = element.GetRedrawRect(movex, movey)
       
  1278             else:
       
  1279                 rect = rect.Union(element.GetRedrawRect(movex, movey))
       
  1280         return rect
       
  1281     
       
  1282     # Clean this group of elements
       
  1283     def Clean(self):
       
  1284         # Clean all the elements of the group
       
  1285         for element in self.Elements:
       
  1286             element.Clean()
       
  1287     
       
  1288     # Delete this group of elements
       
  1289     def Delete(self):
       
  1290         # Delete all the elements of the group
       
  1291         for element in self.Elements:
       
  1292             element.Delete()
       
  1293         self.WireExcluded = []
       
  1294     
       
  1295     # Returns if the point given is in the bounding box of one of the elements of this group
       
  1296     def HitTest(self, pt, connectors=True):
       
  1297         result = False
       
  1298         for element in self.Elements:
       
  1299             result |= element.HitTest(pt, connectors)
       
  1300         return result
       
  1301     
       
  1302     # Returns if the element given is in this group
       
  1303     def IsElementIn(self, element):
       
  1304         return element in self.Elements
       
  1305     
       
  1306     # Change the elements of the group
       
  1307     def SetElements(self, elements):
       
  1308         self.Elements = elements
       
  1309         self.RefreshWireExclusion()
       
  1310         self.RefreshBoundingBox()
       
  1311     
       
  1312     # Returns the elements of the group
       
  1313     def GetElements(self):
       
  1314         return self.Elements
       
  1315     
       
  1316     # Align the group elements
       
  1317     def AlignElements(self, horizontally, vertically):
       
  1318         minx = self.BoundingBox.x + self.BoundingBox.width
       
  1319         miny = self.BoundingBox.y + self.BoundingBox.height
       
  1320         maxx = self.BoundingBox.x
       
  1321         maxy = self.BoundingBox.y
       
  1322         for element in self.Elements:
       
  1323             if not isinstance(element, Wire):
       
  1324                 posx, posy = element.GetPosition()
       
  1325                 width, height = element.GetSize()
       
  1326                 minx = min(minx, posx)
       
  1327                 miny = min(miny, posy)
       
  1328                 maxx = max(maxx, posx + width)
       
  1329                 maxy = max(maxy, posy + height)
       
  1330         for element in self.Elements:
       
  1331             if not isinstance(element, Wire):
       
  1332                 posx, posy = element.GetPosition()
       
  1333                 width, height = element.GetSize()
       
  1334                 movex = movey = 0
       
  1335                 if horizontally == ALIGN_LEFT:
       
  1336                     movex = minx - posx
       
  1337                 elif horizontally == ALIGN_CENTER:
       
  1338                     movex = (maxx + minx - width) / 2 - posx
       
  1339                 elif horizontally == ALIGN_RIGHT:
       
  1340                     movex = maxx - width - posx
       
  1341                 if vertically == ALIGN_TOP:
       
  1342                     movey = miny - posy
       
  1343                 elif vertically == ALIGN_MIDDLE:
       
  1344                     movey = (maxy + miny - height) / 2 - posy
       
  1345                 elif vertically == ALIGN_BOTTOM:
       
  1346                     movey = maxy - height - posy
       
  1347                 if movex != 0 or movey != 0:
       
  1348                     element.Move(movex, movey)
       
  1349                     element.RefreshModel()
       
  1350         self.RefreshWireExclusion()
       
  1351         self.RefreshBoundingBox()
       
  1352     
       
  1353     # Remove or select the given element if it is or not in the group
       
  1354     def SelectElement(self, element):
       
  1355         if element in self.Elements:
       
  1356             self.Elements.remove(element)
       
  1357         else:
       
  1358             self.Elements.append(element)
       
  1359         self.RefreshWireExclusion()
       
  1360         self.RefreshBoundingBox()
       
  1361     
       
  1362     # Move this group of elements
       
  1363     def Move(self, movex, movey):
       
  1364         movex = max(-self.BoundingBox.x, movex)
       
  1365         movey = max(-self.BoundingBox.y, movey)
       
  1366         # Move all the elements of the group
       
  1367         for element in self.Elements:
       
  1368             if not isinstance(element, Wire):
       
  1369                 element.Move(movex, movey, self.WireExcluded)
       
  1370             elif element in self.WireExcluded:
       
  1371                 element.Move(movex, movey, True)
       
  1372         self.RefreshBoundingBox()
       
  1373     
       
  1374     # Refreshes the bounding box of this group of elements
       
  1375     def RefreshBoundingBox(self):
       
  1376         if len(self.Elements) > 0:
       
  1377             bbox = self.Elements[0].GetBoundingBox()
       
  1378             minx, miny = bbox.x, bbox.y
       
  1379             maxx = bbox.x + bbox.width
       
  1380             maxy = bbox.y + bbox.height
       
  1381             for element in self.Elements[1:]:
       
  1382                 bbox = element.GetBoundingBox()
       
  1383                 minx = min(minx, bbox.x)
       
  1384                 miny = min(miny, bbox.y)
       
  1385                 maxx = max(maxx, bbox.x + bbox.width)
       
  1386                 maxy = max(maxy, bbox.y + bbox.height)
       
  1387             self.BoundingBox = wx.Rect(minx, miny, maxx - minx, maxy - miny)
       
  1388         else:
       
  1389             self.BoundingBox = wx.Rect(0, 0, 0, 0)
       
  1390         self.Pos = wx.Point(self.BoundingBox.x, self.BoundingBox.y)
       
  1391         self.Size = wx.Size(self.BoundingBox.width, self.BoundingBox.height)
       
  1392 
       
  1393     # Forbids to change the group position
       
  1394     def SetPosition(x, y):
       
  1395         pass
       
  1396     
       
  1397     # Returns the position of this group
       
  1398     def GetPosition(self, exclude_wires=False):
       
  1399         if exclude_wires:
       
  1400             posx = posy = None
       
  1401             for element in self.Elements:
       
  1402                 if not isinstance(element, Wire) or element in self.WireExcluded:
       
  1403                     bbox = element.GetBoundingBox()
       
  1404                     if posx is None and posy is None:
       
  1405                         posx, posy = bbox.x, bbox.y
       
  1406                     else:
       
  1407                         posx = min(posx, bbox.x)
       
  1408                         posy = min(posy, bbox.y)
       
  1409             if posx is None and posy is None:
       
  1410                 return 0, 0
       
  1411             return posx, posy
       
  1412         return self.BoundingBox.x, self.BoundingBox.y
       
  1413     
       
  1414     # Forbids to change the group size
       
  1415     def SetSize(width, height):
       
  1416         pass
       
  1417     
       
  1418     # Returns the size of this group
       
  1419     def GetSize(self):
       
  1420         return self.BoundingBox.width, self.BoundingBox.height
       
  1421     
       
  1422     # Set size of the group elements to their minimum size
       
  1423     def SetBestSize(self, scaling):
       
  1424         max_movex = max_movey = 0
       
  1425         for element in self.Elements:
       
  1426             movex, movey = element.SetBestSize(scaling)
       
  1427             max_movex = max(max_movex, movex)
       
  1428             max_movey = max(max_movey, movey)
       
  1429         return max_movex, max_movey
       
  1430     
       
  1431     # Refreshes the group elements to move defined and handle selected
       
  1432     def ProcessDragging(self, movex, movey, event, scaling):
       
  1433         handle_type, handle = self.Handle
       
  1434         # If it is a move handle, Move this group elements
       
  1435         if handle_type == HANDLE_MOVE:
       
  1436             movex = max(-self.BoundingBox.x, movex)
       
  1437             movey = max(-self.BoundingBox.y, movey)
       
  1438             if scaling is not None:
       
  1439                 movex = round_scaling(movex, scaling[0])
       
  1440                 movey = round_scaling(movey, scaling[1])
       
  1441             if event.ControlDown():
       
  1442                 self.CurrentDrag.x = self.CurrentDrag.x + movex
       
  1443                 self.CurrentDrag.y = self.CurrentDrag.y + movey
       
  1444                 posx, posy = self.GetPosition(True)
       
  1445                 if abs(self.CurrentDrag.x) > abs(self.CurrentDrag.y):
       
  1446                     movex = self.StartPos.x + self.CurrentDrag.x - posx
       
  1447                     movey = self.StartPos.y - posy
       
  1448                 else:
       
  1449                     movex = self.StartPos.x - posx
       
  1450                     movey = self.StartPos.y + self.CurrentDrag.y - posy
       
  1451             self.Move(movex, movey)
       
  1452             return movex, movey
       
  1453         return 0, 0
       
  1454     
       
  1455     # Change the variable that indicates if this element is highlighted
       
  1456     def SetHighlighted(self, highlighted):
       
  1457         for element in self.Elements:
       
  1458             element.SetHighlighted(highlighted)
       
  1459     
       
  1460     def HighlightPoint(self, pos):
       
  1461         for element in self.Elements:
       
  1462             if isinstance(element, Wire):
       
  1463                 element.HighlightPoint(pos)
       
  1464     
       
  1465     # Method called when a LeftDown event have been generated
       
  1466     def OnLeftDown(self, event, dc, scaling):
       
  1467         Graphic_Element.OnLeftDown(self, event, dc, scaling)
       
  1468         self.StartPos = wx.Point(*self.GetPosition(True))
       
  1469         for element in self.Elements:
       
  1470             element.Handle = self.Handle
       
  1471 
       
  1472     # Change the variable that indicates if the elemente is selected
       
  1473     def SetSelected(self, selected):
       
  1474         for element in self.Elements:
       
  1475             element.SetSelected(selected)
       
  1476 
       
  1477     # Method called when a RightUp event has been generated
       
  1478     def OnRightUp(self, event, dc, scaling):
       
  1479         # Popup the menu with special items for a group
       
  1480         self.Parent.PopupGroupMenu()
       
  1481 
       
  1482     # Refreshes the model of all the elements of this group
       
  1483     def RefreshModel(self):
       
  1484         for element in self.Elements:
       
  1485             element.RefreshModel()
       
  1486 
       
  1487 
       
  1488 #-------------------------------------------------------------------------------
       
  1489 #                         Connector for all types of blocks
       
  1490 #-------------------------------------------------------------------------------
       
  1491 
       
  1492 """
       
  1493 Class that implements a connector for any type of block
       
  1494 """
       
  1495 
       
  1496 class Connector:
       
  1497     
       
  1498     # Create a new connector
       
  1499     def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False):
       
  1500         self.ParentBlock = parent
       
  1501         self.Name = name
       
  1502         self.Type = type
       
  1503         self.Pos = position
       
  1504         self.Direction = direction
       
  1505         self.Wires = []
       
  1506         if self.ParentBlock.IsOfType("BOOL", type):
       
  1507             self.Negated = negated
       
  1508             self.Edge = edge
       
  1509         else:
       
  1510             self.Negated = False
       
  1511             self.Edge = "none"
       
  1512         self.OneConnected = onlyone
       
  1513         self.Valid = True
       
  1514         self.Value = None
       
  1515         self.Forced = False
       
  1516         self.Selected = False
       
  1517         self.Highlights = []
       
  1518         self.RefreshNameSize()
       
  1519     
       
  1520     def Flush(self):
       
  1521         self.ParentBlock = None
       
  1522         for wire, handle in self.Wires:
       
  1523             wire.Flush()
       
  1524         self.Wires = []
       
  1525     
       
  1526     # Returns the RedrawRect
       
  1527     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1528         parent_pos = self.ParentBlock.GetPosition()
       
  1529         x = min(parent_pos[0] + self.Pos.x, parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE)
       
  1530         y = min(parent_pos[1] + self.Pos.y, parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE)
       
  1531         if self.Direction[0] == 0:
       
  1532             width = 5
       
  1533         else:
       
  1534             width = CONNECTOR_SIZE
       
  1535         if self.Direction[1] == 0:
       
  1536             height = 5
       
  1537         else:
       
  1538             height = CONNECTOR_SIZE
       
  1539         return wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey))
       
  1540     
       
  1541     # Change the connector selection
       
  1542     def SetSelected(self, selected):
       
  1543         self.Selected = selected
       
  1544     
       
  1545     # Make a clone of the connector
       
  1546     def Clone(self, parent = None):
       
  1547         if parent is None:
       
  1548             parent = self.ParentBlock
       
  1549         return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]),
       
  1550                 self.Direction, self.Negated)
       
  1551     
       
  1552     # Returns the connector parent block
       
  1553     def GetParentBlock(self):
       
  1554         return self.ParentBlock
       
  1555     
       
  1556     # Returns the connector type
       
  1557     def GetType(self, raw = False):
       
  1558         if self.ParentBlock.IsEndType(self.Type) or raw:
       
  1559             return self.Type
       
  1560         elif (self.Negated or self.Edge != "none") and self.ParentBlock.IsOfType("BOOL", self.Type):
       
  1561             return "BOOL"
       
  1562         else:
       
  1563             return self.ParentBlock.GetConnectionResultType(self, self.Type)
       
  1564     
       
  1565     # Returns the connector type
       
  1566     def GetConnectedType(self):
       
  1567         if self.ParentBlock.IsEndType(self.Type):
       
  1568             return self.Type
       
  1569         elif len(self.Wires) == 1:
       
  1570             return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1])
       
  1571         return self.Type
       
  1572     
       
  1573     # Returns the connector type
       
  1574     def GetConnectedRedrawRect(self, movex, movey):
       
  1575         rect = None
       
  1576         for wire, handle in self.Wires:
       
  1577             if rect is None:
       
  1578                 rect = wire.GetRedrawRect()
       
  1579             else:
       
  1580                 rect = rect.Union(wire.GetRedrawRect())
       
  1581         return rect
       
  1582     
       
  1583     # Returns if connector type is compatible with type given
       
  1584     def IsCompatible(self, type):
       
  1585         reference = self.GetType()
       
  1586         return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type)
       
  1587     
       
  1588     # Changes the connector name
       
  1589     def SetType(self, type):
       
  1590         self.Type = type
       
  1591         for wire, handle in self.Wires:
       
  1592             wire.SetValid(wire.IsConnectedCompatible())
       
  1593     
       
  1594     # Returns the connector name
       
  1595     def GetName(self):
       
  1596         return self.Name
       
  1597     
       
  1598     # Changes the connector name
       
  1599     def SetName(self, name):
       
  1600         self.Name = name
       
  1601         self.RefreshNameSize()
       
  1602 
       
  1603     def RefreshForced(self):
       
  1604         self.Forced = False
       
  1605         for wire, handle in self.Wires:
       
  1606             self.Forced |= wire.IsForced()
       
  1607 
       
  1608     def RefreshValue(self):
       
  1609         self.Value = self.ReceivingCurrent()
       
  1610     
       
  1611     def RefreshValid(self):
       
  1612         self.Valid = True
       
  1613         for wire, handle in self.Wires:
       
  1614             self.Valid &= wire.GetValid()
       
  1615     
       
  1616     def ReceivingCurrent(self):
       
  1617         current = False
       
  1618         for wire, handle in self.Wires:
       
  1619             value = wire.GetValue()
       
  1620             if current != "undefined" and isinstance(value, BooleanType):
       
  1621                 current |= wire.GetValue()
       
  1622             elif value == "undefined":
       
  1623                 current = "undefined"
       
  1624         return current
       
  1625     
       
  1626     def SpreadCurrent(self, spreading):
       
  1627         for wire, handle in self.Wires:
       
  1628             wire.SetValue(spreading)
       
  1629     
       
  1630     # Changes the connector name size
       
  1631     def RefreshNameSize(self):
       
  1632         if self.Name != "":
       
  1633             self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name)
       
  1634         else:
       
  1635             self.NameSize = 0, 0
       
  1636     
       
  1637     # Returns the connector name size
       
  1638     def GetNameSize(self):
       
  1639         return self.NameSize
       
  1640     
       
  1641     # Returns the wires connected to the connector
       
  1642     def GetWires(self):
       
  1643         return self.Wires
       
  1644     
       
  1645     # Returns the parent block Id
       
  1646     def GetBlockId(self):
       
  1647         return self.ParentBlock.GetId()
       
  1648     
       
  1649     # Returns the connector relative position
       
  1650     def GetRelPosition(self):
       
  1651         return self.Pos
       
  1652     
       
  1653     # Returns the connector absolute position
       
  1654     def GetPosition(self, size = True):
       
  1655         parent_pos = self.ParentBlock.GetPosition()
       
  1656         # If the position of the end of the connector is asked
       
  1657         if size:
       
  1658             x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE
       
  1659             y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE
       
  1660         else:
       
  1661             x = parent_pos[0] + self.Pos.x
       
  1662             y = parent_pos[1] + self.Pos.y
       
  1663         return wx.Point(x, y)
       
  1664     
       
  1665     # Change the connector relative position
       
  1666     def SetPosition(self, pos):
       
  1667         self.Pos = pos
       
  1668     
       
  1669     # Returns the connector direction
       
  1670     def GetDirection(self):
       
  1671         return self.Direction
       
  1672     
       
  1673     # Change the connector direction
       
  1674     def SetDirection(self, direction):
       
  1675         self.Direction = direction
       
  1676     
       
  1677     # Connect a wire to this connector at the last place
       
  1678     def Connect(self, wire, refresh = True):
       
  1679         self.InsertConnect(len(self.Wires), wire, refresh)
       
  1680     
       
  1681     # Connect a wire to this connector at the place given
       
  1682     def InsertConnect(self, idx, wire, refresh = True):
       
  1683         if wire not in self.Wires:
       
  1684             self.Wires.insert(idx, wire)
       
  1685             if refresh:
       
  1686                 self.ParentBlock.RefreshModel(False)
       
  1687     
       
  1688     # Returns the index of the wire given in the list of connected
       
  1689     def GetWireIndex(self, wire):
       
  1690         for i, (tmp_wire, handle) in enumerate(self.Wires):
       
  1691             if tmp_wire == wire:
       
  1692                 return i
       
  1693         return None
       
  1694     
       
  1695     # Unconnect a wire or all wires connected to the connector
       
  1696     def UnConnect(self, wire = None, unconnect = True, delete = False):
       
  1697         i = 0
       
  1698         found = False
       
  1699         while i < len(self.Wires) and not found:
       
  1700             if not wire or self.Wires[i][0] == wire:
       
  1701                 # If Unconnect haven't been called from a wire, disconnect the connector in the wire
       
  1702                 if unconnect:
       
  1703                     if self.Wires[i][1] == 0:
       
  1704                         self.Wires[i][0].UnConnectStartPoint(delete)
       
  1705                     else:
       
  1706                         self.Wires[i][0].UnConnectEndPoint(delete)
       
  1707                 # Remove wire from connected
       
  1708                 if wire:
       
  1709                     self.Wires.pop(i)
       
  1710                     found = True
       
  1711             i += 1
       
  1712         # If no wire defined, unconnect all wires
       
  1713         if not wire:
       
  1714             self.Wires = []
       
  1715         if not delete:
       
  1716             self.RefreshValid()
       
  1717             self.ParentBlock.RefreshModel(False)
       
  1718     
       
  1719     # Returns if connector has one or more wire connected
       
  1720     def IsConnected(self):
       
  1721         return len(self.Wires) > 0
       
  1722     
       
  1723     # Move the wires connected
       
  1724     def MoveConnected(self, exclude = []):
       
  1725         if len(self.Wires) > 0:
       
  1726             # Calculate the new position of the end point
       
  1727             parent_pos = self.ParentBlock.GetPosition()
       
  1728             x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE
       
  1729             y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE
       
  1730             # Move the corresponding point on all the wires connected
       
  1731             for wire, index in self.Wires:
       
  1732                 if wire not in exclude:
       
  1733                     if index == 0:
       
  1734                         wire.MoveStartPoint(wx.Point(x, y))
       
  1735                     else:
       
  1736                         wire.MoveEndPoint(wx.Point(x, y))
       
  1737     
       
  1738     # Refreshes the model of all the wires connected
       
  1739     def RefreshWires(self):
       
  1740         for wire in self.Wires:
       
  1741             wire[0].RefreshModel()
       
  1742     
       
  1743     # Refreshes the parent block model
       
  1744     def RefreshParentBlock(self):
       
  1745         self.ParentBlock.RefreshModel(False)
       
  1746     
       
  1747     # Highlight the parent block
       
  1748     def HighlightParentBlock(self, highlight):
       
  1749         self.ParentBlock.SetHighlighted(highlight)
       
  1750         self.ParentBlock.Refresh()
       
  1751     
       
  1752     # Returns all the blocks connected to this connector
       
  1753     def GetConnectedBlocks(self):
       
  1754         blocks = []
       
  1755         for wire, handle in self.Wires:
       
  1756             # Get other connector connected to each wire
       
  1757             if handle == 0:
       
  1758                 connector = wire.GetEndConnected()
       
  1759             else:
       
  1760                 connector = wire.GetStartConnected()
       
  1761             # Get parent block for this connector
       
  1762             if connector:
       
  1763                 block = connector.GetParentBlock()
       
  1764                 if block not in blocks:
       
  1765                     blocks.append(block)
       
  1766         return blocks
       
  1767     
       
  1768     # Returns the connector negated property
       
  1769     def IsNegated(self):
       
  1770         return self.Negated
       
  1771     
       
  1772     # Changes the connector negated property
       
  1773     def SetNegated(self, negated):
       
  1774         if self.ParentBlock.IsOfType("BOOL", self.Type):
       
  1775             self.Negated = negated
       
  1776             self.Edge = "none"
       
  1777     
       
  1778     # Returns the connector edge property
       
  1779     def GetEdge(self):
       
  1780         return self.Edge
       
  1781     
       
  1782     # Changes the connector edge property
       
  1783     def SetEdge(self, edge):
       
  1784         if self.ParentBlock.IsOfType("BOOL", self.Type):
       
  1785             self.Edge = edge    
       
  1786             self.Negated = False
       
  1787     
       
  1788     # Tests if the point given is near from the end point of this connector
       
  1789     def TestPoint(self, pt, direction = None, exclude = True):
       
  1790         parent_pos = self.ParentBlock.GetPosition()
       
  1791         if (not (len(self.Wires) > 0 and self.OneConnected and exclude) or self.Type == "BOOL")\
       
  1792             and direction is None or self.Direction == direction:
       
  1793             # Calculate a square around the end point of this connector
       
  1794             x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE - ANCHOR_DISTANCE
       
  1795             y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE - ANCHOR_DISTANCE
       
  1796             width = ANCHOR_DISTANCE * 2 + abs(self.Direction[0]) * CONNECTOR_SIZE
       
  1797             height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE
       
  1798             rect = wx.Rect(x, y, width, height)
       
  1799             return rect.InsideXY(pt.x, pt.y)
       
  1800         return False
       
  1801     
       
  1802     # Draws the highlightment of this element if it is highlighted
       
  1803     def DrawHighlightment(self, dc):
       
  1804         scalex, scaley = dc.GetUserScale()
       
  1805         dc.SetUserScale(1, 1)
       
  1806         pen = MiterPen(HIGHLIGHTCOLOR, 2 * scalex + 5)
       
  1807         pen.SetCap(wx.CAP_BUTT)
       
  1808         dc.SetPen(pen)
       
  1809         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  1810         dc.SetLogicalFunction(wx.AND)
       
  1811         parent_pos = self.ParentBlock.GetPosition()
       
  1812         posx = parent_pos[0] + self.Pos.x
       
  1813         posy = parent_pos[1] + self.Pos.y
       
  1814         xstart = parent_pos[0] + self.Pos.x 
       
  1815         ystart = parent_pos[1] + self.Pos.y
       
  1816         if self.Direction[0] < 0:
       
  1817             xstart += 1
       
  1818         if self.Direction[1] < 0:
       
  1819             ystart += 1
       
  1820         xend = xstart + CONNECTOR_SIZE * self.Direction[0]
       
  1821         yend = ystart + CONNECTOR_SIZE * self.Direction[1]
       
  1822         dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), 
       
  1823                     round(xend * scalex), round(yend * scaley))
       
  1824         dc.SetLogicalFunction(wx.COPY)
       
  1825         dc.SetUserScale(scalex, scaley)
       
  1826     
       
  1827     # Adds an highlight to the connector
       
  1828     def AddHighlight(self, infos, start, end, highlight_type):
       
  1829         if highlight_type == ERROR_HIGHLIGHT:
       
  1830             for wire, handle in self.Wires:
       
  1831                 wire.SetValid(False)
       
  1832         AddHighlight(self.Highlights, (start, end, highlight_type))
       
  1833     
       
  1834     # Removes an highlight from the connector
       
  1835     def RemoveHighlight(self, infos, start, end, highlight_type):
       
  1836         error = False
       
  1837         highlights = []
       
  1838         for highlight in self.Highlights:
       
  1839             if highlight != (start, end, highlight_type):
       
  1840                 highlights.append(highlight)
       
  1841                 error |= highlight == ERROR_HIGHLIGHT
       
  1842         self.Highlights = highlights
       
  1843         if not error:
       
  1844             for wire, handle in self.Wires:
       
  1845                 wire.SetValid(wire.IsConnectedCompatible())
       
  1846     
       
  1847     # Removes all the highlights of one particular type from the connector
       
  1848     def ClearHighlight(self, highlight_type=None):
       
  1849         error = False
       
  1850         if highlight_type is None:
       
  1851             self.Highlights = []
       
  1852         else:
       
  1853             highlights = []
       
  1854             for highlight in self.Highlights:
       
  1855                 if highlight[2] != highlight_type:
       
  1856                     highlights.append(highlight)
       
  1857                     error |= highlight == ERROR_HIGHLIGHT
       
  1858             self.Highlights = highlights
       
  1859         if not error:
       
  1860             for wire, handle in self.Wires:
       
  1861                 wire.SetValid(wire.IsConnectedCompatible())
       
  1862     
       
  1863     # Draws the connector
       
  1864     def Draw(self, dc):
       
  1865         if self.Selected:
       
  1866             dc.SetPen(MiterPen(wx.BLUE, 3))
       
  1867             dc.SetBrush(wx.WHITE_BRUSH)
       
  1868         #elif len(self.Highlights) > 0:
       
  1869         #    dc.SetPen(MiterPen(self.Highlights[-1][1]))
       
  1870         #    dc.SetBrush(wx.Brush(self.Highlights[-1][0]))
       
  1871         else:
       
  1872             if not self.Valid:
       
  1873                 dc.SetPen(MiterPen(wx.RED))
       
  1874             elif isinstance(self.Value, BooleanType) and self.Value:
       
  1875                 if self.Forced:
       
  1876                     dc.SetPen(MiterPen(wx.CYAN))
       
  1877                 else:
       
  1878                     dc.SetPen(MiterPen(wx.GREEN))
       
  1879             elif self.Value == "undefined":
       
  1880                 dc.SetPen(MiterPen(wx.NamedColour("orange")))
       
  1881             elif self.Forced:
       
  1882                 dc.SetPen(MiterPen(wx.BLUE))
       
  1883             else:
       
  1884                 dc.SetPen(MiterPen(wx.BLACK))
       
  1885             dc.SetBrush(wx.WHITE_BRUSH)
       
  1886         parent_pos = self.ParentBlock.GetPosition()
       
  1887         
       
  1888         if getattr(dc, "printing", False):
       
  1889             name_size = dc.GetTextExtent(self.Name)
       
  1890         else:
       
  1891             name_size = self.NameSize
       
  1892         
       
  1893         if self.Negated:
       
  1894             # If connector is negated, draw a circle
       
  1895             xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2
       
  1896             ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2
       
  1897             dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2)
       
  1898         else:
       
  1899             xstart = parent_pos[0] + self.Pos.x 
       
  1900             ystart = parent_pos[1] + self.Pos.y
       
  1901             if self.Edge == "rising":
       
  1902                 # If connector has a rising edge, draw a right arrow
       
  1903                 dc.DrawLine(xstart, ystart, xstart - 4, ystart - 4)
       
  1904                 dc.DrawLine(xstart, ystart, xstart - 4, ystart + 4)
       
  1905             elif self.Edge == "falling":
       
  1906                 # If connector has a falling edge, draw a left arrow
       
  1907                 dc.DrawLine(xstart, ystart, xstart + 4, ystart - 4)
       
  1908                 dc.DrawLine(xstart, ystart, xstart + 4, ystart + 4)
       
  1909             if self.Direction[0] < 0:
       
  1910                 xstart += 1
       
  1911             if self.Direction[1] < 0:
       
  1912                 ystart += 1
       
  1913             if self.Selected:
       
  1914                 xend = xstart + (CONNECTOR_SIZE - 2) * self.Direction[0]
       
  1915                 yend = ystart + (CONNECTOR_SIZE - 2) * self.Direction[1]
       
  1916                 dc.DrawLine(xstart + 2 * self.Direction[0], ystart + 2 * self.Direction[1], xend, yend)
       
  1917             else:
       
  1918                 xend = xstart + CONNECTOR_SIZE * self.Direction[0]
       
  1919                 yend = ystart + CONNECTOR_SIZE * self.Direction[1]
       
  1920                 dc.DrawLine(xstart + self.Direction[0], ystart + self.Direction[1], xend, yend)
       
  1921         if self.Direction[0] != 0:
       
  1922             ytext = parent_pos[1] + self.Pos.y - name_size[1] / 2
       
  1923             if self.Direction[0] < 0:
       
  1924                 xtext = parent_pos[0] + self.Pos.x + 5
       
  1925             else:
       
  1926                 xtext = parent_pos[0] + self.Pos.x - (name_size[0] + 5)
       
  1927         if self.Direction[1] != 0:
       
  1928             xtext = parent_pos[0] + self.Pos.x - name_size[0] / 2
       
  1929             if self.Direction[1] < 0:
       
  1930                 ytext = parent_pos[1] + self.Pos.y + 5
       
  1931             else:
       
  1932                 ytext = parent_pos[1] + self.Pos.y - (name_size[1] + 5)
       
  1933         # Draw the text
       
  1934         dc.DrawText(self.Name, xtext, ytext)
       
  1935         if not getattr(dc, "printing", False):
       
  1936             DrawHighlightedText(dc, self.Name, self.Highlights, xtext, ytext)
       
  1937 
       
  1938 #-------------------------------------------------------------------------------
       
  1939 #                           Common Wire Element
       
  1940 #-------------------------------------------------------------------------------
       
  1941 
       
  1942 """
       
  1943 Class that implements a wire for connecting two blocks
       
  1944 """
       
  1945 
       
  1946 class Wire(Graphic_Element, DebugDataConsumer):
       
  1947     
       
  1948     # Create a new wire
       
  1949     def __init__(self, parent, start = None, end = None):
       
  1950         Graphic_Element.__init__(self, parent)
       
  1951         DebugDataConsumer.__init__(self)
       
  1952         self.StartPoint = start
       
  1953         self.EndPoint = end
       
  1954         self.StartConnected = None
       
  1955         self.EndConnected = None
       
  1956         # If the start and end points are defined, calculate the wire
       
  1957         if start and end:
       
  1958             self.ResetPoints()
       
  1959             self.GeneratePoints()
       
  1960         else:
       
  1961             self.Points = []
       
  1962             self.Segments = []
       
  1963         self.SelectedSegment = None
       
  1964         self.Valid = True
       
  1965         self.ValueSize = None
       
  1966         self.ComputedValue = None
       
  1967         self.OverStart = False
       
  1968         self.OverEnd = False
       
  1969         self.ComputingType = False
       
  1970         self.Font = parent.GetMiniFont()
       
  1971     
       
  1972     def GetDefinition(self):
       
  1973         if self.StartConnected is not None and self.EndConnected is not None:
       
  1974             startblock = self.StartConnected.GetParentBlock()
       
  1975             endblock = self.EndConnected.GetParentBlock()
       
  1976             return [], [(startblock.GetId(), endblock.GetId())]
       
  1977         return [], []
       
  1978     
       
  1979     def Flush(self):
       
  1980         self.StartConnected = None
       
  1981         self.EndConnected = None
       
  1982     
       
  1983     def GetToolTipValue(self):
       
  1984         if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
       
  1985             wire_type = self.GetEndConnectedType()
       
  1986             if wire_type == "STRING":
       
  1987                 return "'%s'"%self.Value
       
  1988             elif wire_type == "WSTRING":
       
  1989                 return "\"%s\""%self.Value
       
  1990             else:
       
  1991                 return str(self.Value)
       
  1992         return None
       
  1993     
       
  1994     # Returns the RedrawRect
       
  1995     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1996         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
  1997         if self.StartConnected:
       
  1998             rect = rect.Union(self.StartConnected.GetRedrawRect(movex, movey))
       
  1999         if self.EndConnected:
       
  2000             rect = rect.Union(self.EndConnected.GetRedrawRect(movex, movey))
       
  2001         if self.ValueSize is None and isinstance(self.ComputedValue, (StringType, UnicodeType)):
       
  2002             self.ValueSize = self.Parent.GetMiniTextExtent(self.ComputedValue)
       
  2003         if self.ValueSize is not None:
       
  2004             width, height = self.ValueSize
       
  2005             if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
       
  2006                 x = self.Points[0].x + width * self.StartPoint[1][0] / 2
       
  2007                 y = self.Points[0].y + height * (self.StartPoint[1][1] - 1)
       
  2008                 rect = rect.Union(wx.Rect(x, y, width, height))
       
  2009                 x = self.Points[-1].x + width * self.EndPoint[1][0] / 2
       
  2010                 y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1)
       
  2011                 rect = rect.Union(wx.Rect(x, y, width, height))
       
  2012             else:
       
  2013                 middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
       
  2014                 x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
       
  2015                 if self.BoundingBox[3] > height and self.Segments[middle] in [NORTH, SOUTH]:
       
  2016                     y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
       
  2017                 else:
       
  2018                     y = self.Points[middle].y - height
       
  2019                 rect = rect.Union(wx.Rect(x, y, width, height))
       
  2020         return rect
       
  2021     
       
  2022     def Clone(self, parent, connectors = {}, dx = 0, dy = 0):
       
  2023         start_connector = connectors.get(self.StartConnected, None)
       
  2024         end_connector = connectors.get(self.EndConnected, None)
       
  2025         print self.StartConnected, "=>", start_connector, ",", self.EndConnected, "=>", end_connector
       
  2026         if start_connector is not None and end_connector is not None:
       
  2027             wire = Wire(parent)
       
  2028             wire.SetPoints([(point.x + dx, point.y + dy) for point in self.Points])
       
  2029             start_connector.Connect((wire, 0), False)
       
  2030             end_connector.Connect((wire, -1), False)
       
  2031             wire.ConnectStartPoint(start_connector.GetPosition(), start_connector)
       
  2032             wire.ConnectEndPoint(end_connector.GetPosition(), end_connector)
       
  2033             return wire
       
  2034         return None
       
  2035     
       
  2036     # Forbids to change the wire position
       
  2037     def SetPosition(x, y):
       
  2038         pass
       
  2039     
       
  2040     # Forbids to change the wire size
       
  2041     def SetSize(width, height):
       
  2042         pass
       
  2043     
       
  2044     # Forbids to et size of the group elements to their minimum size
       
  2045         pass
       
  2046     
       
  2047     # Moves and Resizes the element for fitting scaling
       
  2048     def SetBestSize(self, scaling):
       
  2049         if scaling is not None:
       
  2050             movex_max = movey_max = 0
       
  2051             for idx, point in enumerate(self.Points):
       
  2052                 if 0 < idx < len(self.Points) - 1:
       
  2053                     movex = round_scaling(point.x, scaling[0]) - point.x
       
  2054                     movey = round_scaling(point.y, scaling[1]) - point.y
       
  2055                     if idx == 1:
       
  2056                         if self.Segments[0][0] == 0:
       
  2057                             movex = 0
       
  2058                         elif (point.x + movex - self.Points[0].x) * self.Segments[0][0] < MIN_SEGMENT_SIZE:
       
  2059                             movex = round_scaling(self.Points[0].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - point.x
       
  2060                         if self.Segments[0][1] == 0:
       
  2061                             movey = 0
       
  2062                         elif (point.y + movey - self.Points[0].y) * self.Segments[0][1] < MIN_SEGMENT_SIZE:
       
  2063                             movey = round_scaling(self.Points[0].y + MIN_SEGMENT_SIZE * self.Segments[0][1], scaling[0], self.Segments[0][1]) - point.y
       
  2064                     elif idx == len(self.Points) - 2:
       
  2065                         if self.Segments[-1][0] == 0:
       
  2066                             movex = 0
       
  2067                         elif (self.Points[-1].x - (point.x + movex)) * self.Segments[-1][0] < MIN_SEGMENT_SIZE:
       
  2068                             movex = round_scaling(self.Points[-1].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - point.x
       
  2069                         if self.Segments[-1][1] == 0:
       
  2070                             movey = 0
       
  2071                         elif (self.Points[-1].y - (point.y + movey)) * self.Segments[-1][1] < MIN_SEGMENT_SIZE:
       
  2072                             movey = round_scaling(self.Points[-1].y - MIN_SEGMENT_SIZE * self.Segments[-1][1], scaling[1], -self.Segments[-1][1]) - point.y
       
  2073                     movex_max = max(movex_max, movex)
       
  2074                     movey_max = max(movey_max, movey)
       
  2075                     point.x += movex
       
  2076                     point.y += movey
       
  2077             return movex_max, movey_max
       
  2078         return 0, 0
       
  2079     
       
  2080     # Returns connector to which start point is connected
       
  2081     def GetStartConnected(self):
       
  2082         return self.StartConnected
       
  2083     
       
  2084     # Returns connector to which start point is connected
       
  2085     def GetStartConnectedType(self):
       
  2086         if self.StartConnected and not self.ComputingType:
       
  2087             self.ComputingType = True
       
  2088             computed_type = self.StartConnected.GetType()
       
  2089             self.ComputingType = False
       
  2090             return computed_type
       
  2091         return None
       
  2092     
       
  2093     # Returns connector to which end point is connected
       
  2094     def GetEndConnected(self):
       
  2095         return self.EndConnected
       
  2096     
       
  2097     # Returns connector to which end point is connected
       
  2098     def GetEndConnectedType(self):
       
  2099         if self.EndConnected and not self.ComputingType:
       
  2100             self.ComputingType = True
       
  2101             computed_type = self.EndConnected.GetType()
       
  2102             self.ComputingType = False
       
  2103             return computed_type
       
  2104         return None
       
  2105     
       
  2106     def GetConnectionDirection(self):
       
  2107         if self.StartConnected is None and self.EndConnected is None:
       
  2108             return None
       
  2109         elif self.StartConnected is not None and self.EndConnected is None:
       
  2110             return (-self.StartPoint[1][0], -self.StartPoint[1][1])
       
  2111         elif self.StartConnected is None and self.EndConnected is not None:
       
  2112             return self.EndPoint
       
  2113         elif self.Handle is not None:
       
  2114             handle_type, handle = self.Handle
       
  2115             # A point has been handled
       
  2116             if handle_type == HANDLE_POINT:
       
  2117                 if handle == 0:
       
  2118                     return self.EndPoint
       
  2119                 else:
       
  2120                     return (-self.StartPoint[1][0], -self.StartPoint[1][1])
       
  2121         return None
       
  2122     
       
  2123     def GetOtherConnected(self, connector):
       
  2124         if self.StartConnected == connector:
       
  2125             return self.EndConnected
       
  2126         else:
       
  2127             return self.StartConnected
       
  2128     
       
  2129     def GetOtherConnectedType(self, handle):
       
  2130         if handle == 0:
       
  2131             return self.GetEndConnectedType()
       
  2132         else:
       
  2133             return self.GetStartConnectedType()
       
  2134     
       
  2135     def IsConnectedCompatible(self):
       
  2136         if self.StartConnected:
       
  2137             return self.StartConnected.IsCompatible(self.GetEndConnectedType())
       
  2138         elif self.EndConnected:
       
  2139             return True
       
  2140         return False
       
  2141     
       
  2142     def SetForced(self, forced):
       
  2143         if self.Forced != forced:
       
  2144             self.Forced = forced
       
  2145             if self.StartConnected:
       
  2146                 self.StartConnected.RefreshForced()
       
  2147             if self.EndConnected:
       
  2148                 self.EndConnected.RefreshForced()
       
  2149             if self.Visible:
       
  2150                 self.Parent.ElementNeedRefresh(self)
       
  2151 
       
  2152     def SetValue(self, value):
       
  2153         if self.Value != value:
       
  2154             self.Value = value
       
  2155             if value is not None and not isinstance(value, BooleanType):
       
  2156                 wire_type = self.GetEndConnectedType()
       
  2157                 if wire_type == "STRING":
       
  2158                     self.ComputedValue = "'%s'"%value
       
  2159                 elif wire_type == "WSTRING":
       
  2160                     self.ComputedValue = "\"%s\""%value
       
  2161                 else:
       
  2162                     self.ComputedValue = str(value)
       
  2163                 if self.ToolTip is not None:
       
  2164                     self.ToolTip.SetTip(self.ComputedValue)
       
  2165                 if len(self.ComputedValue) > 4:
       
  2166                     self.ComputedValue = self.ComputedValue[:4] + "..."
       
  2167             self.ValueSize = None
       
  2168             if self.StartConnected:
       
  2169                 self.StartConnected.RefreshValue()
       
  2170             if self.EndConnected:
       
  2171                 self.EndConnected.RefreshValue()
       
  2172             if self.Visible:
       
  2173                 self.Parent.ElementNeedRefresh(self)
       
  2174             if isinstance(value, BooleanType) and self.StartConnected is not None:
       
  2175                 block = self.StartConnected.GetParentBlock()
       
  2176                 block.SpreadCurrent()
       
  2177     
       
  2178     # Unconnect the start and end points
       
  2179     def Clean(self):
       
  2180         if self.StartConnected:
       
  2181             self.UnConnectStartPoint()
       
  2182         if self.EndConnected:
       
  2183             self.UnConnectEndPoint()
       
  2184     
       
  2185     # Delete this wire by calling the corresponding method
       
  2186     def Delete(self):
       
  2187         self.Parent.DeleteWire(self)
       
  2188     
       
  2189     # Select a segment and not the whole wire. It's useful for Ladder Diagram
       
  2190     def SetSelectedSegment(self, segment):
       
  2191         # The last segment is indicated
       
  2192         if segment == -1:
       
  2193             segment = len(self.Segments) - 1
       
  2194         # The selected segment is reinitialised
       
  2195         if segment == None:
       
  2196             if self.StartConnected:
       
  2197                 self.StartConnected.SetSelected(False)
       
  2198             if self.EndConnected:
       
  2199                 self.EndConnected.SetSelected(False)
       
  2200         # The segment selected is the first
       
  2201         elif segment == 0:
       
  2202             if self.StartConnected:
       
  2203                 self.StartConnected.SetSelected(True)
       
  2204             if self.EndConnected:
       
  2205                 # There is only one segment
       
  2206                 if len(self.Segments) == 1:
       
  2207                     self.EndConnected.SetSelected(True)
       
  2208                 else:
       
  2209                     self.EndConnected.SetSelected(False)
       
  2210         # The segment selected is the last
       
  2211         elif segment == len(self.Segments) - 1:
       
  2212             if self.StartConnected:
       
  2213                 self.StartConnected.SetSelected(False)
       
  2214             if self.EndConnected:
       
  2215                 self.EndConnected.SetSelected(True)
       
  2216         self.SelectedSegment = segment
       
  2217         self.Refresh()
       
  2218     
       
  2219     def SetValid(self, valid):
       
  2220         self.Valid = valid
       
  2221         if self.StartConnected:
       
  2222             self.StartConnected.RefreshValid()
       
  2223         if self.EndConnected:
       
  2224             self.EndConnected.RefreshValid()
       
  2225     
       
  2226     def GetValid(self):
       
  2227         return self.Valid
       
  2228     
       
  2229     # Reinitialize the wire points
       
  2230     def ResetPoints(self):
       
  2231         if self.StartPoint and self.EndPoint:
       
  2232             self.Points = [self.StartPoint[0], self.EndPoint[0]]
       
  2233             self.Segments = [self.StartPoint[1]]
       
  2234         else:
       
  2235             self.Points = []
       
  2236             self.Segments = []
       
  2237     
       
  2238     # Refresh the wire bounding box
       
  2239     def RefreshBoundingBox(self):
       
  2240         if len(self.Points) > 0:
       
  2241             # If startpoint or endpoint is connected, save the point radius
       
  2242             start_radius = end_radius = 0
       
  2243             if not self.StartConnected:
       
  2244                 start_radius = POINT_RADIUS
       
  2245             if not self.EndConnected:
       
  2246                 end_radius = POINT_RADIUS
       
  2247             # Initialize minimum and maximum from the first point
       
  2248             minx, minbbxx = self.Points[0].x, self.Points[0].x - start_radius
       
  2249             maxx, maxbbxx = self.Points[0].x, self.Points[0].x + start_radius
       
  2250             miny, minbbxy = self.Points[0].y, self.Points[0].y - start_radius
       
  2251             maxy, maxbbxy = self.Points[0].y, self.Points[0].y + start_radius
       
  2252             # Actualize minimum and maximum with the other points
       
  2253             for point in self.Points[1:-1]:
       
  2254                 minx, minbbxx = min(minx, point.x), min(minbbxx, point.x)
       
  2255                 maxx, maxbbxx = max(maxx, point.x), max(maxbbxx, point.x)
       
  2256                 miny, minbbxy = min(miny, point.y), min(minbbxy, point.y)
       
  2257                 maxy, maxbbxy = max(maxy, point.y), max(maxbbxy, point.y)
       
  2258             if len(self.Points) > 1:
       
  2259                 minx, minbbxx = min(minx, self.Points[-1].x), min(minbbxx, self.Points[-1].x - end_radius)
       
  2260                 maxx, maxbbxx = max(maxx, self.Points[-1].x), max(maxbbxx, self.Points[-1].x + end_radius)
       
  2261                 miny, minbbxy = min(miny, self.Points[-1].y), min(minbbxy, self.Points[-1].y - end_radius)
       
  2262                 maxy, maxbbxy = max(maxy, self.Points[-1].y), max(maxbbxy, self.Points[-1].y + end_radius)
       
  2263             self.Pos.x, self.Pos.y = minx, miny
       
  2264             self.Size = wx.Size(maxx - minx, maxy - miny)
       
  2265             self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1)
       
  2266     
       
  2267     # Refresh the realpoints that permits to keep the proportionality in wire during resizing
       
  2268     def RefreshRealPoints(self):
       
  2269         if len(self.Points) > 0:
       
  2270             self.RealPoints = []
       
  2271             # Calculate float relative position of each point with the minimum point
       
  2272             for point in self.Points:
       
  2273                 self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)])
       
  2274     
       
  2275     # Returns the wire minimum size 
       
  2276     def GetMinSize(self):
       
  2277         width = 1
       
  2278         height = 1
       
  2279         dir_product = product(self.StartPoint[1], self.EndPoint[1])
       
  2280         # The directions are opposed
       
  2281         if dir_product < 0:
       
  2282             if self.StartPoint[0] != 0:
       
  2283                 width = MIN_SEGMENT_SIZE * 2
       
  2284             if self.StartPoint[1] != 0:
       
  2285                 height = MIN_SEGMENT_SIZE * 2
       
  2286         # The directions are the same
       
  2287         elif dir_product > 0:
       
  2288             if self.StartPoint[0] != 0:
       
  2289                 width = MIN_SEGMENT_SIZE
       
  2290             if self.StartPoint[1] != 0:
       
  2291                 height = MIN_SEGMENT_SIZE
       
  2292         # The directions are perpendiculars
       
  2293         else:
       
  2294             width = MIN_SEGMENT_SIZE
       
  2295             height = MIN_SEGMENT_SIZE
       
  2296         return width + 1, height + 1
       
  2297     
       
  2298     # Returns if the point given is on one of the wire segments
       
  2299     def HitTest(self, pt, connectors=True):
       
  2300         test = False
       
  2301         for i in xrange(len(self.Points) - 1):
       
  2302             rect = wx.Rect(0, 0, 0, 0)
       
  2303             if i == 0 and self.StartConnected is not None:
       
  2304                 x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE
       
  2305                 y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE
       
  2306             else:
       
  2307                 x1, y1 = self.Points[i].x, self.Points[i].y    
       
  2308             if i == len(self.Points) - 2 and self.EndConnected is not None:
       
  2309                 x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE
       
  2310                 y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE
       
  2311             else:
       
  2312                 x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
       
  2313             # Calculate a rectangle around the segment
       
  2314             rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
       
  2315                 abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
       
  2316             test |= rect.InsideXY(pt.x, pt.y) 
       
  2317         return test
       
  2318     
       
  2319     # Returns the wire start or end point if the point given is on one of them 
       
  2320     def TestPoint(self, pt):
       
  2321         # Test the wire start point
       
  2322         rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE,
       
  2323             2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE)
       
  2324         if rect.InsideXY(pt.x, pt.y):
       
  2325             return 0
       
  2326         # Test the wire end point
       
  2327         if len(self.Points) > 1:
       
  2328             rect = wx.Rect(self.Points[-1].x - ANCHOR_DISTANCE, self.Points[-1].y - ANCHOR_DISTANCE,
       
  2329                 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE)
       
  2330             if rect.InsideXY(pt.x, pt.y):
       
  2331                 return -1
       
  2332         return None
       
  2333     
       
  2334     # Returns the wire segment if the point given is on it
       
  2335     def TestSegment(self, pt, all=False):
       
  2336         for i in xrange(len(self.Segments)):
       
  2337             # If wire is not in a Ladder Diagram, first and last segments are excluded
       
  2338             if all or 0 < i < len(self.Segments) - 1:
       
  2339                 x1, y1 = self.Points[i].x, self.Points[i].y
       
  2340                 x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
       
  2341                 # Calculate a rectangle around the segment
       
  2342                 rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
       
  2343                     abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
       
  2344                 if rect.InsideXY(pt.x, pt.y):
       
  2345                     return i, self.Segments[i]
       
  2346         return None
       
  2347     
       
  2348     # Define the wire points
       
  2349     def SetPoints(self, points, verify=True):
       
  2350         if len(points) > 1:
       
  2351             self.Points = [wx.Point(x, y) for x, y in points]
       
  2352             # Calculate the start and end directions
       
  2353             self.StartPoint = [None, vector(self.Points[0], self.Points[1])]
       
  2354             self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])]
       
  2355             # Calculate the start and end points
       
  2356             self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], 
       
  2357                 self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1])
       
  2358             self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], 
       
  2359                 self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1])
       
  2360             self.Points[0] = self.StartPoint[0]
       
  2361             self.Points[-1] = self.EndPoint[0]
       
  2362             # Calculate the segments directions
       
  2363             self.Segments = []
       
  2364             i = 0
       
  2365             while i < len(self.Points) - 1:
       
  2366                 if verify and 0 < i < len(self.Points) - 2 and \
       
  2367                    self.Points[i] == self.Points[i + 1] and \
       
  2368                    self.Segments[-1] == vector(self.Points[i + 1], self.Points[i + 2]):
       
  2369                     for j in xrange(2):
       
  2370                         self.Points.pop(i)
       
  2371                 else:
       
  2372                     segment = vector(self.Points[i], self.Points[i + 1])
       
  2373                     if is_null_vector(segment) and i > 0:
       
  2374                         segment = (self.Segments[-1][1], self.Segments[-1][0])
       
  2375                     if i < len(self.Points) - 2:
       
  2376                         next = vector(self.Points[i + 1], self.Points[i + 2])
       
  2377                         if next == segment or is_null_vector(add_vectors(segment, next)):
       
  2378                             self.Points.insert(i + 1, wx.Point(self.Points[i + 1].x, self.Points[i + 1].y))
       
  2379                     self.Segments.append(segment)
       
  2380                     i += 1
       
  2381             self.RefreshBoundingBox()
       
  2382             self.RefreshRealPoints()
       
  2383     
       
  2384     # Returns the position of the point indicated
       
  2385     def GetPoint(self, index):
       
  2386         if index < len(self.Points):
       
  2387             return self.Points[index].x, self.Points[index].y
       
  2388         return None
       
  2389     
       
  2390     # Returns a list of the position of all wire points
       
  2391     def GetPoints(self, invert = False):
       
  2392         points = self.VerifyPoints()
       
  2393         points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], 
       
  2394                 points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1])
       
  2395         points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], 
       
  2396                 points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1])
       
  2397         # An inversion of the list is asked
       
  2398         if invert:
       
  2399             points.reverse()
       
  2400         return points
       
  2401     
       
  2402     # Returns the position of the two selected segment points
       
  2403     def GetSelectedSegmentPoints(self):
       
  2404         if self.SelectedSegment != None and len(self.Points) > 1:
       
  2405             return self.Points[self.SelectedSegment:self.SelectedSegment + 2]
       
  2406         return []
       
  2407     
       
  2408     # Returns if the selected segment is the first and/or the last of the wire
       
  2409     def GetSelectedSegmentConnections(self):
       
  2410         if self.SelectedSegment != None and len(self.Points) > 1:
       
  2411             return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1
       
  2412         return (True, True)
       
  2413     
       
  2414     # Returns the connectors on which the wire is connected
       
  2415     def GetConnected(self):
       
  2416         connected = []
       
  2417         if self.StartConnected and self.StartPoint[1] == WEST:
       
  2418             connected.append(self.StartConnected)
       
  2419         if self.EndConnected and self.EndPoint[1] == WEST:
       
  2420             connected.append(self.EndConnected)
       
  2421         return connected
       
  2422     
       
  2423     # Returns the id of the block connected to the first or the last wire point
       
  2424     def GetConnectedInfos(self, index):
       
  2425         if index == 0 and self.StartConnected:
       
  2426             return self.StartConnected.GetBlockId(), self.StartConnected.GetName()
       
  2427         elif index == -1 and self.EndConnected:
       
  2428             return self.EndConnected.GetBlockId(), self.EndConnected.GetName()
       
  2429         return None
       
  2430     
       
  2431     # Update the wire points position by keeping at most possible the current positions
       
  2432     def GeneratePoints(self, realpoints = True):
       
  2433         i = 0
       
  2434         # Calculate the start enad end points with the minimum segment size in the right direction
       
  2435         end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE,
       
  2436             self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE)
       
  2437         start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, 
       
  2438             self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE)
       
  2439         # Evaluate the point till it's the last
       
  2440         while i < len(self.Points) - 1:
       
  2441             # The next point is the last
       
  2442             if i + 1 == len(self.Points) - 1:
       
  2443                 # Calculate the direction from current point to end point
       
  2444                 v_end = vector(self.Points[i], end)
       
  2445                 # The current point is the first
       
  2446                 if i == 0:
       
  2447                     # If the end point is not in the start direction, a point is added
       
  2448                     if v_end != self.Segments[0] or v_end == self.EndPoint[1]:
       
  2449                         self.Points.insert(1, wx.Point(start.x, start.y))
       
  2450                         self.Segments.insert(1, DirectionChoice((self.Segments[0][1], 
       
  2451                             self.Segments[0][0]), v_end, self.EndPoint[1]))
       
  2452                 # The current point is the second
       
  2453                 elif i == 1:
       
  2454                     # The previous direction and the target direction are mainly opposed, a point is added
       
  2455                     if product(v_end, self.Segments[0]) < 0:
       
  2456                         self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
       
  2457                         self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
       
  2458                             self.Segments[1][0]), v_end, self.EndPoint[1]))
       
  2459                     # The previous direction and the end direction are the same or they are
       
  2460                     # perpendiculars and the end direction points towards current segment
       
  2461                     elif product(self.Segments[0], self.EndPoint[1]) >= 0 and product(self.Segments[1], self.EndPoint[1]) <= 0:
       
  2462                         # Current point and end point are aligned
       
  2463                         if self.Segments[0][0] != 0:
       
  2464                             self.Points[1].x = end.x
       
  2465                         if self.Segments[0][1] != 0:
       
  2466                             self.Points[1].y = end.y
       
  2467                         # If the previous direction and the end direction are the same, a point is added
       
  2468                         if product(self.Segments[0], self.EndPoint[1]) > 0:
       
  2469                             self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
       
  2470                             self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
       
  2471                                 self.Segments[1][0]), v_end, self.EndPoint[1]))
       
  2472                     else:
       
  2473                         # Current point is positioned in the middle of start point
       
  2474                         # and end point on the current direction and a point is added
       
  2475                         if self.Segments[0][0] != 0:
       
  2476                             self.Points[1].x = (end.x + start.x) / 2
       
  2477                         if self.Segments[0][1] != 0:
       
  2478                             self.Points[1].y = (end.y + start.y) / 2
       
  2479                         self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
       
  2480                         self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
       
  2481                             self.Segments[1][0]), v_end, self.EndPoint[1]))
       
  2482                 else:
       
  2483                     # The previous direction and the end direction are perpendiculars
       
  2484                     if product(self.Segments[i - 1], self.EndPoint[1]) == 0:
       
  2485                         # The target direction and the end direction aren't mainly the same
       
  2486                         if product(v_end, self.EndPoint[1]) <= 0:
       
  2487                             # Current point and end point are aligned
       
  2488                             if self.Segments[i - 1][0] != 0:
       
  2489                                 self.Points[i].x = end.x
       
  2490                             if self.Segments[i - 1][1] != 0:
       
  2491                                 self.Points[i].y = end.y
       
  2492                             # Previous direction is updated from the new point
       
  2493                             if product(vector(self.Points[i - 1], self.Points[i]), self.Segments[i - 1]) < 0:
       
  2494                                 self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
       
  2495                         else:
       
  2496                             test = True
       
  2497                             # If the current point is the third, test if the second 
       
  2498                             # point can be aligned with the end point
       
  2499                             if i == 2:
       
  2500                                 test_point = wx.Point(self.Points[1].x, self.Points[1].y)
       
  2501                                 if self.Segments[1][0] != 0:
       
  2502                                     test_point.y = end.y
       
  2503                                 if self.Segments[1][1] != 0:
       
  2504                                     test_point.x = end.x
       
  2505                                 vector_test = vector(self.Points[0], test_point, False)
       
  2506                                 test = norm(vector_test) > MIN_SEGMENT_SIZE and product(self.Segments[0], vector_test) > 0
       
  2507                             # The previous point can be aligned
       
  2508                             if test:
       
  2509                                 self.Points[i].x, self.Points[i].y = end.x, end.y
       
  2510                                 if self.Segments[i - 1][0] != 0:
       
  2511                                     self.Points[i - 1].y = end.y
       
  2512                                 if self.Segments[i - 1][1] != 0:
       
  2513                                     self.Points[i - 1].x = end.x
       
  2514                                 self.Segments[i] = (-self.EndPoint[1][0], -self.EndPoint[1][1])
       
  2515                             else:
       
  2516                                 # Current point is positioned in the middle of previous point
       
  2517                                 # and end point on the current direction and a point is added
       
  2518                                 if self.Segments[1][0] != 0:
       
  2519                                     self.Points[2].x = (self.Points[1].x + end.x) / 2
       
  2520                                 if self.Segments[1][1] != 0:
       
  2521                                     self.Points[2].y = (self.Points[1].y + end.y) / 2
       
  2522                                 self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y))
       
  2523                                 self.Segments.insert(3, DirectionChoice((self.Segments[2][1], 
       
  2524                                     self.Segments[2][0]), v_end, self.EndPoint[1]))
       
  2525                     else:
       
  2526                         # Current point is aligned with end point
       
  2527                         if self.Segments[i - 1][0] != 0:
       
  2528                             self.Points[i].x = end.x
       
  2529                         if self.Segments[i - 1][1] != 0:
       
  2530                             self.Points[i].y = end.y
       
  2531                         # Previous direction is updated from the new point
       
  2532                         if product(vector(self.Points[i - 1], self.Points[i]), self.Segments[i - 1]) < 0:
       
  2533                             self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
       
  2534                         # If previous direction and end direction are opposed
       
  2535                         if product(self.Segments[i - 1], self.EndPoint[1]) < 0:
       
  2536                             # Current point is positioned in the middle of previous point
       
  2537                             # and end point on the current direction
       
  2538                             if self.Segments[i - 1][0] != 0:
       
  2539                                 self.Points[i].x = (end.x + self.Points[i - 1].x) / 2
       
  2540                             if self.Segments[i - 1][1] != 0:
       
  2541                                 self.Points[i].y = (end.y + self.Points[i - 1].y) / 2
       
  2542                         # A point is added
       
  2543                         self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y))
       
  2544                         self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1], 
       
  2545                                 self.Segments[i][0]), v_end, self.EndPoint[1]))
       
  2546             else:
       
  2547                 # Current point is the first, and second is not mainly in the first direction
       
  2548                 if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0:
       
  2549                     # If first and second directions aren't perpendiculars, a point is added 
       
  2550                     if product(self.Segments[0], self.Segments[1]) != 0:
       
  2551                         self.Points.insert(1, wx.Point(start.x, start.y))
       
  2552                         self.Segments.insert(1, DirectionChoice((self.Segments[0][1], 
       
  2553                             self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1]))
       
  2554                     else:
       
  2555                         self.Points[1].x, self.Points[1].y = start.x, start.y
       
  2556                 else:
       
  2557                     # Next point is aligned with current point
       
  2558                     if self.Segments[i][0] != 0:
       
  2559                         self.Points[i + 1].y = self.Points[i].y
       
  2560                     if self.Segments[i][1] != 0:
       
  2561                         self.Points[i + 1].x = self.Points[i].x
       
  2562                     # Current direction is updated from the new point
       
  2563                     if product(vector(self.Points[i], self.Points[i + 1]), self.Segments[i]) < 0:
       
  2564                         self.Segments[i] = (-self.Segments[i][0], -self.Segments[i][1])
       
  2565             i += 1
       
  2566         self.RefreshBoundingBox()
       
  2567         if realpoints:
       
  2568             self.RefreshRealPoints()
       
  2569     
       
  2570     # Verify that two consecutive points haven't the same position
       
  2571     def VerifyPoints(self):
       
  2572         points = [point for point in self.Points]
       
  2573         segments = [segment for segment in self.Segments]
       
  2574         i = 1
       
  2575         while i < len(points) - 1:
       
  2576             if points[i] == points[i + 1] and segments[i - 1] == segments[i + 1]:
       
  2577                 for j in xrange(2):
       
  2578                     points.pop(i)
       
  2579                     segments.pop(i)
       
  2580             else:
       
  2581                 i += 1
       
  2582         # If the wire isn't in a Ladder Diagram, save the new point list
       
  2583         if self.Parent.__class__.__name__ != "LD_Viewer":
       
  2584             self.Points = [point for point in points]
       
  2585             self.Segments = [segment for segment in segments]
       
  2586             self.RefreshBoundingBox()
       
  2587             self.RefreshRealPoints()
       
  2588         return points
       
  2589     
       
  2590     # Moves all the wire points except the first and the last if they are connected
       
  2591     def Move(self, dx, dy, endpoints = False):
       
  2592         for i, point in enumerate(self.Points):
       
  2593             if endpoints or not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
       
  2594                 point.x += dx
       
  2595                 point.y += dy
       
  2596         self.StartPoint[0] = self.Points[0]
       
  2597         self.EndPoint[0] = self.Points[-1]
       
  2598         self.GeneratePoints()
       
  2599     
       
  2600     # Resize the wire from position and size given
       
  2601     def Resize(self, x, y, width, height):
       
  2602         if len(self.Points) > 1:
       
  2603             # Calculate the new position of each point for testing the new size
       
  2604             minx, miny = self.Pos.x, self.Pos.y
       
  2605             lastwidth, lastheight = self.Size.width, self.Size.height
       
  2606             for i, point in enumerate(self.RealPoints):
       
  2607                 # If start or end point is connected, it's not calculate
       
  2608                 if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
       
  2609                     if i == 0:
       
  2610                         dir = self.StartPoint[1]
       
  2611                     elif i == len(self.Points) - 1:
       
  2612                         dir = self.EndPoint[1]
       
  2613                     else:
       
  2614                         dir = (0, 0)
       
  2615                     pointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0] * width / float(max(lastwidth, 1)))),
       
  2616                             width - dir[0] * MIN_SEGMENT_SIZE))
       
  2617                     pointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1] * height / float(max(lastheight, 1)))),
       
  2618                             height - dir[1] * MIN_SEGMENT_SIZE))
       
  2619                     self.Points[i] = wx.Point(minx + x + pointx, miny + y + pointy)
       
  2620             self.StartPoint[0] = self.Points[0]
       
  2621             self.EndPoint[0] = self.Points[-1]
       
  2622             self.GeneratePoints(False)
       
  2623             # Test if the wire position or size have changed
       
  2624             if x != 0 and minx == self.Pos.x:
       
  2625                 x = 0
       
  2626                 width = lastwidth
       
  2627             if y != 0 and miny == self.Pos.y:
       
  2628                 y = 0
       
  2629                 height = lastwidth
       
  2630             if width != lastwidth and lastwidth == self.Size.width:
       
  2631                 width = lastwidth
       
  2632             if height != lastheight and lastheight == self.Size.height:
       
  2633                 height = lastheight
       
  2634             # Calculate the real points from the new size, it's important for
       
  2635             # keeping a proportionality in the points position with the size
       
  2636             # during a resize dragging
       
  2637             for i, point in enumerate(self.RealPoints):
       
  2638                 if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
       
  2639                     point[0] = point[0] * width / float(max(lastwidth, 1))
       
  2640                     point[1] = point[1] * height / float(max(lastheight, 1))
       
  2641             # Calculate the correct position of the points from real points
       
  2642             for i, point in enumerate(self.RealPoints):
       
  2643                 if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
       
  2644                     if i == 0:
       
  2645                         dir = self.StartPoint[1]
       
  2646                     elif i == len(self.Points) - 1:
       
  2647                         dir = self.EndPoint[1]
       
  2648                     else:
       
  2649                         dir = (0, 0)
       
  2650                     realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0])),
       
  2651                             width - dir[0] * MIN_SEGMENT_SIZE))
       
  2652                     realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1])),
       
  2653                             height - dir[1] * MIN_SEGMENT_SIZE))
       
  2654                     self.Points[i] = wx.Point(minx + x + realpointx, miny + y + realpointy)
       
  2655             self.StartPoint[0] = self.Points[0]
       
  2656             self.EndPoint[0] = self.Points[-1]
       
  2657             self.GeneratePoints(False)
       
  2658     
       
  2659     # Moves the wire start point and update the wire points
       
  2660     def MoveStartPoint(self, point):
       
  2661         if len(self.Points) > 1:
       
  2662             self.StartPoint[0] = point
       
  2663             self.Points[0] = point
       
  2664             self.GeneratePoints()
       
  2665     
       
  2666     # Changes the wire start direction and update the wire points
       
  2667     def SetStartPointDirection(self, dir):
       
  2668         if len(self.Points) > 1:
       
  2669             self.StartPoint[1] = dir
       
  2670             self.Segments[0] = dir
       
  2671             self.GeneratePoints()
       
  2672     
       
  2673     # Rotates the wire start direction by an angle of 90 degrees anticlockwise
       
  2674     def RotateStartPoint(self):
       
  2675         self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0]))
       
  2676     
       
  2677     # Connects wire start point to the connector given and moves wire start point
       
  2678     # to given point
       
  2679     def ConnectStartPoint(self, point, connector):
       
  2680         if point:
       
  2681             self.MoveStartPoint(point)
       
  2682         self.StartConnected = connector
       
  2683         self.RefreshBoundingBox()
       
  2684     
       
  2685     # Unconnects wire start point
       
  2686     def UnConnectStartPoint(self, delete = False):
       
  2687         if delete:
       
  2688             self.StartConnected = None
       
  2689             self.Delete()
       
  2690         elif self.StartConnected:
       
  2691             self.StartConnected.UnConnect(self, unconnect = False)
       
  2692             self.StartConnected = None
       
  2693             self.RefreshBoundingBox()
       
  2694     
       
  2695     # Moves the wire end point and update the wire points
       
  2696     def MoveEndPoint(self, point):
       
  2697         if len(self.Points) > 1:
       
  2698             self.EndPoint[0] = point
       
  2699             self.Points[-1] = point
       
  2700             self.GeneratePoints()
       
  2701 
       
  2702     # Changes the wire end direction and update the wire points
       
  2703     def SetEndPointDirection(self, dir):
       
  2704         if len(self.Points) > 1:
       
  2705             self.EndPoint[1] = dir
       
  2706             self.GeneratePoints()
       
  2707             
       
  2708     # Rotates the wire end direction by an angle of 90 degrees anticlockwise
       
  2709     def RotateEndPoint(self):
       
  2710         self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0]))
       
  2711 
       
  2712     # Connects wire end point to the connector given and moves wire end point
       
  2713     # to given point
       
  2714     def ConnectEndPoint(self, point, connector):
       
  2715         if point:
       
  2716             self.MoveEndPoint(point)
       
  2717         self.EndConnected = connector
       
  2718         self.RefreshBoundingBox()
       
  2719     
       
  2720     # Unconnects wire end point
       
  2721     def UnConnectEndPoint(self, delete = False):
       
  2722         if delete:
       
  2723             self.EndConnected = None
       
  2724             self.Delete()
       
  2725         elif self.EndConnected:
       
  2726             self.EndConnected.UnConnect(self, unconnect = False)
       
  2727             self.EndConnected = None
       
  2728             self.RefreshBoundingBox()
       
  2729     
       
  2730     # Moves the wire segment given by its index
       
  2731     def MoveSegment(self, idx, movex, movey, scaling):
       
  2732         if 0 < idx < len(self.Segments) - 1:
       
  2733             if self.Segments[idx] in (NORTH, SOUTH):
       
  2734                 start_x = self.Points[idx].x
       
  2735                 if scaling is not None:
       
  2736                     movex = round_scaling(self.Points[idx].x + movex, scaling[0]) - self.Points[idx].x
       
  2737                     if idx == 1 and (self.Points[1].x + movex - self.Points[0].x) * self.Segments[0][0] < MIN_SEGMENT_SIZE:
       
  2738                         movex = round_scaling(self.Points[0].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - self.Points[idx].x
       
  2739                     elif idx == len(self.Segments) - 2 and (self.Points[-1].x - (self.Points[-2].x + movex)) * self.Segments[-1][0] < MIN_SEGMENT_SIZE:
       
  2740                         movex = round_scaling(self.Points[-1].x - MIN_SEGMENT_SIZE * self.Segments[-1][0], scaling[0], -self.Segments[-1][0]) - self.Points[idx].x
       
  2741                 self.Points[idx].x += movex
       
  2742                 self.Points[idx + 1].x += movex
       
  2743                 self.GeneratePoints()
       
  2744                 if start_x != self.Points[idx].x:
       
  2745                     return self.Points[idx].x - start_x, 0
       
  2746             elif self.Segments[idx] in (EAST, WEST):
       
  2747                 start_y = self.Points[idx].y
       
  2748                 if scaling is not None:
       
  2749                     movey = round_scaling(self.Points[idx].y + movey, scaling[1]) - self.Points[idx].y
       
  2750                     if idx == 1 and (self.Points[1].y + movey - self.Points[0].y) * self.Segments[0][1] < MIN_SEGMENT_SIZE:
       
  2751                         movex = round_scaling(self.Points[0].y + MIN_SEGMENT_SIZE * self.Segments[0][1], scaling[0], self.Segments[0][1]) - self.Points[idx].y
       
  2752                     elif idx == len(self.Segments) - 2 and (self.Points[-1].y - (self.Points[-2].y + movey)) * self.Segments[-1][1] < MIN_SEGMENT_SIZE:
       
  2753                         movey = round_scaling(self.Points[idx].y - MIN_SEGMENT_SIZE * self.Segments[-1][1], scaling[1], -self.Segments[-1][1]) - self.Points[idx].y
       
  2754                 self.Points[idx].y += movey
       
  2755                 self.Points[idx + 1].y += movey
       
  2756                 self.GeneratePoints()
       
  2757                 if start_y != self.Points[idx].y:
       
  2758                     return 0, self.Points[idx].y - start_y
       
  2759         return 0, 0
       
  2760     
       
  2761     # Adds two points in the middle of the handled segment
       
  2762     def AddSegment(self):
       
  2763         handle_type, handle = self.Handle
       
  2764         if handle_type == HANDLE_SEGMENT:
       
  2765             segment, dir = handle
       
  2766             if len(self.Segments) > 1:
       
  2767                 pointx = self.Points[segment].x
       
  2768                 pointy = self.Points[segment].y
       
  2769                 if dir[0] != 0:
       
  2770                     pointx = (self.Points[segment].x + self.Points[segment + 1].x) / 2
       
  2771                 if dir[1] != 0:
       
  2772                     pointy = (self.Points[segment].y + self.Points[segment + 1].y) / 2
       
  2773                 self.Points.insert(segment + 1, wx.Point(pointx, pointy))
       
  2774                 self.Segments.insert(segment + 1, (dir[1], dir[0]))
       
  2775                 self.Points.insert(segment + 2, wx.Point(pointx, pointy))
       
  2776                 self.Segments.insert(segment + 2, dir)
       
  2777             else:
       
  2778                 p1x = p2x = self.Points[segment].x
       
  2779                 p1y = p2y = self.Points[segment].y
       
  2780                 if dir[0] != 0:
       
  2781                     p1x = (2 * self.Points[segment].x + self.Points[segment + 1].x) / 3
       
  2782                     p2x = (self.Points[segment].x + 2 * self.Points[segment + 1].x) / 3
       
  2783                 if dir[1] != 0:
       
  2784                     p1y = (2 * self.Points[segment].y + self.Points[segment + 1].y) / 3
       
  2785                     p2y = (self.Points[segment].y + 2 * self.Points[segment + 1].y) / 3
       
  2786                 self.Points.insert(segment + 1, wx.Point(p1x, p1y))
       
  2787                 self.Segments.insert(segment + 1, (dir[1], dir[0]))
       
  2788                 self.Points.insert(segment + 2, wx.Point(p1x, p1y))
       
  2789                 self.Segments.insert(segment + 2, dir)
       
  2790                 self.Points.insert(segment + 3, wx.Point(p2x, p2y))
       
  2791                 self.Segments.insert(segment + 3, (dir[1], dir[0]))
       
  2792                 self.Points.insert(segment + 4, wx.Point(p2x, p2y))
       
  2793                 self.Segments.insert(segment + 4, dir)
       
  2794             self.GeneratePoints()
       
  2795     
       
  2796     # Delete the handled segment by removing the two segment points
       
  2797     def DeleteSegment(self):
       
  2798         handle_type, handle = self.Handle
       
  2799         if handle_type == HANDLE_SEGMENT:
       
  2800             segment, dir = handle
       
  2801             for i in xrange(2):
       
  2802                 self.Points.pop(segment)
       
  2803                 self.Segments.pop(segment)
       
  2804             self.GeneratePoints()
       
  2805             self.RefreshModel()
       
  2806             
       
  2807     # Method called when a LeftDown event have been generated
       
  2808     def OnLeftDown(self, event, dc, scaling):
       
  2809         pos = GetScaledEventPosition(event, dc, scaling)
       
  2810         # Test if a point have been handled
       
  2811         #result = self.TestPoint(pos)
       
  2812         #if result != None:
       
  2813         #    self.Handle = (HANDLE_POINT, result)
       
  2814         #    wx.CallAfter(self.Parent.SetCurrentCursor, 1)
       
  2815         #else:
       
  2816         # Test if a segment have been handled
       
  2817         result = self.TestSegment(pos)
       
  2818         if result != None:
       
  2819             if result[1] in (NORTH, SOUTH):
       
  2820                 wx.CallAfter(self.Parent.SetCurrentCursor, 4)
       
  2821             elif result[1] in (EAST, WEST):
       
  2822                 wx.CallAfter(self.Parent.SetCurrentCursor, 5)
       
  2823             self.Handle = (HANDLE_SEGMENT, result)
       
  2824         # Execute the default method for a graphic element
       
  2825         else:
       
  2826             Graphic_Element.OnLeftDown(self, event, dc, scaling)
       
  2827         self.oldPos = pos
       
  2828     
       
  2829     # Method called when a RightUp event has been generated
       
  2830     def OnRightUp(self, event, dc, scaling):
       
  2831         pos = GetScaledEventPosition(event, dc, scaling)
       
  2832         # Test if a segment has been handled
       
  2833         result = self.TestSegment(pos, True)
       
  2834         if result != None:
       
  2835             self.Handle = (HANDLE_SEGMENT, result)
       
  2836             # Popup the menu with special items for a wire
       
  2837             self.Parent.PopupWireMenu(0 < result[0] < len(self.Segments) - 1)
       
  2838         else:
       
  2839             # Execute the default method for a graphic element
       
  2840             Graphic_Element.OnRightUp(self, event, dc, scaling)
       
  2841     
       
  2842     # Method called when a LeftDClick event has been generated
       
  2843     def OnLeftDClick(self, event, dc, scaling):
       
  2844         rect = self.GetRedrawRect()
       
  2845         if event.ControlDown():
       
  2846             direction = (self.StartPoint[1], self.EndPoint[1])
       
  2847             if direction in [(EAST, WEST), (WEST, EAST)]:
       
  2848                 avgy = (self.StartPoint[0].y + self.EndPoint[0].y) / 2
       
  2849                 if scaling is not None:
       
  2850                     avgy = round(float(avgy) / scaling[1]) * scaling[1]
       
  2851                 if self.StartConnected is not None:
       
  2852                     movey = avgy - self.StartPoint[0].y
       
  2853                     startblock = self.StartConnected.GetParentBlock()
       
  2854                     startblock.Move(0, movey)
       
  2855                     startblock.RefreshModel()
       
  2856                     rect.Union(startblock.GetRedrawRect(0, movey))
       
  2857                 else:
       
  2858                     self.MoveStartPoint(wx.Point(self.StartPoint[0].x, avgy))
       
  2859                 if self.EndConnected is not None:
       
  2860                     movey = avgy - self.EndPoint[0].y
       
  2861                     endblock = self.EndConnected.GetParentBlock()
       
  2862                     endblock.Move(0, movey)
       
  2863                     endblock.RefreshModel()
       
  2864                     rect.Union(endblock.GetRedrawRect(0, movey))
       
  2865                 else:
       
  2866                     self.MoveEndPoint(wx.Point(self.EndPoint[0].x, avgy))
       
  2867                 self.Parent.RefreshBuffer()
       
  2868             elif direction in [(NORTH, SOUTH), (SOUTH, NORTH)]:
       
  2869                 avgx = (self.StartPoint[0].x + self.EndPoint[0].x) / 2
       
  2870                 if scaling is not None:
       
  2871                     avgx = round(float(avgx) / scaling[0]) * scaling[0]
       
  2872                 if self.StartConnected is not None:
       
  2873                     movex = avgx - self.StartPoint[0].x
       
  2874                     startblock = self.StartConnected.GetParentBlock()
       
  2875                     startblock.Move(movex, 0)
       
  2876                     startblock.RefreshModel()
       
  2877                     rect.Union(startblock.GetRedrawRect(movex, 0))
       
  2878                 else:
       
  2879                     self.MoveStartPoint(wx.Point(avgx, self.StartPoint[0].y))
       
  2880                 if self.EndConnected is not None:
       
  2881                     movex = avgx - self.EndPoint[0].x
       
  2882                     endblock = self.EndConnected.GetParentBlock()
       
  2883                     endblock.Move(movex, 0)
       
  2884                     endblock.RefreshModel()
       
  2885                     rect.Union(endblock.GetRedrawRect(movex, 0))
       
  2886                 else:
       
  2887                     self.MoveEndPoint(wx.Point(avgx, self.EndPoint[0].y))
       
  2888                 self.Parent.RefreshBuffer()
       
  2889         else:
       
  2890             self.ResetPoints()
       
  2891             self.GeneratePoints()
       
  2892             self.RefreshModel()
       
  2893             self.Parent.RefreshBuffer()
       
  2894         rect.Union(self.GetRedrawRect())
       
  2895         self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
       
  2896         
       
  2897     # Method called when a Motion event has been generated
       
  2898     def OnMotion(self, event, dc, scaling):
       
  2899         pos = GetScaledEventPosition(event, dc, scaling)
       
  2900         if not event.Dragging():
       
  2901             # Test if a segment has been handled
       
  2902             result = self.TestSegment(pos)
       
  2903             if result:
       
  2904                 if result[1] in (NORTH, SOUTH):
       
  2905                     wx.CallAfter(self.Parent.SetCurrentCursor, 4)
       
  2906                 elif result[1] in (EAST, WEST):
       
  2907                     wx.CallAfter(self.Parent.SetCurrentCursor, 5)
       
  2908                 return 0, 0
       
  2909             else:
       
  2910                 # Execute the default method for a graphic element
       
  2911                 return Graphic_Element.OnMotion(self, event, dc, scaling)
       
  2912         else:
       
  2913             # Execute the default method for a graphic element
       
  2914             return Graphic_Element.OnMotion(self, event, dc, scaling)
       
  2915     
       
  2916     # Refreshes the wire state according to move defined and handle selected
       
  2917     def ProcessDragging(self, movex, movey, event, scaling):
       
  2918         handle_type, handle = self.Handle
       
  2919         # A point has been handled
       
  2920         if handle_type == HANDLE_POINT:
       
  2921             movex = max(-self.Points[handle].x + POINT_RADIUS, movex)
       
  2922             movey = max(-self.Points[handle].y + POINT_RADIUS, movey)
       
  2923             if scaling is not None:
       
  2924                 movex = round_scaling(self.Points[handle].x + movex, scaling[0]) - self.Points[handle].x
       
  2925                 movey = round_scaling(self.Points[handle].y + movey, scaling[1]) - self.Points[handle].y
       
  2926             # Try to connect point to a connector
       
  2927             new_pos = wx.Point(self.Points[handle].x + movex, self.Points[handle].y + movey)
       
  2928             connector = self.Parent.FindBlockConnector(new_pos, self.GetConnectionDirection())
       
  2929             if connector:
       
  2930                 if handle == 0 and self.EndConnected != connector:
       
  2931                     connector.HighlightParentBlock(True)
       
  2932                     connector.Connect((self, handle))
       
  2933                     self.SetStartPointDirection(connector.GetDirection())
       
  2934                     self.ConnectStartPoint(connector.GetPosition(), connector)
       
  2935                     pos = connector.GetPosition()
       
  2936                     movex = pos.x - self.oldPos.x
       
  2937                     movey = pos.y - self.oldPos.y
       
  2938                     if not connector.IsCompatible(self.GetEndConnectedType()):
       
  2939                         self.SetValid(False)
       
  2940                     self.Dragging = False
       
  2941                 elif handle != 0 and self.StartConnected != connector:
       
  2942                     connector.HighlightParentBlock(True)
       
  2943                     connector.Connect((self, handle))
       
  2944                     self.SetEndPointDirection(connector.GetDirection())
       
  2945                     self.ConnectEndPoint(connector.GetPosition(), connector)
       
  2946                     pos = connector.GetPosition()
       
  2947                     movex = pos.x - self.oldPos.x
       
  2948                     movey = pos.y - self.oldPos.y
       
  2949                     if not connector.IsCompatible(self.GetStartConnectedType()):
       
  2950                         self.SetValid(False)
       
  2951                     self.Dragging = False
       
  2952                 elif handle == 0:
       
  2953                     self.MoveStartPoint(new_pos)
       
  2954                 else:
       
  2955                     self.MoveEndPoint(new_pos)
       
  2956             # If there is no connector, move the point
       
  2957             elif handle == 0:
       
  2958                 self.SetValid(True)
       
  2959                 if self.StartConnected:
       
  2960                     self.StartConnected.HighlightParentBlock(False)
       
  2961                     self.UnConnectStartPoint()
       
  2962                 self.MoveStartPoint(new_pos)
       
  2963             else:
       
  2964                 self.SetValid(True)
       
  2965                 if self.EndConnected:
       
  2966                     self.EndConnected.HighlightParentBlock(False)
       
  2967                     self.UnConnectEndPoint()
       
  2968                 self.MoveEndPoint(new_pos)
       
  2969             return movex, movey
       
  2970         # A segment has been handled, move a segment
       
  2971         elif handle_type == HANDLE_SEGMENT:
       
  2972             return self.MoveSegment(handle[0], movex, movey, scaling)
       
  2973         # Execute the default method for a graphic element
       
  2974         else:
       
  2975             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
  2976     
       
  2977     # Refreshes the wire model
       
  2978     def RefreshModel(self, move=True):
       
  2979         if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]:
       
  2980             self.StartConnected.RefreshParentBlock()
       
  2981         if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]:
       
  2982             self.EndConnected.RefreshParentBlock()
       
  2983     
       
  2984     # Change the variable that indicates if this element is highlighted
       
  2985     def SetHighlighted(self, highlighted):
       
  2986         self.Highlighted = highlighted
       
  2987         if not highlighted:
       
  2988             self.OverStart = False
       
  2989             self.OverEnd = False
       
  2990         self.Refresh()
       
  2991     
       
  2992     def HighlightPoint(self, pos):
       
  2993         refresh = False
       
  2994         start, end = self.OverStart, self.OverEnd
       
  2995         self.OverStart = False
       
  2996         self.OverEnd = False
       
  2997         # Test if a point has been handled
       
  2998         result = self.TestPoint(pos)
       
  2999         if result != None:
       
  3000             if result == 0 and self.StartConnected is not None:
       
  3001                 self.OverStart = True
       
  3002             elif result != 0 and self.EndConnected is not None:
       
  3003                 self.OverEnd = True
       
  3004         if start != self.OverStart or end != self.OverEnd:
       
  3005             self.Refresh()
       
  3006     
       
  3007     # Draws the highlightment of this element if it is highlighted
       
  3008     def DrawHighlightment(self, dc):
       
  3009         scalex, scaley = dc.GetUserScale()
       
  3010         dc.SetUserScale(1, 1)
       
  3011         dc.SetPen(MiterPen(HIGHLIGHTCOLOR, (2 * scalex + 5)))
       
  3012         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  3013         dc.SetLogicalFunction(wx.AND)
       
  3014         # Draw the start and end points if they are not connected or the mouse is over them
       
  3015         if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
       
  3016             dc.DrawCircle(round(self.Points[0].x * scalex), 
       
  3017                           round(self.Points[0].y * scaley), 
       
  3018                           (POINT_RADIUS + 1) * scalex + 2)
       
  3019         if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
       
  3020             dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2)
       
  3021         # Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
       
  3022         if len(self.Points) > 1:
       
  3023             points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), 
       
  3024                                round((self.Points[0].y - self.Segments[0][1]) * scaley))]
       
  3025             points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]])
       
  3026             points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), 
       
  3027                                    round((self.Points[-1].y + self.Segments[-1][1]) * scaley)))
       
  3028         else:
       
  3029             points = []
       
  3030         dc.DrawLines(points)
       
  3031         dc.SetLogicalFunction(wx.COPY)
       
  3032         dc.SetUserScale(scalex, scaley)
       
  3033         
       
  3034         if self.StartConnected is not None:
       
  3035             self.StartConnected.DrawHighlightment(dc)
       
  3036             self.StartConnected.Draw(dc)
       
  3037         if self.EndConnected is not None:
       
  3038             self.EndConnected.DrawHighlightment(dc)
       
  3039             self.EndConnected.Draw(dc)
       
  3040     
       
  3041     # Draws the wire lines and points
       
  3042     def Draw(self, dc):
       
  3043         Graphic_Element.Draw(self, dc)
       
  3044         if not self.Valid:
       
  3045             dc.SetPen(MiterPen(wx.RED))
       
  3046             dc.SetBrush(wx.RED_BRUSH)
       
  3047         elif isinstance(self.Value, BooleanType) and self.Value:
       
  3048             if self.Forced:
       
  3049                 dc.SetPen(MiterPen(wx.CYAN))
       
  3050                 dc.SetBrush(wx.CYAN_BRUSH)
       
  3051             else:
       
  3052                 dc.SetPen(MiterPen(wx.GREEN))
       
  3053                 dc.SetBrush(wx.GREEN_BRUSH)
       
  3054         elif self.Value == "undefined":
       
  3055             dc.SetPen(MiterPen(wx.NamedColour("orange")))
       
  3056             dc.SetBrush(wx.Brush(wx.NamedColour("orange")))
       
  3057         elif self.Forced:
       
  3058             dc.SetPen(MiterPen(wx.BLUE))
       
  3059             dc.SetBrush(wx.BLUE_BRUSH)
       
  3060         else:
       
  3061             dc.SetPen(MiterPen(wx.BLACK))
       
  3062             dc.SetBrush(wx.BLACK_BRUSH)
       
  3063         # Draw the start and end points if they are not connected or the mouse is over them
       
  3064         if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
       
  3065             dc.DrawCircle(self.Points[0].x, self.Points[0].y, POINT_RADIUS)
       
  3066         if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
       
  3067             dc.DrawCircle(self.Points[-1].x, self.Points[-1].y, POINT_RADIUS)
       
  3068         # Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
       
  3069         if len(self.Points) > 1:
       
  3070             points = [wx.Point(self.Points[0].x - self.Segments[0][0], self.Points[0].y - self.Segments[0][1])]
       
  3071             points.extend([point for point in self.Points[1:-1]])
       
  3072             points.append(wx.Point(self.Points[-1].x + self.Segments[-1][0], self.Points[-1].y + self.Segments[-1][1]))
       
  3073         else:
       
  3074             points = []
       
  3075         dc.DrawLines(points)
       
  3076         # Draw the segment selected in red
       
  3077         if not getattr(dc, "printing", False) and self.SelectedSegment is not None:
       
  3078             dc.SetPen(MiterPen(wx.BLUE, 3))
       
  3079             if self.SelectedSegment == len(self.Segments) - 1:
       
  3080                 end = 0
       
  3081             else:
       
  3082                 end = 1
       
  3083             dc.DrawLine(self.Points[self.SelectedSegment].x - 1, self.Points[self.SelectedSegment].y,
       
  3084                         self.Points[self.SelectedSegment + 1].x + end, self.Points[self.SelectedSegment + 1].y)
       
  3085         if self.Value is not None and not isinstance(self.Value, BooleanType) and self.Value != "undefined":
       
  3086             dc.SetFont(self.Parent.GetMiniFont())
       
  3087             dc.SetTextForeground(wx.NamedColour("purple"))
       
  3088             if self.ValueSize is None and isinstance(self.ComputedValue, (StringType, UnicodeType)):
       
  3089                 self.ValueSize = self.Parent.GetMiniTextExtent(self.ComputedValue)
       
  3090             if self.ValueSize is not None:
       
  3091                 width, height = self.ValueSize
       
  3092                 if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
       
  3093                     x = self.Points[0].x + width * (self.StartPoint[1][0] - 1) / 2
       
  3094                     y = self.Points[0].y + height * (self.StartPoint[1][1] - 1)
       
  3095                     dc.DrawText(self.ComputedValue, x, y)
       
  3096                     x = self.Points[-1].x + width * (self.EndPoint[1][0] - 1) / 2
       
  3097                     y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1)
       
  3098                     dc.DrawText(self.ComputedValue, x, y)
       
  3099                 else:
       
  3100                     middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
       
  3101                     x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
       
  3102                     if self.BoundingBox[3] > height and self.Segments[middle] in [NORTH, SOUTH]:
       
  3103                         y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
       
  3104                     else:
       
  3105                         y = self.Points[middle].y - height
       
  3106                     dc.DrawText(self.ComputedValue, x, y)
       
  3107             dc.SetFont(self.Parent.GetFont())
       
  3108             dc.SetTextForeground(wx.BLACK)
       
  3109 
       
  3110 
       
  3111 #-------------------------------------------------------------------------------
       
  3112 #                           Graphic comment element
       
  3113 #-------------------------------------------------------------------------------
       
  3114 
       
  3115 def FilterHighlightsByRow(highlights, row, length):
       
  3116     _highlights = []
       
  3117     for start, end, highlight_type in highlights:
       
  3118         if start[0] <= row and end[0] >= row:
       
  3119             if start[0] < row:
       
  3120                 start = (row, 0)
       
  3121             if end[0] > row:
       
  3122                 end = (row, length)
       
  3123             _highlights.append((start, end, highlight_type))
       
  3124     return _highlights
       
  3125 
       
  3126 def FilterHighlightsByColumn(highlights, start_col, end_col):
       
  3127     _highlights = []
       
  3128     for start, end, highlight_type in highlights:
       
  3129         if end[1] > start_col and start[1] < end_col:
       
  3130             start = (start[0], max(start[1], start_col) - start_col)
       
  3131             end = (end[0], min(end[1], end_col) - start_col)
       
  3132             _highlights.append((start, end, highlight_type))
       
  3133     return _highlights
       
  3134 
       
  3135 """
       
  3136 Class that implements a comment
       
  3137 """
       
  3138 
       
  3139 class Comment(Graphic_Element):
       
  3140 
       
  3141     # Create a new comment
       
  3142     def __init__(self, parent, content, id = None):
       
  3143         Graphic_Element.__init__(self, parent)
       
  3144         self.Id = id
       
  3145         self.Content = content
       
  3146         self.Pos = wx.Point(0, 0)
       
  3147         self.Size = wx.Size(0, 0)
       
  3148         self.Highlights = []
       
  3149     
       
  3150     # Make a clone of this comment
       
  3151     def Clone(self, parent, id = None, pos = None):
       
  3152         comment = Comment(parent, self.Content, id)
       
  3153         if pos is not None:
       
  3154             comment.SetPosition(pos.x, pos.y)
       
  3155         comment.SetSize(self.Size[0], self.Size[1])
       
  3156         return comment
       
  3157     
       
  3158     # Method for keeping compatibility with others
       
  3159     def Clean(self):
       
  3160         pass
       
  3161     
       
  3162     # Delete this comment by calling the corresponding method
       
  3163     def Delete(self):
       
  3164         self.Parent.DeleteComment(self)
       
  3165     
       
  3166     # Refresh the comment bounding box
       
  3167     def RefreshBoundingBox(self):
       
  3168         self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
  3169     
       
  3170     # Changes the comment size
       
  3171     def SetSize(self, width, height):
       
  3172         self.Size.SetWidth(width)
       
  3173         self.Size.SetHeight(height)
       
  3174         self.RefreshBoundingBox()
       
  3175 
       
  3176     # Returns the comment size
       
  3177     def GetSize(self):
       
  3178         return self.Size.GetWidth(), self.Size.GetHeight()
       
  3179     
       
  3180     # Returns the comment minimum size
       
  3181     def GetMinSize(self):
       
  3182         dc = wx.ClientDC(self.Parent)
       
  3183         min_width = 0
       
  3184         min_height = 0
       
  3185         # The comment minimum size is the maximum size of words in the content
       
  3186         for line in self.Content.splitlines():
       
  3187             for word in line.split(" "):
       
  3188                 wordwidth, wordheight = dc.GetTextExtent(word)
       
  3189                 min_width = max(min_width, wordwidth)
       
  3190                 min_height = max(min_height, wordheight)
       
  3191         return min_width + 20, min_height + 20
       
  3192     
       
  3193     # Changes the comment position
       
  3194     def SetPosition(self, x, y):
       
  3195         self.Pos.x = x
       
  3196         self.Pos.y = y
       
  3197         self.RefreshBoundingBox()
       
  3198 
       
  3199     # Changes the comment content
       
  3200     def SetContent(self, content):
       
  3201         self.Content = content
       
  3202         min_width, min_height = self.GetMinSize()
       
  3203         self.Size[0] = max(self.Size[0], min_width)
       
  3204         self.Size[1] = max(self.Size[1], min_height)
       
  3205         self.RefreshBoundingBox()
       
  3206 
       
  3207     # Returns the comment content
       
  3208     def GetContent(self):
       
  3209         return self.Content
       
  3210 
       
  3211     # Returns the comment position
       
  3212     def GetPosition(self):
       
  3213         return self.Pos.x, self.Pos.y
       
  3214     
       
  3215     # Moves the comment
       
  3216     def Move(self, dx, dy, connected = True):
       
  3217         self.Pos.x += dx
       
  3218         self.Pos.y += dy
       
  3219         self.RefreshBoundingBox()
       
  3220     
       
  3221     # Resizes the comment with the position and the size given
       
  3222     def Resize(self, x, y, width, height):
       
  3223         self.Move(x, y)
       
  3224         self.SetSize(width, height)
       
  3225     
       
  3226     # Method called when a RightUp event have been generated
       
  3227     def OnRightUp(self, event, dc, scaling):
       
  3228         # Popup the default menu
       
  3229         self.Parent.PopupDefaultMenu()
       
  3230     
       
  3231     # Refreshes the wire state according to move defined and handle selected
       
  3232     def ProcessDragging(self, movex, movey, event, scaling):
       
  3233         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD":
       
  3234             movex = movey = 0
       
  3235         return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
  3236         
       
  3237     # Refreshes the comment model
       
  3238     def RefreshModel(self, move=True):
       
  3239         self.Parent.RefreshCommentModel(self)
       
  3240     
       
  3241     # Method called when a LeftDClick event have been generated
       
  3242     def OnLeftDClick(self, event, dc, scaling):
       
  3243         # Edit the comment content
       
  3244         self.Parent.EditCommentContent(self)
       
  3245     
       
  3246     # Adds an highlight to the comment
       
  3247     def AddHighlight(self, infos, start, end, highlight_type):
       
  3248         if infos[0] == "content":
       
  3249             AddHighlight(self.Highlights, (start, end, highlight_type))
       
  3250     
       
  3251     # Removes an highlight from the comment
       
  3252     def RemoveHighlight(self, infos, start, end, highlight_type):
       
  3253         RemoveHighlight(self.Highlights, (start, end, highlight_type))
       
  3254     
       
  3255     # Removes all the highlights of one particular type from the comment
       
  3256     def ClearHighlight(self, highlight_type=None):
       
  3257         self.Highlights = ClearHighlights(self.Highlights, highlight_type)
       
  3258     
       
  3259     # Draws the highlightment of this element if it is highlighted
       
  3260     def DrawHighlightment(self, dc):
       
  3261         scalex, scaley = dc.GetUserScale()
       
  3262         dc.SetUserScale(1, 1)
       
  3263         dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
       
  3264         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  3265         dc.SetLogicalFunction(wx.AND)
       
  3266         
       
  3267         left = (self.Pos.x - 1) * scalex - 2
       
  3268         right = (self.Pos.x + self.Size[0] + 1) * scalex + 2
       
  3269         top = (self.Pos.y - 1) * scaley - 2
       
  3270         bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2
       
  3271         angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2
       
  3272         angle_right = (self.Pos.y + 9) * scaley - 2
       
  3273         
       
  3274         polygon = [wx.Point(left, top), wx.Point(angle_top, top),
       
  3275                    wx.Point(right, angle_right), wx.Point(right, bottom),
       
  3276                    wx.Point(left, bottom)]
       
  3277         dc.DrawPolygon(polygon)
       
  3278         
       
  3279         dc.SetLogicalFunction(wx.COPY)
       
  3280         dc.SetUserScale(scalex, scaley)
       
  3281         
       
  3282     # Draws the comment and its content
       
  3283     def Draw(self, dc):
       
  3284         Graphic_Element.Draw(self, dc)
       
  3285         dc.SetPen(MiterPen(wx.BLACK))
       
  3286         dc.SetBrush(wx.WHITE_BRUSH)
       
  3287         # Draws the comment shape
       
  3288         polygon = [wx.Point(self.Pos.x, self.Pos.y), 
       
  3289                    wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
       
  3290                    wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10),
       
  3291                    wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]),
       
  3292                    wx.Point(self.Pos.x, self.Pos.y + self.Size[1])]
       
  3293         dc.DrawPolygon(polygon)
       
  3294         lines = [wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
       
  3295                  wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y + 10),
       
  3296                  wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10)]
       
  3297         dc.DrawLines(lines)
       
  3298         # Draws the comment content
       
  3299         y = self.Pos.y + 10
       
  3300         for idx, line in enumerate(self.Content.splitlines()):
       
  3301             first = True
       
  3302             linetext = ""
       
  3303             words = line.split(" ")
       
  3304             if not getattr(dc, "printing", False):
       
  3305                 highlights = FilterHighlightsByRow(self.Highlights, idx, len(line))
       
  3306                 highlights_offset = 0
       
  3307             for i, word in enumerate(words):
       
  3308                 if first:
       
  3309                     text = word
       
  3310                 else:
       
  3311                     text = linetext + " " + word
       
  3312                 wordwidth, wordheight = dc.GetTextExtent(text)
       
  3313                 if y + wordheight > self.Pos.y + self.Size[1] - 10:
       
  3314                     break
       
  3315                 if wordwidth < self.Size[0] - 20:
       
  3316                     if i < len(words) - 1:
       
  3317                         linetext = text
       
  3318                         first = False
       
  3319                     else:
       
  3320                         dc.DrawText(text, self.Pos.x + 10, y)
       
  3321                         if not getattr(dc, "printing", False):
       
  3322                             DrawHighlightedText(dc, text, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(text)), self.Pos.x + 10, y)
       
  3323                             highlights_offset += len(text) + 1
       
  3324                         y += wordheight + 5
       
  3325                 else:
       
  3326                     if not first:
       
  3327                         dc.DrawText(linetext, self.Pos.x + 10, y)
       
  3328                         if not getattr(dc, "printing", False):
       
  3329                             DrawHighlightedText(dc, linetext, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(linetext)), self.Pos.x + 10, y)
       
  3330                             highlights_offset += len(linetext) + 1
       
  3331                     if first or i == len(words) - 1:
       
  3332                         if not first:
       
  3333                             y += wordheight + 5
       
  3334                             if y + wordheight > self.Pos.y + self.Size[1] - 10:
       
  3335                                 break
       
  3336                         dc.DrawText(word, self.Pos.x + 10, y)
       
  3337                         if not getattr(dc, "printing", False):
       
  3338                             DrawHighlightedText(dc, word, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(word)), self.Pos.x + 10, y)
       
  3339                             highlights_offset += len(word) + 1
       
  3340                     else:
       
  3341                         linetext = word
       
  3342                     y += wordheight + 5
       
  3343             if y + wordheight > self.Pos.y + self.Size[1] - 10:
       
  3344                 break
       
  3345