
#-------------------------------------------------------------------------------
# Module:     mainEditor.tk
# Activation: Loaded during GOBLET startup
# Scope:      Generation of the editor canvas
#             All editor functions
#             Conversions between library and editor object
#-------------------------------------------------------------------------------



# Conversions of Canvas Coordinates and Goblin Coordinates

proc CanvasCX {cx} {
    global goblinXShift goblinXZoom Zoom

    return [expr {$cx*$goblinXZoom*$Zoom+$goblinXShift*$Zoom}];
}

proc CanvasCY {cy} {
    global goblinYShift goblinYZoom Zoom

    return [expr {$cy*$goblinYZoom*$Zoom+$goblinYShift*$Zoom}];
}

proc GoblinCX {cx gridSize} {
    global goblinXShift goblinXZoom Zoom

    if {$gridSize==0} {
        return [expr {($cx-$goblinXShift*$Zoom)/($goblinXZoom+0.0000000001)/$Zoom}]
    }

    return [expr {round(($cx-$goblinXShift*$Zoom)/($goblinXZoom+0.0000000001)/$Zoom/$gridSize)*$gridSize}]
}

proc GoblinCY {cy gridSize} {
    global goblinYShift goblinYZoom Zoom

    if {$gridSize==0} {
        return [expr {($cy-$goblinYShift*$Zoom)/($goblinYZoom+0.0000000001)/$Zoom}]
    }

    return [expr {round(($cy-$goblinYShift*$Zoom)/($goblinYZoom+0.0000000001)/$Zoom/$gridSize)*$gridSize}]
}

proc GridCX {cx gridSize} {
    return [CanvasCX [GoblinCX $cx $gridSize]];
}

proc GridCY {cy gridSize} {
    return [CanvasCY [GoblinCY $cy $gridSize]];
}

proc CurrentCX {cx gridSize} {
    global xscroll c

    return [GridCX [$c canvasx $cx] $gridSize]
}

proc CurrentCY {cy gridSize} {
    global yscroll c

    return [GridCY [$c canvasy $cy] $gridSize]
}



# Placement of Canvas Nodes

proc DrawNode {focussedGraph v nodeType cx cy fillColour} {
    global c IncIn IncOut NodeIndex RevNodeIndex Zoom goblinXZoom goblinYZoom

    set nodeShapeMode [$focussedGraph layout info -nodeShapeMode]
    set shape oval

    if {[string equal $nodeType "real"]} {
        if {$nodeShapeMode==0} {
            set ratio 8
        } else {
            set ratio [expr {
                 ($goblinXZoom+$goblinYZoom)/2 \
                *[$focussedGraph layout info -nodeSize]/100.0 \
                * $Zoom*[$focussedGraph layout info -bendSpacing]/5.0}]
        }

        set xRatio $ratio
        set yRatio $ratio

        if {$nodeShapeMode==2} {
            set shape rectangle
        }

        if {$nodeShapeMode>0} {
            set w [$focussedGraph layout point $v info -successor]

            if {$w!="*"} {
                set xRatio [expr $ratio + [$focussedGraph layout point $w info -cx]*$goblinXZoom*$Zoom];
                set yRatio [expr $ratio + [$focussedGraph layout point $w info -cy]*$goblinYZoom*$Zoom];
            }
        }
    } else {
        set ratio 4
        set xRatio $ratio
        set yRatio $ratio
    }

    set node [$c create $shape \
        [expr {$cx-$xRatio}] [expr {$cy-$yRatio}] \
        [expr {$cx+$xRatio}] [expr {$cy+$yRatio}] \
        -outline black -fill $fillColour -tags node]

    set IncIn($v) {}
    set IncOut($v) {}
    set NodeIndex($node) $v
    set RevNodeIndex($v) $node
}


proc RedrawNode {focussedGraph v nodeType} {
    global c IncIn IncOut NodeIndex RevNodeIndex Zoom goblinXZoom goblinYZoom

    set nodeShapeMode [$focussedGraph layout info -nodeShapeMode]

    if {[string equal $nodeType "real"]} {
        if {$nodeShapeMode==0} {
            set ratio 8
        } else {
            set ratio [expr {
                 ($goblinXZoom+$goblinYZoom)/2.0
                *[$focussedGraph layout info -nodeSize]/100.0
                *$Zoom*[$focussedGraph layout info -bendSpacing]/5.0}]
        }

        set xRatio $ratio
        set yRatio $ratio

        if {$nodeShapeMode>0} {
            set w [$focussedGraph layout point $v info -successor]

            if {$w!="*"} {
                set xRatio [expr $ratio + [$focussedGraph layout point $w info -cx]*$goblinXZoom*$Zoom];
                set yRatio [expr $ratio + [$focussedGraph layout point $w info -cy]*$goblinYZoom*$Zoom];
            }
        }
    } else {
        set ratio 4
        set xRatio $ratio
        set yRatio $ratio
    }

    set Node $RevNodeIndex($v)
    $c coords $Node \
        [expr {[CanvasCX [$focussedGraph node $v info -cx]]-$xRatio}] \
        [expr {[CanvasCY [$focussedGraph node $v info -cy]]-$yRatio}] \
        [expr {[CanvasCX [$focussedGraph node $v info -cx]]+$xRatio}] \
        [expr {[CanvasCY [$focussedGraph node $v info -cy]]+$yRatio}]
}


proc DropNode {focussedGraph v cx cy gridSize} {
    $focussedGraph layout point $v placeAt \
        [GoblinCX $cx $gridSize] [GoblinCY $cy $gridSize]
    RedrawGraph $focussedGraph
}


# Placement of Canvas Arcs

