Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /home/ift/52_procpy/dataninja/modeler/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //home/ift/52_procpy/dataninja/modeler/block.py

#   coding: utf8

import os,sys,re,time

import PyQt5
# import arrow
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui  import *

import procpy
import procpy.config

#************************************************************************

class Block (object):                          #   REQ-009:REQ-009

    def __init__ (self,canvas,bbox,shape="r",looping=0):
    
        self.canvas     = canvas 
        self.bbox       = bbox[:]
        self.color      = procpy.config.COLOR_BLOCK
        self.arrows_in  = []                   #  arrows coming in
        self.arrows_out = []                   #  arrows going out
        self.nr         = 0                    #  the level of the block respecting the ancestor series
        self.elems      = {}                   #  actual elems of the block (rectangle, triangle, rhombe, circle ...)
        self.fontsize   = 8
        self.exit       = ""                   # defines where arrows should come out of the block
        self.entrance   = ""                   # defines where arrows should enter the block
        self.looping    = looping        
        self.last_event = 0
        self.os         = sys.platform[0].lower()

        self.generate_shape(shape)
        self.format_shape()
        self.mouse_moves = 0

#************************************************************************

    def dump (self):

        try:
            return [self.bbox,self.shape, self.looping, self.content.get("1.0","end-1c")]
        except:
            return [self.bbox,self.shape, self.looping]
            
#************************************************************************

    def bounding_box (self):
    
        try:
            c1 = self.source_block.elems['SHAPE'].boundingRect().getRect()
        except:
            c1 = self.source_block.elems['SHAPE'].geometry().getRect()

#************************************************************************

    def box_coord (self):

        x0 = self.bbox[0]
        y0 = self.bbox[1]
        x1 = self.bbox[2]
        y1 = self.bbox[3]

        x  = 0.5 * (x0 + x1)
        y  = 0.5 * (y0 + y1)
        w  = x1 - x0
        h  = y1 - y0
        
        return(x0,y0,x1,y1,x,y,w,h)
        
#************************************************************************

    def contains (self,x0,y0,x1,y1):
    
        for point in ( (x0,y0),(x0,y1),(x1,y0),(x1,y1) ):
        
            x = point[0]
            y = point[1]

            if self.bbox[0] < x and x < self.bbox[2]:
                if self.bbox[1] < y and y < self.bbox[3]:
                    return(True)
                
        return(False)
        
#************************************************************************

    #   Computes order numbers and path levels (series of branches in which the process runs)

    def compute_level (self,touched_blocks,branch_counter):
    
        predecessor_blocks = []
        self.level         = []
        
        reduce_in  = set(self.arrows_in)  - self.canvas.loop_arrows  #  do not consider formerly identified
        reduce_out = set(self.arrows_out) - self.canvas.loop_arrows  #  loop-forming arrows
        
        if not self.shape == "r" and self.determine_shape() == "r":  #  do not change control shape to a rectngular shape
            return(False)

        if len(reduce_out) > 1 and len(reduce_in) == 0:  #  to avoid that a sequenze starts with a split
            return(False)

        for arrow in reduce_in:            #  all predecessor blocks have to be numbered, otherwise return
            if arrow.source_block.nr == 0: #  This has the impact, that in a backward connection case
                return(True)               #  the blocks cannot be numbered -> they are in a loop 

        for arrow in reduce_in:   #   run through all arrows going into the block
            self.nr    = max(arrow.source_block.nr,self.nr)  #  compute the maximal order number of all predecessor blocks
            self.level = arrow.source_block.level[:]         #  first assumption: path level should be the same as predecessor block
#            print ("PRE",arrow.source_block.nr,arrow.source_block.level)
            if not self.level == []:                                  #  we store in the array predecessor_blocks all
                predecessor_blocks.append("-".join(self.level[:-1]))  #  former paths without the last level. If that
            else:                                                     #  'upper' paths are alle the same, then the block
                predecessor_blocks.append(".NULL.")                   #  is properly joined to its predecessors
        touched_blocks.append(self)

        self.nr = self.nr + 1          #  now increase the block order

        if len(predecessor_blocks) > 1:             #   if there are more than one predecessor, then there is
            if len(set(predecessor_blocks)) == 1:   #   a re-unification of branches, and the level goes up
                self.level.pop()                    #   but only if all predecessors have the same upper path
            else:
               return(False)                        #   otherwise it is wrong joined
               
        if branch_counter:                          #   if there is a branch_counter given (aftera split)
            self.level.append(str(branch_counter))  #   then add it to the path level (split off branches)
        
        if len(self.level) == 0:       #   condition to make sure that a join allways is induced by a split
            return(False)

        if len(reduce_out) > 1:        #  if the are more than one successors,
            branch_counter = 1         #   then they will be enumerated and all of them are put to sub-paths
        else:
            branch_counter = None

        for arrow in reduce_out:
            block = arrow.target_block
            if block in touched_blocks:
                continue
            if not block.compute_level(touched_blocks,branch_counter):
                return(False)
            if branch_counter:
                branch_counter = branch_counter + 1

#        self.canvas.create_text(self.bbox[0],self.bbox[1]-12,anchor=tkinter.NW,         #   only for debugging
#                             text=str(self.nr)+"/"+"-".join(self.level),tag="debug")

        return(True)

#************************************************************************

    def clear_shape (self):
    
        self.canvas.delete(self.elems)
            
#        if 'content' in vars(self):
#            self.content_backup = self.content.get("1.0",tkinter.END)
#            self.content.place_forget()
#            del self.content
#            del self.contentframe
#            self.canvas.delete(self.window)
#            del self.window

#************************************************************************

    def destroy (self, destroy_arrows=True):
    
        if destroy_arrows:

            blocks_to_reshape = []

            for arrow in ( set(self.arrows_in) | set(self.arrows_out) ):
                blocks_to_reshape.append(arrow.source_block)
                arrow.destroy()

            for block in blocks_to_reshape:
                block.fit_shape()
            
        self.clear_shape()

#************************************************************************

    def determine_shape (self):   # this function determines the shape of the block
                                  # up to now, it takes in only the arrows to and from the block
                                  # later, further arguments or algorrithm for determination fo the shape 
                                  # can be added

        shape = { "00": "r",   #  the numbers give the in and out arrows
                  "01": "c",   #  c
                  "02": "r",   #  c
                  "10": "r",   #  e
                  "11": "r",
                  "12": "ifm",
                  "20": "w",   #  e
                  "21": "ws", 
                  "22": "s" } [  str(min( len(set(self.arrows_in)  - self.canvas.loop_arrows) ,2)) + 
                                 str(min( len(set(self.arrows_out) - self.canvas.loop_arrows) ,2))  ]
                  
        if shape == "ifm":   #  split off paths: shape depends on splitting case constraints
            shape         = "i"
            wertebereiche = []
            for arrow in self.arrows_out:
                text = ""    #   arrow.text.get("1.0")
                m    = re.search(r"(\d+?),?(\d*)",text)
                if m:
                    try:
                        val   = [int(m.group(1)),int(m.group(2))]
                        shape = "f"
                    except:
                        val   = [int(m.group(1)),int(m.group(1))]
                    for val1 in wertebereiche:
                        if val1[1] > val[0] or val[1] > val1[0]:
                            shape = "m"
                            break
                    if shape == "m":
                        break                        
                    wertebereiche.append(val)

        if shape == "ws":
            shape = "w"

        return(shape)

#***********************************************************************

    def fit_shape (self,mode=0):               # checks whether correct shape
                                               # mode 0: make new only if shape has changed
        shape = self.determine_shape()
        
        if shape != self.shape or mode == 1:   # if shape (or location) has changed
            
            self.destroy(destroy_arrows=False) # clear old shape
            self.generate_shape(shape)         # create new shape
            self.format_shape()                # format the shape        
        
#************************************************************************

    def generate_shape (self,shape):


        '''
        Shapes:
        
        r: Normale rectangle block
        s: Subprocess (rectangle)
        i: Case switch (diamond with X)
        f: Forking block (diamond with cross)
        m: Multiple switch (diamond with star)
        w: Wait Block (diamond with circle)
        c: Start Block (circle)
        e: End Block (circle)
        a: Swim lane
        
        After defining the shape, the proper measures of the shape and the location
        have to be done with  format_shape()
        '''

        if shape in "r":  #  Rectangle, normal block, r: rectangle, s: swimlane

            self.elems = { "TEXT" : QTextEdit() }
            pl         = QPalette()
            pl         = self.elems['TEXT'].palette()
            pl.setBrush(QPalette.Base, QBrush(QColor(self.color)))
            self.elems['TEXT'].setPalette(pl)
            self.elems['SHAPE'] = self.canvas.addWidget( self.elems['TEXT'] )
            self.elems['TEXT'].setVisible(True)
            self.elems['TEXT'].setMinimumSize(5,5)
            self.elems['TEXT'].mouseDoubleClickEvent = lambda event: self.elems['TEXT'].mousePressEvent(event)


#            self.elems        = { "SHAPE": self.canvas.create_rectangle(0,0,0,0,fill=self.color) }
#            self.contentframe =  tkinter.Frame(self.canvas,background=self.color,bg=self.color,bd=0)
#            self.content      =  tkinter.Text(self.contentframe,wrap="none",font=("Courier",int(self.fontsize)),
#                                   borderwidth=0,insertborderwidth=0,bg=self.color,highlightcolor=self.color)
#            (x0,y0,x1,y1,x,y,w,h) = self.box_coord()
#            self.window       = self.canvas.create_window(x,y,window=self.contentframe)
#            self.contentframe.grid_propagate(False)
#            self.content.config(state=tkinter.DISABLED)

        elif shape in "s":

            self.elems = { "SHAPE" : QGraphicsRectItem() }
            self.canvas.addItem(self.elems['SHAPE'])
            self.elems['SHAPE'].setBrush(PyQt5.QtGui.QColor(self.color))
            self.elems['SHAPE'].setVisible(True)

