graphics/GraphicCommons.py
author laurent
Sun, 08 Jan 2012 19:16:58 +0100
changeset 616 8a60ffcfd70b
parent 612 fdb9501e5cc8
child 625 b7062a7018ec
permissions -rw-r--r--
Adding support for drag'n dropping variable from global defined in configurations and resources to POU variable panel or body editor for declaring external variables
Adding support for drag'n dropping located variables from topology panel to configurations and resources variable panel for declaring global located variables
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard. 
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import wx
from time import time as gettime
from math import *
from types import *
import datetime

#-------------------------------------------------------------------------------
#                               Common constants
#-------------------------------------------------------------------------------

"""
Definition of constants for dimensions of graphic elements
"""

# FBD and SFC constants
MIN_MOVE = 5                            # Minimum move before starting a element dragging
CONNECTOR_SIZE = 8                      # Size of connectors
BLOCK_LINE_SIZE = 20                    # Minimum size of each line in a block
HANDLE_SIZE = 6                         # Size of the squares for handles
ANCHOR_DISTANCE = 5                     # Distance where wire is automativally attached to a connector
POINT_RADIUS = 2                        # Radius of the point of wire ends
MIN_SEGMENT_SIZE = 2                    # Minimum size of the endling segments of a wire

# LD constants
LD_LINE_SIZE = 40                       # Distance between two lines in a ladder rung
LD_ELEMENT_SIZE = (21, 15)              # Size (width, height) of a ladder element (contact or coil)
LD_WIRE_SIZE = 30                       # Size of a wire between two contact
LD_WIRECOIL_SIZE = 70                   # Size of a wire between a coil and a contact
LD_POWERRAIL_WIDTH = 3                  # Width of a Powerrail
LD_OFFSET = (10, 10)                    # Distance (x, y) between each comment and rung of the ladder
LD_COMMENT_DEFAULTSIZE = (600, 40)      # Size (width, height) of a comment box

# SFC constants
SFC_STEP_DEFAULT_SIZE = (40, 30)        # Default size of a SFC step
SFC_TRANSITION_SIZE = (20, 2)           # Size of a SFC transition
SFC_DEFAULT_SEQUENCE_INTERVAL = 40      # Default size of the interval between two divergence branches
SFC_SIMULTANEOUS_SEQUENCE_EXTRA = 20    # Size of extra lines for simultaneous divergence and convergence
SFC_JUMP_SIZE = (12, 13)                # Size of a SFC jump to step
SFC_WIRE_MIN_SIZE = 25                  # Size of a wire between two elements
SFC_ACTION_MIN_SIZE = (100, 30)         # Minimum size of an action block line

# Type definition constants for graphic elements
[INPUT, OUTPUT, INOUT] = range(3)
[CONNECTOR, CONTINUATION] = range(2)
[LEFTRAIL, RIGHTRAIL] = range(2)
[CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] = range(4)
[COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING] = range(6)
[SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] = range(4)

# Constants for defining the type of dragging that has been selected
[HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = range(5)

# List of value for resize handle that are valid
VALID_HANDLES = [(1,1), (1,2), (1,3), (2,3), (3,3), (3,2), (3,1), (2,1)]

# Contants for defining the direction of a connector
[EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)]

# Contants for defining which mode is selected for each view 
[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, 
 MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, 
 MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15)

# Contants for defining alignment types for graphic group 
[ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6)

# Contants for defining which drawing mode is selected for app
[FREEDRAWING_MODE, DRIVENDRAWING_MODE] = [1, 2]

# Color for Highlighting
HIGHLIGHTCOLOR = wx.CYAN

# Define highlight types
ERROR_HIGHLIGHT = (wx.Colour(255, 255, 0), wx.RED)
SEARCH_RESULT_HIGHLIGHT = (wx.Colour(255, 165, 0), wx.WHITE)

# Define highlight refresh inhibition period in second
REFRESH_HIGHLIGHT_PERIOD = 0.1

HANDLE_CURSORS = {
    (1, 1) : 2,
    (3, 3) : 2,
    (1, 3) : 3,
    (3, 1) : 3,
    (1, 2) : 4,
    (3, 2) : 4,
    (2, 1) : 5,
    (2, 3) : 5
}

def round_scaling(x, n, constraint=0):
    fraction = float(x) / float(n)
    if constraint == - 1:
        xround = int(fraction)
    else:
        xround = round(fraction)
        if constraint == 1 and int(fraction) == xround:
            xround += 1
    return xround * n

"""
Basic vector operations for calculate wire points
"""

# Create a vector from two points and define if vector must be normal
def vector(p1, p2, normal = True):
    vector = (p2.x - p1.x, p2.y - p1.y)
    if normal:
        return normalize(vector)
    return vector

# Calculate the norm of a given vector
def norm(v):
    return sqrt(v[0] * v[0] + v[1] * v[1])

# Normalize a given vector
def normalize(v):
    v_norm = norm(v)
    # Verifie if it is not a null vector
    if v_norm > 0:
        return (v[0] / v_norm, v[1] / v_norm)
    else:
        return v

# Calculate the scalar product of two vectors
def is_null_vector(v):
    return v == (0, 0)

# Calculate the scalar product of two vectors
def add_vectors(v1, v2):
    return (v1[0] + v2[0], v1[1] + v2[1])

# Calculate the scalar product of two vectors
def product(v1, v2):
    return v1[0] * v2[0] + v1[1] * v2[1]


"""
Function that calculates the nearest point of the grid defined by scaling for the given point
"""

def GetScaledEventPosition(event, dc, scaling):
    pos = event.GetLogicalPosition(dc)
    if scaling:
        pos.x = round(float(pos.x) / float(scaling[0])) * scaling[0]
        pos.y = round(float(pos.y) / float(scaling[1])) * scaling[1]
    return pos


"""
Function that choose a direction during the wire points generation
"""

def DirectionChoice(v_base, v_target, dir_target):
    dir_product = product(v_base, v_target)
    if dir_product < 0:
        return (-v_base[0], -v_base[1])
    elif dir_product == 0 and product(v_base, dir_target) != 0:
        return dir_target
    return v_base

SECOND = 1000000
MINUTE = 60 * SECOND
HOUR = 60 * MINUTE
DAY = 24 * HOUR

def generate_time(value):
    microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
    negative = microseconds < 0
    microseconds = abs(microseconds)
    data = "T#"
    not_null = False
    if negative:
        data += "-"
    for val, format in [(int(microseconds) / DAY, "%dd"),
                        ((int(microseconds) % DAY) / HOUR, "%dh"),
                        ((int(microseconds) % HOUR) / MINUTE, "%dm"),
                        ((int(microseconds) % MINUTE) / SECOND, "%ds")]:
        if val > 0 or not_null:
            data += format % val
            not_null = True
    data += "%gms" % (microseconds % SECOND / 1000.)
    return data

def generate_date(value):
    base_date = datetime.datetime(1970, 1, 1)
    date = base_date + value 
    return date.strftime("DATE#%Y-%m-%d")

def generate_datetime(value):
    base_date = datetime.datetime(1970, 1, 1)
    date_time = base_date + value 
    return date_time.strftime("DT#%Y-%m-%d-%H:%M:%S.%f")

def generate_timeofday(value):
    microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
    negative = microseconds < 0
    microseconds = abs(microseconds)
    data = "TOD#"
    for val, format in [(int(microseconds) / HOUR, "%2.2d:"),
                        ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"),
                        ((int(microseconds) % MINUTE) / SECOND, "%2.2d."),
                        (microseconds % SECOND, "%6.6d")]:
        data += format % val
    return data

TYPE_TRANSLATOR = {"TIME": generate_time,
                   "DATE": generate_date,
                   "DT": generate_datetime,
                   "TOD": generate_timeofday}

def MiterPen(colour, width=1, style=wx.SOLID):
    pen = wx.Pen(colour, width, style)
    pen.SetJoin(wx.JOIN_MITER)
    pen.SetCap(wx.CAP_PROJECTING)
    return pen

#-------------------------------------------------------------------------------
#                            Debug Data Consumer Class
#-------------------------------------------------------------------------------

class DebugDataConsumer:
    
    def __init__(self):
        self.LastValue = None
        self.Value = None
        self.DataType = None
        self.LastForced = False
        self.Forced = False
        self.Inhibited = False
    
    def Inhibit(self, inhibit):
        self.Inhibited = inhibit
        if not inhibit and self.LastValue is not None:
            self.SetForced(self.LastForced)
            self.SetValue(self.LastValue)
            self.LastValue = None
    
    def SetDataType(self, data_type):
        self.DataType = data_type
    
    def NewValue(self, tick, value, forced=False):
        value = TYPE_TRANSLATOR.get(self.DataType, lambda x:x)(value)
        if self.Inhibited:
            self.LastValue = value
            self.LastForced = forced
        else:
            self.SetForced(forced)
            self.SetValue(value)
    
    def SetValue(self, value):
        self.Value = value
    
    def GetValue(self):
        return self.Value
    
    def SetForced(self, forced):
        self.Forced = forced
    
    def IsForced(self):
        return self.Forced

#-------------------------------------------------------------------------------
#                               Debug Viewer Class
#-------------------------------------------------------------------------------

REFRESH_PERIOD = 0.1

class DebugViewer:
    
    def __init__(self, producer, debug, register_tick=True):
        self.DataProducer = None
        self.Debug = debug
        self.RegisterTick = register_tick
        self.Inhibited = False
        
        self.DataConsumers = {}
        
        self.LastRefreshTime = gettime()
        
        self.RefreshTimer = wx.Timer(self, -1)
        self.Bind(wx.EVT_TIMER, self.OnRefreshTimer, self.RefreshTimer)
        
        self.SetDataProducer(producer)
        
    def __del__(self):
        self.DataProducer = None
        self.DeleteDataConsumers()
        self.RefreshTimer.Stop()
    
    def SetDataProducer(self, producer):
        if self.RegisterTick and self.Debug:
            if producer is not None:
                producer.SubscribeDebugIECVariable("__tick__", self)
            if self.DataProducer is not None:
                self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)        
        self.DataProducer = producer
    
    def IsDebugging(self):
        return self.Debug
    
    def Inhibit(self, inhibit):
        self.Inhibited = inhibit
        for consumer, iec_path in self.DataConsumers.iteritems():
            consumer.Inhibit(inhibit)
    
    def AddDataConsumer(self, iec_path, consumer):
        if self.DataProducer is None:
            return False
        result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer) is not None
        if result is not None and consumer != self:
            self.DataConsumers[consumer] = iec_path
            consumer.SetDataType(self.GetDataType(iec_path))
        return result
    
    def RemoveDataConsumer(self, consumer):
        iec_path = self.DataConsumers.pop(consumer, None)
        if iec_path is not None:
            self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
    
    def GetDataType(self, iec_path):
        if self.DataProducer is not None:
            return self.DataProducer.GetDebugIECVariableType(iec_path)
        return None
    
    def ForceDataValue(self, iec_path, value):
        if self.DataProducer is not None:
            self.DataProducer.ForceDebugIECVariable(iec_path, value)
    
    def ReleaseDataValue(self, iec_path):
        if self.DataProducer is not None:
            self.DataProducer.ReleaseDebugIECVariable(iec_path)
    
    def DeleteDataConsumers(self):
        if self.DataProducer is not None:
            for consumer, iec_path in self.DataConsumers.iteritems():
                self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
        self.DataConsumers = {}
    
    def OnRefreshTimer(self, event):
        self.RefreshNewData()
        event.Skip()
    
    def NewDataAvailable(self):
        self.RefreshTimer.Stop()
        if not self.Inhibited:
            current_time = gettime()
            if current_time - self.LastRefreshTime > REFRESH_PERIOD:
                self.LastRefreshTime = current_time
                self.Inhibit(True)
                wx.CallAfter(self.RefreshViewOnNewData)
            
    def RefreshViewOnNewData(self):
        if self:
            self.RefreshNewData()
            self.RefreshTimer.Start(int(REFRESH_PERIOD * 1000), oneShot=True)
    
    def RefreshNewData(self):
        self.Inhibit(False)

#-------------------------------------------------------------------------------
#                               Viewer Rubberband
#-------------------------------------------------------------------------------

"""
Class that implements a rubberband
"""