proc DrawArc {focussedGraph a fillColour} {
    global c IncIn IncOut Mode ArcIndex RevArcIndex HandleIndex RevHandleIndex thisCoords

    set u [$focussedGraph arc [expr {2*$a}] info -startNode]
    set v [$focussedGraph arc [expr {2*$a}] info -endNode]
    set w [$focussedGraph arc [expr {2*$a}] info -labelAnchorPoint]

    if {$u==$v && [string equal $w "*"]} {return}

    # Draw Handle

    if {[string equal $w "*"]} {
        set cx [expr {([$focussedGraph node $u info -cx]+[$focussedGraph node $v info -cx])/2}]
        set cy [expr {([$focussedGraph node $u info -cy]+[$focussedGraph node $v info -cy])/2}]
    } else {
        set cx [$focussedGraph layout point $w info -cx]
        set cy [$focussedGraph layout point $w info -cy]
    }

    set handle [$c create rectangle \
        [expr {[CanvasCX $cx]-4}] [expr {[CanvasCY $cy]-4}] \
        [expr {[CanvasCX $cx]+4}] [expr {[CanvasCY $cy]+4}] \
        -outline black -fill black -tags handle]


    # Compute Canvas Coordinates

    if {$w!="*"} {set w [$focussedGraph layout point $w info -successor]}

    set thisCoords [list \
        [CanvasCX [$focussedGraph node $u info -cx]] \
        [CanvasCY [$focussedGraph node $u info -cy]]]

    while {$w!="*"} {
        DrawNode $focussedGraph $w artifical \
            [CanvasCX [$focussedGraph layout point $w info -cx]] \
            [CanvasCY [$focussedGraph layout point $w info -cy]] \
            [NodeColour $focussedGraph $v]
        lappend thisCoords \
            [CanvasCX [$focussedGraph layout point $w info -cx]] \
            [CanvasCY [$focussedGraph layout point $w info -cy]]
        set w [$focussedGraph layout point $w info -successor]
    }

    lappend thisCoords \
        [CanvasCX [$focussedGraph node $v info -cx]] \
        [CanvasCY [$focussedGraph node $v info -cy]]


    # Draw Arc

    set lineCommand "$c create line $thisCoords"
    set widthOption "-width 2"
    set smoothOption ""
    if {[$focussedGraph layout info -arcShapeMode] == 1} {set smoothOption "-smooth on"}
    set dirOption ""
    if {[$focussedGraph arc [expr {2*$a}] info -directed]} {set dirOption "-arrow last"}

    # Handle predecessor arcs

    set fillOption "-fill $fillColour"
    if {[string equal $Mode "set predecessors"]} {
        if {[$focussedGraph node $v info -predecessorArc]==[expr 2*$a]} {
            set widthOption "-width 5"
            set fillOption "-fill [goblin colour predecessors]"

            if {[$focussedGraph node $u info -predecessorArc]==[expr 2*$a+1]} {
                set dirOption "-arrow both"
            } else {
                set dirOption "-arrow last"
            }
        } else {
            if {[$focussedGraph node $u info -predecessorArc]==[expr 2*$a+1]} {
                set widthOption "-width 5"
                set fillOption "-fill [goblin colour predecessors]"
                set dirOption "-arrow first"
            }
        }
    }

    global Zoom goblinXZoom goblinYZoom
    set bendSpacing [$focussedGraph layout info -bendSpacing]
    set nodeSpacing [$focussedGraph layout info -nodeSpacing]
    set arrowSize [expr {($goblinXZoom+$goblinYZoom)/2*[$focussedGraph layout info -arrowSize]/100.0*$Zoom}]
    if {[expr {$bendSpacing/2.0}] < [expr {$nodeSpacing/10.0}]} {
        set arrowSize [expr {$arrowSize*[$focussedGraph layout info -bendSpacing]/2.0}]
    } else {
        set arrowSize [expr {$arrowSize*[$focussedGraph layout info -nodeSpacing]/10.0}]
    }

    set arrowShape "-arrowshape {[expr $arrowSize*2] [expr $arrowSize*3] $arrowSize}"

    set Arc [eval $lineCommand $widthOption $smoothOption $fillOption $dirOption $arrowShape]

    # Set Indices

    $c lower $Arc
    lappend IncIn($v) $Arc
    lappend IncOut($u) $Arc
    set ArcIndex($Arc) $a
    set RevArcIndex($a) $Arc

    set RevHandleIndex($handle) $a
    set HandleIndex($a) $handle
}

proc RedrawArc {focussedGraph a} {
    global c RevArcIndex HandleIndex thisCoords

    set u [$focussedGraph arc [expr {2*$a}] info -startNode]
    set v [$focussedGraph arc [expr {2*$a}] info -endNode]
    set w [$focussedGraph arc [expr {2*$a}] info -labelAnchorPoint]

    if {$u==$v && [string equal $w "*"]} {return}


    # Draw Handle

    if {[string equal $w "*"]} {
        set cx [expr {([$focussedGraph node $u info -cx]+[$focussedGraph node $v info -cx])/2}]
        set cy [expr {([$focussedGraph node $u info -cy]+[$focussedGraph node $v info -cy])/2}]
    } else {
        set cx [$focussedGraph layout point $w info -cx]
        set cy [$focussedGraph layout point $w info -cy]
    }

    $c coords $HandleIndex($a) \
        [expr {[CanvasCX $cx]-4}] [expr {[CanvasCY $cy]-4}] \
        [expr {[CanvasCX $cx]+4}] [expr {[CanvasCY $cy]+4}]


    # Compute Canvas Coordinates

    if {$w!="*"} {set w [$focussedGraph layout point $w info -successor]}

    set thisCoords [list \
        [CanvasCX [$focussedGraph node $u info -cx]] \
        [CanvasCY [$focussedGraph node $u info -cy]]]

    while {$w!="*"} {
        RedrawNode $focussedGraph $w artifical
        lappend thisCoords \
            [CanvasCX [$focussedGraph layout point $w info -cx]] \
            [CanvasCY [$focussedGraph layout point $w info -cy]]
        set w [$focussedGraph layout point $w info -successor]
    }

    lappend thisCoords \
        [CanvasCX [$focussedGraph node $v info -cx]] \
        [CanvasCY [$focussedGraph node $v info -cy]]


    # Draw Arc

    eval $c coords $RevArcIndex($a) $thisCoords
    if {[$focussedGraph arc [expr {2*$a}] info -directed]} {
        $c itemconfigure $RevArcIndex($a) -arrow last
    } else {
        $c itemconfigure $RevArcIndex($a) -arrow none
    }
}