#            self.elems['EXPAND'] = self.canvas.create_rectangle(0,0,0,0,width=1)
#            self.elems['X1']     = self.canvas.create_line(0,0,0,0,fill='black',width=1)
#            self.elems['X2']     = self.canvas.create_line(0,0,0,0,fill='black',width=1)

        elif shape in "iwfm":    #   Diamond, i: X inside, f: + inside (fork), m: * inside (multi) 

            self.elems = { "SHAPE" : QGraphicsPolygonItem() }
            self.canvas.addItem(self.elems['SHAPE'])
            self.elems['SHAPE'].setBrush(PyQt5.QtGui.QColor(self.color))
            
            pen = QPen(Qt.black,2,Qt.SolidLine)
            if shape in "im":        

                self.elems['CROSS1'] = QGraphicsLineItem()
                self.canvas.addItem(self.elems['CROSS1'])
                self.elems['CROSS1'].setPen(pen)
                self.elems['CROSS1'].setVisible(True)

                self.elems['CROSS2'] = QGraphicsLineItem()
                self.canvas.addItem(self.elems['CROSS2'])
                self.elems['CROSS2'].setPen(pen)
                self.elems['CROSS2'].setVisible(True)

            if shape in "fm":        

                self.elems['X1'] = QGraphicsLineItem()
                self.canvas.addItem(self.elems['X1'])
                self.elems['X1'].setPen(pen)
                self.elems['X1'].setVisible(True)

                self.elems['X2'] = QGraphicsLineItem()
                self.canvas.addItem(self.elems['X2'])
                self.elems['X2'].setPen(pen)
                self.elems['X2'].setVisible(True)

            if shape in "w":        

                self.elems['CIRCLE'] = QGraphicsEllipseItem()
                self.canvas.addItem(self.elems['CIRCLE'])
                self.elems['CIRCLE'].setPen(pen)
                self.elems['CIRCLE'].setVisible(True)
            

        elif shape in "ce":  #  Circle

            self.elems = {"SHAPE": QGraphicsEllipseItem()}
            self.canvas.addItem(self.elems['SHAPE'])
            self.elems['SHAPE'].setBrush(PyQt5.QtGui.QColor(self.color))
            pen = QPen(Qt.red, 2, Qt.SolidLine)
            if shape in "e":
                self.elems['CIRCLE'] = QGraphicsEllipseItem()
                self.canvas.addItem(self.elems['CIRCLE'])
                self.elems['CIRCLE'].setPen(pen)
                self.elems['CIRCLE'].setVisible(True)

                    
        elif shape in "t":  #  Triangle
            self.elems = { "SHAPE":  self.canvas.create_polygon(0,0,0,0,0,0,fill=self.color,outline='black') }
            self.content = tkinter.Text(self.canvas,wrap="none",bg=self.color,highlightcolor=self.color,
                                                borderwidth=0,insertborderwidth=0)
            self.content.config(state=tkinter.DISABLED)
            self.content.config(font=('Courier',int(self.fontsize)))

        if 'content' in vars(self) and 'content_backup' in vars(self) and not self.content_backup.isspace():
            if self.content.cget("state")==tkinter.DISABLED:
                self.content.config(state=tkinter.NORMAL)
                self.content.insert("1.0",self.content_backup.strip())     # text can be inserted only in NORMAL mode!!!
                self.content.config(state=tkinter.DISABLED)
            else:
                self.content.insert(tkinter.END,self.content_backup)     # text can be inserted only in NORMAL mode!!!

        self.elem_bindings()
        self.shape = shape

#        self.define_exit_and_enter()         # define where arrows should exit and enter

#************************************************************************

    def elem_bindings (self):

        for elem_key in list(self.elems.keys()):
            elem = self.elems[elem_key]
            elem.mousePressEvent   = lambda event: self.press_button_event(elem,event)
            elem.mouseReleaseEvent = lambda event: self.release_button_event(elem,event)
            elem.mouseMoveEvent    = lambda event: self.move_mouse_event(elem,event)

#************************************************************************

    def syntax_error (self):

        try:    # some widgets dont contain text
            text = self.content.get("1.0",tkinter.END)
        except: # these widgets are "ok" anyway           
            return(0)

        try:    # if they do contain text
            exec ("\n" + text + "\n")
        except Exception as e: # return ok only if it is working code
            if re.search("invalid syntax",str(e)):
                return(1)
        return(0)
        
#************************************************************************

    def format_shape (self):
    
        (x0,y0,x1,y1,x,y,w,h) = self.box_coord()
        
        if self.shape in "iwfmce":
            if self.shape in "iwfmce":              #  reduce the bounding box to a square
                w = 40 * self.canvas.zoom_factor    #  always same size for diamonds
            else:                                   #  because diamond shapes shall be always rectangular,
                w = min(w,h)
            h = w                                   #  and circles should be circles, not ovals
            self.oldbbox = self.bbox                # save old size for reusing if square needs to be changed back
            self.bbox  = [x-w/2,y-h/2 ,x+w/2,y+h/2] # bbox must be changed, otherwise problems with arrow length
                                                   
        if self.shape in "rs":
            if 'oldbbox' in vars(self):
                self.bbox = self.oldbbox
                del self.oldbbox
            try:
                self.elems['SHAPE'].setRect(x0,y0,w,h)
            except:
                self.elems['TEXT'].setGeometry(x0,y0,w,h)

#            self.canvas.coords(self.elems['SHAPE'],*self.bbox)               
#            self.contentframe.config(width=int(0.9*w),height=int(0.9*h))
#            self.content.config(font=("Courier",int(self.fontsize)))
#            self.content.place(x=0,y=0,width=int(0.9*w),height=int(0.9*h))
#            (x9,y9) = self.canvas.coords(self.window)
#            self.canvas.move(self.window,x-x9,y-y9)
#            if self.shape == "s":
#                self.canvas.coords(self.elems['EXPAND'], x-0.09*w,y+0.32*h,x+0.09*w,y+0.5*h)
#                self.canvas.coords(self.elems['X1'],     x-0.09*w,y+0.32*h,x+0.09*w,y+0.32*h)
#                self.canvas.coords(self.elems['X2'],     x,y+0.4*h,x,y+0.5*h)

        elif self.shape in "iwfm":

            diamond = QPolygonF()
            diamond.append( QPointF(x-w/2,y) )
            diamond.append( QPointF(x,y+h/2) )
            diamond.append( QPointF(x+w/2,y) )
            diamond.append( QPointF(x,y-h/2) )
            self.elems['SHAPE'].setPolygon( diamond ) 
               
            if self.shape in "fm":
                self.elems['X1'].setLine(     x-0.17*w,y-0.17*h,   x+0.17*w,y+0.17*h)
                self.elems['X2'].setLine(     x+0.17*w,y-0.17*h,   x-0.17*w,y+0.17*h)
            if self.shape in "im":
                self.elems['CROSS1'].setLine( x-0.2404*w,y,        x+0.2404*w,y)
                self.elems['CROSS2'].setLine( x,y-0.2404*h,        x,y+0.2404*h)
            if self.shape == "w":
                self.elems['CIRCLE'].setRect( x-0.19*w,y-0.19*h,   w*2*0.19,h*2*0.19)

        elif self.shape in "ce":
            
            self.elems['SHAPE'].setRect(x0,y0,w,h) 
        

#            if 'oldbbox' in vars(self):    #   code from Xueshang ...   ???
#                self.bbox = self.oldbbox
#                del self.oldbbox
#            try:
#                #self.elems['SHAPE'].setRect(x0+20, y0+25, w, h)
#                if procpy.config.ARROWDIRECTION == "NS":
#                    x0=x-w/2
#                    if x0<=0:
#                        x0=0
#                    self.elems['SHAPE'].setRect(x0, y,  w, h )
#                elif procpy.config.ARROWDIRECTION == "EW":
#                    y0=y-w/2
#                    if y0<=0:
#                        y0=0
#                    self.elems['SHAPE'].setRect(x, y0, w, h)
#            except:
#                #self.elems['TEXT'].setGeometry(x0+20, y0+25, w, h)
#                if procpy.config.ARROWDIRECTION == "NS":
#                    x0 = x - w / 2
#                    if x0 <= 0:
#                        x0 = 0
#                    self.elems['TEXT'].setRect(x - w / 2, y, w, h)
#                elif procpy.config.ARROWDIRECTION == "EW":
#                    y0 = y - w / 2
#                    if y0 <= 0:
#                        y0 = 0
#                    self.elems['TEXT'].setRect(x, y-w/2, w, h)
#            #if self.shape == "e":
#                #self.elems['CIRCLE'].setRect(x - 0.1 * w, y - 0.1 * h, w * 2 * 0.1, h * 2 * 0.1)


        elif self.shape in "t":

            self.canvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[3],x,self.bbox[1],self.bbox[2],self.bbox[3])               
            self.content.place(x=int(x-0.28*w),y=int(y1-0.56*h),width=int(0.56*w),height=int(0.56*h))


        for arrow in self.arrows_in + self.arrows_out:
            arrow.fit_block_connections()
            arrow.fit_description()

#**************************************************************************
# fix the code exit and enter by nxs 2018-04-04, email:xsnai@yahoo.com
    def define_exit_and_enter(self, exit="", entrance=""):  # SHAPE-001 PLANNED 
         # defines where arrows enter and exit the block depending on its shape

        if procpy.config.ARROWDIRECTION =="NS":
            if entrance:
                self.entrance = entrance
            elif self.shape in "rsifm": # rectangle, subprocess, case switch, forking block, multiple switch
                self.entrance = "n"   # arrows enter west   ,self.entrance = "w"
            elif self.shape in "w":   # wait block
                self.entrance = "wn" # arrows enter north ,west , self.entrance = "wns"

            if exit:
                self.exit = exit
            elif self.shape in "rswce":
                self.exit = "s"       # arrows exit east, self.exit = "e"
            elif self.shape in "fim":
                self.exit = "nse"     # arrows exit north and south and east , self.exit = "nse"
                                  # ENDREQ

        if procpy.config.ARROWDIRECTION == "EW":
            if entrance:
                self.entrance = entrance
            elif self.shape in "rsifm":  # rectangle, subprocess, case switch, forking block, multiple switch
                self.entrance = "w"  # arrows enter west   ,self.entrance = "w"
            elif self.shape in "w":  # wait block
                self.entrance = "wns"  # arrows enter north and south and west, self.entrance = "wns"

            if exit:
                self.exit = exit
            elif self.shape in "rsce":
                self.exit = "e"  # arrows exit east, self.exit = "e"
            elif self.shape in "fim":
                self.exit = "nse"  # arrows exit north and south and east , self.exit = "nse"
            # ENDREQ

###nxs
#************************************************************************
#$$$$$
    def get_point (self,elem,event):
    
        try:
            px = event.scenePos().x()
            py = event.scenePos().y()
        except:
            parent_point = elem.mapToParent(QPoint(event.x(),event.y()))
            px = parent_point.x()
            py = parent_point.y()

        return(px,py)    