class RubberBand:
    
    # Create a rubberband by indicated on which window it must be drawn
    def __init__(self, viewer):
        self.Viewer = viewer
        self.drawingSurface = viewer.Editor
        self.Reset()
    
    # Method that initializes the internal attributes of the rubberband
    def Reset(self):
        self.startPoint = None
        self.currentBox = None
        self.lastBox = None
    
    # Method that return if a box is currently edited
    def IsShown(self):
        return self.currentBox != None
    
    # Method that returns the currently edited box
    def GetCurrentExtent(self):
        return self.currentBox
    
    # Method called when a new box starts to be edited
    def OnLeftDown(self, event, dc, scaling):
        pos = GetScaledEventPosition(event, dc, scaling)
        # Save the point for calculate the box position and size
        self.startPoint = pos
        self.currentBox = wx.Rect(pos.x, pos.y, 0, 0)
        self.drawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
        self.Redraw()
    
    # Method called when dragging with a box edited
    def OnMotion(self, event, dc, scaling):
        pos = GetScaledEventPosition(event, dc, scaling)
        # Save the last position and size of the box for erasing it
        self.lastBox = wx.Rect(self.currentBox.x, self.currentBox.y, self.currentBox.width,
            self.currentBox.height)
        # Calculate new position and size of the box 
        if pos.x >= self.startPoint.x:
            self.currentBox.x = self.startPoint.x
            self.currentBox.width = pos.x - self.startPoint.x + 1
        else:
            self.currentBox.x = pos.x
            self.currentBox.width = self.startPoint.x - pos.x + 1
        if pos.y >= self.startPoint.y:
            self.currentBox.y = self.startPoint.y
            self.currentBox.height = pos.y - self.startPoint.y + 1
        else:
            self.currentBox.y = pos.y
            self.currentBox.height = self.startPoint.y - pos.y + 1
        self.Redraw()
    
    # Method called when dragging is stopped
    def OnLeftUp(self, event, dc, scaling):
        self.drawingSurface.SetCursor(wx.NullCursor)
        self.lastBox = self.currentBox
        self.currentBox = None
        self.Redraw()

    # Method that erase the last box and draw the new box
    def Redraw(self, dc = None):
        if not dc:
            dc = self.Viewer.GetLogicalDC()
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetLogicalFunction(wx.XOR)
        if self.lastBox:
            # Erase last box
            dc.DrawRectangle(self.lastBox.x * scalex, self.lastBox.y * scaley, 
                             self.lastBox.width * scalex, self.lastBox.height * scaley)
        if self.currentBox:
            # Draw current box
            dc.DrawRectangle(self.currentBox.x * scalex, self.currentBox.y * scaley, 
                             self.currentBox.width * scalex, self.currentBox.height * scaley)
        dc.SetUserScale(scalex, scaley)
    
    # Erase last box
    def Erase(self, dc = None):
        if not dc:
            dc = self.Viewer.GetLogicalDC()
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetLogicalFunction(wx.XOR)
        if self.lastBox:
            dc.DrawRectangle(self.lastBox.x * scalex, self.lastBox.y * scaley, 
                             self.lastBox.width * scalex, self.lastBox.height * scalex)
        dc.SetUserScale(scalex, scaley)

    # Draw current box
    def Draw(self, dc = None):
        if not dc:
            dc = self.Viewer.GetLogicalDC()
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetLogicalFunction(wx.XOR)
        if self.currentBox:
            # Draw current box
            dc.DrawRectangle(self.currentBox.x * scalex, self.currentBox.y * scaley, 
                             self.currentBox.width * scalex, self.currentBox.height * scaley)
        dc.SetUserScale(scalex, scaley)

#-------------------------------------------------------------------------------
#                               Viewer ToolTip
#-------------------------------------------------------------------------------

"""
Class that implements a custom tool tip
"""

class ToolTip(wx.PopupWindow):
    
    def __init__(self, parent, tip):
        wx.PopupWindow.__init__(self, parent)
        
        self.CurrentPosition = wx.Point(0, 0)
        
        self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
        self.SetTip(tip)
        
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        
    def SetTip(self, tip):
        self.Tip = tip
        wx.CallAfter(self.RefreshTip)
    
    def MoveToolTip(self, pos):
        self.CurrentPosition = pos
        self.SetPosition(pos)
    
    def RefreshTip(self):
        if self:
            w, h = self.GetTextExtent(self.Tip)
            self.SetSize(wx.Size(w + 4, h + 4))
            self.SetPosition(self.CurrentPosition)
            self.Refresh()
        
    def OnPaint(self, event):
        dc = wx.AutoBufferedPaintDC(self)
        dc.Clear()
        dc.SetPen(MiterPen(wx.BLACK))
        dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170)))
        dc.BeginDrawing()
        w, h = dc.GetTextExtent(self.Tip)
        dc.DrawRectangle(0, 0, w + 4, h + 4)
        dc.DrawText(self.Tip, 2, 2)
        dc.EndDrawing()
        event.Skip()

#-------------------------------------------------------------------------------
#                    Helpers for highlighting text
#-------------------------------------------------------------------------------

def AddHighlight(highlights, infos):
    RemoveHighlight(highlights, infos)
    highlights.append(infos)

def RemoveHighlight(highlights, infos):
    if infos in highlights:
        highlights.remove(infos)
        return True
    return False

def ClearHighlight(highlights, highlight_type=None):
    if highlight_type is not None:
        return [highlight for highlight in highlights if highlight[2] != highlight_type]
    return []

def DrawHighlightedText(dc, text, highlights, x, y):
    current_pen = dc.GetPen()
    dc.SetPen(wx.TRANSPARENT_PEN)
    for start, end, highlight_type in highlights:
        dc.SetBrush(wx.Brush(highlight_type[0]))
        offset_width, offset_height = dc.GetTextExtent(text[:start[1]])
        part = text[start[1]:end[1] + 1]
        part_width, part_height = dc.GetTextExtent(part)
        dc.DrawRectangle(x + offset_width, y, part_width, part_height)
        dc.SetTextForeground(highlight_type[1])
        dc.DrawText(part, x + offset_width, y)
    dc.SetPen(current_pen)
    dc.SetTextForeground(wx.BLACK)
    
#-------------------------------------------------------------------------------
#                           Graphic element base class
#-------------------------------------------------------------------------------

"""
Class that implements a generic graphic element
"""

class Graphic_Element:
    
    # Create a new graphic element
    def __init__(self, parent, id = None):
        self.Parent = parent
        self.Id = id
        self.oldPos = None
        self.StartPos = None
        self.CurrentDrag = None
        self.Handle = (None,None)
        self.Dragging = False
        self.Selected = False
        self.Highlighted = False
        self.Pos = wx.Point(0, 0)
        self.Size = wx.Size(0, 0)
        self.BoundingBox = wx.Rect(0, 0, 0, 0)
        self.Visible = False
    
    def GetDefinition(self):
        return [self.Id], []
    
    def TestVisible(self, screen):
        self.Visible = self.GetRedrawRect().Intersects(screen)
    
    def IsVisible(self):
        return self.Visible
    
    def SpreadCurrent(self):
        pass
    
    def GetConnectorTranslation(self, element):
        return {}
    
    def FindNearestConnector(self, position, connectors):
        distances = []
        for connector in connectors:
            connector_pos = connector.GetRelPosition()
            distances.append((sqrt((self.Pos.x + connector_pos.x - position.x) ** 2 +
                                   (self.Pos.y + connector_pos.y - position.y) ** 2),
                              connector))
        distances.sort()
        if len(distances) > 0:
            return distances[0][1]
        return None
        
    def IsOfType(self, type, reference):
        return self.Parent.IsOfType(type, reference)
    
    def IsEndType(self, type):
        return self.Parent.IsEndType(type)
        
    def GetDragging(self):
        return self.Dragging
    
    # Make a clone of this element
    def Clone(self, parent):
        return Graphic_Element(parent, self.Id)
    
    # Changes the block position
    def SetPosition(self, x, y):
        self.Pos.x = x
        self.Pos.y = y
        self.RefreshConnected()
        self.RefreshBoundingBox()

    # Returns the block position
    def GetPosition(self):
        return self.Pos.x, self.Pos.y
    
    # Changes the element size
    def SetSize(self, width, height):
        self.Size.SetWidth(width)
        self.Size.SetHeight(height)
        self.RefreshConnectors()
        self.RefreshBoundingBox()

    # Returns the element size
    def GetSize(self):
        return self.Size.GetWidth(), self.Size.GetHeight()
    
    # Returns the minimum element size
    def GetMinSize(self):
        return 0, 0
    
    # Refresh the element Bounding Box
    def RefreshBoundingBox(self):
        self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
    
    # Refresh the element connectors position
    def RefreshConnectors(self):
        pass
    
    # Refresh the position of wires connected to element inputs and outputs
    def RefreshConnected(self):
        pass
    
    # Change the parent
    def SetParent(self, parent):
        self.Parent = parent
    
    # Override this method for defining the method to call for deleting this element
    def Delete(self):
        pass
    
    # Returns the Id
    def GetId(self):
        return self.Id
    
    # Returns if the point given is in the bounding box
    def HitTest(self, pt):
        rect = self.BoundingBox
        return rect.InsideXY(pt.x, pt.y)
    
    # Returns if the point given is in the bounding box
    def IsInSelection(self, rect):
        return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height)
    
    # Override this method for refreshing the bounding box
    def RefreshBoundingBox(self):
        pass
    
    # Returns the bounding box
    def GetBoundingBox(self):
        return self.BoundingBox
    
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
        dc = self.Parent.GetLogicalDC()
        scalex, scaley = dc.GetUserScale()
        rect = wx.Rect()
        rect.x = self.BoundingBox.x - int(HANDLE_SIZE / scalex) - 3 - abs(movex)
        rect.y = self.BoundingBox.y - int(HANDLE_SIZE / scaley) - 3 - abs(movey)
        rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4
        rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4
        return rect
    
    def Refresh(self, rect = None):
        if self.Visible:
            if rect is not None:
                self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
            else:
                self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False)
    
    # Change the variable that indicates if this element is selected
    def SetSelected(self, selected):
        self.Selected = selected
        self.Refresh()
    
    # Change the variable that indicates if this element is highlighted
    def SetHighlighted(self, highlighted):
        self.Highlighted = highlighted
        self.Refresh()
    
    # Test if the point is on a handle of this element
    def TestHandle(self, event):
        dc = self.Parent.GetLogicalDC()
        scalex, scaley = dc.GetUserScale()
        pos = event.GetPosition()
        pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y))
        
        left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
        center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
        right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
        
        top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
        middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
        bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
        
        extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top)
        intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE)
        
        # Verify that this element is selected
        if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y):
            # Find if point is on a handle horizontally
            if left <= pt.x < left + HANDLE_SIZE:
                handle_x = 1
            elif center <= pt.x < center + HANDLE_SIZE:
                handle_x = 2
            elif right <= pt.x < right + HANDLE_SIZE:
                handle_x = 3
            else:
                handle_x = 0
            # Find if point is on a handle vertically
            if top <= pt.y < top + HANDLE_SIZE:
                handle_y = 1
            elif middle <= pt.y < middle + HANDLE_SIZE:
                handle_y = 2
            elif bottom <= pt.y < bottom + HANDLE_SIZE:
                handle_y = 3
            else:
                handle_y = 0
            # Verify that the result is valid
            if (handle_x, handle_y) in VALID_HANDLES:
                return handle_x, handle_y
        return 0, 0
    
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
        pos = event.GetLogicalPosition(dc)
        # Test if an handle have been clicked
        handle = self.TestHandle(event)
        # Find which type of handle have been clicked,
        # Save a resize event and change the cursor
        cursor = HANDLE_CURSORS.get(handle, 1)
        wx.CallAfter(self.Parent.SetCurrentCursor, cursor)
        if cursor > 1:
            self.Handle = (HANDLE_RESIZE, handle)
        else:
            self.Handle = (HANDLE_MOVE, None)
            self.SetSelected(False)
        # Initializes the last position
        self.oldPos = GetScaledEventPosition(event, dc, scaling)
        self.StartPos = wx.Point(self.Pos.x, self.Pos.y)
        self.CurrentDrag = wx.Point(0, 0)
    
    # Method called when a LeftUp event have been generated
    def OnLeftUp(self, event, dc, scaling):
        # If a dragging have been initiated
        if self.Dragging and self.oldPos:
            self.RefreshModel()
            self.Parent.RefreshBuffer()
        wx.CallAfter(self.Parent.SetCurrentCursor, 0)
        self.SetSelected(True)
        self.oldPos = None

    # Method called when a RightDown event have been generated
    def OnRightDown(self, event, dc, scaling):
        pass

    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
        if self.Dragging and self.oldPos:
            self.RefreshModel()
            self.Parent.RefreshBuffer()
        wx.CallAfter(self.Parent.SetCurrentCursor, 0)
        self.SetSelected(True)
        self.oldPos = None
        if self.Parent.Debug:
            self.Parent.PopupForceMenu()

    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
        pass
    
    # Method called when a Motion event have been generated
    def OnMotion(self, event, dc, scaling):
        # If the cursor is dragging and the element have been clicked
        if event.Dragging() and self.oldPos:
            # Calculate the movement of cursor
            pos = event.GetLogicalPosition(dc)
            movex = pos.x - self.oldPos.x
            movey = pos.y - self.oldPos.y
            # If movement is greater than MIN_MOVE then a dragging is initiated
            if not self.Dragging and (abs(movex) > MIN_MOVE or abs(movey) > MIN_MOVE):
                self.Dragging = True
            # If a dragging have been initiated, refreshes the element state
            if self.Dragging:
                dragx, dragy = self.ProcessDragging(movex, movey, event, scaling)
                if event.ControlDown() and self.Handle[0] == HANDLE_MOVE:
                    self.oldPos.x = self.StartPos.x + self.CurrentDrag.x
                    self.oldPos.y = self.StartPos.y + self.CurrentDrag.y
                else:
                    self.oldPos.x += dragx
                    self.oldPos.y += dragy
                return dragx, dragy
            return movex, movey
        # If cursor just pass over the element, changes the cursor if it is on a handle
        else:
            pos = event.GetLogicalPosition(dc)
            handle = self.TestHandle(event)
            # Find which type of handle have been clicked,
            # Save a resize event and change the cursor
            cursor = HANDLE_CURSORS.get(handle, 0)
            wx.CallAfter(self.Parent.SetCurrentCursor, cursor)
            return 0, 0

    # Moves the element
    def Move(self, dx, dy, exclude = []):
        self.Pos.x += dx
        self.Pos.y += dy
        self.RefreshConnected(exclude)
        self.RefreshBoundingBox()
    
    # Resizes the element from position and size given
    def Resize(self, x, y, width, height):
        self.Move(x, y)
        self.SetSize(width, height)
    
    # Moves and Resizes the element for fitting scaling
    def AdjustToScaling(self, scaling):
        if scaling is not None:
            movex = round_scaling(self.Pos.x, scaling[0]) - self.Pos.x
            movey = round_scaling(self.Pos.y, scaling[1]) - self.Pos.y
            min_width, min_height = self.GetMinSize()
            width = max(round_scaling(min_width, scaling[0], 1),
                        round_scaling(self.Size.width, scaling[0]))
            height = max(round_scaling(min_height, scaling[1], 1),
                         round_scaling(self.Size.height, scaling[1]))
            self.Resize(movex, movey, width, height)
            return movex, movey
        return 0, 0
    
    # Refreshes the element state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1):
        handle_type, handle = self.Handle
        # If it is a resize handle, calculate the values from resizing
        if handle_type == HANDLE_RESIZE:
            if scaling is not None:
                scaling = (scaling[0] * width_fac, scaling[1] * height_fac)
            x = y = start_x = start_y = 0
            width, height = start_width, start_height = self.GetSize()
            if handle[0] == 1:
                movex = max(-self.BoundingBox.x, movex)
                if scaling is not None:
                    movex = -(round_scaling(width - movex, scaling[0]) - width)
                x = movex
                if event.ShiftDown():
                    width -= 2 * movex
                else:
                    width -= movex
            elif handle[0] == 3:
                if scaling is not None:
                    movex = round_scaling(width + movex, scaling[0]) - width
                if event.ShiftDown():
                    movex = min(self.BoundingBox.x, movex)
                    x = -movex
                    width += 2 * movex
                else:
                    width += movex
            if handle[1] == 1:
                movey = max(-self.BoundingBox.y, movey)
                if scaling is not None:
                    movey = -(round_scaling(height - movey, scaling[1]) - height)
                y = movey
                if event.ShiftDown():
                    height -= 2 * movey
                else:
                    height -= movey
            elif handle[1] == 3:
                if scaling is not None:
                    movey = round_scaling(height + movey, scaling[1]) - height
                if event.ShiftDown():
                    movey = min(self.BoundingBox.y, movey)
                    y = -movey
                    height += 2 * movey
                else:
                    height += movey
            # Verify that new size is not lesser than minimum
            min_width, min_height = self.GetMinSize()
            if handle[0] != 2 and (width >= min_width or width > self.Size[0]):
                start_x = x
                start_width = width
            else:
                movex = 0
            if handle[1] != 2 and (height >= min_height or height > self.Size[1]):
                start_y = y
                start_height = height
            else:
                movey = 0
            if movex != 0 or movey != 0:
                self.Resize(start_x, start_y, start_width, start_height)
            return movex, movey
        # If it is a move handle, Move this element
        elif handle_type == HANDLE_MOVE:
            movex = max(-self.BoundingBox.x, movex)
            movey = max(-self.BoundingBox.y, movey)
            if scaling is not None:
                movex = round_scaling(self.Pos.x + movex, scaling[0]) - self.Pos.x
                movey = round_scaling(self.Pos.y + movey, scaling[1]) - self.Pos.y
            if event.ControlDown():
                self.CurrentDrag.x = self.CurrentDrag.x + movex
                self.CurrentDrag.y = self.CurrentDrag.y + movey
                if abs(self.CurrentDrag.x) > abs(self.CurrentDrag.y):
                    movex = self.StartPos.x + self.CurrentDrag.x - self.Pos.x
                    movey = self.StartPos.y - self.Pos.y
                else:
                    movex = self.StartPos.x - self.Pos.x
                    movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y   
            self.Move(movex, movey)
            return movex, movey
        return 0, 0
    
    # Override this method for defining the method to call for adding an highlight to this element
    def AddHighlight(self, infos, start, end, highlight_type):
        pass
    
    # Override this method for defining the method to call for removing an highlight from this element
    def RemoveHighlight(self, infos, start, end, highlight_type):
        pass
    
    # Override this method for defining the method to call for removing all the highlights of one particular type from this element
    def ClearHighlight(self, highlight_type=None):
        pass
    
    # Override this method for defining the method to call for refreshing the model of this element
    def RefreshModel(self, move=True):
        pass
    
    # Draws the highlightment of this element if it is highlighted (can be overwritten)
    def DrawHighlightment(self, dc):
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
        dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
        dc.SetLogicalFunction(wx.AND)
        dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, 
                         int(round((self.Pos.y - 1) * scaley)) - 2, 
                         int(round((self.Size.width + 3) * scalex)) + 5, 
                         int(round((self.Size.height + 3) * scaley)) + 5)
        dc.SetLogicalFunction(wx.COPY)
        dc.SetUserScale(scalex, scaley)
    
    # Draws the handles of this element if it is selected
    def Draw(self, dc):
        if not getattr(dc, "printing", False):
            if self.Highlighted:
                self.DrawHighlightment(dc)
            if self.Selected:
                scalex, scaley = dc.GetUserScale()
                dc.SetUserScale(1, 1)
                dc.SetPen(MiterPen(wx.BLACK))
                dc.SetBrush(wx.BLACK_BRUSH)
                
                left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
                center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
                right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
                
                top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
                middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
                bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
                
                for x, y in [(left, top), (center, top), (right, top),
                             (left, middle), (right, middle),
                             (left, bottom), (center, bottom), (right, bottom)]:
                    dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE)
                
                dc.SetUserScale(scalex, scaley)


