##
## Filename    : chart_model.py
## Author(s)   : Michel Le Borgne
## Created     : 5/12/2011
## Revision    :
## Source      :
##
## Copyright 2009 - 2010 - 2011 - 2020 : IRISA/IRSET
##
## 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
## 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.  The software and
## documentation provided hereunder is on an "as is" basis, and IRISA has
## no obligations to provide maintenance, support, updates, enhancements
## or modifications.
## In no event shall IRISA be liable to any party for direct, indirect,
## special, incidental or consequential damages, including lost profits,
## arising out of the use of this software and its documentation, even if
## IRISA have been advised of the possibility of such damage.  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.
##
## The original code contained here was initially developed by:
##
##     Michel Le Borgne.
##     IRISA
##     Symbiose team
##     IRISA  Campus de Beaulieu
##     35042 RENNES Cedex, FRANCE
##
## Contributor(s):
##
"""
A drawing style contains all the information to display a chart model and its items.
We use essentially two styles Navigator and Plain corresponding to
the two views displayed on the gui.
Classes available::
    :class:`DrawingStyle`
        :class:`PlainDrawing`: For graph editor
        :class:`NavDrawing`: For overview widget
    :class:`Arrow`: For Transitions
"""
from math import sqrt
from gtk.gdk import color_parse
import pango
[docs]class DrawingStyle:
    """Abstract class for chart model drawing"""
    def __init__(self):
        pass