#************************************************************************

    def press_button_event (self,elem,event):

        (px,py) = self.get_point(elem,event)
        self.press_button(px,py)
        
#************************************************************************

    def press_button (self,px,py):

        if self.os == "w":
            timeclock = time.clock()
        else:
            timeclock = time.time()

        if self.last_event == 1 and timeclock - self.last_pressed < procpy.config.DOUBLECLICK:
            self.last_event   = 2
            self.last_pressed = -100
            return()


#        self.canvas.configure(cursor='sizing')    #  Alternative mouse pointers: arrow circle 
                                                  #  clock cross dotbox exchange fleur heart
        self.resize_box            = ""           #  man mouse pirate plus shuttle sizing spider
                                                  #  spraycan star target tcross trek watch

        (x0,y0,x1,y1,x,y,w,h)      = self.box_coord()

        if self.shape in "rs":

            if abs(y1 - py) < 9:            #  if pressing point is near the border of the box
                 self.resize_box = self.resize_box + "S"    #  mark the direction for resizing
            if abs(y0 - py) < 9: 
                 self.resize_box = self.resize_box + "N"
            if abs(x0 - px) < 9:
                 self.resize_box = self.resize_box + "W"
            if abs(x1 - px) < 9: 
                 self.resize_box = self.resize_box + "E"

        self.move_blocks  = []
        self.move_arrowpoints = []
        self.last_event   = 1
        self.mouse_moves  = 0
        self.last_pressed = timeclock

#*************************************************************************
#$$$$$
    def move_mouse_event (self,elem,event):

        (px,py) = self.get_point(elem,event)

        if self.mouse_moves == 0:
            self.x_moved = px
            self.y_moved = py
        
        dx = px - self.x_moved
        dy = py - self.y_moved
            
        if self.resize_box == "":
            self.move(dx,dy)
        else:
            self.resize(dx,dy)
              
        self.x_moved = px
        self.y_moved = py

        self.mouse_moves = self.mouse_moves + 1
        print ("MOVE_THE_MOUSE",self.mouse_moves)
        if self.mouse_moves > 8:  #  to avoid that small mouse movings hinder double clicks
            self.last_event = 3

#************************************************************************

    def move (self,dx,dy):

        self.bbox[0] = self.bbox[0] + dx
        self.bbox[1] = self.bbox[1] + dy
        self.bbox[2] = self.bbox[2] + dx
        self.bbox[3] = self.bbox[3] + dy
        self.format_shape()
#        self.canvas.rearr_grid(0.2)

#************************************************************************

    def resize (self,dx,dy):

        if   "N" in self.resize_box:
            self.bbox[1] = self.bbox[1] + dy
        elif "S" in self.resize_box:
            self.bbox[3] = self.bbox[3] + dy
        if   "W" in self.resize_box:
            self.bbox[0] = self.bbox[0] + dx
        elif "E" in self.resize_box:
            self.bbox[2] = self.bbox[2] + dx

        self.format_shape()
#        self.canvas.rearr_grid(0.2)

        for arrow in self.arrows_in + self.arrows_out:
            arrow.fit_block_connections()
            arrow.fit_description()
	
#************************************************************************

    def scale (self,fac):

        for i in (0,1,2,3):
            self.bbox[i]  = self.bbox[i] * fac
        self.fontsize = self.fontsize * fac

        for arrow in self.arrows_out:
            arrow.scale(fac)
        self.format_shape()

#****************************************************

    def release_button_event (self,elem,event):

        if self.last_event == 2:  # after double click create an arrow from the highlighted block
            self.canvas.create_arrow()
            if not self.canvas.hl_block[0] == self:
                self.canvas.highlight_block(self)
            return(None)

        self.hidden_highlight = False

        if self.canvas.hl_block[0] == self:   # if I am yet highlighted,
            if self.last_event == 3:
                self.canvas.rearr_grid(procpy.config.MOVESPEED)
            return(None)                          # nothing has to be done
        
        if self.canvas.hl_block[0]:  #  if there is a highlighted block:
            if self.canvas.hl_block[0].syntax_error():  # which still has syntax
                return(None)                               # error: do nothing

        self.canvas.highlight_block(self)    #  I set me to the top
                                             #  of the block highlighting listand set it to the last highlighted block, and set myself to highlighted
        self.hl_arrow = None   

        for arrow in self.arrows_in:                           #   now check all incoming arrows. The one where its
            if arrow.source_block == self.canvas.hl_block[1]:  #   source_block is the last highlighted block,
               self.hl_arrow = arrow                          #   has also to be highlighted

        if False and self.hl_arrow == None:   ##  DISABLED         #   check also inverted direction
            for arrow in self.arrows_out:            
                if arrow.target_block == self.canvas.hl_block[1]:  
                    self.hl_arrow = arrow                             
                    
        self.highlight_myself(0)

#                self.content.focus_set()                               # if the block is highlighted, focus on content
#                self.content.mark_set("insert",tkinter.END)            # important if syntax error is given

#        else:    #  last event was a double click
#            self.canvas.create_arrow()
#            self.canvas.rearr_grid(procpy.config.MOVESPEED)
                   
        print (self.last_event)
        if self.last_event == 3:
            self.canvas.rearr_grid(procpy.config.MOVESPEED)

        return (None)


#*****************************************************************************************************

    def highlight_myself (self,nr,mode=1):    #   mode == 1: blinking

        # print("highlight_myself")
        if 'old_color' in vars(self):
            QTimer().singleShot(200,lambda: self.highlight_myself(nr,mode))
            return()

        fillcolor1 = self.color
        if not self.hidden_highlight:  #  block has to be highlighted
            fillcolor1 = [procpy.config.COLOR_HIGHLIGHT_ALT[0],
                          procpy.config.COLOR_HIGHLIGHT_ALT[0],
                          procpy.config.COLOR_HIGHLIGHT[0],
                          procpy.config.COLOR_HIGHLIGHT[0]
                         ][nr+mode]
            self.colorize_block(fillcolor1)

        if self.hl_arrow:   #  arrow also has to be highlighted
            fillcolor2 = [procpy.config.COLOR_HIGHLIGHT[0],
                          self.color,
                          self.color,
                          self.color
                         ][nr+mode]
            # print("arrow also has to be highlighted")
            if len(self.canvas.blocks) < 6:
#                fillcolor2 = fillcolor1
                fillcolor2 = procpy.config.COLOR_HIGHLIGHT[0]
#                 fillcolor2 = 'yellow'
            # print("self.hl_arrow.highlight(fillcolor2)")
            # print("fillcolor2=")
            # print(fillcolor2)
            # self.hl_arrow.highlight(fillcolor2)

            self.hl_arrow.highlight(fillcolor2)
#        else:
#        
#            if 'content' in vars(self):
#                if self.content.cget('state') == tkinter.DISABLED:
#                    self.content.config(state=tkinter.NORMAL)
#                    self.content.focus_set()
#                    self.content.mark_set("insert",tkinter.END)

        if self.canvas.hl_block[0] == self:   #  only if the block has to be highlighted, switch
            delay = 180 + mode*120            #  the delay to certain value > 0, otherwise it is 0
            QTimer().singleShot(delay,lambda: self.highlight_myself(1-nr,2*self.syntax_error()))
#            self.canvas.after(delay,self.highlight_myself,1-nr,2*self.syntax_error())
        else:
        
            if nr == 1:        
                QTimer().singleShot(0,lambda: self.highlight_myself(0,2*self.syntax_error()))
#                self.canvas.after(0,self.highlight_myself,0,2*self.syntax_error())
        
            else:
                self.colorize_block(self.color)   #  chain of calls ends here

                if self.hl_arrow:
                   self.hl_arrow.highlight()
#                    if not self.hl_arrow.text.get("1.0","end-1c"):
#                        self.canvas.itemconfig(self.hl_arrow.window,state=tkinter.HIDDEN)
                else:
                    self.hl_arrow = None
#                        self.hl_arrow.text.config(state=tkinter.DISABLED)
#                         self.hl_arrow = None
#                if 'content' in vars(self):
#                    self.content.config(state=tkinter.DISABLED)
        
#************************************************************************

    def colorize_block (self,color1):


        try:
            self.elems['SHAPE'].setBrush(PyQt5.QtGui.QColor(color1))
        except:
            pl = QPalette()
            pl = self.elems['TEXT'].palette()
            pl.setBrush(QPalette.Base, QBrush(QColor(color1)))
            self.elems['TEXT'].setPalette(pl)
            # self.elems['TEXT'].setStyleSheet("background-color: " + color1)
#        if 'content' in vars(self):
#            self.content.config(bg=color1,highlightcolor=color1)
#            self.contentframe.config(background=color1,bg=color1,highlightcolor=color1)
#        self.canvas.update()
        
    
#************************************************************************

    def mark_as_running (self,mode=0):
    
        if 'old_color' in vars(self) and mode < 1:
            self.colorize_block(self.old_color)
            del self.old_color
        elif not 'old_color' in vars(self) and mode > -1:
            self.old_color = self.canvas.itemcget(self.elems['SHAPE'],'fill')
            self.colorize_block(procpy.config.COLOR_RUNNING)