#-------------------------------------------------------------------------------
#                           Group of graphic elements
#-------------------------------------------------------------------------------

"""
Class that implements a group of graphic elements
"""

class Graphic_Group(Graphic_Element):
    
    # Create a new group of graphic elements
    def __init__(self, parent):
        Graphic_Element.__init__(self, parent)
        self.Elements = []
        self.RefreshWireExclusion()
        self.RefreshBoundingBox()
    
    # Destructor
    def __del__(self):
        self.Elements = []
    
    def GetDefinition(self):
        blocks = [] 
        wires = []
        for element in self.Elements:
            block, wire = element.GetDefinition()
            blocks.extend(block)
            wires.extend(wire)
        return blocks, wires
    
    # Make a clone of this element
    def Clone(self, parent, pos = None):
        group = Graphic_Group(parent)
        connectors = {}
        exclude_names = {}
        wires = []
        if pos is not None:
            dx, dy = pos.x - self.BoundingBox.x, pos.y - self.BoundingBox.y
        for element in self.Elements:
            if isinstance(element, Wire):
                wires.append(element)
            else:
                if pos is not None:
                    x, y = element.GetPosition()
                    new_pos = wx.Point(x + dx, y + dy)
                    newid = parent.GetNewId()
                    if parent.IsNamedElement(element):
                        name = parent.GenerateNewName(element, exclude_names)
                        exclude_names[name.upper()] = True
                        new_element = element.Clone(parent, newid, name, pos = new_pos)
                    else:
                        new_element = element.Clone(parent, newid, pos = new_pos)
                    new_element.AdjustToScaling(parent.Scaling)
                else:
                    new_element = element.Clone(parent)
                connectors.update(element.GetConnectorTranslation(new_element))
                group.SelectElement(new_element)
        for element in wires:
            if pos is not None:
                new_wire = element.Clone(parent, connectors, dx, dy)
            else:
                new_wire = element.Clone(parent, connectors)
            if new_wire is not None:
                if pos is not None:
                    parent.AddWire(new_wire)
                group.SelectElement(new_wire)
        if pos is not None:
            for element in group.Elements:
                if not isinstance(element, Wire):
                    parent.AddBlockInModel(element)
        return group
    
    def CanAddBlocks(self, parent):
        valid = True
        for element in self.Elements:
            if not isinstance(element, Wire):
                valid &= parent.CanAddElement(element)
        return valid
    
    def IsVisible(self):
        for element in self.Elements:
            if element.IsVisible():
                return True
        return False
    
    # Refresh the list of wire excluded
    def RefreshWireExclusion(self):
        self.WireExcluded = []
        for element in self.Elements:
            if isinstance(element, Wire):
                startblock = element.StartConnected.GetParentBlock()
                endblock = element.EndConnected.GetParentBlock()
                if startblock in self.Elements and endblock in self.Elements:
                    self.WireExcluded.append(element)
    
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
        rect = None
        for element in self.Elements:
            if rect is None:
                rect = element.GetRedrawRect(movex, movey)
            else:
                rect = rect.Union(element.GetRedrawRect(movex, movey))
        return rect
    
    # Clean this group of elements
    def Clean(self):
        # Clean all the elements of the group
        for element in self.Elements:
            element.Clean()
    
    # Delete this group of elements
    def Delete(self):
        # Delete all the elements of the group
        for element in self.Elements:
            element.Delete()
        self.WireExcluded = []
    
    # Returns if the point given is in the bounding box of one of the elements of this group
    def HitTest(self, pt):
        result = False
        for element in self.Elements:
            result |= element.HitTest(pt)
        return result
    
    # Returns if the element given is in this group
    def IsElementIn(self, element):
        return element in self.Elements
    
    # Change the elements of the group
    def SetElements(self, elements):
        self.Elements = elements
        self.RefreshWireExclusion()
        self.RefreshBoundingBox()
    
    # Returns the elements of the group
    def GetElements(self):
        return self.Elements
    
    # Align the group elements
    def AlignElements(self, horizontally, vertically):
        minx = self.BoundingBox.x + self.BoundingBox.width
        miny = self.BoundingBox.y + self.BoundingBox.height
        maxx = self.BoundingBox.x
        maxy = self.BoundingBox.y
        for element in self.Elements:
            if not isinstance(element, Wire):
                posx, posy = element.GetPosition()
                width, height = element.GetSize()
                minx = min(minx, posx)
                miny = min(miny, posy)
                maxx = max(maxx, posx + width)
                maxy = max(maxy, posy + height)
        for element in self.Elements:
            if not isinstance(element, Wire):
                posx, posy = element.GetPosition()
                width, height = element.GetSize()
                movex = movey = 0
                if horizontally == ALIGN_LEFT:
                    movex = minx - posx
                elif horizontally == ALIGN_CENTER:
                    movex = (maxx + minx - width) / 2 - posx
                elif horizontally == ALIGN_RIGHT:
                    movex = maxx - width - posx
                if vertically == ALIGN_TOP:
                    movey = miny - posy
                elif vertically == ALIGN_MIDDLE:
                    movey = (maxy + miny - height) / 2 - posy
                elif vertically == ALIGN_BOTTOM:
                    movey = maxy - height - posy
                if movex != 0 or movey != 0:
                    element.Move(movex, movey)
                    element.RefreshModel()
        self.RefreshWireExclusion()
        self.RefreshBoundingBox()
    
    # Remove or select the given element if it is or not in the group
    def SelectElement(self, element):
        if element in self.Elements:
            self.Elements.remove(element)
        else:
            self.Elements.append(element)
        self.RefreshWireExclusion()
        self.RefreshBoundingBox()
    
    # Move this group of elements
    def Move(self, movex, movey):
        # Move all the elements of the group
        for element in self.Elements:
            if not isinstance(element, Wire):
                element.Move(movex, movey, self.WireExcluded)
            elif element in self.WireExcluded:
                element.Move(movex, movey, True)
        self.RefreshBoundingBox()
    
    # Refreshes the bounding box of this group of elements
    def RefreshBoundingBox(self):
        if len(self.Elements) > 0:
            bbox = self.Elements[0].GetBoundingBox()
            minx, miny = bbox.x, bbox.y
            maxx = bbox.x + bbox.width
            maxy = bbox.y + bbox.height
            for element in self.Elements[1:]:
                bbox = element.GetBoundingBox()
                minx = min(minx, bbox.x)
                miny = min(miny, bbox.y)
                maxx = max(maxx, bbox.x + bbox.width)
                maxy = max(maxy, bbox.y + bbox.height)
            self.BoundingBox = wx.Rect(minx, miny, maxx - minx, maxy - miny)
        else:
            self.BoundingBox = wx.Rect(0, 0, 0, 0)
        self.Pos = wx.Point(self.BoundingBox.x, self.BoundingBox.y)
        self.Size = wx.Size(self.BoundingBox.width, self.BoundingBox.height)

    # Forbids to change the group position
    def SetPosition(x, y):
        pass
    
    # Returns the position of this group
    def GetPosition(self):
        return self.BoundingBox.x, self.BoundingBox.y
    
    # Forbids to change the group size
    def SetSize(width, height):
        pass
    
    # Returns the size of this group
    def GetSize(self):
        return self.BoundingBox.width, self.BoundingBox.height

    # Moves and Resizes the group elements for fitting scaling
    def AdjustToScaling(self, scaling):
        movex_max = movey_max = 0
        for element in self.Elements:
            movex, movey = element.AdjustToScaling(scaling)
            movex_max = max(movex_max, abs(movex))
            movey_max = max(movey_max, abs(movey))
        return movex_max, movey_max
    
    # Refreshes the group elements to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
        handle_type, handle = self.Handle
        # If it is a move handle, Move this group elements
        if handle_type == HANDLE_MOVE:
            movex = max(-self.BoundingBox.x, movex)
            movey = max(-self.BoundingBox.y, movey)
            if scaling is not None:
                movex = round_scaling(movex, scaling[0])
                movey = round_scaling(movey, scaling[1])
            if event.ControlDown():
                self.CurrentDrag.x = self.CurrentDrag.x + movex
                self.CurrentDrag.y = self.CurrentDrag.y + movey
                if abs(self.CurrentDrag.x) > abs(self.CurrentDrag.y):
                    movex = self.StartPos.x + self.CurrentDrag.x - self.Pos.x
                    movey = self.StartPos.y - self.Pos.y
                else:
                    movex = self.StartPos.x - self.Pos.x
                    movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y
            self.Move(movex, movey)
            return movex, movey
        return 0, 0
    
    # Change the variable that indicates if this element is highlighted
    def SetHighlighted(self, highlighted):
        for element in self.Elements:
            element.SetHighlighted(highlighted)

    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
        Graphic_Element.OnLeftDown(self, event, dc, scaling)
        for element in self.Elements:
            element.Handle = self.Handle

    # Change the variable that indicates if the elemente is selected
    def SetSelected(self, selected):
        for element in self.Elements:
            element.SetSelected(selected)

    # Method called when a RightUp event has been generated
    def OnRightUp(self, event, dc, scaling):
        # Popup the menu with special items for a group
        self.Parent.PopupGroupMenu()

    # Refreshes the model of all the elements of this group
    def RefreshModel(self):
        for element in self.Elements:
            element.RefreshModel()


#-------------------------------------------------------------------------------
#                         Connector for all types of blocks
#-------------------------------------------------------------------------------