# Initialize Canvas with Graph in GOBLIN Library

proc NodeColour {focussedGraph v} {
    global Mode

    if {[string equal $Mode "set colours"]} {
        return [goblin colour [$focussedGraph node $v info -nodeColour]]
    }

    if {[$focussedGraph info -bipartite] && $v<[$focussedGraph #leftHand]} {
        return sienna
    } else {
        return peru
    }
}

proc EdgeColour {focussedGraph a} {
    global Mode

    if {[string equal $Mode "set colours"]} {
        set thisColour [$focussedGraph arc [expr 2*$a] info -edgeColour]
        if {$thisColour=="*"} {
            # Prevent from drawing white edges
            return [goblin colour maximum]
        } else {
            return [goblin colour $thisColour]
        }
    }

    return black
}

proc RGBColour {rSat gSat bSat} {
    return [format "#%X%X%X" $rSat $gSat $bSat];
}

proc HighlightIncidences {focussedGraph v} {
    global c RevArcIndex ThisNode

    for {set a 0} {$a<[$focussedGraph #edges]} {incr a} {
        $c itemconfigure $RevArcIndex($a) -fill black -width 2
    }

    set aFirst [$focussedGraph node $v info -firstIncidence]

    if {$aFirst=="*"} {break}

    set a $aFirst
    set aNext [$focussedGraph arc $a info -righthandArc]
    set incList [list]
    set lLength 1

    while {$aNext!=$aFirst} {
        lappend incList $a
        set a $aNext
        set aNext [$focussedGraph arc $aNext info -righthandArc]
        incr lLength
    }
    lappend incList $a

    set i 1
    foreach a $incList {
        $c itemconfigure $RevArcIndex([expr $a/2]) -fill \
            [RGBColour \
                [expr 16+round(239.0*($lLength-$i+1)/$lLength)] \
                16 [expr 16+round(239.0*$i/$lLength)] ]
        incr i
    }
}

proc DrawGraph {focussedGraph} {
    global c goblinWallpaper

    for {set v 0} {$v<[$focussedGraph #nodes]} {incr v} {
        DrawNode $focussedGraph $v real \
            [CanvasCX [$focussedGraph node $v info -cx]] \
            [CanvasCY [$focussedGraph node $v info -cy]] \
            [NodeColour $focussedGraph $v]
    }

    if {[$focussedGraph info -sparse]} {
        for {set a 0} {$a<[$focussedGraph #edges]} {incr a} {
            DrawArc $focussedGraph $a [EdgeColour $focussedGraph $a]
        }
    } else {
        for {set v 0} {$v<[$focussedGraph #nodes]} {incr v} {
            set thisPred [$focussedGraph node $v info -predecessorArc]
            if {$thisPred!="*"} {
                DrawArc $focussedGraph [expr $thisPred/2] [EdgeColour $focussedGraph [expr $thisPred/2]]
            }
        }
    }

    if {$goblinWallpaper!=""} {
        image create photo wallpaper -file "$goblinWallpaper"
        $c lower [$c create image 0 0 -image wallpaper -anchor nw]
    }

    BackupScrollPos
}

proc RedrawGraph {focussedGraph} {
    global c CanvasMargin

    for {set v 0} {$v<[$focussedGraph #nodes]} {incr v} {RedrawNode $focussedGraph $v real}

    if {[$focussedGraph info -sparse]} {
        for {set a 0} {$a<[$focussedGraph #edges]} {incr a} {RedrawArc $focussedGraph $a}
    } else {
        for {set v 0} {$v<[$focussedGraph #nodes]} {incr v} {
            set thisPred [$focussedGraph node $v info -predecessorArc]
            if {$thisPred!="*"} {RedrawArc $focussedGraph [expr $thisPred/2]}
        }
    }

    $c configure -scrollregion [list \
        [expr [lindex [$c bbox all] 0]-$CanvasMargin] \
        [expr [lindex [$c bbox all] 1]-$CanvasMargin] \
        [expr [lindex [$c bbox all] 2]+$CanvasMargin] \
        [expr [lindex [$c bbox all] 3]+$CanvasMargin] ]
}



# Editor Event Bindings

proc MakeBalloonEdit {label cx cy} {
    global c
    destroy .balloon
    toplevel .balloon
    append wdSize 100x20+ \
        [expr [winfo rootx $c]+$cx+15] + \
        [expr [winfo rooty $c]+$cy+20]
    wm geometry .balloon $wdSize
    label .balloon.text -text $label -bg yellow -relief solid -anchor w
    pack .balloon.text -fill both

    set wdSize ""
    append wdSize [winfo reqwidth .balloon.text] x \
        [winfo reqheight .balloon.text] + \
        [expr [winfo rootx $c]+$cx+15]  + \
        [expr [winfo rooty $c]+$cy+20]
    wm geometry .balloon $wdSize
    wm overrideredirect .balloon 1
}

proc InitEditor {graphObject} {
    global currentNode currentArc c Mode StartNode EndNode ThisArcIndex \
        MoveNode MoveArc ThisArcIndex2

    InitCanvasRegion
    MakeEditorBar $graphObject

    set currentNode ""
    set currentArc ""
    set ThisNode ""
    set ThisArcIndex ""
    set ThisArcIndex2 ""
    set StartNode ""
    set EndNode ""
    set MoveNode ""
    set MoveArc ""

    PostStatusInfo ""


    $c bind node <Any-Enter> {
        set thisObj [$c find withtag current]
        $c itemconfigure current -width 3 -outline ivory3
        set currentNode $NodeIndex($thisObj)
        MakeBalloonEdit "node index $currentNode" %x %y
    }

    $c bind handle <Any-Enter> {
        set thisObj [$c find withtag current]
        $c itemconfigure current -fill grey -width 3 -outline ivory3
        set currentArc $RevHandleIndex($thisObj)
        MakeBalloonEdit "arc index $currentArc" %x %y
    }

    $c bind node <Any-Leave> {
        set thisObj [$c find withtag current]
        $c itemconfigure current -width 1 -outline black
        set currentNode ""
        DestroyBalloon
    }

    $c bind handle <Any-Leave> {
        set thisObj [$c find withtag current]
        $c itemconfigure current -fill black -width 1 -outline black
        set currentArc ""
        DestroyBalloon
    }

    bind $c <Button-1> {
        set focussedGraph [GetFocussedObject]
        set fineSpacing [$focussedGraph layout info -fineSpacing]
        set bendSpacing [$focussedGraph layout info -bendSpacing]
        set nodeSpacing [$focussedGraph layout info -nodeSpacing]

        if {[string equal $Mode "move nodes"]} {
            if {$currentNode!=""} {
                if {$currentNode<[$focussedGraph #nodes]} {
                    UploadNodeLabels $focussedGraph
                    DownloadNodeLabels $focussedGraph $currentNode
                }

                set MoveNode $currentNode
            }

            if {$currentArc!=""} {
                if {[$focussedGraph arc [expr {2*$currentArc}] info -labelAnchorPoint]=="*"} {
                    set p [$focussedGraph arc [expr {2*$currentArc}] provide -labelAnchorPoint]
                    $focussedGraph layout point $p placeAt \
                        [GoblinCX %x $fineSpacing] [GoblinCY %y $fineSpacing]
                }

                UploadArcLabels $focussedGraph
                DownloadArcLabels $focussedGraph $currentArc
                set MoveArc $currentArc
            }
        }

        if {[string equal $Mode "change labels"]} {
            if {$currentNode!="" && $currentNode<[$focussedGraph #nodes]} {
                UploadNodeLabels $focussedGraph
                DownloadNodeLabels $focussedGraph $currentNode
                source [file join $GoblinTclPath editNodeLabels.tk]}
            if {$currentArc!=""} {
                UploadArcLabels $focussedGraph
                DownloadArcLabels $focussedGraph $currentArc

                source [file join $GoblinTclPath editArcLabels.tk]}
        }

        if {[string equal $Mode "redirect arcs"]} {
            if {$currentArc!=""} {
                $focussedGraph arc [expr {2*$currentArc}] reverse
                RedrawArc $focussedGraph $currentArc
                UploadArcLabels $focussedGraph
                DownloadArcLabels $focussedGraph $currentArc
                PropagateModifications $focussedGraph
            }
        }

        if {[string equal $Mode "sort incidences"]} {
            if {$currentNode!="" && $currentNode<[$focussedGraph #nodes]} {
                if {$ThisNode!=$currentNode || $ThisArcIndex==""} {
                    set ThisArcIndex [$focussedGraph node $currentNode info -firstIncidence]
                    UploadAndDownloadLabels $focussedGraph $currentNode [expr $ThisArcIndex/2]
                    HighlightIncidences $focussedGraph $ThisNode
                } else {
                    $c itemconfigure $RevArcIndex([expr $ThisArcIndex/2]) -width 2
                    set ThisArcIndex [$focussedGraph arc $ThisArcIndex info -righthandArc]
                    UploadAndDownloadLabels $focussedGraph $ThisNode [expr $ThisArcIndex/2]
                }

                MakeHelp "(L) scan/select node  (R) select incidence"
                $c itemconfigure $RevArcIndex([expr $ThisArcIndex/2]) -width 5
            }
        }

        if {[string equal $Mode "shift incidence"]} {
            $focussedGraph node $ThisNode configure -firstIncidence $ThisArcIndex
            set Mode "sort incidences"
            MakeHelp "(L) scan/select node  (R) select incidence"
            HighlightIncidences $focussedGraph $ThisNode
            PropagateModifications $focussedGraph
            break
        }

        if {[string equal $Mode "paste incidence"]} {
            $c itemconfigure $RevArcIndex([expr $ThisArcIndex/2]) -width 2
            set ThisArcIndex [$focussedGraph arc $ThisArcIndex info -righthandArc]
            $c itemconfigure $RevArcIndex([expr $ThisArcIndex/2]) -width 5
            break
        }

        if {[string equal $Mode "insert nodes"]} {
            if {$currentNode == ""} {
                set w [expr [$focussedGraph layout #points]]
                set v [$focussedGraph #nodes]

                # This uses implementational details about index changes!
                if {$w!=$v && [info exists RevNodeIndex($v)]} {
                    # w is a bend node
                    set RevNodeIndex($w) $RevNodeIndex($v)
                    set NodeIndex($RevNodeIndex($w)) $w
                } else {
                    # w is an edge label alignment point
                    # or no artificial nodes exist at all
                    set RevNodeIndex($w) ""
                }

                $focussedGraph node insert
                PropagateModifications $focussedGraph

                DrawNode $focussedGraph $v real [CurrentCX %x $nodeSpacing] \
                    [CurrentCY %y $nodeSpacing] [NodeColour $focussedGraph $v]
                DropNode $focussedGraph $v [CurrentCX %x $nodeSpacing] \
                    [CurrentCY %y $nodeSpacing] $nodeSpacing

                # Allow to drop the new node elsewhere
                UploadNodeLabels $focussedGraph
                DownloadNodeLabels $focussedGraph $v
                set MoveNode $v
            }
        }

        if {[string equal $Mode "set colours"]} {
            if {$currentNode != "" && $currentNode<[$focussedGraph #nodes]} {
                set newColour [$focussedGraph node $currentNode info -nodeColour]

                if {$newColour!="*" && $newColour!=0} {
                    incr newColour -1
                } else {
                    if {$newColour=="*"} {
                        set newColour [expr [$focussedGraph #nodes]-1]
                    } else {
                        set newColour "*"
                    }
                }

                $c itemconfigure current -fill [goblin colour $newColour]
                UploadNodeLabels $focussedGraph
                $focussedGraph node $currentNode configure -nodeColour $newColour
                DownloadNodeLabels $focussedGraph $currentNode
                PropagateModifications $focussedGraph
            }

            if {$currentArc != ""} {
                set newColour [$focussedGraph arc [expr {2*$currentArc}] info -edgeColour]

                if {$newColour=="*" || $newColour!=0} {
                    if {$newColour=="*"} {
                        set newColour [expr [$focussedGraph #edges]-1]
                    } else {
                        incr newColour -1
                    }
                    $c itemconfigure $RevArcIndex($currentArc) \
                        -fill [goblin colour $newColour]
                } else {
                    set newColour "*"
                    $c itemconfigure $RevArcIndex($currentArc) -fill black
                }

                UploadArcLabels $focussedGraph
                $focussedGraph arc [expr {2*$currentArc}] configure -edgeColour $newColour
                DownloadArcLabels $focussedGraph $currentArc
                PropagateModifications $focussedGraph
            }
        }

        if {[string equal $Mode "set predecessors"]} {
            if {$EndNode == ""} {
                if {$currentNode != "" && $currentNode<[$focussedGraph #nodes]} {
                    UploadNodeLabels $focussedGraph
                    set EndNode $currentNode
                    DownloadNodeLabels $focussedGraph $currentNode

                    MakeHelp "(L) set predecessor node/arc  (C) dismiss"
                }
            } else {
                set thisPred ""
                if {$currentNode!="" && $currentNode<[$focussedGraph #nodes]} {
                    set StartNode $currentNode
                    set thisPred [$focussedGraph adjacency $StartNode $EndNode]
                }

                if {$currentArc!=""} {
                    set thisPred [expr 2*$currentArc]
                    if {[$focussedGraph arc $thisPred info -endNode]!=$EndNode} {
                        incr thisPred
                    }
                    set StartNode [$focussedGraph arc $thisPred info -startNode]
                }

                if {$thisPred!="" && [$focussedGraph arc $thisPred info -endNode]==$EndNode} {
                    UploadNodeLabels $focussedGraph
                    UploadArcLabels $focussedGraph
                    $focussedGraph node $EndNode configure -predecessorArc $thisPred
                    DownloadNodeLabels $focussedGraph $EndNode
                    DownloadArcLabels $focussedGraph [expr $thisPred/2]
                    PropagateModifications $focussedGraph
                    set EndNode ""
                    MakeHelp "(L) select node"
                    InitEditor $focussedGraph
                }
            }
        }

        if {[string equal $Mode "insert arcs"]} {
            if {$StartNode == ""} {
                if {$currentNode != "" && $currentNode<[$focussedGraph #nodes]} {
                    set StartNode $currentNode
                    set EndNode ""

                    set ThisCoords [list \
                        [CanvasCX [$focussedGraph node $StartNode info -cx]] \
                        [CanvasCY [$focussedGraph node $StartNode info -cy]] \
                        [CurrentCX %x $nodeSpacing] \
                        [CurrentCY %y $nodeSpacing] ]

                    set smoothOption ""
                    if {[$focussedGraph layout info -arcShapeMode] == 1} {
                        set smoothOption "-smooth on"
                    }
                    set ThisArcIndex2 [eval $c create line $ThisCoords $smoothOption]
                    $c lower $ThisArcIndex2

                    MakeHelp "(L) select end node / place bend  (C) cancel"
                }
            } else {
                set EndNode $currentNode

                if {$currentNode != "" && $currentNode<[$focussedGraph #nodes]} {
                    set u $StartNode
                    set v $currentNode
                    $c delete $ThisArcIndex2
                    set ThisArcIndex2 ""

                    if {[$focussedGraph info -bipartite] && \
                        (($u<[$focussedGraph #leftHand] && $v<[$focussedGraph #leftHand]) || \
                         ($u>=[$focussedGraph #leftHand] && $v>=[$focussedGraph #leftHand])) } \
                    {
                        PostStatusInfo "end nodes must be in different components"
                        set StartNode ""
                        break
                    }

                    set a [$focussedGraph arc insert $u $v]
                    set thisLength [llength $ThisCoords]

                    if {$thisLength>4} {
                        set goblinCoords ""
                        for {set i 0} {$i < $thisLength} {incr i 2} {
                            lappend goblinCoords \
                                [GoblinCX [lindex $ThisCoords $i] $bendSpacing] \
                                [GoblinCY [lindex $ThisCoords [expr $i+1]] $bendSpacing]
                        }

                        set LabelNode [$focussedGraph arc [expr {2*$a}] provide -labelAnchorPoint]
                        $focussedGraph layout point $LabelNode placeAt \
                            [GoblinCX [$c canvasx %x] $fineSpacing] \
                            [GoblinCY [$c canvasy %y] $fineSpacing]

                        set p [$focussedGraph arc [expr {2*$a}] provide -portNode]
                        $focussedGraph layout point $p placeAt \
                            [lindex $goblinCoords 2] [lindex $goblinCoords 3]

                        for {set i 4} {$i < [expr {$thisLength-3}]} {incr i 2} {
                            set p [$focussedGraph layout point $p insertSuccessor]

                            $focussedGraph layout point $p placeAt \
                                [lindex $goblinCoords $i] [lindex $goblinCoords [expr $i+1]]
                        }

                        MakeHelp "(L) align label"
                    } else {
                        set LabelNode ""
                        MakeHelp "(L) align label  (R) automatic alignment"
                    }

                    DrawArc $focussedGraph $a [EdgeColour $focussedGraph $a]

                    if {[info exists HandleIndex($a)]} {
                        UploadArcLabels $focussedGraph
                        DownloadArcLabels $focussedGraph $a
                        set NewArc $a
                        set Mode "align label"

                        PropagateModifications $focussedGraph
                        set StartNode ""
                    } else {
                        MakeHelp "Missing interpolation points"
                        set StartNode ""
                    }
                } else {
                    lappend ThisCoords [CurrentCX %x $bendSpacing] [CurrentCY %y $bendSpacing]
                    eval $c coords $ThisArcIndex2 $ThisCoords
                }
            }
        }

        if {[string equal $Mode "align label"] && $currentNode == ""} {
            if {$LabelNode == ""} {
                set LabelNode [$focussedGraph arc [expr {2*$ThisArc}] provide -labelAnchorPoint]
            }

            $focussedGraph layout point $LabelNode placeAt \
                [GoblinCX [$c canvasx %x] $fineSpacing] \
                [GoblinCY [$c canvasy %y] $fineSpacing]

            set Mode "insert arcs"
            MakeHelp "(L) select start node  (R) add bend nodes"
        }

        if {[string equal $Mode "add bend nodes"]} {
            if {$currentNode!=""} {
                if {$StartNode == ""} {
                    set StartNode $currentNode
                    set EndNode ""

                    set ThisCoords [list \
                        [CanvasCX [$focussedGraph node $StartNode info -cx]] \
                        [CanvasCY [$focussedGraph node $StartNode info -cy]] \
                        [CurrentCX %x $bendSpacing] [CurrentCY %y $bendSpacing] ]

                    set smoothOption ""
                    if {[$focussedGraph layout info -arcShapeMode] == 1} {
                        set smoothOption "-smooth on"
                    }
                    set ThisArcIndex2 [eval $c create line $ThisCoords $smoothOption]
                    $c lower $ThisArcIndex2

                    MakeHelp "(L) place bend node / select segment end node"
                    break
                }

                if {$EndNode == ""} {
                    set EndNode $currentNode
                    $c delete $ThisArcIndex2
                    set ThisArcIndex2 ""

                    # Check if StartNode and EndNode are different, adjacent
                    # with each other and incident with ThisArcIndex

                    set x [$focussedGraph arc $ThisArcIndex info -startNode]
                    set y [$focussedGraph arc $ThisArcIndex info -labelAnchorPoint]
                    if {$y!="*"} {set y [$focussedGraph layout point $y info -successor]}

                    while {$x!=$StartNode && $x!=$EndNode && $y!="*"} {
                        set x $y
                        set y [$focussedGraph layout point $y info -successor]
                    }

                    if {$y=="*"} {set y [$focussedGraph arc $ThisArcIndex info -endNode]}

                    if {($x!=$StartNode && $x!=$EndNode) || \
                        ($y!=$StartNode && $y!=$EndNode) || \
                        ($x==$y)} \
                    {
                        set StartNode ""
                        set Mode "insert arcs"
                        MakeHelp "bad segment end nodes"
                        break
                    }

                    set thisLength [llength $ThisCoords]

                    if {$thisLength == 4} {
                        # No bend nodes were added

                        set StartNode ""
                        set Mode "insert arcs"
                        MakeHelp "(L) select start node  (R) add bend nodes"
                        break
                    }

                    set goblinCoords ""

                    if {$x==$EndNode} {
                        # Revert the order of bend nodes

                        for {set i [expr {$thisLength-4}]} {$i > 1} {incr i -2} {
                            lappend goblinCoords \
                                [GoblinCX [lindex $ThisCoords $i] $bendSpacing] \
                                [GoblinCY [lindex $ThisCoords [expr $i+1]] $bendSpacing]
                        }
                    } else {
                        for {set i 2} {$i < [expr {$thisLength-2}]} {incr i 2} {
                            lappend goblinCoords \
                                [GoblinCX [lindex $ThisCoords $i] $bendSpacing] \
                                [GoblinCY [lindex $ThisCoords [expr $i+1]] $bendSpacing]
                        }
                    }

                    if {[$focussedGraph arc $ThisArcIndex info -labelAnchorPoint] == "*"} {
                        set LabelNode [$focussedGraph arc $ThisArcIndex provide -labelAnchorPoint]
                        $focussedGraph layout point $LabelNode placeAt \
                            [GoblinCX %x $fineSpacing] [GoblinCY %y $fineSpacing] ]
                        set p $LabelNode

                        for {set i 0} {$i < [expr {$thisLength-4}]} {incr i 2} {
                            set p [$focussedGraph layout point $p insertSuccessor]

                            $focussedGraph layout point $p placeAt \
                                [lindex $goblinCoords $i] [lindex $goblinCoords [expr $i+1]]
                        }

                        set Mode "align label"
                        MakeHelp "(L) align label"
                    } else {
                        if {$x==[$focussedGraph arc $ThisArcIndex info -startNode]} {
                            set x [$focussedGraph arc $ThisArcIndex info -labelAnchorPoint]
                        }

                        for {set i 0} {$i < [expr {$thisLength-4}]} {incr i 2} {
                            set x [$focussedGraph layout point $x insertSuccessor]

                            $focussedGraph layout point $x placeAt \
                                [lindex $goblinCoords $i] [lindex $goblinCoords [expr $i+1]]
                        }

                        set Mode "insert arcs"
                        MakeHelp "(L) select start node  (R) add bend nodes"
                    }

                    $c delete $RevArcIndex([expr $ThisArcIndex/2])
                    $c delete $HandleIndex([expr $ThisArcIndex/2])
                    DrawArc $focussedGraph [expr $ThisArcIndex/2] \
                        [EdgeColour $focussedGraph [expr $ThisArcIndex/2]]

                    set StartNode ""

                    PropagateModifications $focussedGraph
                }
            } else {
                lappend ThisCoords [CurrentCX %x $bendSpacing] [CurrentCY %y $bendSpacing]
                eval $c coords $ThisArcIndex2 $ThisCoords
            }
        }

        if {[string equal $Mode "delete objects"]} {
            if {$currentNode != ""} {
                UploadNodeLabels $focussedGraph
                $focussedGraph node $currentNode delete
                DownloadNodeLabels $focussedGraph ""
                PropagateModifications $focussedGraph
                InitEditor $focussedGraph
            }

            if {$currentArc != ""} {
                UploadArcLabels $focussedGraph

                set v [$focussedGraph arc [expr {2*$currentArc}] info -labelAnchorPoint]
                set x [$focussedGraph arc [expr {2*$currentArc}] info -endNode]
                set y [$focussedGraph arc [expr {2*$currentArc}] info -startNode]

                if {$v == "*" || $x == $y} {
                    $focussedGraph arc [expr {2*$currentArc}] delete
                    DownloadArcLabels $focussedGraph ""
                } else {
                    $focussedGraph arc [expr {2*$currentArc}] straightLine
                    DownloadArcLabels $focussedGraph $currentArc
                }

                PropagateModifications $focussedGraph
                InitEditor $focussedGraph
            }
        }
    }

    bind $c <B1-Motion> {
        set focussedGraph [GetFocussedObject]

        if {[string equal $Mode "move nodes"] || \
            [string equal $Mode "insert nodes"]} \
        {
            if {$MoveNode != ""} {
                if {$MoveNode<[$focussedGraph #nodes]} {
                    set nodeSpacing [$focussedGraph layout info -nodeSpacing]
                    DropNode $focussedGraph $MoveNode [CurrentCX %x $nodeSpacing] \
                        [CurrentCY %y $nodeSpacing] $nodeSpacing
                    DownloadNodeLabels $focussedGraph $MoveNode
                } else {
                    set bendSpacing [$focussedGraph layout info -bendSpacing]
                    DropNode $focussedGraph $MoveNode [CurrentCX %x $bendSpacing] \
                        [CurrentCY %y $bendSpacing] $bendSpacing
                }
            }

            if {$MoveArc != ""} {
                DropNode $focussedGraph \
                    [$focussedGraph arc [expr {2*$MoveArc}] info -labelAnchorPoint] \
                    [$c canvasx %x] [$c canvasy %y] [$focussedGraph layout info -fineSpacing]
            }
        }
    }

    bind $c <Motion> {
        set focussedGraph [GetFocussedObject]

        if {[string equal $Mode "insert arcs"] || \
            [string equal $Mode "add bend nodes"]} \
        {
            set bendSpacing [$focussedGraph layout info -bendSpacing]

            if {$StartNode != ""} {
                set thisLength [llength $ThisCoords]
                set thiscx [CurrentCX %x $bendSpacing]
                set thiscy [CurrentCY %y $bendSpacing]

                if {[$focussedGraph layout info -arcShapeMode]==2} {
                    set prevcx [lindex $ThisCoords [expr {$thisLength-4}]]
                    set prevcy [lindex $ThisCoords [expr {$thisLength-3}]]

                    if {$thiscx > $prevcx} {
                        set diffcx [expr {$thiscx-$prevcx}]
                    } else {
                        set diffcx [expr {$prevcx-$thiscx}]
                    }

                    if {$thiscy > $prevcy} {
                        set diffcy [expr {$thiscy-$prevcy}]
                    } else {
                        set diffcy [expr {$prevcy-$thiscy}]
                    }

                    if {$diffcx*0.95>$diffcy} {set thiscy $prevcy}
                    if {$diffcx<$diffcy*0.95} {set thiscx $prevcx}
                }

                set ThisCoords \
                    [lreplace $ThisCoords \
                        [expr {$thisLength-2}] \
                        [expr {$thisLength-1}] \
                        $thiscx $thiscy \
                    ]
                eval $c coords $ThisArcIndex2 $ThisCoords
            }
        }

        if {[string equal $Mode "align label"]} {
            set fineSpacing [$focussedGraph layout info -fineSpacing]
            $c coords $HandleIndex($ThisArc) \
                [expr {[CurrentCX %x $fineSpacing]-4}] \
                [expr {[CurrentCY %y $fineSpacing]-4}] \
                [expr {[CurrentCX %x $fineSpacing]+4}] \
                [expr {[CurrentCY %y $fineSpacing]+4}]
        }
    }

    bind $c <ButtonRelease-1> {
        set focussedGraph [GetFocussedObject]

        if {[string equal $Mode "move nodes"] || \
            [string equal $Mode "insert nodes"]} \
        {
            if {$MoveNode != ""} {
                set MoveNode ""
                PropagateModifications $focussedGraph
            }

            if {$MoveArc != ""} {
                set MoveArc ""
                PropagateModifications $focussedGraph
            }
        }
    }

    bind $c <Button-3> {
        set focussedGraph [GetFocussedObject]

        if {[string equal $Mode "insert nodes"]} {
            if {![$focussedGraph info -bipartite]} {break}

            if {$currentNode == ""} {break}

            if {[$focussedGraph node $currentNode first]=="*"} {
                UploadNodeLabels $focussedGraph
                $focussedGraph node $currentNode swap
                DownloadNodeLabels $focussedGraph ""
                InitEditor $focussedGraph
                PropagateModifications $focussedGraph
                MakeHelp "(L) place new node  (R) Move to other component"
            } else {
                PostStatusInfo "node must be isolated"
            }
        }

        if {[string equal $Mode "insert arcs"]} {
            if {$ThisArcIndex2 != ""} {
                $c delete $ThisArcIndex2
                set ThisArcIndex2 ""
                set StartNode ""
                MakeHelp "(L) select start node  (R) add bend nodes"
            } else {
                if {$currentArc == ""} {break}

                set ThisArcIndex [expr {2*$currentArc}]
                set StartNode ""
                UploadArcLabels $focussedGraph
                DownloadArcLabels $focussedGraph $currentArc
                set Mode "add bend nodes"
                MakeHelp "(L) select segment start node"
            }
        }

        if {[string equal $Mode "sort incidences"] && $ThisNode!=""} {
            set Mode "shift incidence"
            MakeHelp "(L) mark as first  (R) cut incidence"
            break
        }

        if {[string equal $Mode "shift incidence"]} {
            set Mode "paste incidence"
            MakeHelp "(L) scan incidences  (R) paste here"
            set ThisCutArc $ThisArcIndex
            break
        }

        if {[string equal $Mode "paste incidence"]} {
            set Mode "sort incidences"
            MakeHelp "(L) scan/select node  (R) select incidence"
            $focussedGraph arc $ThisArcIndex configure -righthandArc $ThisCutArc
            HighlightIncidences $focussedGraph $ThisNode
            PropagateModifications $focussedGraph
            break
        }

        if {[string equal $Mode "redirect arcs"]} {
            if {   $currentArc!=""                   \
                && ![$focussedGraph info -undirected]   \
                && ![$focussedGraph info -directed]   } \
            {
                $focussedGraph arc [expr {2*$currentArc}] configure -directed \
                    [expr {1-[$focussedGraph arc [expr {2*$currentArc}] info -directed]}]
                RedrawArc $focussedGraph $currentArc
                UploadArcLabels $focussedGraph
                DownloadArcLabels $focussedGraph $currentArc
                PropagateModifications $focussedGraph
            }
        }

        if {[string equal $Mode "align label"] && $LabelNode==""} {
            $c delete $HandleIndex($NewArc)
            $c delete $RevArcIndex($NewArc)
            DrawArc $focussedGraph $NewArc [EdgeColour $focussedGraph $NewArc]
            set Mode "insert arcs"
            MakeHelp "(L) select start node"
        }

        if {[string equal $Mode "set colours"]} {
            if {$currentNode != "" && $currentNode<[$focussedGraph #nodes]} {
                set newColour [$focussedGraph node $currentNode info -nodeColour]

                if {$newColour!="*" && $newColour<[expr [$focussedGraph #nodes]-1]} {
                    incr newColour
                } else {
                    if {$newColour=="*"} {
                        set newColour 0
                    } else {
                        set newColour "*"
                    }
                }

                $c itemconfigure current -fill [goblin colour $newColour]
                UploadNodeLabels $focussedGraph
                $focussedGraph node $currentNode configure -nodeColour $newColour
                DownloadNodeLabels $focussedGraph $currentNode
                PropagateModifications $focussedGraph
            }

            if {$currentArc != ""} {
                set newColour [$focussedGraph arc [expr {2*$currentArc}] info -edgeColour]

                if {$newColour=="*" || $newColour!=[expr [$focussedGraph #edges]-1]} {
                    if {$newColour=="*"} {
                        set newColour 0
                    } else {
                        incr newColour
                    }
                    $c itemconfigure $RevArcIndex($currentArc) \
                        -fill [goblin colour $newColour]
                } else {
                    set newColour "*"
                    $c itemconfigure $RevArcIndex($currentArc) -fill black
                }

                UploadArcLabels $focussedGraph
                $focussedGraph arc [expr {2*$currentArc}] configure -edgeColour $newColour
                DownloadArcLabels $focussedGraph $currentArc
                PropagateModifications $focussedGraph
            }
        }

        if {[string equal $Mode "set predecessors"]} {
            if {$EndNode != ""} {
                UploadNodeLabels $focussedGraph
                $focussedGraph node $EndNode configure -predecessorArc "*"
                PropagateModifications $focussedGraph
                DownloadNodeLabels $focussedGraph $EndNode
                set EndNode ""
                MakeHelp "(L) select node"
                InitEditor $focussedGraph
            }
        }

        if {[string equal $Mode "delete objects"]} {
            if {$currentArc != ""} {
                UploadArcLabels $focussedGraph
                $focussedGraph arc [expr {2*$currentArc}] contract
                DownloadArcLabels $focussedGraph $currentArc
                PropagateModifications $focussedGraph
                InitEditor $focussedGraph
            }
        }
    }

    DrawGraph $graphObject
}