#************************************************************************

    def get_shape_connection_point (self,p1):

        '''
        Computes the point on the the border of the surrounding shape of the block
        which connects an outer point p to the block
        '''

        (x0,y0,x1,y1,x,y,w,h) = self.box_coord()

        p = [ min(max(x0, p1[0]), x1), min(max(y0, p1[1]), y1) ]
        
        #  p2: touches the bounding box. This basic point has to be extended into the bounding box depending on the shape:


        if not self.shape in "rs":
            p2 = p[:]
            if p2[0] == p1[0]:
                p2[1] = y
            if p2[1] == p1[1]:
                p2[0] = x

        if self.shape in "iwfm":    #   diamond
         
            fac = 1
            if p1[0] < x:
                p3 = [x0,y]
            else:           
                p3 = [x1,y]
                fac = -fac
            if p1[1] < y:
                p4 = [x,y0]
            else:           
                p4 = [x,y1]
                fac = -fac

            try:
                m = ( p4[0] - p2[0] + fac * (p4[1] - p2[1]) ) / ( p1[0] - p2[0] + fac * (p1[1] - p2[1]) ) 
                p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
            except:  # in this case, the outer point points to the closest edge 
                pass

        elif self.shape in "ce":   #   circle
         
            a  = (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2   #  a * m*m - 2 * b * m + c = 0, quadratic equation to solve  
            b  = (p1[0]-p2[0])*(x-p2[0]) + (p1[1]-p2[1])*(y-p2[1])
            c  = (x-p2[0])**2 + (y-p2[1])**2 - ((x0-x1)/2)**2

            try:
                m = ( b + (b**2 - a*c)**0.5 ) / a
                p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
            except:  # in this case, the outer point points to the most west, east, north or south point of the circle 
                pass

        elif self.shape in "t":   #   triangle
         
            if p1[1] > y1:        #   point is underneath the bottom of the triangle.
                pass              #   Nothing to do -> the same behaviour as in rectangle shape mode
            else:
                p3 = [x,y0]
                if p1[0] < x:
                    p4 = [x0,y1]
                else:           
                    p4 = [x1,y1]

                a = (p2[0]-p4[0])*(p3[1]-p4[1]) - (p2[1]-p4[1])*(p3[0]-p4[0]) 
                b = (p2[0]-p1[0])*(p3[1]-p4[1]) - (p2[1]-p1[1])*(p3[0]-p4[0])
            
                try:
                    m = a / b
                    p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
                except:  # in this case, the outer point points to the closest edge 
                    pass
                    
        return(p)

#************************************************************************

    def gridpos (self,i=None,j=None):

#        if 'newbbox' in vars(self):
#            return(tuple(self.ij))
        px = (1-self.canvas.snap['OBJX']) * self.bbox[0] + self.canvas.snap['OBJX'] * self.bbox[2]
        py = (1-self.canvas.snap['OBJY']) * self.bbox[1] + self.canvas.snap['OBJY'] * self.bbox[3]
        if not i:
            i = int((px - self.canvas.griddata['OFFSETX'] + (0.5+self.canvas.snap['GRIDX'])*self.canvas.griddata['X'])/self.canvas.griddata['X'])
            j = int((py - self.canvas.griddata['OFFSETY'] + (0.5+self.canvas.snap['GRIDY'])*self.canvas.griddata['Y'])/self.canvas.griddata['Y'])
        x = self.canvas.griddata['OFFSETX'] + (i + self.canvas.snap['GRIDX']) * self.canvas.griddata['X'] 
        y = self.canvas.griddata['OFFSETY'] + (j + self.canvas.snap['GRIDY']) * self.canvas.griddata['Y']

        self.newbbox = [ self.bbox[0] + (x-px), self.bbox[1] + (y-py), self.bbox[2] + (x-px), self.bbox[3] + (y-py) ]

        self.ij      = [i,j]

        return(i,j)

#**************************************************************************

    def bounding_box (self,extbox=None):
    
        '''
        Returns the bounding box of the block.
        If an optional 4-elem array is given, this
        will be extended to the bounding box.
        ''' 

        if extbox == None:
            extbox = []

        if len(extbox) == 0:
            extbox.append(self.bbox[0])
            extbox.append(self.bbox[1])
            extbox.append(self.bbox[2])
            extbox.append(self.bbox[3])
        else:
            extbox[0] = min(extbox[0],self.bbox[0])
            extbox[1] = min(extbox[1],self.bbox[1])
            extbox[2] = max(extbox[2],self.bbox[2])
            extbox[3] = max(extbox[3],self.bbox[3])

        return(extbox)

#************************************************************************


#************************************************************************
#************************************************************************
#************************************************************************
#************************************************************************
#************************************************************************

class Block1 (object):

    def __init__ (self,mode,frame,obj):
    
        self.mode           = mode                 #  no functionality
        self.frame         = frame                 #  the gui_file-object
        self.graphcanvas    = frame.graphcanvas    #  the canvas
        self.vars           = []                   #  the variables of the block
        self.text_invisible = []                   #  the hiddenvariables
        self.arrows_in      = []                   #  arrows coming in
        self.arrows_out     = []                   #  arrows going out
        self.nr             = 0                    #  the level of the block respecting the ancestor series
        self.elems          = {}                   #  actual elems of the block (rectangle, triangle, rhombe, circle ...)
        self.shape          = "x"                  #  the shape of the block
        self.syntax_error   = 0                    #  indicates whether text insinde block is valid python syntax
        self.fontsize       = self.frame.current_zoom_factor * procpy.config.TEXT_SIZE  #  size of inside text
        self.fkt            = obj._FKT_
        (x,y,w,h)           = obj.BLOCK[1:5]
        self.bbox           = [x-w/2,y-h/2,x+w/2,y+h/2]
        self.color          = procpy.config.COLOR_BLOCK
        self.sync_variables_to_text(obj.__dict__,obj.BLOCK[5:])
        self.text           = re.sub(r"$","",self.text,99999999)
        if len(obj.BLOCK) > 5 and not obj.BLOCK[5] == "":
            self.color = obj.BLOCK[5]

        m = re.search(r"^([A-Za-z]+)([0]+)$",self.fkt)   #   choose a new name
        if m:
            zaehler    = 0
            formheader = "%0"+str(len(m.group(2)))+"u"
            while (0 == 0):
                zaehler  = zaehler + 1 
                self.fkt = m.group(1) + (formheader % zaehler)
                for block in self.frame.blocks:
                   if self.fkt == block.fkt:
                       self.fkt = ""
                       break 
                if not self.fkt == "":
                    break   
        
        if self.bbox[0] <= 0:    #  make a swimlane          #  --> 078-SWIM
            self.color   = procpy.config.COLOR_SWIMLANE
            self.bbox[0] = 0
            self.bbox[2] = self.frame.current_zoom_factor * procpy.config.SWIMLANE_WIDTH
            if self.bbox[3]-self.bbox[1]==40: # only make different size, if the block size 40 is given; 
                self.bbox[1] = max(0, y - 3 * self.bbox[2]) # this indicates, that the block was not predefined by the user
                self.bbox[3] =        y + 3 * self.bbox[2]  # but it is defined for the first time (not save/open)
                                                            #   <-- 078-SWIM

            for block in self.frame.blocks:  #  check whether there is a proper space for the swimlane


                if block.shape == "a":
                    if block.bbox[3] < y:
                        self.bbox[1] = max(self.bbox[1],block.bbox[3])
                    elif block.bbox[1] > y:
                        self.bbox[3] = min(self.bbox[3],block.bbox[1])
                    else:
                        self.not_valid = 1    #  Clickpoint is inside of an existing swimlane
                        return()
				
            self.fit_content("a")
        else:
            self.fit_content("x")
            
            
#************************************************************************

    def sync_variables_to_text (self,varslist,varsorder):
   
        blockvars = {}
        maxv      = 0
        for v in varslist:   #  check all variables of the block object
            if re.search(r"^(BLOCK|pars|JUMP|_FKT_)$",v):
                continue
            varskey = v
            for v1 in varsorder:   #   special ordering
                if v.find(v1) == 0:
                    varskey = "AAAAAAAA" + v
                    break
            blockvars[varskey] = v
            maxv = max(maxv,len(v))   #   align '=' at left

        blockvarskeys = list(blockvars.keys())
        blockvarskeys.sort()

        self.text = ""
        for vkey in blockvarskeys:
            v    = blockvars[vkey]
            self.text = self.text + "$" + (v+" "*maxv)[0:maxv] + " = " + str(obj.__dict__[v]) + "\n"

#************************************************************************

    def sync_text_to_varslist (self,text=None):  
                                          
        if not text:
            text = self.text
        varslist  = re.sub(r"[\n ]*([a-zA-Z\_][a-zA-Z0-9\_]*)[ =].*?(\n[\n ]*|$)","\\1,",text,99999999,re.DOTALL)
        if re.sub(r"(^|,)[a-zA-\_][a-zA-Z0-9\_]*","",varslist,99999999,re.DOTALL) == ",":
            return(varslist.split(","))
        return(None)

#************************************************************************

#    def sync_varslist_to_varsorder (self,varslist):  
#                                          
#        varsorder = []
#        var0      = ""
#        for var in varslist:  #  Makes a sorting order array for the variables in varslist
#            if var == "":     #  where sorting order patterns are as short as possible
#                continue
#            zaehler = 0
#            while (0 == 0):
#                zaehler = zaehler + 1
#                var1    = var0[:zaehler]
#                if not var0.find(var1) == 0:
#                    varsorder.append(var1)
#                    var0 = var
#                    break
#                if var1 == var0:
#                    break
#                    
#        return(varsorder)

#************************************************************************

    def sync_content_to_fkt_and_text (self):   #  synchronize the content of the Text Area with fkt and text
        print("sync_content_to_fkt_and_text")
        if not 'content' in vars(self):
            return(0)
            
        fkt  = self.content.get("1.0",tkinter.END)
#        fkt  = re.sub(r"\n([a-zA-Z])","\n\\1",fkt,re.DOTALL,99999999)    #  seems to be superfluous
        text = ""
        m    = re.search(r"^(.*?)\n(.*?) *$",fkt,flags=re.DOTALL)
        if m:
            fkt  = m.group(1)
            text = m.group(2)
        if not re.search(r"^[a-zA-Z][a-zA-Z\_0-9]+$",fkt):
            return(-1)                       #   not a valid fkt
        if re.search(r"\S",text):
            if self.shape == "a":
                return(-1)                   #   a swimlane should only carry a fkt, text shall be empty
            
            if not self.sync_text_to_varslist(text):   #   no valid program code
                return(-1)
            try:              #  check also validity as python program code
                exec(text)
            except:
                return(-1)   
        self.fkt  = fkt
        self.text = text        
        return(1)
            
#************************************************************************

    def destroy (self,destroy_arrows=True):
    
        for elem in list(self.elems.values()):
            self.graphcanvas.delete(elem)
            
        if 'content' in vars(self):
            self.content.place_forget()
            del self.content
            del self.contentframe
            self.graphcanvas.delete(self.window)
            del self.window

        if destroy_arrows:
            for arrow in self.arrows_in:
                arrow.source_block.arrows_out.remove(arrow)
                arrow.destroy()
            for arrow in self.arrows_out:
                arrow.target_block.arrows_in.remove(arrow)
                arrow.destroy()
            
#************************************************************************

    def scale (self,fac):
    
        for i in (0,1,2,3):
            self.bbox[i] = self.bbox[i] * fac
        for arrow in self.arrows_out:
            arrow.scale(fac)

        self.fontsize = self.fontsize * fac

        self.fit_content(self.shape)

        
#************************************************************************

    def zoom_text (self,fac):
    
        self.fontsize = self.fontsize * fac
#        self.fit_content(self.shape)
        
#************************************************************************


    def fit_content (self,set_shape=None):


        
        '''
        Fits all contents of the block so that all is fitted
        properly into the shape.
        
        shape not None: includes checking and reformating header and text
        
        Shapes:
        
        r: Normale rectangle block
        s: Subprocess (rectangle)
        i: Case switch (diamond with X)
        f: Forking block (diamond with cross)
        m: Multiple switch (diamond with star)
        w: Wait Block (diamond with circle)
        c: Start Block (circle)
        e: End Block (circle)
        a: Swim lane
        '''

        if set_shape:

            if set_shape in "rceifmwsa":   #  explicit definition of block shape
                shape = set_shape
            else:                          #  implicit definiton of block shape by counting
                shape = { "00": "r",       #  in and out arrows
                          "01": "c",
                          "02": "c",
                          "10": "r",   #  e
                          "11": "r",
                          "12": "ifm",
                          "20": "r",   #  e
                          "21": "ws",
                          "22": "s" } [ str(min(len(self.arrows_in),2)) + str(min(len(self.arrows_out),2)) ]

            if shape == "ifm":   #  split off paths: shape depends on splitting case constraints
                shape         = "i"
                wertebereiche = []
                for arrow in self.arrows_out:
                    text = self.graphcanvas.itemcget(arrow.text,'text')
                    m    = re.search(r"(\d+?),?(\d*)",text)
                    if m:
                        try:
                            val   = [int(m.group(1)),int(m.group(2))]
                            shape = "f"
                        except:
                            val   = [int(m.group(1)),int(m.group(1))]
                        for val1 in wertebereiche:
                            if val1[1] > val[0] or val[1] > val1[0]:
                                shape = "m"
                                break
                        if shape == "m":
                            break                        
                        wertebereiche.append(val)

            if shape == "ws":
                shape = "w"

            if self.shape in shape:
                shape = self.shape

            if not shape == self.shape:
                self.destroy(destroy_arrows=False)

            x = (self.bbox[0]+self.bbox[2])/2
            y = (self.bbox[1]+self.bbox[3])/2
            self.content_text = self.fkt + "\n" + self.text

            if shape in "ars":  #  Rectangle, normal block
                if not shape == self.shape:  #  Shape has changed
                    self.elems        = { "SHAPE":   self.graphcanvas.create_rectangle(0,0,0,0,fill=self.color) }

                    self.contentframe =  tkinter.Frame(self.graphcanvas,background=self.color,bg=self.color,bd=0)
                    self.content      =  tkinter.Text(self.contentframe,wrap={"r":"none","s":"none","a":"char"}[shape],
                                          borderwidth=0,insertborderwidth=0,bg=self.color,highlightcolor=self.color)
                    self.window       = self.graphcanvas.create_window(x,y,window=self.contentframe)
                    self.contentframe.grid_propagate(False)
                    self.content.insert("0.0",self.fkt + "\n" + self.text)
                    self.content.config(state=tkinter.DISABLED)
                    if shape in "s":
                        self.elems['EXPAND'] = self.graphcanvas.create_rectangle(0,0,0,0,width=1)
                    if shape in "s":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=1)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=1)

                    if shape in "a":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=2)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=2)
                        
                        #Solution part 2: move the swim lane behind the blocks. Part 1 in the module Gui_file.
                        self.graphcanvas.tag_lower(self.elems['X1'])
                        self.graphcanvas.tag_lower(self.elems['X2'])						