"""
Class that implements a connector for any type of block
"""

class Connector:
    
    # Create a new connector
    def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False):
        self.ParentBlock = parent
        self.Name = name
        self.Type = type
        self.Pos = position
        self.Direction = direction
        self.Wires = []
        if self.ParentBlock.IsOfType("BOOL", type):
            self.Negated = negated
            self.Edge = edge
        else:
            self.Negated = False
            self.Edge = "none"
        self.OneConnected = onlyone
        self.Valid = True
        self.Value = None
        self.Forced = False
        self.Selected = False
        self.Highlights = []
        self.RefreshNameSize()
    
    def Flush(self):
        self.ParentBlock = None
        for wire, handle in self.Wires:
            wire.Flush()
        self.Wires = []
    
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
        parent_pos = self.ParentBlock.GetPosition()
        x = min(parent_pos[0] + self.Pos.x, parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE)
        y = min(parent_pos[1] + self.Pos.y, parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE)
        if self.Direction[0] == 0:
            width = 5
        else:
            width = CONNECTOR_SIZE
        if self.Direction[1] == 0:
            height = 5
        else:
            height = CONNECTOR_SIZE
        return wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey))
    
    # Change the connector selection
    def SetSelected(self, selected):
        self.Selected = selected
    
    # Make a clone of the connector
    def Clone(self, parent = None):
        if parent is None:
            parent = self.ParentBlock
        return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]),
                self.Direction, self.Negated)
    
    # Returns the connector parent block
    def GetParentBlock(self):
        return self.ParentBlock
    
    # Returns the connector type
    def GetType(self, raw = False):
        if self.ParentBlock.IsEndType(self.Type) or raw:
            return self.Type
        elif (self.Negated or self.Edge != "none") and self.ParentBlock.IsOfType("BOOL", self.Type):
            return "BOOL"
        else:
            return self.ParentBlock.GetConnectionResultType(self, self.Type)
    
    # Returns the connector type
    def GetConnectedType(self):
        if self.ParentBlock.IsEndType(self.Type):
            return self.Type
        elif len(self.Wires) == 1:
            return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1])
        return self.Type
    
    # Returns the connector type
    def GetConnectedRedrawRect(self, movex, movey):
        rect = None
        for wire, handle in self.Wires:
            if rect is None:
                rect = wire.GetRedrawRect()
            else:
                rect = rect.Union(wire.GetRedrawRect())
        return rect
    
    # Returns if connector type is compatible with type given
    def IsCompatible(self, type):
        reference = self.GetType()
        return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type)
    
    # Changes the connector name
    def SetType(self, type):
        self.Type = type
    
    # Returns the connector name
    def GetName(self):
        return self.Name
    
    # Changes the connector name
    def SetName(self, name):
        self.Name = name
        self.RefreshNameSize()

    def RefreshForced(self):
        self.Forced = False
        for wire, handle in self.Wires:
            self.Forced |= wire.IsForced()

    def RefreshValue(self):
        self.Value = self.ReceivingCurrent()
    
    def RefreshValid(self):
        self.Valid = True
        for wire, handle in self.Wires:
            self.Valid &= wire.GetValid()
    
    def ReceivingCurrent(self):
        current = False
        for wire, handle in self.Wires:
            value = wire.GetValue()
            if current != "undefined" and isinstance(value, BooleanType):
                current |= wire.GetValue()
            elif value == "undefined":
                current = "undefined"
        return current
    
    def SpreadCurrent(self, spreading):
        for wire, handle in self.Wires:
            wire.SetValue(spreading)
    
    # Changes the connector name size
    def RefreshNameSize(self):
        if self.Name != "":
            self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name)
        else:
            self.NameSize = 0, 0
    
    # Returns the connector name size
    def GetNameSize(self):
        return self.NameSize
    
    # Returns the wires connected to the connector
    def GetWires(self):
        return self.Wires
    
    # Returns the parent block Id
    def GetBlockId(self):
        return self.ParentBlock.GetId()
    
    # Returns the connector relative position
    def GetRelPosition(self):
        return self.Pos
    
    # Returns the connector absolute position
    def GetPosition(self, size = True):
        parent_pos = self.ParentBlock.GetPosition()
        # If the position of the end of the connector is asked
        if size:
            x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE
            y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE
        else:
            x = parent_pos[0] + self.Pos.x
            y = parent_pos[1] + self.Pos.y
        return wx.Point(x, y)
    
    # Change the connector relative position
    def SetPosition(self, pos):
        self.Pos = pos
    
    # Returns the connector direction
    def GetDirection(self):
        return self.Direction
    
    # Change the connector direction
    def SetDirection(self, direction):
        self.Direction = direction
    
    # Connect a wire to this connector at the last place
    def Connect(self, wire, refresh = True):
        self.InsertConnect(len(self.Wires), wire, refresh)
    
    # Connect a wire to this connector at the place given
    def InsertConnect(self, idx, wire, refresh = True):
        if wire not in self.Wires:
            self.Wires.insert(idx, wire)
            if refresh:
                self.ParentBlock.RefreshModel(False)
    
    # Returns the index of the wire given in the list of connected
    def GetWireIndex(self, wire):
        for i, (tmp_wire, handle) in enumerate(self.Wires):
            if tmp_wire == wire:
                return i
        return None
    
    # Unconnect a wire or all wires connected to the connector
    def UnConnect(self, wire = None, unconnect = True, delete = False):
        i = 0
        found = False
        while i < len(self.Wires) and not found:
            if not wire or self.Wires[i][0] == wire:
                # If Unconnect haven't been called from a wire, disconnect the connector in the wire
                if unconnect:
                    if self.Wires[i][1] == 0:
                        self.Wires[i][0].UnConnectStartPoint(delete)
                    else:
                        self.Wires[i][0].UnConnectEndPoint(delete)
                # Remove wire from connected
                if wire:
                    self.Wires.pop(i)
                    found = True
            i += 1
        # If no wire defined, unconnect all wires
        if not wire:
            self.Wires = []
        self.RefreshValid()
        self.ParentBlock.RefreshModel(False)
    
    # Returns if connector has one or more wire connected
    def IsConnected(self):
        return len(self.Wires) > 0
    
    # Move the wires connected
    def MoveConnected(self, exclude = []):
        if len(self.Wires) > 0:
            # Calculate the new position of the end point
            parent_pos = self.ParentBlock.GetPosition()
            x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE
            y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE
            # Move the corresponding point on all the wires connected
            for wire, index in self.Wires:
                if wire not in exclude:
                    if index == 0:
                        wire.MoveStartPoint(wx.Point(x, y))
                    else:
                        wire.MoveEndPoint(wx.Point(x, y))
    
    # Refreshes the model of all the wires connected
    def RefreshWires(self):
        for wire in self.Wires:
            wire[0].RefreshModel()
    
    # Refreshes the parent block model
    def RefreshParentBlock(self):
        self.ParentBlock.RefreshModel(False)
    
    # Highlight the parent block
    def HighlightParentBlock(self, highlight):
        self.ParentBlock.SetHighlighted(highlight)
        self.ParentBlock.Refresh()
    
    # Returns all the blocks connected to this connector
    def GetConnectedBlocks(self):
        blocks = []
        for wire, handle in self.Wires:
            # Get other connector connected to each wire
            if handle == 0:
                connector = wire.GetEndConnected()
            else:
                connector = wire.GetStartConnected()
            # Get parent block for this connector
            if connector:
                block = connector.GetParentBlock()
                if block not in blocks:
                    blocks.append(block)
        return blocks
    
    # Returns the connector negated property
    def IsNegated(self):
        return self.Negated
    
    # Changes the connector negated property
    def SetNegated(self, negated):
        if self.ParentBlock.IsOfType("BOOL", self.Type):
            self.Negated = negated
            self.Edge = "none"
    
    # Returns the connector edge property
    def GetEdge(self):
        return self.Edge
    
    # Changes the connector edge property
    def SetEdge(self, edge):
        if self.ParentBlock.IsOfType("BOOL", self.Type):
            self.Edge = edge    
            self.Negated = False
    
    # Tests if the point given is near from the end point of this connector
    def TestPoint(self, pt, direction = None, exclude = True):
        parent_pos = self.ParentBlock.GetPosition()
        if (not (len(self.Wires) > 0 and self.OneConnected and exclude) or self.Type == "BOOL")\
            and direction is None or self.Direction == direction:
            # Calculate a square around the end point of this connector
            x = parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE - ANCHOR_DISTANCE
            y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE - ANCHOR_DISTANCE
            width = ANCHOR_DISTANCE * 2 + abs(self.Direction[0]) * CONNECTOR_SIZE
            height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE
            rect = wx.Rect(x, y, width, height)
            return rect.InsideXY(pt.x, pt.y)
        return False
    
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        pen = MiterPen(HIGHLIGHTCOLOR, 2 * scalex + 5)
        pen.SetCap(wx.CAP_BUTT)
        dc.SetPen(pen)
        dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
        dc.SetLogicalFunction(wx.AND)
        parent_pos = self.ParentBlock.GetPosition()
        posx = parent_pos[0] + self.Pos.x
        posy = parent_pos[1] + self.Pos.y
        xstart = parent_pos[0] + self.Pos.x 
        ystart = parent_pos[1] + self.Pos.y
        if self.Direction[0] < 0:
            xstart += 1
        if self.Direction[1] < 0:
            ystart += 1
        xend = xstart + CONNECTOR_SIZE * self.Direction[0]
        yend = ystart + CONNECTOR_SIZE * self.Direction[1]
        dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), 
                    round(xend * scalex), round(yend * scaley))
        dc.SetLogicalFunction(wx.COPY)
        dc.SetUserScale(scalex, scaley)
    
    # Adds an highlight to the connector
    def AddHighlight(self, infos, start, end, highlight_type):
        if highlight_type == ERROR_HIGHLIGHT:
            for wire, handle in self.Wires:
                wire.SetValid(False)
        AddHighlight(self.Highlights, (start, end, highlight_type))
    
    # Removes an highlight from the connector
    def RemoveHighlight(self, infos, start, end, highlight_type):
        error = False
        highlights = []
        for highlight in self.Highlights:
            if highlight != (start, end, highlight_type):
                highlights.append(highlight)
                error |= highlight == ERROR_HIGHLIGHT
        self.Highlights = highlights
        if not error:
            for wire, handle in self.Wires:
                wire.SetValid(wire.IsConnectedCompatible())
    
    # Removes all the highlights of one particular type from the connector
    def ClearHighlight(self, highlight_type=None):
        error = False
        if highlight_type is None:
            self.Highlights = []
        else:
            highlights = []
            for highlight in self.Highlights:
                if highlight[2] != highlight_type:
                    highlights.append(highlight)
                    error |= highlight == ERROR_HIGHLIGHT
            self.Highlights = highlights
        if not error:
            for wire, handle in self.Wires:
                wire.SetValid(wire.IsConnectedCompatible())
    
    # Draws the connector
    def Draw(self, dc):
        if self.Selected:
            dc.SetPen(MiterPen(wx.BLUE, 3))
            dc.SetBrush(wx.WHITE_BRUSH)
        #elif len(self.Highlights) > 0:
        #    dc.SetPen(MiterPen(self.Highlights[-1][1]))
        #    dc.SetBrush(wx.Brush(self.Highlights[-1][0]))
        else:
            if not self.Valid:
                dc.SetPen(MiterPen(wx.RED))
            elif isinstance(self.Value, BooleanType) and self.Value:
                if self.Forced:
                    dc.SetPen(MiterPen(wx.CYAN))
                else:
                    dc.SetPen(MiterPen(wx.GREEN))
            elif self.Value == "undefined":
                dc.SetPen(MiterPen(wx.NamedColour("orange")))
            elif self.Forced:
                dc.SetPen(MiterPen(wx.BLUE))
            else:
                dc.SetPen(MiterPen(wx.BLACK))
            dc.SetBrush(wx.WHITE_BRUSH)
        parent_pos = self.ParentBlock.GetPosition()
        
        if getattr(dc, "printing", False):
            name_size = dc.GetTextExtent(self.Name)
        else:
            name_size = self.NameSize
        
        if self.Negated:
            # If connector is negated, draw a circle
            xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2
            ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2
            dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2)
        else:
            xstart = parent_pos[0] + self.Pos.x 
            ystart = parent_pos[1] + self.Pos.y
            if self.Edge == "rising":
                # If connector has a rising edge, draw a right arrow
                dc.DrawLine(xstart, ystart, xstart - 4, ystart - 4)
                dc.DrawLine(xstart, ystart, xstart - 4, ystart + 4)
            elif self.Edge == "falling":
                # If connector has a falling edge, draw a left arrow
                dc.DrawLine(xstart, ystart, xstart + 4, ystart - 4)
                dc.DrawLine(xstart, ystart, xstart + 4, ystart + 4)
            if self.Direction[0] < 0:
                xstart += 1
            if self.Direction[1] < 0:
                ystart += 1
            if self.Selected:
                xend = xstart + (CONNECTOR_SIZE - 2) * self.Direction[0]
                yend = ystart + (CONNECTOR_SIZE - 2) * self.Direction[1]
                dc.DrawLine(xstart + 2 * self.Direction[0], ystart + 2 * self.Direction[1], xend, yend)
            else:
                xend = xstart + CONNECTOR_SIZE * self.Direction[0]
                yend = ystart + CONNECTOR_SIZE * self.Direction[1]
                dc.DrawLine(xstart + self.Direction[0], ystart + self.Direction[1], xend, yend)
        if self.Direction[0] != 0:
            ytext = parent_pos[1] + self.Pos.y - name_size[1] / 2
            if self.Direction[0] < 0:
                xtext = parent_pos[0] + self.Pos.x + 5
            else:
                xtext = parent_pos[0] + self.Pos.x - (name_size[0] + 5)
        if self.Direction[1] != 0:
            xtext = parent_pos[0] + self.Pos.x - name_size[0] / 2
            if self.Direction[1] < 0:
                ytext = parent_pos[1] + self.Pos.y + 5
            else:
                ytext = parent_pos[1] + self.Pos.y - (name_size[1] + 5)
        # Draw the text
        dc.DrawText(self.Name, xtext, ytext)
        if not getattr(dc, "printing", False):
            DrawHighlightedText(dc, self.Name, self.Highlights, xtext, ytext)