[docs]    def draw_start(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_trap(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_top_node(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_macro(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_simple(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_perm(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_transition(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        pass 
[docs]    def draw_transition_group(self, tgr, xfv, yfv, wfv, hfv):
        """
        Draw transitions with common extremities
        @param tgr: is a list of transitions with same extremities
        @param xfv,yfv: view coordinates of the macro node
        @param wfv,hfv: affinity ratios father node -> virtual screen
        """
        pass 
        # visitors give virtual size of the node and limits
[docs]    def visit_cstart_node(self, node):
        """
        @return: (v_width, v_height, 0.0)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_ctrap_node(self, node):
        """
        @return: (v_width, v_height, 0.0)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_csimple_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_cperm_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_cmacro_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit, handle_w_limit, handle_h_limit)
        """
        return (0.0, 0.0, 0.0, 0.0, 0.0) 
[docs]    def visit_ctop_node(self, node):
        """
        @return:(0.0, 0.0, 0.0)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_ctransition(self, node):
        """
        @return: (v_w_detect_limit, v_h_detect_limit
        """
        return (0.0, 0.0)  
[docs]class PlainDrawing(DrawingStyle):
    """
    Main drawing style used in main window of charter
    """
    LABEL_H = 22.0
    W_SNODE = 65.0
    H_SNODE = 20.0
    W_INODE = 60.0
    H_INODE = 30.0
    U_SLOPE = H_SNODE / W_SNODE
    NRADIUS = 8
    SNODE_COLOR = "#EDFFEE"
    MNODE_COLOR = "ivory"
    ENODE_COLOR = "#f9fff9"
    PNODE_COLOR = "#fff7ed"
    INODE_COLOR = "green"
    SELECT_COLOR = "red"
    ACTIV_COLOR = "#FF9F7C"
    SEARCH_COLOR = "#DA70D6"
    DELTA = 7.0  # for handler detection
    DIST = 6.0  # distance for transition detection in pixel
    def __init__(self):
        self.view = None
[docs]    def draw_simple(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates in view
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # must be included in macro
        #        wr = min(self.W_SNODE, (xfv - xr+wfv))
        #        her = min(self.H_SNODE, (yfv*hview - yr+hfv))
        wir = self.W_SNODE
        her = self.H_SNODE
        # find colors
        if node.selected:
            line_color = self.SELECT_COLOR
        else:
            line_color = "black"
        if node.activated:
            node_color = self.ACTIV_COLOR
        elif node.search_mark:
            node_color = self.SEARCH_COLOR
        else:
            node_color = self.SNODE_COLOR
        self.draw_node_gen(view, xxr, yyr, wir, her, node.name, node_color, line_color) 
[docs]    def draw_macro(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates in drawing window
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        if node.model.show_macro:
            wir = int(node.wloc * wfv * wview)
            her = int(node.hloc * hfv * hview)
        else:
            #            wir = min(self.W_SNODE, xfv - xr+wfv)
            #            her = min(self.H_SNODE, yfv-yr+hfv)
            wir = self.W_SNODE
            her = self.H_SNODE
        if node.selected:
            line_color = self.SELECT_COLOR
        else:
            line_color = "black"
        if node.model.show_macro:
            name = node.name
        else:
            name = node.name[0:9]
        if node.model.show_macro:
            self.draw_macro_node_gen(view, xxr, yyr, wir, her,
                                     self.LABEL_H, name,
                                     self.MNODE_COLOR, line_color)
        else:
            self.draw_node_gen(
                view, xxr, yyr, wir, her, name, self.MNODE_COLOR, line_color
            ) 
    def draw_start(self, node, xfv, yfv, wfv, hfv):
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview + self.H_SNODE / 2)
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse("black"))
        wid = int(xxr - self.NRADIUS)
        hei = int(yyr - self.NRADIUS)
        pixmap.draw_arc(
            grc, True, wid, hei, 2 * self.NRADIUS, 2 * self.NRADIUS, 0, 360 * 64
        )
    def draw_trap(self, node, xfv, yfv, wfv, hfv):
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse("black"))
        grc.line_width = 2
        wid = int(xxr - self.NRADIUS)
        hei = int(yyr - self.NRADIUS)
        pixmap.draw_arc(
            grc, False, wid, hei, 2 * self.NRADIUS, 2 * self.NRADIUS, 0, 360 * 64
        )
[docs]    def draw_perm(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates in view
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # must be included in macro
        #        wir = min(self.W_SNODE, xfv - xxr+wfv)
        #        her = min(self.H_SNODE, yfv-yyr+hfv)
        wir = self.W_SNODE
        her = self.H_SNODE
        # find colors
        if node.selected:
            line_color = self.SELECT_COLOR
        else:
            line_color = "black"
        if node.activated:
            node_color = self.ACTIV_COLOR
        elif node.search_mark:
            node_color = self.SEARCH_COLOR
        else:
            node_color = self.PNODE_COLOR
        self.draw_node_gen(view, xxr, yyr, wir, her, node.name, node_color, line_color) 
    def draw_input(self, node, xfv, yfv, wfv, hfv):
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview + self.W_SNODE / 2)
        yyr = int((yfv + node.yloc * hfv) * hview + self.H_SNODE / 2)
        # color choice (activated or not)
        if node.activated:
            color = self.ACTIV_COLOR
        else:
            color = self.INODE_COLOR
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        # diamond drawing
        rax = int(self.W_INODE / 2.0)
        ray = int(self.H_INODE / 2.0)
        grc.set_rgb_fg_color(color_parse(color))
        xr1 = xxr - rax
        xr2 = xxr + rax
        yr1 = yyr - ray
        yr2 = yyr + ray
        lpe = [(xr1, yyr), (xxr, yr1), (xr2, yyr), (xxr, yr2)]
        pixmap.draw_polygon(grc, True, lpe)
        if node.selected:
            color = self.SELECT_COLOR
        else:
            color = "black"
        grc.set_rgb_fg_color(color_parse(color))
        grc.line_width = 2
        pixmap.draw_polygon(grc, False, lpe)
        # name
        view.stylepango.set_text(node.name)
        (xps, yps) = view.stylepango.get_pixel_size()
        xxx = xxr - xps / 2.0
        yyy = yyr - yps / 2.0
        pixmap.draw_layout(grc, int(xxx), int(yyy), view.stylepango)
[docs]    def draw_transition(self, trans, x1v, y1v, x2v, y2v, nnn, hor, xfv, yfv):
        """
        @param trans: the transition
        @param x1v, y1v: origin coordinates in virtual screen
        @param x2v, y2v: extremity coordinates in virtual screen
        @param xfv, yfv: father coordinate in virtual screen
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # find colors
        if trans.selected:
            color = self.SELECT_COLOR
        elif trans.search_mark:
            color = self.SEARCH_COLOR
        elif trans.activated:
            color = self.ACTIV_COLOR
        else:
            color = "black"
        label = trans.event + "[" + trans.condition + "]" + trans.action
        # coordinates in view
        xr1 = int((x1v) * wview)
        yr1 = int((y1v) * hview)
        if trans.ori.is_start():
            yr1 = yr1 + int(self.H_SNODE / 2)
        if trans.ori.is_input():
            xr1 = xr1 + int(self.W_SNODE / 2)
            yr1 = yr1 + int(self.H_SNODE / 2)
        xr2 = int((x2v) * wview)
        yr2 = int((y2v) * hview)
        self.draw_edge_gen(view, xr1, yr1, xr2, yr2, hor, nnn, label, color) 
[docs]    def draw_transition_group(self, tgr, xfv, yfv, wfv, hfv):
        """
        Draw transitions with common extremities
        @param tgr: is a list of transitions with same extremities
        @param xfv, yfv: view coordinates of the macro node
        @param wfv, hfv: affinity ratios father node -> virtual screen
        """
        # 1 transition per couple of nodes
        trans = tgr[0]
        ori = trans.ori
        ext = trans.ext
        nbtrans = len(tgr)
        # extremities coordinates in local frame
        (xo1, yo1, gap1, hor1) = ori.intersect(ext, self, nbtrans, wfv, hfv)
        (xe2, ye2, gap2, hor2) = ext.intersect(ori, self, nbtrans, wfv, hfv)
        # change to virtual view coordinates
        x1v = xfv + xo1 * wfv
        y1v = yfv + yo1 * hfv
        if hor1:
            gap1v = gap1 * wfv
        else:
            gap1v = gap1 * hfv
        x2v = xfv + xe2 * wfv
        y2v = yfv + ye2 * hfv
        if hor2:
            gap2v = gap2 * wfv
        else:
            gap2v = gap2 * hfv
        # draw each transition
        cpt = 1
        for trans in tgr:
            if trans.ori == ori:  # same direction as first one
                self.draw_transition(trans, x1v, y1v, x2v, y2v, cpt, hor1, xfv, yfv)
                # record positions in virtual screen for find -
                # position in local coordinate
                trans.ori_coord = (xo1, yo1)
                trans.ext_coord = (xe2, ye2)
            else:  # opposite direction
                self.draw_transition(trans, x2v, y2v, x1v, y1v, cpt, hor1, xfv, yfv)
                # record positions in virtual screen for find
                # position in local frame
                trans.ext_coord = (xo1, yo1)
                trans.ori_coord = (xe2, ye2)
            # next position
            if hor1:
                x1v = x1v + gap1v
                xo1 = xo1 + gap1
            else:
                y1v = y1v + gap1v
                yo1 = yo1 + gap1
            if hor2:
                x2v = x2v + gap2v
                xe2 = xe2 + gap2
            else:
                y2v = y2v + gap2v
                ye2 = ye2 + gap2
            cpt += 1 
    # visitor method
    # visitors give virtual size of the node and limits
    def visit_cstart_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        # 1.1 value is a "magic" value due to the resize of start nodes
        return (
            self.NRADIUS / wid * 1.1,
            self.NRADIUS / hei * 1.1,
            self.H_SNODE / (2 * hei),
        )
    def visit_ctrap_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        return (self.NRADIUS / wid, self.NRADIUS / hei, 0.0)
    def visit_cinput_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        hlabel = self.LABEL_H / hei
        return (self.W_INODE / wid, self.H_INODE / hei, hlabel)
    def visit_csimple_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        return (self.W_SNODE / wid, self.H_SNODE / hei, self.LABEL_H / hei)
    def visit_cperm_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        return (self.W_SNODE / wid, self.H_SNODE / hei, self.LABEL_H / hei)
    def visit_cmacro_node(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        del_x = self.DELTA / wid
        del_y = self.DELTA / hei
        return (
            self.W_SNODE / wid,
            self.H_SNODE / hei,
            self.LABEL_H / hei,
            del_x,
            del_y,
        )
    def visit_ctop_node(self, node):
        return (0.0, 0.0, 0.0)
    def visit_ctransition(self, node):
        wid = float(self.view.draw_width)
        hei = float(self.view.draw_height)
        return (self.DELTA / wid, self.DELTA / hei)
    # generic drawing
[docs]    def draw_macro_node_gen(
        self, view, xxr, yyr, wir, her, hlabel, name, color, line_color
    ):
        """
        Rectangle with round corners
        """
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        cac = pixmap.cairo_create()
        # rounded rectangle
        #   A****BQ
        #  H      C
        #  *      *
        #  G      D
        #   F****E
        col = color_parse(line_color)
        cac.set_line_width(1.0)
        ray = 12
        # Move to A
        cac.move_to(xxr + ray, yyr)
        # Straight line to B
        cac.line_to(xxr + wir - ray, yyr)
        # Curve to C, Control points are both at Q
        cac.curve_to(xxr + wir, yyr, xxr + wir, yyr, xxr + wir, yyr + ray)
        # Move to D
        cac.line_to(xxr + wir, yyr + her - ray)
        # Curve to E
        cac.curve_to(
            xxr + wir, yyr + her, xxr + wir, yyr + her, xxr + wir - ray, yyr + her
        )
        # Line to F
        cac.line_to(xxr + ray, yyr + her)
        # Curve to G
        cac.curve_to(xxr, yyr + her, xxr, yyr + her, xxr, yyr + her - ray)
        # Line to H
        cac.line_to(xxr, yyr + ray)
        # Curve to A
        cac.curve_to(xxr, yyr, xxr, yyr, xxr + ray, yyr)
        #    cac.move_to(xr, yyr+LABEL_H)
        #    cac.line_to(xr+wir, yyr+LABEL_H)
        cac.set_source_rgba(col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0)
        cac.stroke_preserve()
        col = color_parse(color)
        cac.set_source_rgba(col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0)
        cac.fill()
        grc.set_rgb_fg_color(color_parse(line_color))
        pixmap.draw_line(
            grc,
            int(xxr),
            int(yyr + self.LABEL_H),
            int(xxr + wir),
            int(yyr + self.LABEL_H),
        )
        # label
        view.stylepango.set_text(name)
        fod = pango.FontDescription("normal 9")
        view.stylepango.set_font_description(fod)
        (xpl, ypl) = view.stylepango.get_pixel_size()
        # center
        xpos = xxr + wir / 2 - xpl / 2
        ypos = yyr + 4
        pixmap.draw_layout(grc, int(xpos), int(ypos), view.stylepango) 
[docs]    def draw_node_gen(self, view, xxr, yyr, wir, her, name, color, line_color):
        """
        @param xxr,yyr: coordinates in view
        @param wir, her: size in view
        """
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        cac = pixmap.cairo_create()
        # rounded rectangle
        #   A****BQ
        #  H      C
        #  *      *
        #  G      D
        #   F****E
        col = color_parse(line_color)
        cac.set_line_width(1.5)
        ray = 7
        # Move to A
        cac.move_to(xxr + ray, yyr)
        # Straight line to B
        cac.line_to(xxr + wir - ray, yyr)
        # Curve to C, Control points are both at Q
        cac.curve_to(xxr + wir, yyr, xxr + wir, yyr, xxr + wir, yyr + ray)
        # Move to D
        cac.line_to(xxr + wir, yyr + her - ray)
        # Curve to E
        cac.curve_to(
            xxr + wir, yyr + her, xxr + wir, yyr + her, xxr + wir - ray, yyr + her
        )
        # Line to F
        cac.line_to(xxr + ray, yyr + her)
        # Curve to G
        cac.curve_to(xxr, yyr + her, xxr, yyr + her, xxr, yyr + her - ray)
        # Line to H
        cac.line_to(xxr, yyr + ray)
        # Curve to A
        cac.curve_to(xxr, yyr, xxr, yyr, xxr + ray, yyr)
        cac.set_source_rgba(col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0)
        cac.stroke_preserve()
        col = color_parse(color)
        cac.set_source_rgba(col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0)
        cac.fill()
        # label
        view.stylepango.set_text(name[:9])
        fod = pango.FontDescription("normal 9")
        view.stylepango.set_font_description(fod)
        (xpl, ypl) = view.stylepango.get_pixel_size()
        # center
        xpos = xxr + wir / 2 - xpl / 2
        ypos = yyr + 4
        pixmap.draw_layout(grc, int(xpos), int(ypos), view.stylepango) 
[docs]    def draw_edge_gen(self, view, xr1, yr1, xr2, yr2, hor, nnn, label, color):
        """
        draw an edge
        """
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse(color))
        # line
        pixmap.draw_line(grc, xr1, yr1, xr2, yr2)
        # arrow
        vux = xr2 - xr1
        vuy = yr2 - yr1
        norm = sqrt(vux ** 2 + vuy ** 2)
        if norm != 0:
            vux = vux / norm
            vuy = vuy / norm
            arr = Arrow()
            arr.draw(view, (vux, vuy), (xr2, yr2))
        # label
        if len(label) > 20:
            label1 = label[0:18] + "..."
        else:
            label1 = label
        old_fd = view.stylepango.get_font_description()
        fod = pango.FontDescription("normal 9")
        view.stylepango.set_font_description(fod)
        view.stylepango.set_text(label1)
        (wpl, hpl) = view.stylepango.get_pixel_size()
        # draw label if enough room
        if wpl < abs(xr2 - xr1) or 2 * hpl < abs(yr2 - yr1):
            if not hor:
                xxl = (xr1 + xr2) / 2 - wpl / 2
                yyl = (yr1 + yr2) / 2 - hpl + 2
                pixmap.draw_layout(grc, xxl, yyl, view.stylepango)
            else:
                xxl = (nnn * xr1 + xr2) / (nnn + 1) - wpl / 2
                yyl = (nnn * yr1 + yr2) / (nnn + 1)
                pixmap.draw_layout(grc, xxl, yyl, view.stylepango)
        view.stylepango.set_font_description(old_fd)  
[docs]class NavDrawing(DrawingStyle):
    """Drawing style for the overview widget"""
    W_SNODE = 8.0
    H_SNODE = 6.0
    W_INODE = 6.0
    H_INODE = 3.0
    U_SLOPE = H_SNODE / W_SNODE
    NRADIUS = 3
    SNODE_COLOR = "#EDFFEE"
    MNODE_COLOR = "ivory"
    ENODE_COLOR = "#f9fff9"
    PNODE_COLOR = "#FFC1C1"  # "#ffabab"
    INODE_COLOR = "green"
    TNODE_COLOR = "black"
    StNODE_COLOR = "blue"
    SELECT_COLOR = "red"
    ACTIV_COLOR = "#FF9F7C"
    SEARCH_COLOR = "#CB1DC4"
    DELTA = 7.0  # for handler detection
    DIST = 6.0  # distance for transition detection in pixel
    def __init__(self):
        self.view = None
        pass
    def draw_top_node(self, node, xfv, yfv, wfv, hfv):
        pass
[docs]    def draw_macro(self, node, xfv, yfv, wfv, hfv):
        """
        @param xfv: x coordinate of father in virtual view
        @param yfv: y coordinate of father in virtual view
        @param wfv: width of father in virtual view
        @param hfv: height of father in virtual view
        """
        if not node.model.show_macro:
            self.draw_rectangle(node, xfv, yfv, wfv, hfv, True, self.MNODE_COLOR)
            return
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse(self.MNODE_COLOR))
        wid = int(node.wloc * wfv * wview)
        hei = int(node.hloc * hfv * hview)
        pixmap.draw_rectangle(grc, True, xxr, yyr, wid, hei)
        grc.set_rgb_fg_color(color_parse("black"))
        pixmap.draw_rectangle(grc, False, xxr, yyr, wid, hei) 
    def draw_start(self, node, xfv, yfv, wfv, hfv):
        self.draw_circle(node, xfv, yfv, wfv, hfv, True, self.StNODE_COLOR)
    def draw_trap(self, node, xfv, yfv, wfv, hfv):
        self.draw_circle(node, xfv, yfv, wfv, hfv, True, self.TNODE_COLOR)
    def draw_simple(self, node, xfv, yfv, wfv, hfv):
        self.draw_rectangle(node, xfv, yfv, wfv, hfv, True, self.SNODE_COLOR)
    def draw_perm(self, node, xfv, yfv, wfv, hfv):
        self.draw_rectangle(node, xfv, yfv, wfv, hfv, True, self.PNODE_COLOR)
    def draw_input(self, node, xfv, yfv, wfv, hfv):
        self.draw_circle(node, xfv, yfv, wfv, hfv, True, self.INODE_COLOR)
    def draw_transition(self, node, xfv, yfv, wfv, hfv):
        pass
[docs]    def draw_transition_group(self, tgr, xfv, yfv, wfv, hfv):
        """
        Draw transitions with common extremities
        @param tgr: is a list of transitions with same extremities
        @param xfv,yfv: view coordinates of the macro node
        @param wfv,hfv: affinity ratios father node -> virtual screen
        """
        trans = tgr[0]
        ori = trans.ori
        ext = trans.ext
        # change to virtual view coordinates
        x1v = xfv + ori.xloc * wfv
        y1v = yfv + ori.yloc * hfv
        x2v = xfv + ext.xloc * wfv
        y2v = yfv + ext.yloc * hfv
        view = self.view
        v_size = ori.accept(self)
        if ori.is_macro():
            if ori.model.show_macro:
                xcr = v_size[0] * wfv * view.draw_width
                ycr = v_size[1] * hfv * view.draw_height
            else:
                xcr = (self.W_SNODE / 2.0) / view.draw_width
                ycr = (self.H_SNODE / 2.0) / view.draw_height
        else:
            xcr = v_size[0]
            ycr = v_size[1]
        x1r = int(x1v * view.draw_width + xcr)
        y1r = int(y1v * view.draw_height + ycr)
        v_size = ext.accept(self)
        if ext.is_macro():
            if ext.model.show_macro:
                xcr = v_size[0] * wfv * view.draw_width
                ycr = v_size[1] * hfv * view.draw_height
            else:
                xcr = (self.W_SNODE / 2.0) / view.draw_width
                ycr = (self.H_SNODE / 2.0) / view.draw_height
        else:
            xcr = v_size[0]
            ycr = v_size[1]
        x2r = int(x2v * view.draw_width + xcr)
        y2r = int(y2v * view.draw_height + ycr)
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse("black"))
        # line
        pixmap.draw_line(grc, x1r, y1r, x2r, y2r) 
[docs]    def draw_circle(self, node, xfv, yfv, wfv, hfv, fill, color):
        """
        As it says
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse(color))
        wid = int(xxr - self.NRADIUS)
        hei = int(yyr - self.NRADIUS)
        pixmap.draw_arc(
            grc, True, wid, hei, 2 * self.NRADIUS, 2 * self.NRADIUS, 0, 360 * 64
        )
        grc.set_rgb_fg_color(color_parse("black"))
        pixmap.draw_arc(
            grc, False, wid, hei, 2 * self.NRADIUS, 2 * self.NRADIUS, 0, 360 * 64
        ) 
[docs]    def draw_rectangle(self, node, xfv, yfv, wfv, hfv, fill, color):
        """
        As it says
        """
        view = self.view
        wview = view.draw_width
        hview = view.draw_height
        # coordinates
        xxr = int((xfv + node.xloc * wfv) * wview)
        yyr = int((yfv + node.yloc * hfv) * hview)
        # color
        if node.activated:
            color = self.ACTIV_COLOR
        elif node.search_mark:
            color = self.SEARCH_COLOR
        # graphic context
        pixmap = view.pixmap
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse(color))
        wid = int(self.W_SNODE)
        hei = int(self.H_SNODE)
        pixmap.draw_rectangle(grc, True, xxr, yyr, wid, hei)
        grc.set_rgb_fg_color(color_parse("black"))
        pixmap.draw_rectangle(grc, False, xxr, yyr, wid, hei) 
[docs]    def visit_cstart_node(self, node):
        """
        @return: (v_width, v_height, 0.0)
        """
        return (self.NRADIUS / 2, self.NRADIUS / 2, 0.0) 
[docs]    def visit_ctrap_node(self, node):
        """
        @return: (v_width, v_height, 0.0)
        """
        return (self.NRADIUS / 2.0, self.NRADIUS / 2.0, 0.0) 
[docs]    def visit_csimple_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit)
        """
        return (self.W_SNODE / 2, self.H_SNODE / 2, 0.0) 
[docs]    def visit_cperm_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit)
        """
        return (self.W_SNODE / 2, self.H_SNODE / 2, 0.0) 
[docs]    def visit_cmacro_node(self, node):
        """
        @return: (v_width, v_height, v_h_top_limit, handle_w_limit, handle_h_limit)
        """
        return (node.wloc / 2.0, node.hloc / 2.0, 0.0, 0.0, 0.0) 
[docs]    def visit_ctop_node(self, node):
        """
        @return:(0.0, 0.0, 0.0)
        """
        return (0.0, 0.0, 0.0) 
[docs]    def visit_ctransition(self, node):
        """
        @return: (v_w_detect_limit, v_h_detect_limit
        """
        return (0.0, 0.0)  
[docs]class Arrow:
    """Generic arrow (Transition item)"""
    ALENGTH = 10
    SAANGLE = 0.342020
    CAANGLE = 0.939693
    ARROWCOLOR = "black"
    def __init__(self):
        pass
[docs]    def draw(self, view, direct, pos):
        """
        draw the arrow
        """
        pixmap = view.pixmap
        vux = -direct[0]
        vuy = -direct[1]
        poly = [(int(pos[0]), int(pos[1]))]
        xx0 = pos[0]
        yy0 = pos[1]
        u1x = vux * self.CAANGLE - vuy * self.SAANGLE
        u1y = vux * self.SAANGLE + vuy * self.CAANGLE
        xxx = int(xx0 + self.ALENGTH * 1.2 * u1x)
        yyy = int(yy0 + self.ALENGTH * 1.2 * u1y)
        poly.append((xxx, yyy))
        xxx = int(xx0 + self.ALENGTH * vux)
        yyy = int(yy0 + self.ALENGTH * vuy)
        poly.append((xxx, yyy))
        u1x = vux * self.CAANGLE + vuy * self.SAANGLE
        u1y = vuy * self.CAANGLE - vux * self.SAANGLE
        xxx = int(xx0 + self.ALENGTH * 1.2 * u1x)
        yyy = int(yy0 + self.ALENGTH * 1.2 * u1y)
        poly.append((xxx, yyy))
        grc = view.window.new_gc()
        grc.set_rgb_fg_color(color_parse(self.ARROWCOLOR))
        pixmap.draw_polygon(grc, True, poly)