#                        self.elems['X3']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                self.content.config(font=({"r":"Courier","s":"Courier","a":"Courier New Bold"}[shape],int(self.fontsize)))

            elif shape in "iwfm":    #   Diamond
                if not shape == self.shape:  #  Shape has changed
                    self.elems = { "SHAPE":  self.graphcanvas.create_polygon(0,0,0,0,0,0,0,0,fill=self.color,outline='black'),
                                   "HEADER": self.graphcanvas.create_text(0,0,anchor=tkinter.N,text=self.fkt),
                                   "TEXT":   self.graphcanvas.create_text(0,0,anchor=tkinter.CENTER,text=self.text)
                                 }
                    if shape in "im":
                        self.elems['CROSS1'] = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                        self.elems['CROSS2'] = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                    if shape in "fm":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                    if shape == "w":
                        self.elems['CIRCLE'] = self.graphcanvas.create_oval(0,0,0,0,outline='black',width=3)
                self.graphcanvas.itemconfig(self.elems['HEADER'],font=('Courier',int(self.fontsize)))
                self.graphcanvas.itemconfig(self.elems['TEXT'],  font=('Courier',int(self.fontsize)))

            elif shape in "ce":  #  Circle
                if not shape == self.shape:  #  Shape has changed
                    self.elems        = { "SHAPE":  self.graphcanvas.create_oval(0,0,0,0,fill=self.color,outline='black') }
                    self.contentframe =  tkinter.Frame(self.graphcanvas,background=self.color,bg=self.color)
                    self.content      =  tkinter.Text(self.contentframe,wrap="none",bg=self.color,
                                                      highlightcolor=self.color,borderwidth=0,insertborderwidth=0)
                    self.window       = self.graphcanvas.create_window(158,99,window=self.contentframe)
                    self.contentframe.grid_propagate(False)
                    self.content.insert("0.0",self.fkt + "\n" + self.text)
                    self.content.config(state=tkinter.DISABLED)
                self.content.config(font=('Courier',int(self.fontsize)))

                    
            elif shape in "t":  #  Triangle
                if not shape == self.shape:  #  Shape has changed
                    self.elems = { "SHAPE":  self.graphcanvas.create_polygon(0,0,0,0,0,0,fill=self.color,outline='black') }
                    self.content = tkinter.Text(self.graphcanvas,wrap="none",bg=self.color,highlightcolor=self.color,
                                                borderwidth=0,insertborderwidth=0)
                    self.content.insert("0.0",self.fkt + "\n" + self.text)
                    self.content.config(state=tkinter.DISABLED)
                self.content.config(font=('Courier',int(self.fontsize)))


            if not shape == self.shape:    #  Shape has changed
                self.shape = shape
                if 'content' in vars(self):
                    self.content.bind("<ButtonPress-1>",   lambda event: self.press_button(event))
                    self.content.bind("<ButtonRelease-1>", self.release_button_event)
                    self.content.bind("<B1-Motion>",       self.move_mouse_event)
                    self.content.bind("<Double-Button-1>", self.double_click_button_event)
                for elem in list(self.elems.values()):
                    self.graphcanvas.tag_bind(elem,"<ButtonPress-1>",   lambda event: self.press_button_event(event))
                    self.graphcanvas.tag_bind(elem,"<ButtonRelease-1>", self.release_button_event)
                    self.graphcanvas.tag_bind(elem,"<B1-Motion>",       self.move_mouse_event)
                    self.graphcanvas.tag_bind(elem,"<Double-Button-1>", self.double_click_button_event)


        x = (self.bbox[0]+self.bbox[2])/2
        y = (self.bbox[1]+self.bbox[3])/2
        w = self.bbox[2] - self.bbox[0]        
        h = self.bbox[3] - self.bbox[1]        
        

        if self.shape in "iwfmce":                  #  reduce the bounding box to a square 
            if w > h:                               #  because diamond shapes shall be always rectangular,
                self.bbox[0] = x - h/2              #  and circles should be circles, not ovals
                self.bbox[2] = x + h/2
                w = h
            else:
                self.bbox[1] = y - w/2
                self.bbox[3] = y + w/2
                h = w

        if self.shape in "ars":
            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[1],self.bbox[2],self.bbox[3])               
            self.contentframe.config(width=int(0.9*w),height=int(0.9*h))
            self.content.place(x=0,y=0,width=int(0.9*w),height=int(0.9*h))
            (x1,y1) = self.graphcanvas.coords(self.window)
            self.graphcanvas.move(self.window,x-x1,y-y1)
            if self.shape == "s":
                self.graphcanvas.coords(self.elems['EXPAND'], x-0.09*w,y+0.32*h,x+0.09*w,y+0.5*h)
                self.graphcanvas.coords(self.elems['X1'],     x-0.09*w,y+0.32*h,x+0.09*w,y+0.32*h)
                self.graphcanvas.coords(self.elems['X2'],     x,y+0.4*h,x,y+0.5*h)
            if self.shape == "a":
                self.graphcanvas.coords(self.elems['X1'],     x-0.5*w,y-0.5*h,99999999,y-0.5*h)
                #self.graphcanvas.coords(self.elems['X2'],     x-0.5*w,y+0.5*h,99999999,y+0.5*h)
                self.graphcanvas.coords(self.elems['X2'],     x-0.5*w,y+0.5*h,99,y+0.5*h)