#-------------------------------------------------------------------------------
#                           Common Wire Element
#-------------------------------------------------------------------------------

"""
Class that implements a wire for connecting two blocks
"""

class Wire(Graphic_Element, DebugDataConsumer):
    
    # Create a new wire
    def __init__(self, parent, start = None, end = None):
        Graphic_Element.__init__(self, parent)
        DebugDataConsumer.__init__(self)
        self.StartPoint = start
        self.EndPoint = end
        self.StartConnected = None
        self.EndConnected = None
        # If the start and end points are defined, calculate the wire
        if start and end:
            self.ResetPoints()
            self.GeneratePoints()
        else:
            self.Points = []
            self.Segments = []
        self.SelectedSegment = None
        self.Valid = True
        self.ValueSize = None
        self.ComputedValue = None
        self.OverStart = False
        self.OverEnd = False
        self.ComputingType = False
        self.ToolTip = None
        self.Font = parent.GetMiniFont()
    
    def GetDefinition(self):
        if self.StartConnected is not None and self.EndConnected is not None:
            startblock = self.StartConnected.GetParentBlock()
            endblock = self.EndConnected.GetParentBlock()
            return [], [(startblock.GetId(), endblock.GetId())]
        return [], []
    
    def Flush(self):
        self.StartConnected = None
        self.EndConnected = None
    
    def CreateToolTip(self, pos):
        if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
            if isinstance(self.Value, StringType) and self.Value.find("#") == -1:
                computed_value = "\"%s\""%self.Value
            else:
                computed_value = str(self.Value)
            self.ToolTip = ToolTip(self.Parent, computed_value)
            self.ToolTip.MoveToolTip(pos)
            self.ToolTip.Show()
    
    def MoveToolTip(self, pos):
        if self.ToolTip is not None:
            self.ToolTip.MoveToolTip(pos)
    
    def ClearToolTip(self):
        if self.ToolTip is not None:
            self.ToolTip.Destroy()
            self.ToolTip = None
    
    # Returns the RedrawRect
    def GetRedrawRect(self, movex = 0, movey = 0):
        rect = Graphic_Element.GetRedrawRect(self, movex, movey)
        if self.StartConnected:
            rect = rect.Union(self.StartConnected.GetRedrawRect(movex, movey))
        if self.EndConnected:
            rect = rect.Union(self.EndConnected.GetRedrawRect(movex, movey))
        if self.ValueSize is not None:
            width, height = self.ValueSize
            if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
                x = self.Points[0].x + width * self.StartPoint[1][0] / 2
                y = self.Points[0].y + height * (self.StartPoint[1][1] - 1)
                rect = rect.Union(wx.Rect(x, y, width, height))
                x = self.Points[-1].x + width * self.EndPoint[1][0] / 2
                y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1)
                rect = rect.Union(wx.Rect(x, y, width, height))
            else:
                middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
                x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
                if self.BoundingBox[3] > height and self.Segments[middle] in [NORTH, SOUTH]:
                    y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
                else:
                    y = self.Points[middle].y - height
                rect = rect.Union(wx.Rect(x, y, width, height))
        return rect
    
    def Clone(self, parent, connectors = {}, dx = 0, dy = 0):
        start_connector = connectors.get(self.StartConnected, None)
        end_connector = connectors.get(self.EndConnected, None)
        if start_connector is not None and end_connector is not None:
            wire = Wire(parent)
            wire.SetPoints([(point.x + dx, point.y + dy) for point in self.Points])
            start_connector.Connect((wire, 0), False)
            end_connector.Connect((wire, -1), False)
            wire.ConnectStartPoint(start_connector.GetPosition(), start_connector)
            wire.ConnectEndPoint(end_connector.GetPosition(), end_connector)
            return wire
        return None
    
    # Forbids to change the wire position
    def SetPosition(x, y):
        pass
    
    # Forbids to change the wire size
    def SetSize(width, height):
        pass
    
    # Moves and Resizes the element for fitting scaling
    def AdjustToScaling(self, scaling):
        if scaling is not None:
            movex_max = movey_max = 0
            for idx, point in enumerate(self.Points):
                if 0 < idx < len(self.Points) - 1:
                    movex = round_scaling(point.x, scaling[0]) - point.x
                    movey = round_scaling(point.y, scaling[1]) - point.y
                    if idx == 1:
                        if self.Segments[0][0] == 0:
                            movex = 0
                        elif (point.x + movex - self.Points[0].x) * self.Segments[0][0] < MIN_SEGMENT_SIZE:
                            movex = round_scaling(self.Points[0].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - point.x
                        if self.Segments[0][1] == 0:
                            movey = 0
                        elif (point.y + movey - self.Points[0].y) * self.Segments[0][1] < MIN_SEGMENT_SIZE:
                            movey = round_scaling(self.Points[0].y + MIN_SEGMENT_SIZE * self.Segments[0][1], scaling[0], self.Segments[0][1]) - point.y
                    elif idx == len(self.Points) - 2:
                        if self.Segments[-1][0] == 0:
                            movex = 0
                        elif (self.Points[-1].x - (point.x + movex)) * self.Segments[-1][0] < MIN_SEGMENT_SIZE:
                            movex = round_scaling(self.Points[-1].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - point.x
                        if self.Segments[-1][1] == 0:
                            movey = 0
                        elif (self.Points[-1].y - (point.y + movey)) * self.Segments[-1][1] < MIN_SEGMENT_SIZE:
                            movey = round_scaling(self.Points[-1].y - MIN_SEGMENT_SIZE * self.Segments[-1][1], scaling[1], -self.Segments[-1][1]) - point.y
                    movex_max = max(movex_max, movex)
                    movey_max = max(movey_max, movey)
                    point.x += movex
                    point.y += movey
            return movex_max, movey_max
        return 0, 0
    
    # Returns connector to which start point is connected
    def GetStartConnected(self):
        return self.StartConnected
    
    # Returns connector to which start point is connected
    def GetStartConnectedType(self):
        if self.StartConnected and not self.ComputingType:
            self.ComputingType = True
            computed_type = self.StartConnected.GetType()
            self.ComputingType = False
            return computed_type
        return None
    
    # Returns connector to which end point is connected
    def GetEndConnected(self):
        return self.EndConnected
    
    # Returns connector to which end point is connected
    def GetEndConnectedType(self):
        if self.EndConnected and not self.ComputingType:
            self.ComputingType = True
            computed_type = self.EndConnected.GetType()
            self.ComputingType = False
            return computed_type
        return None
    
    def GetConnectionDirection(self):
        if self.StartConnected is None and self.EndConnected is None:
            return None
        elif self.StartConnected is not None and self.EndConnected is None:
            return (-self.StartPoint[1][0], -self.StartPoint[1][1])
        elif self.StartConnected is None and self.EndConnected is not None:
            return self.EndPoint
        elif self.Handle is not None:
            handle_type, handle = self.Handle
            # A point has been handled
            if handle_type == HANDLE_POINT:
                if handle == 0:
                    return self.EndPoint
                else:
                    return (-self.StartPoint[1][0], -self.StartPoint[1][1])
        return None
    
    def GetOtherConnected(self, connector):
        if self.StartConnected == connector:
            return self.EndConnected
        else:
            return self.StartConnected
    
    def GetOtherConnectedType(self, handle):
        if handle == 0:
            return self.GetEndConnectedType()
        else:
            return self.GetStartConnectedType()
    
    def IsConnectedCompatible(self):
        if self.StartConnected:
            return self.StartConnected.IsCompatible(self.GetEndConnectedType())
        elif self.EndConnected:
            return True
        return False
    
    def SetForced(self, forced):
        if self.Forced != forced:
            self.Forced = forced
            if self.StartConnected:
                self.StartConnected.RefreshForced()
            if self.EndConnected:
                self.EndConnected.RefreshForced()
            if self.Visible:
                self.Parent.UpdateRefreshRect(self.GetRedrawRect())

    def SetValue(self, value):
        if self.Value != value:
            self.Value = value
            if value is not None and not isinstance(value, BooleanType):
                if isinstance(value, StringType) and value.find('#') == -1:
                    self.ComputedValue = "\"%s\""%value
                else:
                    self.ComputedValue = str(value)
                if self.ToolTip is not None:
                    self.ToolTip.SetTip(self.ComputedValue)
                if len(self.ComputedValue) > 4:
                    self.ComputedValue = self.ComputedValue[:4] + "..."
            if isinstance(self.ComputedValue, (StringType, UnicodeType)):
                self.ValueSize = self.Parent.GetMiniTextExtent(self.ComputedValue)
            else:
                self.ValueSize = None
            if self.StartConnected:
                self.StartConnected.RefreshValue()
            if self.EndConnected:
                self.EndConnected.RefreshValue()
            if self.Visible:
                self.Parent.UpdateRefreshRect(self.GetRedrawRect())
            if isinstance(value, BooleanType) and self.StartConnected is not None:
                block = self.StartConnected.GetParentBlock()
                block.SpreadCurrent()
    
    # Unconnect the start and end points
    def Clean(self):
        if self.StartConnected:
            self.UnConnectStartPoint()
        if self.EndConnected:
            self.UnConnectEndPoint()
    
    # Delete this wire by calling the corresponding method
    def Delete(self):
        self.Parent.DeleteWire(self)
    
    # Select a segment and not the whole wire. It's useful for Ladder Diagram
    def SetSelectedSegment(self, segment):
        # The last segment is indicated
        if segment == -1:
            segment = len(self.Segments) - 1
        # The selected segment is reinitialised
        if segment == None:
            if self.StartConnected:
                self.StartConnected.SetSelected(False)
            if self.EndConnected:
                self.EndConnected.SetSelected(False)
        # The segment selected is the first
        elif segment == 0:
            if self.StartConnected:
                self.StartConnected.SetSelected(True)
            if self.EndConnected:
                # There is only one segment
                if len(self.Segments) == 1:
                    self.EndConnected.SetSelected(True)
                else:
                    self.EndConnected.SetSelected(False)
        # The segment selected is the last
        elif segment == len(self.Segments) - 1:
            if self.StartConnected:
                self.StartConnected.SetSelected(False)
            if self.EndConnected:
                self.EndConnected.SetSelected(True)
        self.SelectedSegment = segment
        self.Refresh()
    
    def SetValid(self, valid):
        self.Valid = valid
        if self.StartConnected:
            self.StartConnected.RefreshValid()
        if self.EndConnected:
            self.EndConnected.RefreshValid()
    
    def GetValid(self):
        return self.Valid
    
    # Reinitialize the wire points
    def ResetPoints(self):
        if self.StartPoint and self.EndPoint:
            self.Points = [self.StartPoint[0], self.EndPoint[0]]
            self.Segments = [self.StartPoint[1]]
        else:
            self.Points = []
            self.Segments = []
    
    # Refresh the wire bounding box
    def RefreshBoundingBox(self):
        if len(self.Points) > 0:
            # If startpoint or endpoint is connected, save the point radius
            start_radius = end_radius = 0
            if not self.StartConnected:
                start_radius = POINT_RADIUS
            if not self.EndConnected:
                end_radius = POINT_RADIUS
            # Initialize minimum and maximum from the first point
            minx, minbbxx = self.Points[0].x, self.Points[0].x - start_radius
            maxx, maxbbxx = self.Points[0].x, self.Points[0].x + start_radius
            miny, minbbxy = self.Points[0].y, self.Points[0].y - start_radius
            maxy, maxbbxy = self.Points[0].y, self.Points[0].y + start_radius
            # Actualize minimum and maximum with the other points
            for point in self.Points[1:-1]:
                minx, minbbxx = min(minx, point.x), min(minbbxx, point.x)
                maxx, maxbbxx = max(maxx, point.x), max(maxbbxx, point.x)
                miny, minbbxy = min(miny, point.y), min(minbbxy, point.y)
                maxy, maxbbxy = max(maxy, point.y), max(maxbbxy, point.y)
            if len(self.Points) > 1:
                minx, minbbxx = min(minx, self.Points[-1].x), min(minbbxx, self.Points[-1].x - end_radius)
                maxx, maxbbxx = max(maxx, self.Points[-1].x), max(maxbbxx, self.Points[-1].x + end_radius)
                miny, minbbxy = min(miny, self.Points[-1].y), min(minbbxy, self.Points[-1].y - end_radius)
                maxy, maxbbxy = max(maxy, self.Points[-1].y), max(maxbbxy, self.Points[-1].y + end_radius)
            self.Pos.x, self.Pos.y = minx, miny
            self.Size = wx.Size(maxx - minx, maxy - miny)
            self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1)
    
    # Refresh the realpoints that permits to keep the proportionality in wire during resizing
    def RefreshRealPoints(self):
        if len(self.Points) > 0:
            self.RealPoints = []
            # Calculate float relative position of each point with the minimum point
            for point in self.Points:
                self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)])
    
    # Returns the wire minimum size 
    def GetMinSize(self):
        width = 1
        height = 1
        dir_product = product(self.StartPoint[1], self.EndPoint[1])
        # The directions are opposed
        if dir_product < 0:
            if self.StartPoint[0] != 0:
                width = MIN_SEGMENT_SIZE * 2
            if self.StartPoint[1] != 0:
                height = MIN_SEGMENT_SIZE * 2
        # The directions are the same
        elif dir_product > 0:
            if self.StartPoint[0] != 0:
                width = MIN_SEGMENT_SIZE
            if self.StartPoint[1] != 0:
                height = MIN_SEGMENT_SIZE
        # The directions are perpendiculars
        else:
            width = MIN_SEGMENT_SIZE
            height = MIN_SEGMENT_SIZE
        return width + 1, height + 1
    
    # Returns if the point given is on one of the wire segments
    def HitTest(self, pt):
        test = False
        for i in xrange(len(self.Points) - 1):
            rect = wx.Rect(0, 0, 0, 0)
            x1, y1 = self.Points[i].x, self.Points[i].y
            x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
            # Calculate a rectangle around the segment
            rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
                abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
            test |= rect.InsideXY(pt.x, pt.y) 
        return test
    
    # Returns the wire start or end point if the point given is on one of them 
    def TestPoint(self, pt):
        # Test the wire start point
        rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE,
            2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE)
        if rect.InsideXY(pt.x, pt.y):
            return 0
        # Test the wire end point
        if len(self.Points) > 1:
            rect = wx.Rect(self.Points[-1].x - ANCHOR_DISTANCE, self.Points[-1].y - ANCHOR_DISTANCE,
                2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE)
            if rect.InsideXY(pt.x, pt.y):
                return -1
        return None
    
    # Returns the wire segment if the point given is on it
    def TestSegment(self, pt, all=False):
        for i in xrange(len(self.Segments)):
            # If wire is not in a Ladder Diagram, first and last segments are excluded
            if all or 0 < i < len(self.Segments) - 1:
                x1, y1 = self.Points[i].x, self.Points[i].y
                x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
                # Calculate a rectangle around the segment
                rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
                    abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
                if rect.InsideXY(pt.x, pt.y):
                    return i, self.Segments[i]
        return None
    
    # Define the wire points
    def SetPoints(self, points, verify=True):
        if len(points) > 1:
            self.Points = [wx.Point(x, y) for x, y in points]
            # Calculate the start and end directions
            self.StartPoint = [None, vector(self.Points[0], self.Points[1])]
            self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])]
            # Calculate the start and end points
            self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], 
                self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1])
            self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], 
                self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1])
            self.Points[0] = self.StartPoint[0]
            self.Points[-1] = self.EndPoint[0]
            # Calculate the segments directions
            self.Segments = []
            i = 0
            while i < len(self.Points) - 1:
                if verify and 0 < i < len(self.Points) - 2 and \
                   self.Points[i] == self.Points[i + 1] and \
                   self.Segments[-1] == vector(self.Points[i + 1], self.Points[i + 2]):
                    for j in xrange(2):
                        self.Points.pop(i)
                else:
                    segment = vector(self.Points[i], self.Points[i + 1])
                    if is_null_vector(segment) and i > 0:
                        segment = (self.Segments[-1][1], self.Segments[-1][0])
                    if i < len(self.Points) - 2:
                        next = vector(self.Points[i + 1], self.Points[i + 2])
                        if next == segment or is_null_vector(add_vectors(segment, next)):
                            self.Points.insert(i + 1, wx.Point(self.Points[i + 1].x, self.Points[i + 1].y))
                    self.Segments.append(segment)
                    i += 1
            self.RefreshBoundingBox()
            self.RefreshRealPoints()
    
    # Returns the position of the point indicated
    def GetPoint(self, index):
        if index < len(self.Points):
            return self.Points[index].x, self.Points[index].y
        return None
    
    # Returns a list of the position of all wire points
    def GetPoints(self, invert = False):
        points = self.VerifyPoints()
        points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], 
                points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1])
        points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], 
                points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1])
        # An inversion of the list is asked
        if invert:
            points.reverse()
        return points
    
    # Returns the position of the two selected segment points
    def GetSelectedSegmentPoints(self):
        if self.SelectedSegment != None and len(self.Points) > 1:
            return self.Points[self.SelectedSegment:self.SelectedSegment + 2]
        return []
    
    # Returns if the selected segment is the first and/or the last of the wire
    def GetSelectedSegmentConnections(self):
        if self.SelectedSegment != None and len(self.Points) > 1:
            return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1
        return (True, True)
    
    # Returns the connectors on which the wire is connected
    def GetConnected(self):
        connected = []
        if self.StartConnected and self.StartPoint[1] == WEST:
            connected.append(self.StartConnected)
        if self.EndConnected and self.EndPoint[1] == WEST:
            connected.append(self.EndConnected)
        return connected
    
    # Returns the id of the block connected to the first or the last wire point
    def GetConnectedInfos(self, index):
        if index == 0 and self.StartConnected:
            return self.StartConnected.GetBlockId(), self.StartConnected.GetName()
        elif index == -1 and self.EndConnected:
            return self.EndConnected.GetBlockId(), self.EndConnected.GetName()
        return None
    
    # Update the wire points position by keeping at most possible the current positions
    def GeneratePoints(self, realpoints = True):
        i = 0
        # Calculate the start enad end points with the minimum segment size in the right direction
        end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE,
            self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE)
        start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, 
            self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE)
        # Evaluate the point till it's the last
        while i < len(self.Points) - 1:
            # The next point is the last
            if i + 1 == len(self.Points) - 1:
                # Calculate the direction from current point to end point
                v_end = vector(self.Points[i], end)
                # The current point is the first
                if i == 0:
                    # If the end point is not in the start direction, a point is added
                    if v_end != self.Segments[0] or v_end == self.EndPoint[1]:
                        self.Points.insert(1, wx.Point(start.x, start.y))
                        self.Segments.insert(1, DirectionChoice((self.Segments[0][1], 
                            self.Segments[0][0]), v_end, self.EndPoint[1]))
                # The current point is the second
                elif i == 1:
                    # The previous direction and the target direction are mainly opposed, a point is added
                    if product(v_end, self.Segments[0]) < 0:
                        self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
                        self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
                            self.Segments[1][0]), v_end, self.EndPoint[1]))
                    # The previous direction and the end direction are the same or they are
                    # perpendiculars and the end direction points towards current segment
                    elif product(self.Segments[0], self.EndPoint[1]) >= 0 and product(self.Segments[1], self.EndPoint[1]) <= 0:
                        # Current point and end point are aligned
                        if self.Segments[0][0] != 0:
                            self.Points[1].x = end.x
                        if self.Segments[0][1] != 0:
                            self.Points[1].y = end.y
                        # If the previous direction and the end direction are the same, a point is added
                        if product(self.Segments[0], self.EndPoint[1]) > 0:
                            self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
                            self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
                                self.Segments[1][0]), v_end, self.EndPoint[1]))
                    else:
                        # Current point is positioned in the middle of start point
                        # and end point on the current direction and a point is added
                        if self.Segments[0][0] != 0:
                            self.Points[1].x = (end.x + start.x) / 2
                        if self.Segments[0][1] != 0:
                            self.Points[1].y = (end.y + start.y) / 2
                        self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
                        self.Segments.insert(2, DirectionChoice((self.Segments[1][1], 
                            self.Segments[1][0]), v_end, self.EndPoint[1]))
                else:
                    # The previous direction and the end direction are perpendiculars
                    if product(self.Segments[i - 1], self.EndPoint[1]) == 0:
                        # The target direction and the end direction aren't mainly the same
                        if product(v_end, self.EndPoint[1]) <= 0:
                            # Current point and end point are aligned
                            if self.Segments[i - 1][0] != 0:
                                self.Points[i].x = end.x
                            if self.Segments[i - 1][1] != 0:
                                self.Points[i].y = end.y
                            # Previous direction is updated from the new point
                            if product(vector(self.Points[i - 1], self.Points[i]), self.Segments[i - 1]) < 0:
                                self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
                        else:
                            test = True
                            # If the current point is the third, test if the second 
                            # point can be aligned with the end point
                            if i == 2:
                                test_point = wx.Point(self.Points[1].x, self.Points[1].y)
                                if self.Segments[1][0] != 0:
                                    test_point.y = end.y
                                if self.Segments[1][1] != 0:
                                    test_point.x = end.x
                                vector_test = vector(self.Points[0], test_point, False)
                                test = norm(vector_test) > MIN_SEGMENT_SIZE and product(self.Segments[0], vector_test) > 0
                            # The previous point can be aligned
                            if test:
                                self.Points[i].x, self.Points[i].y = end.x, end.y
                                if self.Segments[i - 1][0] != 0:
                                    self.Points[i - 1].y = end.y
                                if self.Segments[i - 1][1] != 0:
                                    self.Points[i - 1].x = end.x
                                self.Segments[i] = (-self.EndPoint[1][0], -self.EndPoint[1][1])
                            else:
                                # Current point is positioned in the middle of previous point
                                # and end point on the current direction and a point is added
                                if self.Segments[1][0] != 0:
                                    self.Points[2].x = (self.Points[1].x + end.x) / 2
                                if self.Segments[1][1] != 0:
                                    self.Points[2].y = (self.Points[1].y + end.y) / 2
                                self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y))
                                self.Segments.insert(3, DirectionChoice((self.Segments[2][1], 
                                    self.Segments[2][0]), v_end, self.EndPoint[1]))
                    else:
                        # Current point is aligned with end point
                        if self.Segments[i - 1][0] != 0:
                            self.Points[i].x = end.x
                        if self.Segments[i - 1][1] != 0:
                            self.Points[i].y = end.y
                        # Previous direction is updated from the new point
                        if product(vector(self.Points[i - 1], self.Points[i]), self.Segments[i - 1]) < 0:
                            self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
                        # If previous direction and end direction are opposed
                        if product(self.Segments[i - 1], self.EndPoint[1]) < 0:
                            # Current point is positioned in the middle of previous point
                            # and end point on the current direction
                            if self.Segments[i - 1][0] != 0:
                                self.Points[i].x = (end.x + self.Points[i - 1].x) / 2
                            if self.Segments[i - 1][1] != 0:
                                self.Points[i].y = (end.y + self.Points[i - 1].y) / 2
                        # A point is added
                        self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y))
                        self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1], 
                                self.Segments[i][0]), v_end, self.EndPoint[1]))
            else:
                # Current point is the first, and second is not mainly in the first direction
                if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0:
                    # If first and second directions aren't perpendiculars, a point is added 
                    if product(self.Segments[0], self.Segments[1]) != 0:
                        self.Points.insert(1, wx.Point(start.x, start.y))
                        self.Segments.insert(1, DirectionChoice((self.Segments[0][1], 
                            self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1]))
                    else:
                        self.Points[1].x, self.Points[1].y = start.x, start.y
                else:
                    # Next point is aligned with current point
                    if self.Segments[i][0] != 0:
                        self.Points[i + 1].y = self.Points[i].y
                    if self.Segments[i][1] != 0:
                        self.Points[i + 1].x = self.Points[i].x
                    # Current direction is updated from the new point
                    if product(vector(self.Points[i], self.Points[i + 1]), self.Segments[i]) < 0:
                        self.Segments[i] = (-self.Segments[i][0], -self.Segments[i][1])
            i += 1
        self.RefreshBoundingBox()
        if realpoints:
            self.RefreshRealPoints()
    
    # Verify that two consecutive points haven't the same position
    def VerifyPoints(self):
        points = [point for point in self.Points]
        segments = [segment for segment in self.Segments]
        i = 1
        while i < len(points) - 1:
            if points[i] == points[i + 1] and segments[i - 1] == segments[i + 1]:
                for j in xrange(2):
                    points.pop(i)
                    segments.pop(i)
            else:
                i += 1
        # If the wire isn't in a Ladder Diagram, save the new point list
        if self.Parent.__class__.__name__ != "LD_Viewer":
            self.Points = [point for point in points]
            self.Segments = [segment for segment in segments]
            self.RefreshBoundingBox()
            self.RefreshRealPoints()
        return points
    
    # Moves all the wire points except the first and the last if they are connected
    def Move(self, dx, dy, endpoints = False):
        for i, point in enumerate(self.Points):
            if endpoints or not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
                point.x += dx
                point.y += dy
        self.StartPoint[0] = self.Points[0]
        self.EndPoint[0] = self.Points[-1]
        self.GeneratePoints()
    
    # Resize the wire from position and size given
    def Resize(self, x, y, width, height):
        if len(self.Points) > 1:
            # Calculate the new position of each point for testing the new size
            minx, miny = self.Pos.x, self.Pos.y
            lastwidth, lastheight = self.Size.width, self.Size.height
            for i, point in enumerate(self.RealPoints):
                # If start or end point is connected, it's not calculate
                if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
                    if i == 0:
                        dir = self.StartPoint[1]
                    elif i == len(self.Points) - 1:
                        dir = self.EndPoint[1]
                    else:
                        dir = (0, 0)
                    pointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0] * width / float(max(lastwidth, 1)))),
                            width - dir[0] * MIN_SEGMENT_SIZE))
                    pointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1] * height / float(max(lastheight, 1)))),
                            height - dir[1] * MIN_SEGMENT_SIZE))
                    self.Points[i] = wx.Point(minx + x + pointx, miny + y + pointy)
            self.StartPoint[0] = self.Points[0]
            self.EndPoint[0] = self.Points[-1]
            self.GeneratePoints(False)
            # Test if the wire position or size have changed
            if x != 0 and minx == self.Pos.x:
                x = 0
                width = lastwidth
            if y != 0 and miny == self.Pos.y:
                y = 0
                height = lastwidth
            if width != lastwidth and lastwidth == self.Size.width:
                width = lastwidth
            if height != lastheight and lastheight == self.Size.height:
                height = lastheight
            # Calculate the real points from the new size, it's important for
            # keeping a proportionality in the points position with the size
            # during a resize dragging
            for i, point in enumerate(self.RealPoints):
                if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
                    point[0] = point[0] * width / float(max(lastwidth, 1))
                    point[1] = point[1] * height / float(max(lastheight, 1))
            # Calculate the correct position of the points from real points
            for i, point in enumerate(self.RealPoints):
                if not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected):
                    if i == 0:
                        dir = self.StartPoint[1]
                    elif i == len(self.Points) - 1:
                        dir = self.EndPoint[1]
                    else:
                        dir = (0, 0)
                    realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0])),
                            width - dir[0] * MIN_SEGMENT_SIZE))
                    realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1])),
                            height - dir[1] * MIN_SEGMENT_SIZE))
                    self.Points[i] = wx.Point(minx + x + realpointx, miny + y + realpointy)
            self.StartPoint[0] = self.Points[0]
            self.EndPoint[0] = self.Points[-1]
            self.GeneratePoints(False)
    
    # Moves the wire start point and update the wire points
    def MoveStartPoint(self, point):
        if len(self.Points) > 1:
            self.StartPoint[0] = point
            self.Points[0] = point
            self.GeneratePoints()
    
    # Changes the wire start direction and update the wire points
    def SetStartPointDirection(self, dir):
        if len(self.Points) > 1:
            self.StartPoint[1] = dir
            self.Segments[0] = dir
            self.GeneratePoints()
    
    # Rotates the wire start direction by an angle of 90 degrees anticlockwise
    def RotateStartPoint(self):
        self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0]))
    
    # Connects wire start point to the connector given and moves wire start point
    # to given point
    def ConnectStartPoint(self, point, connector):
        if point:
            self.MoveStartPoint(point)
        self.StartConnected = connector
        self.RefreshBoundingBox()
    
    # Unconnects wire start point
    def UnConnectStartPoint(self, delete = False):
        if delete:
            self.StartConnected = None
            self.Delete()
        elif self.StartConnected:
            self.StartConnected.UnConnect(self, unconnect = False)
            self.StartConnected = None
            self.RefreshBoundingBox()
    
    # Moves the wire end point and update the wire points
    def MoveEndPoint(self, point):
        if len(self.Points) > 1:
            self.EndPoint[0] = point
            self.Points[-1] = point
            self.GeneratePoints()

    # Changes the wire end direction and update the wire points
    def SetEndPointDirection(self, dir):
        if len(self.Points) > 1:
            self.EndPoint[1] = dir
            self.GeneratePoints()
            
    # Rotates the wire end direction by an angle of 90 degrees anticlockwise
    def RotateEndPoint(self):
        self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0]))

    # Connects wire end point to the connector given and moves wire end point
    # to given point
    def ConnectEndPoint(self, point, connector):
        if point:
            self.MoveEndPoint(point)
        self.EndConnected = connector
        self.RefreshBoundingBox()
    
    # Unconnects wire end point
    def UnConnectEndPoint(self, delete = False):
        if delete:
            self.EndConnected = None
            self.Delete()
        elif self.EndConnected:
            self.EndConnected.UnConnect(self, unconnect = False)
            self.EndConnected = None
            self.RefreshBoundingBox()
    
    # Moves the wire segment given by its index
    def MoveSegment(self, idx, movex, movey, scaling):
        if 0 < idx < len(self.Segments) - 1:
            if self.Segments[idx] in (NORTH, SOUTH):
                start_x = self.Points[idx].x
                if scaling is not None:
                    movex = round_scaling(self.Points[idx].x + movex, scaling[0]) - self.Points[idx].x
                    if idx == 1 and (self.Points[1].x + movex - self.Points[0].x) * self.Segments[0][0] < MIN_SEGMENT_SIZE:
                        movex = round_scaling(self.Points[0].x + MIN_SEGMENT_SIZE * self.Segments[0][0], scaling[0], self.Segments[0][0]) - self.Points[idx].x
                    elif idx == len(self.Segments) - 2 and (self.Points[-1].x - (self.Points[-2].x + movex)) * self.Segments[-1][0] < MIN_SEGMENT_SIZE:
                        movex = round_scaling(self.Points[-1].x - MIN_SEGMENT_SIZE * self.Segments[-1][0], scaling[0], -self.Segments[-1][0]) - self.Points[idx].x
                self.Points[idx].x += movex
                self.Points[idx + 1].x += movex
                self.GeneratePoints()
                if start_x != self.Points[idx].x:
                    return self.Points[idx].x - start_x, 0
            elif self.Segments[idx] in (EAST, WEST):
                start_y = self.Points[idx].y
                if scaling is not None:
                    movey = round_scaling(self.Points[idx].y + movey, scaling[1]) - self.Points[idx].y
                    if idx == 1 and (self.Points[1].y + movey - self.Points[0].y) * self.Segments[0][1] < MIN_SEGMENT_SIZE:
                        movex = round_scaling(self.Points[0].y + MIN_SEGMENT_SIZE * self.Segments[0][1], scaling[0], self.Segments[0][1]) - self.Points[idx].y
                    elif idx == len(self.Segments) - 2 and (self.Points[-1].y - (self.Points[-2].y + movey)) * self.Segments[-1][1] < MIN_SEGMENT_SIZE:
                        movey = round_scaling(self.Points[idx].y - MIN_SEGMENT_SIZE * self.Segments[-1][1], scaling[1], -self.Segments[-1][1]) - self.Points[idx].y
                self.Points[idx].y += movey
                self.Points[idx + 1].y += movey
                self.GeneratePoints()
                if start_y != self.Points[idx].y:
                    return 0, self.Points[idx].y - start_y
        return 0, 0
    
    # Adds two points in the middle of the handled segment
    def AddSegment(self):
        handle_type, handle = self.Handle
        if handle_type == HANDLE_SEGMENT:
            segment, dir = handle
            if len(self.Segments) > 1:
                pointx = self.Points[segment].x
                pointy = self.Points[segment].y
                if dir[0] != 0:
                    pointx = (self.Points[segment].x + self.Points[segment + 1].x) / 2
                if dir[1] != 0:
                    pointy = (self.Points[segment].y + self.Points[segment + 1].y) / 2
                self.Points.insert(segment + 1, wx.Point(pointx, pointy))
                self.Segments.insert(segment + 1, (dir[1], dir[0]))
                self.Points.insert(segment + 2, wx.Point(pointx, pointy))
                self.Segments.insert(segment + 2, dir)
            else:
                p1x = p2x = self.Points[segment].x
                p1y = p2y = self.Points[segment].y
                if dir[0] != 0:
                    p1x = (2 * self.Points[segment].x + self.Points[segment + 1].x) / 3
                    p2x = (self.Points[segment].x + 2 * self.Points[segment + 1].x) / 3
                if dir[1] != 0:
                    p1y = (2 * self.Points[segment].y + self.Points[segment + 1].y) / 3
                    p2y = (self.Points[segment].y + 2 * self.Points[segment + 1].y) / 3
                self.Points.insert(segment + 1, wx.Point(p1x, p1y))
                self.Segments.insert(segment + 1, (dir[1], dir[0]))
                self.Points.insert(segment + 2, wx.Point(p1x, p1y))
                self.Segments.insert(segment + 2, dir)
                self.Points.insert(segment + 3, wx.Point(p2x, p2y))
                self.Segments.insert(segment + 3, (dir[1], dir[0]))
                self.Points.insert(segment + 4, wx.Point(p2x, p2y))
                self.Segments.insert(segment + 4, dir)
            self.GeneratePoints()
    
    # Delete the handled segment by removing the two segment points
    def DeleteSegment(self):
        handle_type, handle = self.Handle
        if handle_type == HANDLE_SEGMENT:
            segment, dir = handle
            for i in xrange(2):
                self.Points.pop(segment)
                self.Segments.pop(segment)
            self.GeneratePoints()
            self.RefreshModel()
            
    # Method called when a LeftDown event have been generated
    def OnLeftDown(self, event, dc, scaling):
        pos = GetScaledEventPosition(event, dc, scaling)
        # Test if a point have been handled
        #result = self.TestPoint(pos)
        #if result != None:
        #    self.Handle = (HANDLE_POINT, result)
        #    wx.CallAfter(self.Parent.SetCurrentCursor, 1)
        #else:
        # Test if a segment have been handled
        result = self.TestSegment(pos)
        if result != None:
            if result[1] in (NORTH, SOUTH):
                wx.CallAfter(self.Parent.SetCurrentCursor, 4)
            elif result[1] in (EAST, WEST):
                wx.CallAfter(self.Parent.SetCurrentCursor, 5)
            self.Handle = (HANDLE_SEGMENT, result)
        # Execute the default method for a graphic element
        else:
            Graphic_Element.OnLeftDown(self, event, dc, scaling)
        self.oldPos = pos
    
    # Method called when a RightUp event has been generated
    def OnRightUp(self, event, dc, scaling):
        pos = GetScaledEventPosition(event, dc, scaling)
        # Test if a segment has been handled
        result = self.TestSegment(pos, True)
        if result != None:
            self.Handle = (HANDLE_SEGMENT, result)
            # Popup the menu with special items for a wire
            self.Parent.PopupWireMenu(0 < result[0] < len(self.Segments) - 1)
        else:
            # Execute the default method for a graphic element
            Graphic_Element.OnRightUp(self, event, dc, scaling)
    
    # Method called when a LeftDClick event has been generated
    def OnLeftDClick(self, event, dc, scaling):
        rect = self.GetRedrawRect()
        if event.ControlDown():
            direction = (self.StartPoint[1], self.EndPoint[1])
            if direction in [(EAST, WEST), (WEST, EAST)]:
                avgy = (self.StartPoint[0].y + self.EndPoint[0].y) / 2
                if scaling is not None:
                    avgy = round(float(avgy) / scaling[1]) * scaling[1]
                if self.StartConnected is not None:
                    movey = avgy - self.StartPoint[0].y
                    startblock = self.StartConnected.GetParentBlock()
                    startblock.Move(0, movey)
                    startblock.RefreshModel()
                    rect.Union(startblock.GetRedrawRect(0, movey))
                else:
                    self.MoveStartPoint(wx.Point(self.StartPoint[0].x, avgy))
                if self.EndConnected is not None:
                    movey = avgy - self.EndPoint[0].y
                    endblock = self.EndConnected.GetParentBlock()
                    endblock.Move(0, movey)
                    endblock.RefreshModel()
                    rect.Union(endblock.GetRedrawRect(0, movey))
                else:
                    self.MoveEndPoint(wx.Point(self.EndPoint[0].x, avgy))
                self.Parent.RefreshBuffer()
            elif direction in [(NORTH, SOUTH), (SOUTH, NORTH)]:
                avgx = (self.StartPoint[0].x + self.EndPoint[0].x) / 2
                if scaling is not None:
                    avgx = round(float(avgx) / scaling[0]) * scaling[0]
                if self.StartConnected is not None:
                    movex = avgx - self.StartPoint[0].x
                    startblock = self.StartConnected.GetParentBlock()
                    startblock.Move(movex, 0)
                    startblock.RefreshModel()
                    rect.Union(startblock.GetRedrawRect(movex, 0))
                else:
                    self.MoveStartPoint(wx.Point(avgx, self.StartPoint[0].y))
                if self.EndConnected is not None:
                    movex = avgx - self.EndPoint[0].x
                    endblock = self.EndConnected.GetParentBlock()
                    endblock.Move(movex, 0)
                    endblock.RefreshModel()
                    rect.Union(endblock.GetRedrawRect(movex, 0))
                else:
                    self.MoveEndPoint(wx.Point(avgx, self.EndPoint[0].y))
                self.Parent.RefreshBuffer()
        else:
            self.ResetPoints()
            self.GeneratePoints()
            self.RefreshModel()
            self.Parent.RefreshBuffer()
        rect.Union(self.GetRedrawRect())
        self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
        
    # Method called when a Motion event has been generated
    def OnMotion(self, event, dc, scaling):
        pos = GetScaledEventPosition(event, dc, scaling)
        if not event.Dragging():
            # Test if a segment has been handled
            result = self.TestSegment(pos)
            if result:
                if result[1] in (NORTH, SOUTH):
                    wx.CallAfter(self.Parent.SetCurrentCursor, 4)
                elif result[1] in (EAST, WEST):
                    wx.CallAfter(self.Parent.SetCurrentCursor, 5)
                return 0, 0
            else:
                # Test if a point has been handled
                #result = self.TestPoint(pos)
                #if result != None:
                #    if result == 0 and self.StartConnected:
                #        self.OverStart = True
                #    elif result != 0 and self.EndConnected:
                #        self.OverEnd = True
                #else:
                #    self.OverStart = False
                #    self.OverEnd = False
                # Execute the default method for a graphic element
                return Graphic_Element.OnMotion(self, event, dc, scaling)
        else:
            # Execute the default method for a graphic element
            return Graphic_Element.OnMotion(self, event, dc, scaling)
    
    # Refreshes the wire state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
        handle_type, handle = self.Handle
        # A point has been handled
        if handle_type == HANDLE_POINT:
            movex = max(-self.Points[handle].x + POINT_RADIUS, movex)
            movey = max(-self.Points[handle].y + POINT_RADIUS, movey)
            if scaling is not None:
                movex = round_scaling(self.Points[handle].x + movex, scaling[0]) - self.Points[handle].x
                movey = round_scaling(self.Points[handle].y + movey, scaling[1]) - self.Points[handle].y
            # Try to connect point to a connector
            new_pos = wx.Point(self.Points[handle].x + movex, self.Points[handle].y + movey)
            connector = self.Parent.FindBlockConnector(new_pos, self.GetConnectionDirection())
            if connector:
                if handle == 0 and self.EndConnected != connector:
                    connector.HighlightParentBlock(True)
                    connector.Connect((self, handle))
                    self.SetStartPointDirection(connector.GetDirection())
                    self.ConnectStartPoint(connector.GetPosition(), connector)
                    pos = connector.GetPosition()
                    movex = pos.x - self.oldPos.x
                    movey = pos.y - self.oldPos.y
                    if not connector.IsCompatible(self.GetEndConnectedType()):
                        self.SetValid(False)
                    self.Dragging = False
                elif handle != 0 and self.StartConnected != connector:
                    connector.HighlightParentBlock(True)
                    connector.Connect((self, handle))
                    self.SetEndPointDirection(connector.GetDirection())
                    self.ConnectEndPoint(connector.GetPosition(), connector)
                    pos = connector.GetPosition()
                    movex = pos.x - self.oldPos.x
                    movey = pos.y - self.oldPos.y
                    if not connector.IsCompatible(self.GetStartConnectedType()):
                        self.SetValid(False)
                    self.Dragging = False
                elif handle == 0:
                    self.MoveStartPoint(new_pos)
                else:
                    self.MoveEndPoint(new_pos)
            # If there is no connector, move the point
            elif handle == 0:
                self.SetValid(True)
                if self.StartConnected:
                    self.StartConnected.HighlightParentBlock(False)
                    self.UnConnectStartPoint()
                self.MoveStartPoint(new_pos)
            else:
                self.SetValid(True)
                if self.EndConnected:
                    self.EndConnected.HighlightParentBlock(False)
                    self.UnConnectEndPoint()
                self.MoveEndPoint(new_pos)
            return movex, movey
        # A segment has been handled, move a segment
        elif handle_type == HANDLE_SEGMENT:
            return self.MoveSegment(handle[0], movex, movey, scaling)
        # Execute the default method for a graphic element
        else:
            return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
    
    # Refreshes the wire model
    def RefreshModel(self, move=True):
        if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]:
            self.StartConnected.RefreshParentBlock()
        if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]:
            self.EndConnected.RefreshParentBlock()
    
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(MiterPen(HIGHLIGHTCOLOR, (2 * scalex + 5)))
        dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
        dc.SetLogicalFunction(wx.AND)
        # Draw the start and end points if they are not connected or the mouse is over them
        if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
            dc.DrawCircle(round(self.Points[0].x * scalex), 
                          round(self.Points[0].y * scaley), 
                          (POINT_RADIUS + 1) * scalex + 2)
        if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
            dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2)
        # Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
        if len(self.Points) > 1:
            points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), 
                               round((self.Points[0].y - self.Segments[0][1]) * scaley))]
            points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]])
            points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), 
                                   round((self.Points[-1].y + self.Segments[-1][1]) * scaley)))
        else:
            points = []
        dc.DrawLines(points)
        dc.SetLogicalFunction(wx.COPY)
        dc.SetUserScale(scalex, scaley)
        
        if self.StartConnected is not None:
            self.StartConnected.DrawHighlightment(dc)
            self.StartConnected.Draw(dc)
        if self.EndConnected is not None:
            self.EndConnected.DrawHighlightment(dc)
            self.EndConnected.Draw(dc)
    
    # Draws the wire lines and points
    def Draw(self, dc):
        Graphic_Element.Draw(self, dc)
        if not self.Valid:
            dc.SetPen(MiterPen(wx.RED))
            dc.SetBrush(wx.RED_BRUSH)
        elif isinstance(self.Value, BooleanType) and self.Value:
            if self.Forced:
                dc.SetPen(MiterPen(wx.CYAN))
                dc.SetBrush(wx.CYAN_BRUSH)
            else:
                dc.SetPen(MiterPen(wx.GREEN))
                dc.SetBrush(wx.GREEN_BRUSH)
        elif self.Value == "undefined":
            dc.SetPen(MiterPen(wx.NamedColour("orange")))
            dc.SetBrush(wx.Brush(wx.NamedColour("orange")))
        elif self.Forced:
            dc.SetPen(MiterPen(wx.BLUE))
            dc.SetBrush(wx.BLUE_BRUSH)
        else:
            dc.SetPen(MiterPen(wx.BLACK))
            dc.SetBrush(wx.BLACK_BRUSH)
        # Draw the start and end points if they are not connected or the mouse is over them
        if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
            dc.DrawCircle(self.Points[0].x, self.Points[0].y, POINT_RADIUS)
        if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
            dc.DrawCircle(self.Points[-1].x, self.Points[-1].y, POINT_RADIUS)
        # Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
        if len(self.Points) > 1:
            points = [wx.Point(self.Points[0].x - self.Segments[0][0], self.Points[0].y - self.Segments[0][1])]
            points.extend([point for point in self.Points[1:-1]])
            points.append(wx.Point(self.Points[-1].x + self.Segments[-1][0], self.Points[-1].y + self.Segments[-1][1]))
        else:
            points = []
        dc.DrawLines(points)
        # Draw the segment selected in red
        if not getattr(dc, "printing", False) and self.SelectedSegment is not None:
            dc.SetPen(MiterPen(wx.BLUE, 3))
            if self.SelectedSegment == len(self.Segments) - 1:
                end = 0
            else:
                end = 1
            dc.DrawLine(self.Points[self.SelectedSegment].x - 1, self.Points[self.SelectedSegment].y,
                        self.Points[self.SelectedSegment + 1].x + end, self.Points[self.SelectedSegment + 1].y)
        if self.Value is not None and not isinstance(self.Value, BooleanType) and self.Value != "undefined":
            dc.SetFont(self.Parent.GetMiniFont())
            dc.SetTextForeground(wx.NamedColour("purple"))
            if self.ValueSize is not None:
                width, height = self.ValueSize
                if self.BoundingBox[2] > width * 4 or self.BoundingBox[3] > height * 4:
                    x = self.Points[0].x + width * (self.StartPoint[1][0] - 1) / 2
                    y = self.Points[0].y + height * (self.StartPoint[1][1] - 1)
                    dc.DrawText(self.ComputedValue, x, y)
                    x = self.Points[-1].x + width * (self.EndPoint[1][0] - 1) / 2
                    y = self.Points[-1].y + height * (self.EndPoint[1][1] - 1)
                    dc.DrawText(self.ComputedValue, x, y)
                else:
                    middle = len(self.Segments) / 2 + len(self.Segments) % 2 - 1
                    x = (self.Points[middle].x + self.Points[middle + 1].x - width) / 2
                    if self.BoundingBox[3] > height and self.Segments[middle] in [NORTH, SOUTH]:
                        y = (self.Points[middle].y + self.Points[middle + 1].y - height) / 2
                    else:
                        y = self.Points[middle].y - height
                    dc.DrawText(self.ComputedValue, x, y)
            dc.SetFont(self.Parent.GetFont())
            dc.SetTextForeground(wx.BLACK)