#                self.graphcanvas.coords(self.elems['X3'],     x-0.5*w,y-0.5*h,x-0.5*w,y+0.5*h)

        elif self.shape in "iwfm":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],y,x,self.bbox[1],self.bbox[2],y,x,self.bbox[3])               
            self.graphcanvas.coords(self.elems['HEADER'],x,self.bbox[1])
            self.graphcanvas.coords(self.elems['TEXT'],  x,y)
            if self.shape in "im":
                self.graphcanvas.coords(self.elems['CROSS1'], x-0.17*w,y-0.17*h,x+0.17*w,y+0.17*h)
                self.graphcanvas.coords(self.elems['CROSS2'], x+0.17*w,y-0.17*h,x-0.17*w,y+0.17*h)
            if self.shape in "fm":
                self.graphcanvas.coords(self.elems['X1'], x-0.17*w,y,x+0.17*w,y)
                self.graphcanvas.coords(self.elems['X2'], x,y-0.17*h,x,y+0.17*h)
            if self.shape == "w":
                self.graphcanvas.coords(self.elems['CIRCLE'], x-0.19*w,y-0.19*h,x+0.19*w,y+0.19*h)

        elif self.shape in "ce":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[1],self.bbox[2],self.bbox[3])               
            self.contentframe.config(width=int(0.7*w),height=int(0.7*h))
            self.content.place(x=0,y=0,width=int(0.7*w),height=int(0.7*h))
            (x1,y1) = self.graphcanvas.coords(self.window)
            self.graphcanvas.move(self.window,x-x1,y-y1)

        elif self.shape in "t":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[3],x,self.bbox[1],self.bbox[2],self.bbox[3])               
            self.content.place(x=int(x-0.28*w),y=int(y-0.28*h),width=int(0.56*w),height=int(0.56*h))


        for arrow in self.arrows_in + self.arrows_out:
            arrow.fit_block_connections()
            arrow.fit_description()

        if set_shape:

            if set_shape in "rceifmwsa":   #  explicit definition of block shape
                shape = set_shape
            else:                          #  implicit definiton of block shape by counting
                shape = { "00": "r",       #  in and out arrows
                          "01": "c",
                          "02": "c",
                          "10": "r",   #  e
                          "11": "r",
                          "12": "ifm",
                          "20": "r",   #  e
                          "21": "ws",
                          "22": "s" } [ str(min(len(self.arrows_in),2)) + str(min(len(self.arrows_out),2)) ]

            if shape == "ifm":   #  split off paths: shape depends on splitting case constraints
                shape         = "i"
                wertebereiche = []
                for arrow in self.arrows_out:
                    text = self.graphcanvas.itemcget(arrow.text,'text')
                    m    = re.search(r"(\d+?),?(\d*)",text)
                    if m:
                        try:
                            val   = [int(m.group(1)),int(m.group(2))]
                            shape = "f"
                        except:
                            val   = [int(m.group(1)),int(m.group(1))]
                        for val1 in wertebereiche:
                            if val1[1] > val[0] or val[1] > val1[0]:
                                shape = "m"
                                break
                        if shape == "m":
                            break                        
                        wertebereiche.append(val)

            if shape == "ws":
                shape = "w"

            if self.shape in shape:
                shape = self.shape

            if not shape == self.shape:
                self.destroy(destroy_arrows=False)

            x = (self.bbox[0]+self.bbox[2])/2
            y = (self.bbox[1]+self.bbox[3])/2
            self.content_text = self.fkt + "\n" + self.text

            if shape in "ars":  #  Rectangle, normal block
                if not shape == self.shape:  #  Shape has changed
                    self.elems        = { "SHAPE":   self.graphcanvas.create_rectangle(0,0,0,0,fill=self.color) }
                    self.contentframe =  tkinter.Frame(self.graphcanvas,background=self.color,bg=self.color)
                    self.content      =  tkinter.Text(self.contentframe,wrap={"r":"none","s":"none","a":"char"}[shape],
                                          bg=self.color,highlightcolor=self.color,borderwidth=0,insertborderwidth=0)
                    self.window       = self.graphcanvas.create_window(x,y,window=self.contentframe)
                    self.contentframe.grid_propagate(False)
                    self.content.insert("0.0",self.fkt + "\n" + self.text)
                    self.content.config(state=tkinter.DISABLED)
                    if shape in "s":
                        self.elems['EXPAND'] = self.graphcanvas.create_rectangle(0,0,0,0,width=1)
                    if shape in "s":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=1)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=1)
                    if shape in "a":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=2)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=2)
                        
                        #Solution part 2: move the swim lane behind the blocks. Part 1 in the module Gui_file.
                        self.graphcanvas.tag_lower(self.slems['X1'])
                        self.graphcanvas.tag_lower(self.slems['X2'])
#                        self.elems['X3']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                self.content.config(font=({"r":"Courier","s":"Courier","a":"Courier New Bold"}[shape],int(self.fontsize)))


            elif shape in "iwfm":    #   Diamond
                if not shape == self.shape:  #  Shape has changed
                    self.elems = { "SHAPE":  self.graphcanvas.create_polygon(0,0,0,0,0,0,0,0,fill=self.color,outline='black'),
                                   "HEADER": self.graphcanvas.create_text(0,0,anchor=tkinter.N,text=self.fkt),
                                   "TEXT":   self.graphcanvas.create_text(0,0,anchor=tkinter.CENTER,text=self.text)
                                 }
                    if shape in "im":
                        self.elems['CROSS1'] = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                        self.elems['CROSS2'] = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                    if shape in "fm":
                        self.elems['X1']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                        self.elems['X2']     = self.graphcanvas.create_line(0,0,0,0,fill='black',width=3)
                    if shape == "w":
                        self.elems['CIRCLE'] = self.graphcanvas.create_oval(0,0,0,0,outline='black',width=3)

                self.graphcanvas.itemconfig(self.elems['HEADER'],font=('Courier',int(self.fontsize)))
                self.graphcanvas.itemconfig(self.elems['TEXT'],  font=('Courier',int(self.fontsize)))


            elif shape in "ce":  #  Circle
                if not shape == self.shape:  #  Shape has changed
                    self.elems        = { "SHAPE":  self.graphcanvas.create_oval(0,0,0,0,fill=self.color,outline='black') }

                    self.contentframe =  tkinter.Frame(self.graphcanvas,background=self.color,bg=self.color)

                    self.content      =  tkinter.Text(self.contentframe,wrap="none",bg=self.color,
                                                      highlightcolor=self.color,borderwidth=0,insertborderwidth=0)
                    self.window       = self.graphcanvas.create_window(158,99,window=self.contentframe)
                    self.contentframe.grid_propagate(False)

                    self.content.insert("0.0",self.fkt + "\n" + self.text)

                    self.content.config(state=tkinter.DISABLED)
                self.content.config(font=('Courier',int(self.fontsize)))

                    
            elif shape in "t":  #  Triangle
                if not shape == self.shape:  #  Shape has changed
                    self.elems = { "SHAPE":  self.graphcanvas.create_polygon(0,0,0,0,0,0,fill=self.color,outline='black') }
                    self.content = tkinter.Text(self.graphcanvas,wrap="none",bg=self.color,highlightcolor=self.color,
                                                borderwidth=0,insertborderwidth=0)

                    self.content.insert("0.0",self.fkt + "\n" + self.text)
                    self.content.config(state=tkinter.DISABLED)
                self.content.config(font=('Courier',int(self.fontsize)))



            if not shape == self.shape:    #  Shape has changed
                self.shape = shape
                if 'content' in vars(self):
                    self.content.bind("<ButtonPress-1>",   lambda event: self.press_button(event))
                    self.content.bind("<ButtonRelease-1>", self.release_button)
                    self.content.bind("<B1-Motion>",       self.move_mouse)
                    self.content.bind("<Double-Button-1>", self.double_click_button)
                for elem in list(self.elems.values()):
                    self.graphcanvas.tag_bind(elem,"<ButtonPress-1>",   lambda event: self.press_button(event))
                    self.graphcanvas.tag_bind(elem,"<ButtonRelease-1>", self.release_button)
                    self.graphcanvas.tag_bind(elem,"<B1-Motion>",       self.move_mouse)
                    self.graphcanvas.tag_bind(elem,"<Double-Button-1>", self.double_click_button)


        x = (self.bbox[0]+self.bbox[2])/2
        y = (self.bbox[1]+self.bbox[3])/2
        w = self.bbox[2] - self.bbox[0]        
        h = self.bbox[3] - self.bbox[1]        
        

        if self.shape in "iwfmce":                  #  reduce the bounding box to a square 
            if w > h:                               #  because diamond shapes shall be always rectangular,
                self.bbox[0] = x - h/2              #  and circles should be circles, not ovals
                self.bbox[2] = x + h/2
                w = h
            else:
                self.bbox[1] = y - w/2
                self.bbox[3] = y + w/2
                h = w

        if self.shape in "ars":
            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[1],self.bbox[2],self.bbox[3])               
            self.contentframe.config(width=int(0.9*w),height=int(0.9*h))
            self.content.place(x=0,y=0,width=int(0.9*w),height=int(0.9*h))
            (x1,y1) = self.graphcanvas.coords(self.window)
            self.graphcanvas.move(self.window,x-x1,y-y1)
            if self.shape == "s":
                self.graphcanvas.coords(self.elems['EXPAND'], x-0.09*w,y+0.32*h,x+0.09*w,y+0.5*h)
                self.graphcanvas.coords(self.elems['X1'],     x-0.09*w,y+0.32*h,x+0.09*w,y+0.32*h)
                self.graphcanvas.coords(self.elems['X2'],     x,y+0.4*h,x,y+0.5*h)
            if self.shape == "a":
                self.graphcanvas.coords(self.elems['X1'],     x-0.5*w,y-0.5*h,99999999,y-0.5*h)
                self.graphcanvas.coords(self.elems['X2'],     x-0.5*w,y+0.5*h,99999999,y+0.5*h)
#                self.graphcanvas.coords(self.elems['X3'],     x-0.5*w,y-0.5*h,x-0.5*w,y+0.5*h)

        elif self.shape in "iwfm":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],y,x,self.bbox[1],self.bbox[2],y,x,self.bbox[3])               
            self.graphcanvas.coords(self.elems['HEADER'],x,self.bbox[1])
            self.graphcanvas.coords(self.elems['TEXT'],  x,y)
            if self.shape in "im":
                self.graphcanvas.coords(self.elems['CROSS1'], x-0.17*w,y-0.17*h,x+0.17*w,y+0.17*h)
                self.graphcanvas.coords(self.elems['CROSS2'], x+0.17*w,y-0.17*h,x-0.17*w,y+0.17*h)
            if self.shape in "fm":
                self.graphcanvas.coords(self.elems['X1'], x-0.17*w,y,x+0.17*w,y)
                self.graphcanvas.coords(self.elems['X2'], x,y-0.17*h,x,y+0.17*h)
            if self.shape == "w":
                self.graphcanvas.coords(self.elems['CIRCLE'], x-0.19*w,y-0.19*h,x+0.19*w,y+0.19*h)

        elif self.shape in "ce":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[1],self.bbox[2],self.bbox[3])               
            self.contentframe.config(width=int(0.7*w),height=int(0.7*h))
            self.content.place(x=0,y=0,width=int(0.7*w),height=int(0.7*h))
            (x1,y1) = self.graphcanvas.coords(self.window)
            self.graphcanvas.move(self.window,x-x1,y-y1)

        elif self.shape in "t":

            self.graphcanvas.coords(self.elems['SHAPE'], self.bbox[0],self.bbox[3],x,self.bbox[1],self.bbox[2],self.bbox[3])               
            self.content.place(x=int(x-0.28*w),y=int(y-0.28*h),width=int(0.56*w),height=int(0.56*h))


        for arrow in self.arrows_in + self.arrows_out:
            arrow.fit_block_connections()
            arrow.fit_description()


#************************************************************************


    def sync_content_to_text_and_header (self):
    
        if 'content' in vars(self):
            header = self.content.get("1.0",tkinter.END)
            text   = ""
            m = re.search(r"^(.*?)\n(.*)$",header,re.DOTALL)
            if m:
                header = m.group(1)
                text   = m.group(2)
            if not re.search(r"^[a-z][a-zA-Z\_0-9]+$",header):
                return(-1)                   #   not a valid header
            try:
                exec(text)
            except:
                return(-1)                   #   no valid program code
            self.header = header
            self.text   = text
            return(1)                        #   self.content successful synced
            
        return(0)                            #   no self.content variable
                

#************************************************************************

    def get_shape_connection_point (self,p1):

        '''
        Computes the point on the the border of the surrounding shape of the block
        which connects an outer point p to the block
        '''

        p = [ min(max(self.bbox[0], p1[0]), self.bbox[2]), min(max(self.bbox[1], p1[1]), self.bbox[3]) ]

        
        #  p2: touches the bounding box. This basic point has to be extended into the bounding box depending on the shape:


        if not self.shape in "rs":
            x  = (self.bbox[0]+self.bbox[2])/2
            y  = (self.bbox[1]+self.bbox[3])/2
            p2 = p[:]
            if p2[0] == p1[0]:
                p2[1] = y
            if p2[1] == p1[1]:
                p2[0] = x

        if self.shape in "iwfm":    #   diamond
         
            fac = 1
            if p1[0] < x:
                p3 = [self.bbox[0],y]
            else:           
                p3 = [self.bbox[2],y]
                fac = -fac
            if p1[1] < y:
                p4 = [x,self.bbox[1]]
            else:           
                p4 = [x,self.bbox[3]]
                fac = -fac

            try:
                m = ( p4[0] - p2[0] + fac * (p4[1] - p2[1]) ) / ( p1[0] - p2[0] + fac * (p1[1] - p2[1]) ) 
                p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
            except:  # in this case, the outer point points to the closest edge 
                pass

        elif self.shape in "ce":   #   circle
         
            a  = (p1[0]-p2[0])**2 + (p1[1]-p2[1])**2   #  a * m*m - 2 * b * m + c = 0, quadratic equation to solve  
            b  = (p1[0]-p2[0])*(x-p2[0]) + (p1[1]-p2[1])*(y-p2[1])
            c  = (x-p2[0])**2 + (y-p2[1])**2 - ((self.bbox[0]-self.bbox[2])/2)**2

            try:
                m = ( b + (b**2 - a*c)**0.5 ) / a
                p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
            except:  # in this case, the outer point points to the most west, east, north or south point of the circle 
                pass

        elif self.shape in "t":   #   triangle
         
            if p1[1] > self.bbox[3]:     #   point is underneath the bottom of the triangle.
                pass                     #   Nothing to do -> the same behaviour as in rectangle shape mode
            else:
                p3 = [x,self.bbox[1]]
                if p1[0] < x:
                    p4 = [self.bbox[0],self.bbox[3]]
                else:           
                    p4 = [self.bbox[2],self.bbox[3]]

                a = (p2[0]-p4[0])*(p3[1]-p4[1]) - (p2[1]-p4[1])*(p3[0]-p4[0]) 
                b = (p2[0]-p1[0])*(p3[1]-p4[1]) - (p2[1]-p1[1])*(p3[0]-p4[0])
            
                try:
                    m = a / b
                    p = [ p1[0] * m + p2[0] * (1-m), p1[1] * m + p2[1] * (1-m) ]
                except:  # in this case, the outer point points to the closest edge 
                    pass
                    
        return(p)


#************************************************************************

    def xxget_varlen (self):
    
        '''
        Gives the maximal length of variable names.
        Given as a value lower than 0 if no arrows exist where
        the block is target. E.g. if the block is the starting node.
        '''
    
        maxv = 5
        for textline in self.graphcanvas.itemcget(self.text,'text').split("\n"):
            m = re.search(r"^(.*?)[ \=]",textline)
            if m:
                maxv = max(maxv,len(m.group(1)))
        
        if not self.arrows_out:
            maxv = -maxv
            
        return(maxv)

#************************************************************************

    def get_vars (self):
    
        '''
        Gives the variables with their actual values.
        '''
        block  = [ self.graphcanvas.itemcget(self.fkt,'text') ]   #hier stand: self.header  collecting block informations
        m = re.search(r"^ *(.*?)( *\/ *)(.*?) *$",block[0])
        if m:
            block = [ m.group(1), m.group(3) ]
        coords = self.bbox
        block.append( "%3.1f" % ((coords[0]+coords[2])/2) )
        block.append( "%3.1f" % ((coords[1]+coords[3])/2) )
        block.append( "%3.1f" % (coords[2]-coords[0]) )
        block.append( "%3.1f" % (coords[3]-coords[1]) )

        varsval  = [ ['BLOCK',block] ]

        varsval1 = {}        #  collecting other visible variables
        for textline in self.graphcanvas.itemcget(self.text,'text').split("\n"):
            m = re.search(r"^(.*?)( *\= *)(.*?) *$",textline)
            if m:
                varsval1[m.group(1)] = m.group(3)
        varsval1_keys = list(varsval1.keys())
        varsval1_keys.sort()
        for item in varsval1_keys:
            varsval.append([item,varsval1[item]])
            
        jump = {}           #   collecting infos where to jump:
        for arrow in self.arrows_out: 
            jumpinfo = [ self.graphcanvas.itemcget(arrow.text,'text') ]
            for arrowline in arrow.arrowlines[1:]:
                for c in self.graphcanvas.coords(arrowline)[:2]:
                    jumpinfo.append("%3.1f" % c)
            jump[arrow.target_block] = jumpinfo
                
        varsval.append( ['JUMP',jump] )
            
        return(varsval)

#************************************************************************

    def bounding_box (self,extbox=None):
    
        '''
        Returns the bounding box of the block.
        If an optional 4-elem array is given, this
        will be extended to the bounding box.
        ''' 

        if extbox == None:
            extbox = []

        if len(extbox) == 0:
            extbox.append(self.bbox[0])
            extbox.append(self.bbox[1])
            extbox.append(self.bbox[2])
            extbox.append(self.bbox[3])
        else:
            extbox[0] = min(extbox[0],self.bbox[0])
            extbox[1] = min(extbox[1],self.bbox[1])
            extbox[2] = max(extbox[2],self.bbox[2])
            extbox[3] = max(extbox[3],self.bbox[3])

        return(extbox)

#************************************************************************

    def press_button (self,event=[]):

        if type(event) == type([]):  #  Testschnittstelle
            try:
                self.x0 = event[0]
            except:
                self.x0 = self.bbox[0] + self.bbox[2]/2
            try:
                self.y0 = event[1]
            except:
                self.y0 = self.bbox[1] + self.bbox[3]/2
            (self.x1,self.y1) = (self.x0,self.y0)
        else:
            (self.x0,self.y0) = (event.x,event.y)
            (self.x1,self.y1) = (event.x_root,event.y_root)

        

        self.graphcanvas.configure(cursor='sizing')    #  Alternative mouse pointers: arrow circle 
                                                       #  clock cross dotbox exchange fleur heart
        self.resize_box            = ""                #  man mouse pirate plus shuttle sizing spider
        self.move_blocks           = []                #  spraycan star target tcross trek watch
        self.move_arrows           = {}
        self.move_arrowlines_start = {}
        self.move_arrowlines_end   = {}
        self.bbox_press            = self.bbox[:]
        self.w                     = self.bbox[2] - self.bbox[0]
        self.h                     = self.bbox[3] - self.bbox[1]

        if self.shape in "ars":

            if abs(self.y0 - self.bbox[3]) < 4:             #  if pressing point is near the border of the box
                 self.resize_box = self.resize_box + "S"    #  mark the direction for resizing
            if abs(self.y0 - self.bbox[1]) < 4: 
                 self.resize_box = self.resize_box + "N"
        
        if self.shape in "rs":
            if abs(self.x0 - self.bbox[0]) < 4:
                 self.resize_box = self.resize_box + "W"
            if abs(self.x0 - self.bbox[2]) < 4: 
                 self.resize_box = self.resize_box + "E"


        if self.resize_box == "" and self.shape == "a":  #  Shifting a swimlane means also shifting all blocks in its area 
        
            (self.move_blocks,self.move_arrows,self.move_singlepoints) = self.blocks_and_arrows_in_swimlane()
            for block in self.frame.blocks:
                if not self == block and block.shape == "a":
                    (block.move_blocks,block.move_arrows,block.move_singlepoints) = block.blocks_and_arrows_in_swimlane()

        self.last_event = 1

        return "break"
#**********************************************************

    def blocks_and_arrows_in_swimlane (self,mode=0):   #  Returns the blocks which are in the swimlane if i am swimlane
        
        erg = []
        for block in self.frame.blocks:
            if self.bbox[1] <= block.bbox[1] <= self.bbox[3]:
                if not block.shape == "a":
                    erg.append(block)
        
        arrows = []
        for block in erg:
            for arrow in block.arrows_in:
                if arrow.source_block in erg:
                    arrows.append(arrow)
                    
        if mode > 0:
            return(arrows)

        exclude_arrows = self.arrows_covered_by_some_swimlane()

        singlepoints = []
        for block in self.frame.blocks:
            for arrow in block.arrows_in:
                if not arrow in exclude_arrows:
                    zaehler = 0
                    for arrowline in arrow.arrowlines:
                        coords = self.graphcanvas.coords(arrowline)
                        for i in (0,2):
                            if self.bbox[1] <= coords[i+1] <= self.bbox[3]:
                                singlepoints.append([arrow,zaehler,i])
                        zaehler = zaehler + 1
                                                            
        return(erg,arrows,singlepoints)
			 
#**********************************************************		

    def arrows_covered_by_some_swimlane (self):
    
        erg = []
        for swimlane in self.frame.blocks:
            if swimlane.shape == "a":
                erg = erg + swimlane.blocks_and_arrows_in_swimlane(1)
                pass
        return(erg)

#**********************************************************

    def blocks_in_swimlane (self,mode=0):   #  Returns the blocks which are in the swimlane if self is a swimlane
    
        if not self.shape == "a":
            return([])
            
        erg = { 'BLOCKS':           [],    #  Blocks in the swimlane
                'ARROWLINES_BEGIN': {},    #  Starting arrow lines in the swimlane
                'ARROWLINES_END'  : {},    #  Ending arrow lines in the swimlane
                'ARROWS'          : {} }   #  Arrows from one block in the swimlane to another
               
        sub_swimlanes = []
        for block in self.frame.blocks:
            if self.bbox[1] <= block.bbox[1] <= self.bbox[3]:
                if self.bbox[0] <= block.bbox[0]:
                    if not block.shape == "a":