#-------------------------------------------------------------------------------
#                           Graphic comment element
#-------------------------------------------------------------------------------

def FilterHighlightsByRow(highlights, row, length):
    _highlights = []
    for start, end, highlight_type in highlights:
        if start[0] <= row and end[0] >= row:
            if start[0] < row:
                start = (row, 0)
            if end[0] > row:
                end = (row, length)
            _highlights.append((start, end, highlight_type))
    return _highlights

def FilterHighlightsByColumn(highlights, start_col, end_col):
    _highlights = []
    for start, end, highlight_type in highlights:
        if end[1] > start_col and start[1] < end_col:
            start = (start[0], max(start[1], start_col) - start_col)
            end = (end[0], min(end[1], end_col) - start_col)
            _highlights.append((start, end, highlight_type))
    return _highlights

"""
Class that implements a comment
"""

class Comment(Graphic_Element):

    # Create a new comment
    def __init__(self, parent, content, id = None):
        Graphic_Element.__init__(self, parent)
        self.Id = id
        self.Content = content
        self.Pos = wx.Point(0, 0)
        self.Size = wx.Size(0, 0)
        self.Highlights = []
    
    # Make a clone of this comment
    def Clone(self, parent, id = None, pos = None):
        comment = Comment(parent, self.Content, id)
        if pos is not None:
            comment.SetPosition(pos.x, pos.y)
        comment.SetSize(self.Size[0], self.Size[1])
        return comment
    
    # Method for keeping compatibility with others
    def Clean(self):
        pass
    
    # Delete this comment by calling the corresponding method
    def Delete(self):
        self.Parent.DeleteComment(self)
    
    # Refresh the comment bounding box
    def RefreshBoundingBox(self):
        self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
    
    # Changes the comment size
    def SetSize(self, width, height):
        self.Size.SetWidth(width)
        self.Size.SetHeight(height)
        self.RefreshBoundingBox()

    # Returns the comment size
    def GetSize(self):
        return self.Size.GetWidth(), self.Size.GetHeight()
    
    # Returns the comment minimum size
    def GetMinSize(self):
        dc = wx.ClientDC(self.Parent)
        min_width = 0
        min_height = 0
        # The comment minimum size is the maximum size of words in the content
        for line in self.Content.splitlines():
            for word in line.split(" "):
                wordwidth, wordheight = dc.GetTextExtent(word)
                min_width = max(min_width, wordwidth)
                min_height = max(min_height, wordheight)
        return min_width + 20, min_height + 20
    
    # Changes the comment position
    def SetPosition(self, x, y):
        self.Pos.x = x
        self.Pos.y = y
        self.RefreshBoundingBox()

    # Changes the comment content
    def SetContent(self, content):
        self.Content = content
        min_width, min_height = self.GetMinSize()
        self.Size[0] = max(self.Size[0], min_width)
        self.Size[1] = max(self.Size[1], min_height)
        self.RefreshBoundingBox()

    # Returns the comment content
    def GetContent(self):
        return self.Content

    # Returns the comment position
    def GetPosition(self):
        return self.Pos.x, self.Pos.y
    
    # Moves the comment
    def Move(self, dx, dy, connected = True):
        self.Pos.x += dx
        self.Pos.y += dy
        self.RefreshBoundingBox()
    
    # Resizes the comment with the position and the size given
    def Resize(self, x, y, width, height):
        self.Move(x, y)
        self.SetSize(width, height)
    
    # Method called when a RightUp event have been generated
    def OnRightUp(self, event, dc, scaling):
        # Popup the default menu
        self.Parent.PopupDefaultMenu()
    
    # Refreshes the wire state according to move defined and handle selected
    def ProcessDragging(self, movex, movey, event, scaling):
        if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD":
            movex = movey = 0
        return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
        
    # Refreshes the comment model
    def RefreshModel(self, move=True):
        self.Parent.RefreshCommentModel(self)
    
    # Method called when a LeftDClick event have been generated
    def OnLeftDClick(self, event, dc, scaling):
        # Edit the comment content
        self.Parent.EditCommentContent(self)
    
    # Adds an highlight to the comment
    def AddHighlight(self, infos, start, end, highlight_type):
        if infos[0] == "content":
            AddHighlight(self.Highlights, (start, end, highlight_type))
    
    # Removes an highlight from the comment
    def RemoveHighlight(self, infos, start, end, highlight_type):
        RemoveHighlight(self.Highlights, (start, end, highlight_type))
    
    # Removes all the highlights of one particular type from the comment
    def ClearHighlight(self, highlight_type=None):
        self.Highlights = ClearHighlights(self.Highlights, highlight_type)
    
    # Draws the highlightment of this element if it is highlighted
    def DrawHighlightment(self, dc):
        scalex, scaley = dc.GetUserScale()
        dc.SetUserScale(1, 1)
        dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
        dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
        dc.SetLogicalFunction(wx.AND)
        
        left = (self.Pos.x - 1) * scalex - 2
        right = (self.Pos.x + self.Size[0] + 1) * scalex + 2
        top = (self.Pos.y - 1) * scaley - 2
        bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2
        angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2
        angle_right = (self.Pos.y + 9) * scaley - 2
        
        polygon = [wx.Point(left, top), wx.Point(angle_top, top),
                   wx.Point(right, angle_right), wx.Point(right, bottom),
                   wx.Point(left, bottom)]
        dc.DrawPolygon(polygon)
        
        dc.SetLogicalFunction(wx.COPY)
        dc.SetUserScale(scalex, scaley)
        
    # Draws the comment and its content
    def Draw(self, dc):
        Graphic_Element.Draw(self, dc)
        dc.SetPen(MiterPen(wx.BLACK))
        dc.SetBrush(wx.WHITE_BRUSH)
        # Draws the comment shape
        polygon = [wx.Point(self.Pos.x, self.Pos.y), 
                   wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
                   wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10),
                   wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]),
                   wx.Point(self.Pos.x, self.Pos.y + self.Size[1])]
        dc.DrawPolygon(polygon)
        lines = [wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
                 wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y + 10),
                 wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10)]
        dc.DrawLines(lines)
        # Draws the comment content
        y = self.Pos.y + 10
        for idx, line in enumerate(self.Content.splitlines()):
            first = True
            linetext = ""
            words = line.split(" ")
            if not getattr(dc, "printing", False):
                highlights = FilterHighlightsByRow(self.Highlights, idx, len(line))
                highlights_offset = 0
            for i, word in enumerate(words):
                if first:
                    text = word
                else:
                    text = linetext + " " + word
                wordwidth, wordheight = dc.GetTextExtent(text)
                if y + wordheight > self.Pos.y + self.Size[1] - 10:
                    break
                if wordwidth < self.Size[0] - 20:
                    if i < len(words) - 1:
                        linetext = text
                        first = False
                    else:
                        dc.DrawText(text, self.Pos.x + 10, y)
                        if not getattr(dc, "printing", False):
                            DrawHighlightedText(dc, text, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(text)), self.Pos.x + 10, y)
                            highlights_offset += len(text) + 1
                        y += wordheight + 5
                else:
                    if not first:
                        dc.DrawText(linetext, self.Pos.x + 10, y)
                        if not getattr(dc, "printing", False):
                            DrawHighlightedText(dc, linetext, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(linetext)), self.Pos.x + 10, y)
                            highlights_offset += len(linetext) + 1
                    if first or i == len(words) - 1:
                        if not first:
                            y += wordheight + 5
                            if y + wordheight > self.Pos.y + self.Size[1] - 10:
                                break
                        dc.DrawText(word, self.Pos.x + 10, y)
                        if not getattr(dc, "printing", False):
                            DrawHighlightedText(dc, word, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(word)), self.Pos.x + 10, y)
                            highlights_offset += len(word) + 1
                    else:
                        linetext = word
                    y += wordheight + 5
            if y + wordheight > self.Pos.y + self.Size[1] - 10:
                break