#                        print("a = ",block)
                        erg['BLOCKS'].append(block)
                    elif not block == self and ( not self.bbox[0] == block.bbox[0] or
                                                 self.bbox[1] < block.bbox[1]      or
                                  ( self.bbox[1] == block.bbox[1]   and self.bbox[3] < block.bbox[3] ) ):
                            sub_swimlanes.append(block)
        #print("Blocks in swimlane: ",erg['BLOCKS'])
        for block in erg['BLOCKS']:
            for arrow in block.arrows_in + block.arrows_out:
                erg['ARROWS'][arrow] = 1
                
                for arrowline in arrow.arrowlines:
                    coords = self.graphcanvas.coords(arrowline)
                    
                    bed1   = arrow in block.arrows_in  and arrow.source_block in self.move_blocks
                    bed2   = arrow in block.arrows_out and arrow.target_block in self.move_blocks
                    if bed1 or bed2 or self.bbox[1] < coords[1] and coords[1] < self.bbox[3]:
                        erg['ARROWLINES_BEGIN'][arrowline] = 1
                    if bed1 or bed2 or self.bbox[1] < coords[3] and coords[3] < self.bbox[3]:
                        erg['ARROWLINES_END'][arrowline] = 1
                    
                    for arrow in block.arrows_out:
                        erg['ARROWLINES_BEGIN'][arrow.arrowlines[0]] = 1
                    for arrow in block.arrows_in:
                        erg['ARROWLINES_END'][arrow.arrowlines[-1]] = 1

        return(erg)


        self.last_event = 1


#**********************************************************

    def xxdouble_click_button (self,event):

        (self.x0,self.y0) = (event.x,event.y)
        (self.x1,self.y1) = (event.x_root,event.y_root)
        self.last_event   = 2
        return "break"

#****************************************************

    def release_button (self,event=None):

          
        if (abs(self.bbox[0] - self.bbox_press[0]) + abs(self.bbox[1] - self.bbox_press[1]) +
            abs(self.bbox[2] - self.bbox_press[2]) + abs(self.bbox[3] - self.bbox_press[3]) > 10):
#            self.last_event = 4
            print(8765,"CHECK_RELEASE_BUTTON")

        if self.shape == "a" and self.last_event == 3: #before: in (3,4):  old version creates error with changing size of the swimlane
            self.frame.rearr_swimlane(0.2)
            
        if self.last_event in (1,3):

            self.resize_box = ""
            self.graphcanvas.configure(cursor='arrow')

            if self.frame.hl_block == self:
                self.syntax_error = 0
            if not self.frame.hl_block == None:
                if self.frame.hl_block.sync_content_to_fkt_and_text() == -1:
                    if not self.frame.hl_block == self:
                        self.frame.hl_block.syntax_error = 1
                        return()
                self.frame.hl_block.syntax_error = 0
            bed = None
            if not self.frame.hl_block == self:  #  if I am not yet highlighted, un-highlight the former highlighted block
                self.frame.hl_block_0 = self.frame.hl_block  #  and set it to the last highlighted block,
                self.frame.hl_block   = self                  #  and set myself to highlighted
                self.hl_arrow          = None
                for arrow in self.arrows_in:                           #   now check all incoming arrows. The one where its
                    if arrow.source_block == self.frame.hl_block_0:   #   source_block is the last highlighted block,
                        self.hl_arrow = arrow                          #   has also to be highlighted
                if False and self.hl_arrow == None:   ##  DISABLED         #   check also inverted direction
                    for arrow in self.arrows_out:            
                        if arrow.target_block == self.frame.hl_block_0:  
                            self.hl_arrow = arrow                             
                self.highlight_myself(0)
            else:
                self.syntax_error = 0

        elif self.last_event == 2:    #  last event was a double click
            self.frame.create_arrow()

#        elif self.frame.griddata['SNAP']:
#            self.frame.rearr_grid(0.2)
#        else:
#            self.frame.rearr_grid(0.2,1)

        self.last_event = 1
        return "break"

#****************************************************

    def highlight_block (self,nr,mode=0):    #   mode == 1: blinking
        print("highlight_block")
        fillcolor1 = [procpy.config.COLOR_HIGHLIGHT_ALT[0],procpy.config.COLOR_HIGHLIGHT_ALT[0],

                      procpy.config.COLOR_HIGHLIGHT[0],procpy.config.COLOR_HIGHLIGHT[0]
                     ][nr+mode]

        
        self.graphcanvas.itemconfig(self.elems['SHAPE'],fill=fillcolor1)
        if 'content' in vars(self):
            self.content.config(bg=fillcolor1,highlightcolor=fillcolor1)
           
            self.contentframe.config(background=fillcolor1,bg=fillcolor1,highlightcolor=fillcolor1)

        if self.hl_arrow:
            fillcolor2 = [procpy.config.COLOR_HIGHLIGHT[0],self.color,self.color,self.color][nr+mode]
            if len(self.frame.blocks) < 6:
#                fillcolor2 = fillcolor1
                fillcolor2 = procpy.config.COLOR_HIGHLIGHT[0]
            self.hl_arrow.highlight(fillcolor2)
        else:
            if 'content' in vars(self):
                if self.content.cget('state') == tkinter.DISABLED:
                    self.content.config(state=tkinter.NORMAL)
                    self.content.focus_set()
                    self.content.mark_set("insert",tkinter.END)
        

        if self.sync_content_to_fkt_and_text() == -1:

            self.syntax_error = 1
        else:
            self.syntax_error = 0

        delay = -nr
        if self.frame.hl_block == self:
            delay = 180 + mode*120
        if delay > -1:
            self.frame.loopobj.after(delay,self.highlight_myself,1-nr,2*self.syntax_error)
        else:
            self.graphcanvas.itemconfig(self.elems['SHAPE'],fill=self.color)
            if 'content' in vars(self):
                self.content.config(bg=self.color,highlightcolor=self.color)

                self.contentframe.config(background=self.color,bg=self.color,highlightcolor=self.color)

            if self.hl_arrow:
                self.hl_arrow.highlight()
                self.hl_arrow = None
            if 'content' in vars(self):
                self.content.config(state=tkinter.DISABLED)
        
#************************************************************************

    def move_mouse (self,event):
 
        dx = event.x_root - self.x1
        dy = event.y_root - self.y1

        self.last_event = 3

        if self.resize_box == "":
            self.move(dx,dy)
        else:
            self.resize(dx,dy)

        self.x1 = event.x_root
        self.y1 = event.y_root
        return "break"

#****************************************************

    def move (self,dx,dy,swimlane_sub_move=None):

        if ( self.bbox[0]+dx < 0 or False and self.bbox[2]+dx > self.graphcanvas.winfo_width() or
             self.bbox[1]+dy < 0 or False and self.bbox[3]+dy > self.graphcanvas.winfo_height() ) :
            return()    #  keine gute Loesung, um zu verhindern, dass die Objekte aus dem Zeichenbereich
                        #  gehen. Insbesondere die rechte und untere Grenze wird mit dem obigen Code
                        #  beim Scalen zu stark geschuetzt. Hier muss eine bessere Loesung her. Am besten
                        #  waere es, den Mauszeiger auf das Gebiet des graphcanvas zu beschraenken, das scheint
                        #  aber schwierig zu sein.  --> task_001
                        

        if self.shape == "a":
            bed     = False
            sublane = None   #  the sub swimlane also to have been moved (when a block or ancesters block is highlighted)
            for swimlane in self.frame.blocks:
                if swimlane == self or not swimlane.shape == "a":
                    continue
                if (swimlane.bbox[1] < self.bbox[1] < swimlane.bbox[3] or swimlane.bbox[1] < self.bbox[3] < swimlane.bbox[3]
                      or self.bbox[1] < swimlane.bbox[1] < self.bbox[3] or self.bbox[1] < swimlane.bbox[3] < self.bbox[3]):
                    if swimlane_sub_move or self == self.frame.hl_block:
                        if (swimlane.bbox[1] - self.bbox[1]) * dy > 0:
                            if swimlane_sub_move or self == self.frame.hl_block:
                                sublane = swimlane
                                bed     = True
                            break
                    else:
                        bed     = True   #  block intersection
                        break
                        
            if bed:
                if sublane:
                    sublane.move(dx,dy,1)
                elif self.bbox[0] == 0:
                    self.bbox[0] = self.bbox[0] + self.frame.current_zoom_factor * procpy.config.SWIMLANE_WIDTH
                    self.bbox[2] = self.bbox[2] + self.frame.current_zoom_factor * procpy.config.SWIMLANE_WIDTH
            else:
                if not self.bbox[0] == 0:
                    self.bbox[0] = self.bbox[0] - self.frame.current_zoom_factor * procpy.config.SWIMLANE_WIDTH
                    self.bbox[2] = self.bbox[2] - self.frame.current_zoom_factor * procpy.config.SWIMLANE_WIDTH
      

        self.bbox[1] = self.bbox[1] + dy
        self.bbox[3] = self.bbox[3] + dy


        if not self.shape == "a":
            self.bbox[0] = self.bbox[0] + dx
            self.bbox[2] = self.bbox[2] + dx
        else:
            for block in self.move_blocks:
                block.move(0,dy)
            for arrow in self.move_arrows:
                arrow.move(0,dy)
            for point in self.move_singlepoints:
                arrow       = point[0]
                arrowline   = arrow.arrowlines[point[1]]
                coords      = self.graphcanvas.coords(arrowline)
                coords[point[2]]   = coords[point[2]]   + 0
                coords[point[2]+1] = coords[point[2]+1] + dy
                self.graphcanvas.coords(arrowline,tuple(coords))
                arrow.fit_description() 

        self.fit_content()

#**********************************************************

    def resize (self,dx,dy):


        if   re.search(r"N",self.resize_box):
            self.bbox[1] = self.bbox[1] + dy
        elif re.search(r"S",self.resize_box):
            self.bbox[3] = self.bbox[3] + dy
        if   re.search(r"W",self.resize_box):
            self.bbox[0] = self.bbox[0] + dx
        elif re.search(r"E",self.resize_box):
            self.bbox[2] = self.bbox[2] + dx
         
        self.fit_content(self.shape)

        for arrow in self.arrows_in + self.arrows_out:
            arrow.fit_block_connections()
            arrow.fit_description()
	
    def __repr__(self):
        return repr(self.arrows_in)+'-->['+repr(self.bbox)+']'+'--> '+repr(self.arrows_out)+'['+repr(self.elems)+']'

#****************************************************


bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net