core-extra/gui/editor.tcl

5351 lines
156 KiB
Tcl

#
# Copyright 2004-2008 University of Zagreb, Croatia.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# This work was supported in part by the Croatian Ministry of Science
# and Technology through the research contract #IP-2003-143.
#
#****h* imunes/editor.tcl
# NAME
# editor.tcl -- file used for defining functions that can be used in
# edit mode as well as all the functions which change the appearance
# of the imunes GUI.
# FUNCTION
# This module is used for defining all possible actions in imunes
# edit mode. It is also used for all the GUI related actions.
#****
proc animateCursor {} {
global cursorState
global clock_seconds
if { [clock seconds] == $clock_seconds } {
update
return
}
set clock_seconds [clock seconds]
if { $cursorState } {
.c config -cursor watch
set cursorState 0
} else {
.c config -cursor pirate
set cursorState 1
}
update
}
#****f* editor.tcl/removeGUILink
# NAME
# removeGUILink -- remove link from GUI
# SYNOPSIS
# renoveGUILink $link_id $atomic
# FUNCTION
# Removes link from GUI. It removes standard links as well as
# split links and links connecting nodes on different canvases.
# INPUTS
# * link_id -- the link id
# * atomic -- defines if the remove was atomic action or a part
# of a composed, non-atomic action (relevant for updating log
# for undo).
#****
proc removeGUILink { link atomic } {
global changed
set nodes [linkPeers $link]
set node1 [lindex $nodes 0]
set node2 [lindex $nodes 1]
if { [nodeType $node1] == "pseudo" } {
removeLink [getLinkMirror $link]
removeLink $link
removeNode [getNodeMirror $node1]
removeNode $node1
.c delete $node1
} elseif { [nodeType $node2] == "pseudo" } {
removeLink [getLinkMirror $link]
removeLink $link
removeNode [getNodeMirror $node2]
removeNode $node2
.c delete $node2
} else {
removeLink $link
}
.c delete $link
if { $atomic == "atomic" } {
set changed 1
updateUndoLog
}
}
#****f* editor.tcl/removeGUINode
# NAME
# removeGUINode -- remove node from GUI
# SYNOPSIS
# renoveGUINode $node_id
# FUNCTION
# Removes node from GUI. When removing a node from GUI the links
# connected to that node are also removed.
# INPUTS
# * node_id -- node id
#****
proc removeGUINode { node } {
set type [nodeType $node]
foreach ifc [ifcList $node] {
set peer [peerByIfc $node $ifc]
set link [lindex [.c gettags "link && $node && $peer"] 1]
removeGUILink $link non-atomic
}
if { [lsearch -exact "oval rectangle label text marker" $type] != -1 } {
deleteAnnotation .c $type $node
} elseif { $type != "pseudo" } {
removeNode $node
.c delete $node
}
}
#****f* editor.tcl/updateUndoLog
# NAME
# updateUndoLog -- update the undo log
# SYNOPSIS
# updateUndoLog
# FUNCTION
# Updates the undo log. Writes the current configuration to the
# undolog array and updates the undolevel variable.
#****
proc updateUndoLog {} {
global changed undolog undolevel redolevel
if { $changed } {
global t_undolog undolog
set t_undolog ""
dumpCfg string t_undolog
incr undolevel
set undolog($undolevel) $t_undolog
set redolevel $undolevel
updateUndoRedoMenu ""
# Boeing: XXX why is this set here?
set changed 0
}
}
#****f* editor.tcl/undo
# NAME
# undo -- undo function
# SYNOPSIS
# undo
# FUNCTION
# Undo the change. Reads the undolog and updates the current
# configuration. Reduces the value of undolevel.
#****
proc undo {} {
global undolevel undolog oper_mode
if {$oper_mode == "edit" && $undolevel > 0} {
incr undolevel -1
updateUndoRedoMenu ""
.c config -cursor watch
loadCfg $undolog($undolevel)
switchCanvas none
}
}
#****f* editor.tcl/redo
# NAME
# redo
# SYNOPSIS
# redo
# FUNCTION
# Redo the change if possible (redolevel is greater than
# undolevel). Reads the configuration from undolog and
# updates the current configuration. Increases the value
# of undolevel.
#****
proc redo {} {
global undolevel redolevel undolog oper_mode
if {$oper_mode == "edit" && $redolevel > $undolevel} {
incr undolevel
updateUndoRedoMenu ""
.c config -cursor watch
loadCfg $undolog($undolevel)
switchCanvas none
}
}
proc updateUndoRedoMenu { forced } {
global undolevel redolevel
if { $forced == "" } {
if { $undolevel > 0 } { set undo "normal" } else { set undo "disabled" }
if { $redolevel > $undolevel } { set redo "normal"
} else { set redo "disabled" }
} else {
set undo $forced
set redo $forced
}
.menubar.edit entryconfigure "Undo" -state $undo
.menubar.edit entryconfigure "Redo" -state $redo
}
#****f* editor.tcl/redrawAll
# NAME
# redrawAll
# SYNOPSIS
# redrawAll
# FUNCTION
# Redraws all the objects on the current canvas.
#****
proc redrawAll {} {
global node_list plot_list link_list annotation_list plot_list background sizex sizey grid
global curcanvas zoom
global showAnnotations showGrid
#Call_Trace ;# debugging when things disappear
.bottom.zoom config -text "zoom [expr {int($zoom * 100)}]%"
set e_sizex [expr {int($sizex * $zoom)}]
set e_sizey [expr {int($sizey * $zoom)}]
set border 28
.c configure -scrollregion \
"-$border -$border [expr {$e_sizex + $border}] \
[expr {$e_sizey + $border}]"
saveRestoreWlanLinks .c save
.c delete all
set background [.c create rectangle 0 0 $e_sizex $e_sizey \
-fill white -tags "background"]
# Boeing: wallpaper
set wallpaper [lindex [getCanvasWallpaper $curcanvas] 0]
set wallpaperStyle [lindex [getCanvasWallpaper $curcanvas] 1]
if { $wallpaper != "" } {
drawWallpaper .c $wallpaper $wallpaperStyle
}
# end Boeing
if { $showAnnotations == 1 } {
foreach obj $annotation_list {
# fix annotations having no canvas (from old config)
if { [getNodeCanvas $obj] == "" } { setNodeCanvas $obj $curcanvas}
if { [getNodeCanvas $obj] == $curcanvas } {
drawAnnotation $obj
}
}
}
# Grid
set e_grid [expr {int($grid * $zoom)}]
set e_grid2 [expr {$e_grid * 2}]
if { $showGrid } {
for { set x $e_grid } { $x < $e_sizex } { incr x $e_grid } {
if { [expr {$x % $e_grid2}] != 0 } {
if { $zoom > 0.5 } {
.c create line $x 1 $x $e_sizey \
-fill gray -dash {1 7} -tags "grid"
}
} else {
.c create line $x 1 $x $e_sizey -fill gray -dash {1 3} \
-tags "grid"
}
}
for { set y $e_grid } { $y < $e_sizey } { incr y $e_grid } {
if { [expr {$y % $e_grid2}] != 0 } {
if { $zoom > 0.5 } {
.c create line 1 $y $e_sizex $y \
-fill gray -dash {1 7} -tags "grid"
}
} else {
.c create line 1 $y $e_sizex $y -fill gray -dash {1 3} \
-tags "grid"
}
}
}
.c lower -withtags background
foreach node $node_list {
if { [getNodeCanvas $node] == $curcanvas } {
drawNode .c $node
}
}
redrawAllThruplots
foreach link $link_list {
set nodes [linkPeers $link]
if { [getNodeCanvas [lindex $nodes 0]] != $curcanvas ||
[getNodeCanvas [lindex $nodes 1]] != $curcanvas } {
continue
}
drawLink $link
redrawLink $link
updateLinkLabel $link
}
saveRestoreWlanLinks .c restore
.c config -cursor left_ptr
raiseAll .c
}
#****f* editor.tcl/drawNode
# NAME
# drawNode
# SYNOPSIS
# drawNode node_id
# FUNCTION
# Draws the specified node. Draws node's image (router pc
# host lanswitch rj45 hub pseudo) and label.
# The visibility of the label depends on the showNodeLabels
# variable for all types of nodes and on invisible variable
# for pseudo nodes.
# INPUTS
# * node_id -- node id
#****
proc drawNode { c node } {
global showNodeLabels
global router pc host lanswitch rj45 hub pseudo OVS
global curcanvas zoom
global wlan
if { $c == "" } { set c .c } ;# default canvas
set type [nodeType $node]
set coords [getNodeCoords $node]
set x [expr {[lindex $coords 0] * $zoom}]
set y [expr {[lindex $coords 1] * $zoom}]
# special handling for custom images, dummy nodes
# could move this to separate getImage function
set model ""
set cimg ""
set imgzoom $zoom
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
if { $type == "router" || $type == "OVS" } {
set model [getNodeModel $node]
set cimg [getNodeTypeImage $model normal]
}
set tmp [absPathname [getCustomImage $node]]
if { $tmp != "" } { set cimg $tmp }
if { $cimg != "" } {
# name of global variable storing the image is the filename without path
set img [file tail $cimg]
# create the variable if the image hasn't been loaded before
global [set img]
if { ![info exists $img] } {
if { [catch {
set [set img] [image create photo -file $cimg]
createScaledImages $img
} e ] } { ;# problem loading image file
puts "icon error: $e"
set cimg "" ;# fall back to default model icon
setCustomImage $node "" ;# prevent errors elsewhere
}
}
if { $cimg != "" } { ;# only if image file loaded
global $img$imgzoom
$c create image $x $y -image [set $img$imgzoom] -tags "node $node"
}
}
if { $cimg == "" } {
if { $type == "pseudo" } {
$c create image $x $y -image [set $type] -tags "node $node"
} else {
# create scaled images based on zoom level
global $type$imgzoom
$c create image $x $y -image [set $type$imgzoom] \
-tags "node $node"
}
}
set coords [getNodeLabelCoords $node]
set x [expr {[lindex $coords 0] * $zoom}]
set y [expr {[lindex $coords 1] * $zoom}]
if { [nodeType $node] != "pseudo" } { ;# Boeing: show remote server
set loc [getNodeLocation $node]
set labelstr0 ""
if { $loc != "" } { set labelstr0 "([getNodeLocation $node]):" }
set labelstr1 [getNodeName $node];
set labelstr2 ""
if [info exists getNodePartition] { [getNodePartition $node]; }
set l [format "%s%s\n%s" $labelstr0 $labelstr1 $labelstr2];
set label [$c create text $x $y -fill blue \
-text "$l" \
-tags "nodelabel $node"]
} else {
set pnode [getNodeName $node]
set pcanvas [getNodeCanvas $pnode]
set ifc [ifcByPeer $pnode [getNodeMirror $node]]
if { $pcanvas != $curcanvas } {
set label [$c create text $x $y -fill blue \
-text "[getNodeName $pnode]:$ifc
@[getCanvasName $pcanvas]" \
-tags "nodelabel $node" -justify center]
} else {
set label [$c create text $x $y -fill blue \
-text "[getNodeName $pnode]:$ifc" \
-tags "nodelabel $node" -justify center]
}
}
if { $showNodeLabels == 0} {
$c itemconfigure $label -state hidden
}
global invisible
if { $invisible == 1 && [nodeType $node] == "pseudo" } {
$c itemconfigure $label -state hidden
}
}
#****f* editor.tcl/drawLink
# NAME
# drawLink
# SYNOPSIS
# drawLink link_id
# FUNCTION
# Draws the specified link. An arrow is displayed for links
# connected to pseudo nodes. If the variable invisible
# is specified link connecting a pseudo node stays hidden.
# INPUTS
# * link_id -- link id
#****
proc drawLink { link } {
set nodes [linkPeers $link]
set lnode1 [lindex $nodes 0]
set lnode2 [lindex $nodes 1]
set lwidth [getLinkWidth $link]
if { [getLinkMirror $link] != "" } {
set newlink [.c create line 0 0 0 0 \
-fill [getLinkColor $link] -width $lwidth \
-tags "link $link $lnode1 $lnode2" -arrow both]
} else {
set newlink [.c create line 0 0 0 0 \
-fill [getLinkColor $link] -width $lwidth \
-tags "link $link $lnode1 $lnode2"]
}
# Boeing: links between two nodes on different servers
if { [getNodeLocation $lnode1] != [getNodeLocation $lnode2]} {
.c itemconfigure $newlink -dash ",";
}
# end Boeing
# XXX Invisible pseudo-liks
global invisible
if { $invisible == 1 && [getLinkMirror $link] != "" } {
.c itemconfigure $link -state hidden
}
# Boeing: wlan links are hidden
if { [nodeType $lnode1] == "wlan" || [nodeType $lnode2] == "wlan" } {
global zoom
set imgzoom $zoom
if { $zoom == 0.75 || $zoom == 1.5 } { set imgzoom 1.0 }
global antenna$imgzoom
.c itemconfigure $link -state hidden
.c create image 0 0 -image [set antenna$imgzoom] \
-tags "antenna $lnode2 $link"
.c create text 0 0 -tags "interface $lnode1 $link" -justify center
.c create text 0 0 -tags "interface $lnode2 $link" -justify center
.c raise interface "link || linklabel || background"
} else {
.c raise $newlink background
.c create text 0 0 -tags "linklabel $link" -justify center
.c create text 0 0 -tags "interface $lnode1 $link" -justify center
.c create text 0 0 -tags "interface $lnode2 $link" -justify center
.c raise linklabel "link || background"
.c raise interface "link || linklabel || background"
}
foreach n [list $lnode1 $lnode2] {
if { [getNodeHidden $n] } {
hideNode $n
statline "Hidden node(s) exist."
}
}
}
# draw a green link between wireless nodes (or other color if multiple WLANs)
# WLAN links appear on the canvas but not in the global link_list
proc drawWlanLink { node1 node2 wlan } {
global zoom defLinkWidth curcanvas
set c .c
set wlanlink [$c find withtag "wlanlink && $node1 && $node2 && $wlan"]
if { $wlanlink != "" } {
return $wlanlink ;# already exists
}
set color [getWlanColor $wlan]
set xy [getNodeCoords $node1]
set x [lindex $xy 0]; set y [lindex $xy 1]
set pxy [getNodeCoords $node2]
set px [lindex $pxy 0]; set py [lindex $pxy 1]
set wlanlink [$c create line [expr {$x*$zoom}] [expr {$y*$zoom}] \
[expr {$px*$zoom}] [expr {$py*$zoom}] \
-fill $color -width $defLinkWidth \
-tags "wlanlink $node1 $node2 $wlan"]
if { [getNodeCanvas $node1] == $curcanvas &&
[getNodeCanvas $node2] == $curcanvas} {
$c itemconfigure $wlanlink -state normal
$c raise $wlanlink "background || grid || oval || rectangle"
} else {
$c itemconfigure $wlanlink -state hidden
}
return $wlanlink
}
#****f* editor.tcl/chooseIfName
# NAME
# chooseIfName -- choose interface name
# SYNOPSIS
# set ifcName [chooseIfName $lnode1 $lnode2]
# FUNCTION
# Choose intreface name. The name can be:
# * eth -- for interface connecting pc, host and router
# * e -- for interface connecting hub and lanswitch
# INPUTS
# * link_id -- link id
# RESULT
# * ifcName -- the name of the interface
#****
proc chooseIfName { lnode1 lnode2 } {
global $lnode1 $lnode2
# TODO: just check if layer == NETWORK and return eth, LINK return e
switch -exact -- [nodeType $lnode1] {
pc {
return eth
}
host {
return eth
}
hub {
return e
}
lanswitch {
return e
}
router {
return eth
}
rj45 {
return
}
tunnel {
return e
}
ktunnel {
return
}
wlan {
return e
}
default {
return eth
# end Boeing: below
}
}
}
#****f* editor.tcl/listLANNodes
# NAME
# listLANNodes -- list LAN nodes
# SYNOPSIS
# set l2peers [listLANNodes $l2node $l2peers]
# FUNCTION
# Recursive function for finding all link layer nodes that are
# connected to node l2node. Returns the list of all link layer
# nodes that are on the same LAN as l2node.
# INPUTS
# * l2node -- node id of a link layer node
# * l2peers -- old link layer nodes on the same LAN
# RESULT
# * l2peers -- new link layer nodes on the same LAN
#****
proc listLANnodes { l2node l2peers } {
lappend l2peers $l2node
foreach ifc [ifcList $l2node] {
set peer [logicalPeerByIfc $l2node $ifc]
set type [nodeType $peer]
# Boeing
if { [ lsearch {lanswitch hub wlan} $type] != -1 } {
if { [lsearch $l2peers $peer] == -1 } {
set l2peers [listLANnodes $peer $l2peers]
}
}
}
return $l2peers
}
#****f* editor.tcl/calcDxDy
# NAME
# calcDxDy lnode -- list LAN nodes
# SYNOPSIS
# calcDxDy $lnode
# FUNCTION
# Calculates dx and dy variables of the calling function.
# INPUTS
# * lnode -- node id of a node whose dx and dy coordinates are
# calculated
#****
proc calcDxDy { lnode } {
global showIfIPaddrs showIfIPv6addrs zoom
upvar dx x
upvar dy y
if { $zoom > 1.0 } {
set x 1
set y 1
return
}
switch -exact -- [nodeType $lnode] {
hub {
set x [expr {1.5 / $zoom}]
set y [expr {2.6 / $zoom}]
}
lanswitch {
set x [expr {1.5 / $zoom}]
set y [expr {2.6 / $zoom}]
}
router {
set x [expr {1 / $zoom}]
set y [expr {2 / $zoom}]
}
rj45 {
set x [expr {1 / $zoom}]
set y [expr {1 / $zoom}]
}
tunnel {
set x [expr {1 / $zoom}]
set y [expr {1 / $zoom}]
}
wlan {
set x [expr {1.5 / $zoom}]
set y [expr {2.6 / $zoom}]
}
default {
set x [expr {1 / $zoom}]
set y [expr {2 / $zoom}]
}
}
return
}
#****f* editor.tcl/updateIfcLabel
# NAME
# updateIfcLabel -- update interface label
# SYNOPSIS
# updateIfcLabel $lnode1 $lnode2
# FUNCTION
# Updates the interface label, including interface name,
# interface state (* for interfaces that are down), IPv4
# address and IPv6 address.
# INPUTS
# * lnode1 -- node id of a node where the interface resides
# * lnode2 -- node id of the node that is connected by this
# interface.
#****
proc updateIfcLabel { lnode1 lnode2 } {
global showIfNames showIfIPaddrs showIfIPv6addrs
set link [lindex [.c gettags "link && $lnode1 && $lnode2"] 1]
set ifc [ifcByPeer $lnode1 $lnode2]
set ifipv4addr [getIfcIPv4addr $lnode1 $ifc]
set ifipv6addr [getIfcIPv6addr $lnode1 $ifc]
if { $ifc == 0 } {
set ifc ""
}
if { [getIfcOperState $lnode1 $ifc] == "down" } {
set labelstr "*"
} else {
set labelstr ""
}
if { $showIfNames } {
set labelstr "$labelstr$ifc
"
}
if { $showIfIPaddrs && $ifipv4addr != "" } {
set labelstr "$labelstr$ifipv4addr
"
}
if { $showIfIPv6addrs && $ifipv6addr != "" } {
set labelstr "$labelstr$ifipv6addr
"
}
set labelstr \
[string range $labelstr 0 [expr {[string length $labelstr] - 2}]]
.c itemconfigure "interface && $lnode1 && $link" \
-text "$labelstr"
# Boeing: hide ifc label on wlans
if { [nodeType $lnode1] == "wlan" } {
.c itemconfigure "interface && $lnode1 && $link" -state hidden
}
}
#****f* editor.tcl/updateLinkLabel
# NAME
# updateLinkLabel -- update link label
# SYNOPSIS
# updateLinkLabel $link
# FUNCTION
# Updates the link label, including link bandwidth, link delay,
# BER and duplicate values.
# INPUTS
# * link -- link id of the link whose labels are updated.
#****
proc updateLinkLabel { link } {
global showLinkLabels
set bwstr [getLinkBandwidthString $link]
set delstr [getLinkDelayString $link]
set berstr [getLinkBERString $link]
set dupstr [getLinkDupString $link]
set labelstr "
"
if { "$bwstr" != "" } {
set labelstr "$labelstr$bwstr
"
}
if { "$delstr" != "" } {
set labelstr "$labelstr$delstr
"
}
if { "$berstr" != "" } {
set labelstr "$labelstr$berstr
"
}
if { "$dupstr" != "" } {
set labelstr "$labelstr$dupstr
"
}
set labelstr \
[string range $labelstr 0 [expr {[string length $labelstr] - 2}]]
.c itemconfigure "linklabel && $link" -text "$labelstr"
if { $showLinkLabels == 0} {
.c itemconfigure "linklabel && $link" -state hidden
}
}
#****f* editor.tcl/redrawAllLinks
# NAME
# redrawAllLinks -- redraw all links
# SYNOPSIS
# redrawAllLinks
# FUNCTION
# Redraws all links on the current canvas.
#****
proc redrawAllLinks {} {
global link_list curcanvas
foreach link $link_list {
set nodes [linkPeers $link]
if { [getNodeCanvas [lindex $nodes 0]] != $curcanvas ||
[getNodeCanvas [lindex $nodes 1]] != $curcanvas } {
continue
}
redrawLink $link
}
}
#****f* editor.tcl/redrawLink
# NAME
# redrawLink -- redraw a links
# SYNOPSIS
# redrawLink $link
# FUNCTION
# Redraws the specified link.
# INPUTS
# * link -- link id
#****
proc redrawLink { link } {
global $link
set limages [.c find withtag "link && $link"]
set limage1 [lindex $limages 0]
set limage2 [lindex $limages 1]
set tags [.c gettags $limage1]
set link [lindex $tags 1]
set lnode1 [lindex $tags 2]
set lnode2 [lindex $tags 3]
set coords1 [.c coords "node && $lnode1"]
set coords2 [.c coords "node && $lnode2"]
set x1 [lindex $coords1 0]
set y1 [lindex $coords1 1]
set x2 [lindex $coords2 0]
set y2 [lindex $coords2 1]
.c coords $limage1 $x1 $y1 $x2 $y2
.c coords $limage2 $x1 $y1 $x2 $y2
set lx [expr {0.5 * ($x1 + $x2)}]
set ly [expr {0.5 * ($y1 + $y2)}]
.c coords "linklabel && $link" $lx $ly
set n [expr {sqrt (($x1 - $x2) * ($x1 - $x2) + \
($y1 - $y2) * ($y1 - $y2)) * 0.015}]
if { $n < 1 } {
set n 1
}
calcDxDy $lnode1
set lx [expr {($x1 * ($n * $dx - 1) + $x2) / $n / $dx}]
set ly [expr {($y1 * ($n * $dy - 1) + $y2) / $n / $dy}]
.c coords "interface && $lnode1 && $link" $lx $ly
updateIfcLabel $lnode1 $lnode2
calcDxDy $lnode2
set lx [expr {($x1 + $x2 * ($n * $dx - 1)) / $n / $dx}]
set ly [expr {($y1 + $y2 * ($n * $dy - 1)) / $n / $dy}]
.c coords "interface && $lnode2 && $link" $lx $ly
updateIfcLabel $lnode2 $lnode1
# Boeing - wlan antennas
if { [nodeType $lnode1] == "wlan" } {
global zoom
set an [lsearch -exact [findWlanNodes $lnode2] $lnode1]
if { $an < 0 || $an >= 5 } { set an 0 }
set dx [expr {20 - (10*$an)}]
.c coords "antenna && $lnode2 && $link" [expr {$x2-($dx*$zoom)}] \
[expr {$y2-(20*$zoom)}]
}
}
# Boeing
proc redrawWlanLink { link } {
global $link
set tags [.c gettags $link]
set lnode1 [lindex $tags 1]
set lnode2 [lindex $tags 2]
set coords1 [.c coords "node && $lnode1"]
set coords2 [.c coords "node && $lnode2"]
set x1 [lindex $coords1 0]
set y1 [lindex $coords1 1]
set x2 [lindex $coords2 0]
set y2 [lindex $coords2 1]
set lx [expr {0.5 * ($x1 + $x2)}]
set ly [expr {0.5 * ($y1 + $y2)}]
.c coords $link $x1 $y1 $x2 $y2
.c coords "linklabel && $lnode2 && $lnode1" $lx $ly
return
}
# end Boeing
#****f* editor.tcl/splitGUILink
# NAME
# splitGUILink -- splits a links
# SYNOPSIS
# splitGUILink $link
# FUNCTION
# Splits the link and draws new links and new pseudo nodes
# on the canvas.
# INPUTS
# * link -- link id
#****
proc splitGUILink { link } {
global changed zoom
set peer_nodes [linkPeers $link]
set new_nodes [splitLink $link pseudo]
set orig_node1 [lindex $peer_nodes 0]
set orig_node2 [lindex $peer_nodes 1]
set new_node1 [lindex $new_nodes 0]
set new_node2 [lindex $new_nodes 1]
set new_link1 [linkByPeers $orig_node1 $new_node1]
set new_link2 [linkByPeers $orig_node2 $new_node2]
setLinkMirror $new_link1 $new_link2
setLinkMirror $new_link2 $new_link1
setNodeMirror $new_node1 $new_node2
setNodeMirror $new_node2 $new_node1
setNodeName $new_node1 $orig_node2
setNodeName $new_node2 $orig_node1
set x1 [lindex [getNodeCoords $orig_node1] 0]
set y1 [lindex [getNodeCoords $orig_node1] 1]
set x2 [lindex [getNodeCoords $orig_node2] 0]
set y2 [lindex [getNodeCoords $orig_node2] 1]
setNodeCoords $new_node1 \
"[expr {($x1 + 0.4 * ($x2 - $x1)) / $zoom}] \
[expr {($y1 + 0.4 * ($y2 - $y1)) / $zoom}]"
setNodeCoords $new_node2 \
"[expr {($x1 + 0.6 * ($x2 - $x1)) / $zoom}] \
[expr {($y1 + 0.6 * ($y2 - $y1)) / $zoom}]"
setNodeLabelCoords $new_node1 [getNodeCoords $new_node1]
setNodeLabelCoords $new_node2 [getNodeCoords $new_node2]
set changed 1
updateUndoLog
redrawAll
}
#****f* editor.tcl/selectNode
# NAME
# selectNode -- select node
# SYNOPSIS
# selectNode $c $obj
# FUNCTION
# Crates the selecting box around the specified canvas
# object.
# INPUTS
# * c -- tk canvas
# * obj -- tk canvas object tag id
#****
proc selectNode { c obj } {
set node [lindex [$c gettags $obj] 1]
if { $node == "" } { return } ;# Boeing: fix occassional error
$c addtag selected withtag "node && $node"
if { [nodeType $node] == "pseudo" } {
set bbox [$c bbox "nodelabel && $node"]
} elseif { [nodeType $node] == "rectangle" } {
$c addtag selected withtag "rectangle && $node"
set bbox [$c bbox "rectangle && $node"]
} elseif { [nodeType $node] == "text" } {
$c addtag selected withtag "text && $node"
set bbox [$c bbox "text && $node"]
} elseif { [nodeType $node] == "oval" } {
$c addtag selected withtag "oval && $node"
set bbox [$c bbox "oval && $node"]
} else {
set bbox [$c bbox "node && $node"]
}
set bx1 [expr {[lindex $bbox 0] - 2}]
set by1 [expr {[lindex $bbox 1] - 2}]
set bx2 [expr {[lindex $bbox 2] + 1}]
set by2 [expr {[lindex $bbox 3] + 1}]
$c delete -withtags "selectmark && $node"
$c create line $bx1 $by1 $bx2 $by1 $bx2 $by2 $bx1 $by2 $bx1 $by1 \
-dash {6 4} -fill black -width 1 -tags "selectmark $node"
}
proc selectNodes { nodelist } {
foreach node $nodelist {
selectNode .c [.c find withtag "node && $node"]
}
}
proc selectedNodes {} {
set selected {}
foreach obj [.c find withtag "node && selected"] {
lappend selected [lindex [.c gettags $obj] 1]
}
foreach obj [.c find withtag "oval && selected"] {
lappend selected [lindex [.c gettags $obj] 1]
}
foreach obj [.c find withtag "rectangle && selected"] {
lappend selected [lindex [.c gettags $obj] 1]
}
foreach obj [.c find withtag "text && selected"] {
lappend selected [lindex [.c gettags $obj] 1]
}
return $selected
}
proc selectedRealNodes {} {
set selected {}
foreach obj [.c find withtag "node && selected"] {
set node [lindex [.c gettags $obj] 1]
if { [getNodeMirror $node] != "" ||
[nodeType $node] == "rj45" } {
continue
}
lappend selected $node
}
return $selected
}
proc selectAdjacent {} {
global curcanvas
set selected [selectedNodes]
set adjacent {}
foreach node $selected {
foreach ifc [ifcList $node] {
set peer [peerByIfc $node $ifc]
if { [getNodeMirror $peer] != "" } {
return
}
if { [lsearch $adjacent $peer] < 0 } {
lappend adjacent $peer
}
}
}
selectNodes $adjacent
}
#****f* editor.tcl/button3link
# NAME
# button3link
# SYNOPSIS
# button3link $c $x $y
# FUNCTION
# This procedure is called when a right mouse button is
# clicked on the canvas. If there is a link on the place of
# mouse click this procedure creates and configures a popup
# menu. The options in the menu are:
# * Configure -- configure the link
# * Delete -- delete the link
# * Split -- split the link
# * Merge -- this option is active only if the link is previously
# been split, by this action the link is merged.
# INPUTS
# * c -- tk canvas
# * x -- x coordinate for popup menu
# * y -- y coordinate for popup menu
#****
proc button3link { c x y } {
global oper_mode env eid canvas_list node_list
global curcanvas
set link [lindex [$c gettags {link && current}] 1]
if { $link == "" } {
set link [lindex [$c gettags {linklabel && current}] 1]
if { $link == "" } {
return
}
}
.button3menu delete 0 end
#
# Configure link
#
.button3menu add command -label "Configure" \
-command "popupConfigDialog $c"
#
# Delete link
#
if { $oper_mode != "exec" } {
.button3menu add command -label "Delete" \
-command "removeGUILink $link atomic"
} else {
.button3menu add command -label "Delete" \
-state disabled
}
#
# Split link
#
if { $oper_mode != "exec" && [getLinkMirror $link] == "" } {
.button3menu add command -label "Split" \
-command "splitGUILink $link"
} else {
.button3menu add command -label "Split" \
-state disabled
}
#
# Merge two pseudo nodes / links
#
if { $oper_mode != "exec" && [getLinkMirror $link] != "" &&
[getNodeCanvas [getNodeMirror [lindex [linkPeers $link] 1]]] ==
$curcanvas } {
.button3menu add command -label "Merge" \
-command "mergeGUINode [lindex [linkPeers $link] 1]"
} else {
.button3menu add command -label "Merge" -state disabled
}
set x [winfo pointerx .]
set y [winfo pointery .]
tk_popup .button3menu $x $y
}
#****f* editor.tcl/movetoCanvas
# NAME
# movetoCanvas -- move to canvas
# SYNOPSIS
# movetoCanvas $canvas
# FUNCTION
# This procedure moves all the nodes selected in the GUI to
# the specified canvas.
# INPUTS
# * canvas -- canvas id.
#****
proc movetoCanvas { canvas } {
global changed
set selected_nodes [selectedNodes]
foreach node $selected_nodes {
setNodeCanvas $node $canvas
set changed 1
}
foreach obj [.c find withtag "linklabel"] {
set link [lindex [.c gettags $obj] 1]
set link_peers [linkPeers $link]
set peer1 [lindex $link_peers 0]
set peer2 [lindex $link_peers 1]
set peer1_in_selected [lsearch $selected_nodes $peer1]
set peer2_in_selected [lsearch $selected_nodes $peer2]
if { ($peer1_in_selected == -1 && $peer2_in_selected != -1) ||
($peer1_in_selected != -1 && $peer2_in_selected == -1) } {
if { [nodeType $peer2] == "pseudo" } {
setNodeCanvas $peer2 $canvas
if { [getNodeCanvas [getNodeMirror $peer2]] == $canvas } {
mergeLink $link
}
continue
}
set new_nodes [splitLink $link pseudo]
set new_node1 [lindex $new_nodes 0]
set new_node2 [lindex $new_nodes 1]
setNodeMirror $new_node1 $new_node2
setNodeMirror $new_node2 $new_node1
setNodeName $new_node1 $peer2
setNodeName $new_node2 $peer1
set link1 [linkByPeers $peer1 $new_node1]
set link2 [linkByPeers $peer2 $new_node2]
setLinkMirror $link1 $link2
setLinkMirror $link2 $link1
}
}
updateUndoLog
redrawAll
}
#****f* editor.tcl/mergeGUINode
# NAME
# mergeGUINode -- merge GUI node
# SYNOPSIS
# mergeGUINode $node
# FUNCTION
# This procedure removes the specified pseudo node as well
# as it's mirror copy. Also this procedure removes the
# pseudo links and reestablish the original link between
# the non-pseudo nodes.
# INPUTS
# * node -- node id of a pseudo node.
#****
proc mergeGUINode { node } {
set link [lindex [linkByIfc $node [ifcList $node]] 0]
mergeLink $link
redrawAll
}
#****f* editor.tcl/button3node
# NAME
# button3node
# SYNOPSIS
# button3node $c $x $y
# FUNCTION
# This procedure is called when a right mouse button is
# clicked on the canvas. Also called when double-clicking
# on a node during runtime.
# If there is a node on the place of
# mouse click this procedure creates and configures a popup
# menu. The options in the menu are:
# * Configure -- configure the node
# * Create link to -- create a link to any available node,
# it can be on the same canvas or on a different canvas.
# * Move to -- move to some other canvas
# * Merge -- this option is available only for pseudo nodes
# that have mirror nodes on the same canvas (Pseudo nodes
# created by splitting a link).
# * Delete -- delete the node
# * Shell window -- specifies the shell window to open in
# exec mode. This option is available only to nodes on a
# network layer
# * Ethereal -- opens a Ethereal program for the specified
# node and the specified interface. This option is available
# only for network layer nodes in exec mode.
# INPUTS
# * c -- tk canvas
# * x -- x coordinate for popup menu
# * y -- y coordinate for popup menu
#****
#old proc button3node { c x y }
#Boeing
proc button3node { c x y button } {
global oper_mode env eid canvas_list node_list curcanvas systype g_prefs
set node [lindex [$c gettags {node && current}] 1]
if { $node == "" } {
set node [lindex [$c gettags {nodelabel && current}] 1]
if { $node == "" } {
return
}
}
set mirror_node [getNodeMirror $node]
if { [$c gettags "node && $node && selected"] == "" } {
$c dtag node selected
$c delete -withtags selectmark
selectNode $c [$c find withtag "current"]
}
# open up shells upon double-click or shift/ctrl-click
set shell $g_prefs(shell)
if { $button == "shift" || $button == "ctrl" } {
if { [nodeType $node] == "pseudo" } {
#
# Hyperlink to another canvas
#
set curcanvas [getNodeCanvas [getNodeMirror $node]]
switchCanvas none
return
}
# only open bash shells for NETWORK nodes and remote routers
if { [[typemodel $node].layer] != "NETWORK" } {
if { [typemodel $node] == "wlan" } {
wlanDoubleClick $node $button
}
return
}
if { $button == "shift" } { ;# normal bash shell
spawnShell $node $shell
} else { ;# right-click vtysh shell
set cmd [[typemodel $node].shellcmd $node]
if { $cmd != "/bin/sh" && $cmd != "" } { spawnShell $node $cmd }
}
return ;# open shell, don't post a menu
}
#
# below here we build and post a menu
#
.button3menu delete 0 end
#
# Configure node
#
if { [nodeType $node] != "pseudo" } {
.button3menu add command -label "Configure" \
-command "popupConfigDialog $c"
} else {
.button3menu add command -label "Configure" \
-command "popupConfigDialog $c" -state disabled
}
#
# Select adjacent
#
if { [nodeType $node] != "pseudo" } {
.button3menu add command -label "Select adjacent" \
-command "selectAdjacent"
} else {
.button3menu add command -label "Select adjacent" \
-command "selectAdjacent" -state disabled
}
#
# Create a new link - can be between different canvases
#
.button3menu.connect delete 0 end
if { $oper_mode == "exec" || [nodeType $node] == "pseudo" } {
#.button3menu add cascade -label "Create link to" \
-menu .button3menu.connect -state disabled
} else {
.button3menu add cascade -label "Create link to" \
-menu .button3menu.connect
}
destroy .button3menu.connect.selected
menu .button3menu.connect.selected -tearoff 0
.button3menu.connect add cascade -label "Selected" \
-menu .button3menu.connect.selected
.button3menu.connect.selected add command \
-label "Chain" -command "P \[selectedRealNodes\]"
.button3menu.connect.selected add command \
-label "Star" \
-command "Kb \[lindex \[selectedRealNodes\] 0\] \
\[lrange \[selectedNodes\] 1 end\]"
.button3menu.connect.selected add command \
-label "Cycle" -command "C \[selectedRealNodes\]"
.button3menu.connect.selected add command \
-label "Clique" -command "K \[selectedRealNodes\]"
.button3menu.connect add separator
foreach canvas $canvas_list {
destroy .button3menu.connect.$canvas
menu .button3menu.connect.$canvas -tearoff 0
.button3menu.connect add cascade -label [getCanvasName $canvas] \
-menu .button3menu.connect.$canvas
}
foreach peer_node $node_list {
set canvas [getNodeCanvas $peer_node]
if { $node != $peer_node && [nodeType $node] != "rj45" &&
[lsearch {pseudo rj45} [nodeType $peer_node]] < 0 &&
[ifcByLogicalPeer $node $peer_node] == "" } {
.button3menu.connect.$canvas add command \
-label [getNodeName $peer_node] \
-command "newGUILink $node $peer_node"
} elseif { [nodeType $peer_node] != "pseudo" } {
.button3menu.connect.$canvas add command \
-label [getNodeName $peer_node] \
-state disabled
}
}
#
# assign to emulation server
#
if { $oper_mode != "exec" } {
global exec_servers node_location
.button3menu.assign delete 0 end
.button3menu add cascade -label "Assign to" -menu .button3menu.assign
.button3menu.assign add command -label "(none)" \
-command "assignSelection \"\""
foreach server [lsort -dictionary [array names exec_servers]] {
.button3menu.assign add command -label "$server" \
-command "assignSelection $server"
}
}
#
# wlan link to all nodes
#
if { [nodeType $node] == "wlan" } {
.button3menu add command -label "Link to all routers" \
-command "linkAllNodes $node"
set msg "Select new WLAN $node members:"
set cmd "linkSelectedNodes $node"
.button3menu add command -label "Select WLAN members..." \
-command "popupSelectNodes \"$msg\" \"\" {$cmd}"
set state normal
if { $oper_mode != "exec" } { set state disabled }
.button3menu add command -label "Mobility script..." \
-command "showMobilityScriptPopup $node" -state $state
}
#
# Move to another canvas
#
.button3menu.moveto delete 0 end
if { $oper_mode != "exec" && [nodeType $node] != "pseudo" } {
.button3menu add cascade -label "Move to" \
-menu .button3menu.moveto
.button3menu.moveto add command -label "Canvas:" -state disabled
foreach canvas $canvas_list {
if { $canvas != $curcanvas } {
.button3menu.moveto add command \
-label [getCanvasName $canvas] \
-command "movetoCanvas $canvas"
} else {
.button3menu.moveto add command \
-label [getCanvasName $canvas] -state disabled
}
}
}
#
# Merge two pseudo nodes / links
#
if { $oper_mode != "exec" && [nodeType $node] == "pseudo" && \
[getNodeCanvas $mirror_node] == $curcanvas } {
.button3menu add command -label "Merge" \
-command "mergeGUINode $node"
}
#
# Delete selection
#
if { $oper_mode != "exec" } {
.button3menu add command -label "Cut" -command cutSelection
.button3menu add command -label "Copy" -command copySelection
.button3menu add command -label "Paste" -command pasteSelection
.button3menu add command -label "Delete" -command deleteSelection
}
.button3menu add command -label "Hide" -command "hideSelected"
# Boeing: flag used below
set execstate disabled
if { $oper_mode == "exec" } { set execstate normal }
#
# Shell selection
#
.button3menu.shell delete 0 end
if { $oper_mode == "exec" && [[typemodel $node].layer] == "NETWORK" } {
.button3menu add cascade -label "Shell window" \
-menu .button3menu.shell
set cmd [[typemodel $node].shellcmd $node]
if { $cmd != "/bin/sh" && $cmd != "" } { ;# typically adds vtysh
.button3menu.shell add command -label "$cmd" \
-command "spawnShell $node $cmd"
}
.button3menu.shell add command -label "/bin/sh" \
-command "spawnShell $node sh"
.button3menu.shell add command -label "$shell" \
-command "spawnShell $node $shell"
}
#
# services
#
.button3menu.services delete 0 end
if { $oper_mode == "exec" && [[typemodel $node].layer] == "NETWORK" } {
addServicesRightClickMenu .button3menu $node
} else {
.button3menu add command -label "Services..." -command \
"sendConfRequestMessage -1 $node services 0x1 -1 \"\""
}
#
# Tcpdump, gpsd
#
if { $oper_mode == "exec" && [[typemodel $node].layer] == "NETWORK" } {
addInterfaceCommand $node .button3menu "Tcpdump" "tcpdump -n -l -i" \
$execstate 1
addInterfaceCommand $node .button3menu "TShark" "tshark -n -l -i" \
$execstate 1
addInterfaceCommand $node .button3menu "Wireshark" "wireshark -k -i" \
$execstate 0
# wireshark on host veth pair -- need veth pair name
#wireshark -k -i
if { [lindex $systype 0] == "Linux" } {
set name [getNodeName $node]
.button3menu add command -label "View log..." -state $execstate \
-command "spawnShell $node \"less ../$name.log\""
}
}
#
# Finally post the popup menu on current pointer position
#
set x [winfo pointerx .]
set y [winfo pointery .]
tk_popup .button3menu $x $y
}
#****f* editor.tcl/spawnShell
# NAME
# spawnShell -- spawn shell
# SYNOPSIS
# spawnShell $node $cmd
# FUNCTION
# This procedure spawns a new shell for a specified node.
# The shell is specified in cmd parameter.
# INPUTS
# * node -- node id of the node for which the shell
# is spawned.
# * cmd -- the path to the shell.
#****
proc spawnShell { node cmd } {
# request an interactive terminal
set sock [lindex [getEmulPlugin $node] 2]
set flags 0x44 ;# set TTY, critical flags
set exec_num [newExecCallbackRequest shell]
sendExecMessage $sock $node $cmd $exec_num $flags
}
# add a sub-menu to the parentmenu with the given command for each interface
proc addInterfaceCommand { node parentmenu txt cmd state isnodecmd } {
global g_current_session
set childmenu "$parentmenu.[lindex $cmd 0]"
$childmenu delete 0 end
$parentmenu add cascade -label $txt -menu $childmenu -state $state
if { ! $isnodecmd } {
if { $g_current_session == 0 } { set state disabled }
set ssid [shortSessionID $g_current_session]
}
foreach ifc [ifcList $node] {
set addr [lindex [getIfcIPv4addr $node $ifc] 0]
if { $addr != "" } { set addr " ($addr)" }
if { $isnodecmd } { ;# run command in a node
set icmd "spawnShell $node \"$cmd $ifc\""
} else { ;# exec a command directly
set node_num [string range $node 1 end]
set hex [format "%x" $node_num]
set ifnum [string range $ifc 3 end]
set ifname "veth$hex\\.$ifnum\\.$ssid"
set icmd "exec $cmd $ifname &"
}
$childmenu add command -label "$ifc$addr" -state $state -command $icmd
}
}
# Boeing: consolodate various raise statements here
proc raiseAll {c} {
$c raise rectangle background
$c raise oval "rectangle || background"
$c raise grid "oval || rectangle || background"
$c raise link "grid || oval || rectangle || background"
$c raise linklabel "link || grid || oval || rectangle || background"
$c raise newlink "linklabel || link || grid || oval || rectangle || background"
$c raise wlanlink "newlink || linklabel || link || grid || oval || rectangle || background"
$c raise antenna "wlanlink || newlink || linklabel || link || grid || oval || rectangle || background"
$c raise interface "antenna || wlanlink || newlink || linklabel || link || grid || oval || rectangle || background"
$c raise node "interface || antenna || wlanlink || newlink || linklabel || link || grid || oval || rectangle || background"
$c raise nodelabel "node || interface || antenna || wlanlink || newlink || linklabel || link || grid || oval || rectangle || background"
$c raise text "nodelabel || node || interface || antenna || wlanlink || newlink || linklabel || link || grid || oval || rectangle || background"
$c raise -cursor
}
# end Boeing
#****f* editor.tcl/button1
# NAME
# button1
# SYNOPSIS
# button1 $c $x $y $button
# FUNCTION
# This procedure is called when a left mouse button is
# clicked on the canvas. This procedure selects a new
# node or creates a new node, depending on the selected
# tool.
# INPUTS
# * c -- tk canvas
# * x -- x coordinate
# * y -- y coordinate
# * button -- the keyboard button that is pressed.
#****
proc button1 { c x y button } {
global node_list plot_list curcanvas zoom
global activetool activetoolp newlink curobj changed def_router_model
global router pc host lanswitch rj45 hub OVS
global oval rectangle text
global lastX lastY
global background selectbox
global defLinkColor defLinkWidth
global resizemode resizeobj
global wlan g_twoNodeSelect
global g_view_locked
set x [$c canvasx $x]
set y [$c canvasy $y]
set lastX $x
set lastY $y
# TODO: clean this up
# - too many global variables
# - too many hardcoded cases (lanswitch, router, etc)
# - should be functionalized since lengthy if-else difficult to read
set curobj [$c find withtag current]
set curtype [lindex [$c gettags current] 0]
if { $curtype == "node" || \
$curtype == "oval" || $curtype == "rectangle" || $curtype == "text" \
|| ( $curtype == "nodelabel" && \
[nodeType [lindex [$c gettags $curobj] 1]] == "pseudo") } {
set node [lindex [$c gettags current] 1]
set wasselected \
[expr {[lsearch [$c find withtag "selected"] \
[$c find withtag "node && $node"]] > -1}]
if { $button == "ctrl" } {
if { $wasselected } {
$c dtag $node selected
$c delete -withtags "selectmark && $node"
}
} elseif { !$wasselected } {
$c dtag node selected
$c delete -withtags selectmark
}
if { $activetool == "select" && !$wasselected} {
selectNode $c $curobj
}
} elseif { $curtype == "selectmark" } {
setResizeMode $c $x $y
} elseif { $activetool == "plot" } {
# plot tool: create new plot windows when clicking on a link
set link ""
set tags [$c gettags $curobj]
if { $curtype == "link" || $curtype == "linklabel" } {
set link [lindex $tags 1]
} elseif { $curtype == "interface" } {
set link [lindex $tags 2]
}
if { $link != "" } {
thruPlot $c $link $x $y 150 220 false
}
return
} elseif { $button != "ctrl" || $activetool != "select" } {
$c dtag node selected
$c delete -withtags selectmark
}
# user has clicked on a blank area or background item
if { [lsearch [.c gettags $curobj] background] != -1 ||
[lsearch [.c gettags $curobj] grid] != -1 ||
[lsearch [.c gettags $curobj] annotation] != -1 } {
# left mouse button pressed to create a new node
if { [lsearch {select marker link mobility twonode run stop oval \
rectangle text} $activetool] < 0 } {
if { $g_view_locked == 1 } { return }
if { $activetoolp == "routers" } {
if {$activetool != "OVS"} {
set node [newNode router]
} else {
set node [newNode OVS]}
setNodeModel $node $activetool
} else {
set node [newNode $activetool]
}
setNodeCanvas $node $curcanvas
setNodeCoords $node "[expr {$x / $zoom}] [expr {$y / $zoom}]"
lassign [getDefaultLabelOffsets $activetool] dx dy
setNodeLabelCoords $node "[expr {$x / $zoom + $dx}] \
[expr {$y / $zoom + $dy}]"
drawNode $c $node
selectNode $c [$c find withtag "node && $node"]
set changed 1
# remove any existing select box
} elseif { $activetool == "select" \
&& $curtype != "node" && $curtype != "nodelabel"} {
$c config -cursor cross
set lastX $x
set lastY $y
if {$selectbox != ""} {
# We actually shouldn't get here!
$c delete $selectbox
set selectbox ""
}
# begin drawing an annotation
} elseif { $activetoolp == "bgobjs" } {
set newcursor cross
if { $activetool == "text" } { set newcursor xterm }
$c config -cursor $newcursor
set lastX $x
set lastY $y
# draw with the marker
} elseif { $activetool == "marker" } {
global markersize markercolor
set newline [$c create oval $lastX $lastY $x $y \
-width $markersize -outline $markercolor -tags "marker"]
$c raise $newline "background || link || linklabel || interface"
set lastX $x
set lastY $y
}
} else {
if {$curtype == "node" || $curtype == "nodelabel"} {
$c config -cursor fleur
}
if {$activetool == "link" && $curtype == "node"} {
$c config -cursor cross
set lastX [lindex [$c coords $curobj] 0]
set lastY [lindex [$c coords $curobj] 1]
set newlink [$c create line $lastX $lastY $x $y \
-fill $defLinkColor -width $defLinkWidth \
-tags "link"]
# twonode tool support
} elseif {$g_twoNodeSelect != "" && $curtype == "node"} {
set curnode [lindex [$c gettags $curobj] 1]
selectTwoNode $curnode
} elseif { $curtype == "node" } {
selectNode $c $curobj
}
# end Boeing
}
raiseAll $c
}
proc setResizeMode { c x y } {
set isThruplot false
set type1 notset
if {$c == ".c"} {
set t1 [$c gettags current]
set o1 [lindex $t1 1]
set type1 [nodeType $o1]
} else {
set o1 $c
set c .c
set isThruplot true
}
#DYL
#puts "RESIZE NODETYPE = $type1"
global resizemode resizeobj
if {$type1== "oval" || $type1== "rectangle" || $isThruplot == true} {
set resizeobj $o1
set bbox1 [$c bbox $o1]
set x1 [lindex $bbox1 0]
set y1 [lindex $bbox1 1]
set x2 [lindex $bbox1 2]
set y2 [lindex $bbox1 3]
set l 0 ;# left
set r 0 ;# right
set u 0 ;# up
set d 0 ;# down
if { $x < [expr $x1+($x2-$x1)/8.0]} { set l 1 }
if { $x > [expr $x2-($x2-$x1)/8.0]} { set r 1 }
if { $y < [expr $y1+($y2-$y1)/8.0]} { set u 1 }
if { $y > [expr $y2-($y2-$y1)/8.0]} { set d 1 }
if {$l==1} {
if {$u==1} {
set resizemode lu
} elseif {$d==1} {
set resizemode ld
} else {
set resizemode l
}
} elseif {$r==1} {
if {$u==1} {
set resizemode ru
} elseif {$d==1} {
set resizemode rd
} else {
set resizemode r
}
} elseif {$u==1} {
set resizemode u
} elseif {$d==1} {
set resizemode d
} else {
set resizemode false
}
}
}
#****f* editor.tcl/button1-motion
# NAME
# button1-motion
# SYNOPSIS
# button1-motion $c $x $y
# FUNCTION
# This procedure is called when a left mouse button is
# pressed and the mouse is moved around the canvas.
# This procedure creates new select box, moves the
# selected nodes or draws a new link.
# INPUTS
# * c -- tk canvas
# * x -- x coordinate
# * y -- y coordinate
#****
proc button1-motion { c x y } {
global activetool newlink changed
global lastX lastY sizex sizey selectbox background
global oper_mode newoval newrect resizemode
global zoom
global g_view_locked
global thruPlotCur thruPlotDragStart
set x [$c canvasx $x]
set y [$c canvasy $y]
if {$thruPlotDragStart == "dragging"} {
#puts "active tool is $activetool"
thruPlotDrag $c $thruPlotCur $x $y null true
return
}
# fix occasional error
if { $x == "" || $y == "" || $lastX == "" || $lastY == "" } { return }
set curobj [$c find withtag current]
set curtype [lindex [$c gettags current] 0]
# display <x, y> coordinates in the status bar
set zoomx [expr {$x / $zoom}]
set zoomy [expr {$y / $zoom}]
.bottom.textbox config -text "<$zoomx, $zoomy>"
# prevent dragging outside of the canvas area
if { $x < 0 } {
set x 0
} elseif { $x > $sizex } {
set x $sizex
}
if { $y < 0 } {
set y 0
} elseif { $y > $sizey } {
set y $sizey
}
# marker tool drawing on the canvas
if { $activetool == "marker" } {
global markersize markercolor
set dx [expr {$x-$lastX} ]
set dy [expr {$y-$lastY} ]
# this provides smoother drawing
if { $dx > $markersize || $dy > $markersize } {
set mark [$c create line $lastX $lastY $x $y \
-width $markersize -fill $markercolor -tags "marker"]
$c raise $mark \
"marker || background || link || linklabel || interface"
}
set mark [$c create oval $x $y $x $y \
-width $markersize -fill $markercolor \
-outline $markercolor -tags "marker"]
$c raise $mark "marker || background || link || linklabel || interface"
set lastX $x
set lastY $y
return
}
# disable all other mouse drags in locked mode
if { $g_view_locked == 1 } { return }
# don't move nodelabels in exec mode, use calcx,y instead of x,y
if {$oper_mode == "exec" && $curtype == "nodelabel" } {
set node [lindex [$c gettags $curobj] 1]
set curobj [$c find withtag "node && $node"]
set curtype "node"
set coords [$c coords $curobj]
set calcx [expr {[lindex $coords 0] / $zoom}]
set calcy [expr {[lindex $coords 1] / $zoom}]
selectNode $c $curobj
} else {
set calcx $x
set calcy $y
}
# drawing a new link
if {$activetool == "link" && $newlink != ""} {
$c coords $newlink $lastX $lastY $x $y
# draw a selection box
} elseif { $activetool == "select" && \
( $curobj == $selectbox || $curtype == "background" || $curtype == "grid")} {
if {$selectbox == ""} {
set selectbox [$c create line \
$lastX $lastY $x $lastY $x $y $lastX $y $lastX $lastY \
-dash {10 4} -fill black -width 1 -tags "selectbox"]
$c raise $selectbox "background || link || linklabel || interface"
} else {
$c coords $selectbox \
$lastX $lastY $x $lastY $x $y $lastX $y $lastX $lastY
}
# move a text annotation
} elseif { $activetool == "select" && $curtype == "text" } {
$c move $curobj [expr {$x - $lastX}] [expr {$y - $lastY}]
set changed 1
set lastX $x
set lastY $y
$c delete [$c find withtag "selectmark"]
# move a nodelabel apart from a node (edit mode only)
} elseif { $activetool == "select" && $curtype == "nodelabel" \
&& [nodeType [lindex [$c gettags $curobj] 1]] != "pseudo" } {
$c move $curobj [expr {$x - $lastX}] [expr {$y - $lastY}]
set changed 1
set lastX $x
set lastY $y
# actually we should check if curobj==bkgImage
# annotations
} elseif { $activetool == "oval" && \
( $curobj == $newoval || $curobj == $background || $curtype == "background" || $curtype == "grid")} {
# Draw a new oval
if {$newoval == ""} {
set newoval [$c create oval $lastX $lastY $x $y \
-dash {10 4} -width 1 -tags "newoval"]
$c raise $newoval "background || link || linklabel || interface"
} else {
$c coords $newoval \
$lastX $lastY $x $y
}
# actually we should check if curobj==bkgImage
} elseif { $activetool == "rectangle" && \
( $curobj == $newrect || $curobj == $background || $curtype == "background" || $curtype == "grid")} {
# Draw a new rectangle
if {$newrect == ""} {
set newrect [$c create rectangle $lastX $lastY $x $y \
-outline blue \
-dash {10 4} -width 1 -tags "newrect"]
$c raise $newrect "oval || background || link || linklabel || interface"
} else {
$c coords $newrect $lastX $lastY $x $y
}
# resizing an annotation
} elseif { $curtype == "selectmark" } {
foreach o [$c find withtag "selected"] {
set node [lindex [$c gettags $o] 1]
set tagovi [$c gettags $o]
set koord [getNodeCoords $node]
set oldX1 [lindex $koord 0]
set oldY1 [lindex $koord 1]
set oldX2 [lindex $koord 2]
set oldY2 [lindex $koord 3]
switch -exact -- $resizemode {
lu {
set oldX1 $x
set oldY1 $y
}
ld {
set oldX1 $x
set oldY2 $y
}
l {
set oldX1 $x
}
ru {
set oldX2 $x
set oldY1 $y
}
rd {
set oldX2 $x
set oldY2 $y
}
r {
set oldX2 $x
}
u {
set oldY1 $y
}
d {
set oldY2 $y
}
}
if {$selectbox == ""} {
# Boeing: fix "bad screen distance" error
if { $oldX1 == "" || $oldX2 == "" || $oldY1 == "" || \
$oldY2 == "" } { return }
# end Boeing
set selectbox [$c create line \
$oldX1 $oldY1 $oldX2 $oldY1 $oldX2 $oldY2 $oldX1 \
$oldY2 $oldX1 $oldY1 \
-dash {10 4} -fill black -width 1 -tags "selectbox"]
$c raise $selectbox \
"background || link || linklabel || interface"
} else {
$c coords $selectbox \
$oldX1 $oldY1 $oldX2 $oldY1 $oldX2 $oldY2 $oldX1 \
$oldY2 $oldX1 $oldY1
}
}
# selected node(s) are being moved
} else {
foreach img [$c find withtag "selected"] {
set node [lindex [$c gettags $img] 1]
set newcoords [$c coords $img] ;# different than getNodeCoords
set img [$c find withtag "selectmark && $node"]
if {$curtype == "oval" || $curtype == "rectangle"} {
$c move $img [expr {($x - $lastX) / 2}] \
[expr {($y - $lastY) / 2}]
} else {
$c move $img [expr {$x - $lastX}] [expr {$y - $lastY}]
set img [$c find withtag "node && $node"]
$c move $img [expr {$x - $lastX}] [expr {$y - $lastY}]
set img [$c find withtag "nodelabel && $node"]
$c move $img [expr {$x - $lastX}] [expr {$y - $lastY}]
set img [$c find withtag "twonode && $node"]
if {$img != "" } {; # move Two Node Tool circles around node
$c move $img [expr {$x - $lastX}] [expr {$y - $lastY}]
};
set img [$c find withtag "rangecircles && $node"]
if {$img != "" } {; # move throughput circles around node
$c move $img [expr {$x - $lastX}] [expr {$y - $lastY}]
};
$c addtag need_redraw withtag "link && $node"
}
if { $oper_mode == "exec" } {
set newx [expr {[lindex $newcoords 0] / $zoom}]
set newy [expr {[lindex $newcoords 1] / $zoom}]
sendNodePosMessage -1 $node -1 $newx $newy -1 0
}
$c addtag need_redraw withtag "wlanlink && $node"
widgets_move_node $c $node 0
}
foreach link [$c find withtag "link && need_redraw"] {
redrawLink [lindex [$c gettags $link] 1]
}
foreach wlanlink [$c find withtag "wlanlink && need_redraw"] {
redrawWlanLink $wlanlink
}
$c dtag wlanlink need_redraw
$c dtag link need_redraw
set changed 1
set lastX $x
set lastY $y
}
}
#****f* editor.tcl/pseudo.layer
# NAME
# pseudo.layer
# SYNOPSIS
# set layer [pseudo.layer]
# FUNCTION
# Returns the layer on which the pseudo node operates
# i.e. returns no layer.
# RESULT
# * layer -- returns an empty string
#****
proc pseudo.layer {} {
}
#****f* editor.tcl/newGUILink
# NAME
# newGUILink -- new GUI link
# SYNOPSIS
# newGUILink $lnode1 $lnode2
# FUNCTION
# This procedure is called to create a new link between
# nodes lnode1 and lnode2. Nodes can be on the same canvas
# or on different canvases. The result of this function
# is directly visible in GUI.
# INPUTS
# * lnode1 -- node id of the first node
# * lnode2 -- node id of the second node
#****
proc newGUILink { lnode1 lnode2 } {
global changed
set link [newLink $lnode1 $lnode2]
if { $link == "" } {
return
}
if { [getNodeCanvas $lnode1] != [getNodeCanvas $lnode2] } {
set new_nodes [splitLink $link pseudo]
set orig_nodes [linkPeers $link]
set new_node1 [lindex $new_nodes 0]
set new_node2 [lindex $new_nodes 1]
set orig_node1 [lindex $orig_nodes 0]
set orig_node2 [lindex $orig_nodes 1]
set new_link1 [linkByPeers $orig_node1 $new_node1]
set new_link2 [linkByPeers $orig_node2 $new_node2]
setNodeMirror $new_node1 $new_node2
setNodeMirror $new_node2 $new_node1
setNodeName $new_node1 $orig_node2
setNodeName $new_node2 $orig_node1
setLinkMirror $new_link1 $new_link2
setLinkMirror $new_link2 $new_link1
}
redrawAll
set changed 1
updateUndoLog
}
#****f* editor.tcl/button1-release
# NAME
# button1-release
# SYNOPSIS
# button1-release $c $x $y
# FUNCTION
# This procedure is called when a left mouse button is
# released.
# The result of this function depends on the actions
# during the button1-motion procedure.
# INPUTS
# * c -- tk canvas
# * x -- x coordinate
# * y -- y coordinate
#****
proc button1-release { c x y } {
global node_list plot_list activetool newlink curobj grid
global changed undolog undolevel redolevel selectbox
global lastX lastY sizex sizey zoom
global autorearrange_enabled
global resizemode resizeobj
set redrawNeeded 0
global oper_mode
global g_prefs
global g_view_locked
set x [$c canvasx $x]
set y [$c canvasy $y]
$c config -cursor left_ptr
# place a new link between items
if {$activetool == "link" && $newlink != ""} {
if { $g_view_locked == 1 } { return }
$c delete $newlink
set newlink ""
set destobj ""
foreach obj [$c find overlapping $x $y $x $y] {
if {[lindex [$c gettags $obj] 0] == "node"} {
set destobj $obj
break
}
}
if {$destobj != "" && $curobj != "" && $destobj != $curobj} {
set lnode1 [lindex [$c gettags $curobj] 1]
set lnode2 [lindex [$c gettags $destobj] 1]
if { [ifcByLogicalPeer $lnode1 $lnode2] == "" } {
set link [newLink $lnode1 $lnode2]
if { $link != "" } {
drawLink $link
redrawLink $link
updateLinkLabel $link
set changed 1
}
}
}
# annotations
} elseif {$activetool == "rectangle" || $activetool == "oval" } {
if { $g_view_locked == 1 } { return }
popupAnnotationDialog $c 0 "false"
# edit text annotation
} elseif {$activetool == "text" } {
if { $g_view_locked == 1 } { return }
textEnter $c $x $y
}
if { $changed == 1 } {
set regular true
if { [lindex [$c gettags $curobj] 0] == "nodelabel" } {
set node [lindex [$c gettags $curobj] 1]
selectNode $c [$c find withtag "node && $node"]
}
set selected {}
foreach img [$c find withtag "selected"] {
set node [lindex [$c gettags $img] 1]
lappend selected $node
set coords [$c coords $img]
set x [expr {[lindex $coords 0] / $zoom}]
set y [expr {[lindex $coords 1] / $zoom}]
if { $autorearrange_enabled == 0 && $g_prefs(gui_snap_grid)} {
set dx [expr {(int($x / $grid + 0.5) * $grid - $x) * $zoom}]
set dy [expr {(int($y / $grid + 0.5) * $grid - $y) * $zoom}]
$c move $img $dx $dy
set coords [$c coords $img]
set x [expr {[lindex $coords 0] / $zoom}]
set y [expr {[lindex $coords 1] / $zoom}]
} else {
set dx 0
set dy 0
}
if {$x < 0 || $y < 0 || $x > $sizex || $y > $sizey} {
set regular false
}
# nodes with four coordinates
if { [lindex [$c gettags $node] 0] == "oval" ||
[lindex [$c gettags $node] 0] == "rectangle" } {
set bbox [$c bbox "selectmark && $node"]
# Boeing: bbox causes annotations to grow, subtract 5
if { [llength $bbox] > 3 } {
set x1 [lindex $bbox 0]
set y1 [lindex $bbox 1]
set x2 [expr {[lindex $bbox 2] - 5}]
set y2 [expr {[lindex $bbox 3] - 5}]
setNodeCoords $node "$x1 $y1 $x2 $y2"
set redrawNeeded 1
if {$x1 < 0 || $y1 < 0 || $x1 > $sizex || $y1 > $sizey || \
$x2 < 0 || $y2 < 0 || $x2 > $sizex || $y2 > $sizey} {
set regular false
}
}
# nodes with two coordinates
} else {
setNodeCoords $node "$x $y"
}
if {[$c find withtag "nodelabel && $node"] != "" } {
$c move "nodelabel && $node" $dx $dy
set coords [$c coords "nodelabel && $node"]
set x [expr {[lindex $coords 0] / $zoom}]
set y [expr {[lindex $coords 1] / $zoom}]
setNodeLabelCoords $node "$x $y"
if {$x < 0 || $y < 0 || $x > $sizex || $y > $sizey} {
set regular false
}
}
$c move "selectmark && $node" $dx $dy
$c addtag need_redraw withtag "link && $node"
set changed 1
if { $oper_mode == "exec" } {
# send node position update using x,y stored in node
set xy [getNodeCoords $node] ;# read new coordinates
sendNodePosMessage -1 $node -1 [lindex $xy 0] [lindex $xy 1] \
-1 0
widgets_move_node $c $node 1
}
$c addtag need_redraw withtag "wlanlink && $node"
} ;# end of: foreach img selected
if {$regular == "true"} {
# user has dragged something within the canvas boundaries
foreach link [$c find withtag "link && need_redraw"] {
redrawLink [lindex [$c gettags $link] 1]
}
} else {
# user has dragged something beyond the canvas boundaries
.c config -cursor watch
loadCfg $undolog($undolevel)
redrawAll
if {$activetool == "select" } {
selectNodes $selected
}
set changed 0
}
$c dtag link need_redraw
nodeEnter $c
# $changed!=1
} elseif {$activetool == "select" } {
if {$selectbox == ""} {
set x1 $x
set y1 $y
rearrange_off
} else {
set coords [$c coords $selectbox]
set x [lindex $coords 0]
set y [lindex $coords 1]
set x1 [lindex $coords 4]
set y1 [lindex $coords 5]
$c delete $selectbox
set selectbox ""
}
if { $resizemode == "false" } {
# select tool mouse button release while drawing select box
set enclosed {}
# fix occasional error
if { $x == "" || $y == "" || $x1 == "" || $y1 == "" } { return }
foreach obj [$c find enclosed $x $y $x1 $y1] {
set tags [$c gettags $obj]
if {[lindex $tags 0] == "node" && [lsearch $tags selected] == -1} {
lappend enclosed $obj
}
if {[lindex $tags 0] == "oval" && [lsearch $tags selected] == -1} {
lappend enclosed $obj
}
if {[lindex $tags 0] == "rectangle" && [lsearch $tags selected] == -1} {
lappend enclosed $obj
}
if {[lindex $tags 0] == "text" && [lsearch $tags selected] == -1} {
lappend enclosed $obj
}
}
foreach obj $enclosed {
selectNode $c $obj
}
} else {
# select tool resizing an object by dragging its handles
# DYL bugfix. if x,y does not change, do not resize!
# fixes a bug where the object dissappears
if { $x != $x1 || $y != $y1 } {
setNodeCoords $resizeobj "$x $y $x1 $y1"
}
set redrawNeeded 1
set resizemode false
}
}
if { $redrawNeeded } {
set redrawNeeded 0
redrawAll
} else {
raiseAll $c
}
update
updateUndoLog
}
#****f* editor.tcl/nodeEnter
# NAME
# nodeEnter
# SYNOPSIS
# nodeEnter $c
# FUNCTION
# This procedure prints the node id, node name and
# node model (if exists), as well as all the interfaces
# of the node in the status line.
# Information is presented for the node above which is
# the mouse pointer.
# INPUTS
# * c -- tk canvas
#****
proc nodeEnter { c } {
global activetool
set curtags [$c gettags current]
if { [lsearch -exact "node nodelabel" [lindex $curtags 0]] < 0 } {
return ;# allow this proc to be called from button1-release
}
set node [lindex $curtags 1]
set type [nodeType $node]
set name [getNodeName $node]
set model [getNodeModel $node]
if { $model != "" } {
set line "{$node} $name ($model):"
} else {
set line "{$node} $name:"
}
if { $type != "rj45" && $type != "tunnel" } {
foreach ifc [ifcList $node] {
set line "$line $ifc:[getIfcIPv4addr $node $ifc]"
}
}
set xy [getNodeCoords $node]
set line "$line <[lindex $xy 0], [lindex $xy 1]>"
.bottom.textbox config -text "$line"
widgetObserveNode $c $node
}
#****f* editor.tcl/linkEnter
# NAME
# linkEnter
# SYNOPSIS
# linkEnter $c
# FUNCTION
# This procedure prints the link id, link bandwidth
# and link delay in the status line.
# Information is presented for the link above which is
# the mouse pointer.
# INPUTS
# * c -- tk canvas
#****
proc linkEnter {c} {
global activetool link_list
set link [lindex [$c gettags current] 1]
if { [lsearch $link_list $link] == -1 } {
return
}
set line "$link: [getLinkBandwidthString $link] [getLinkDelayString $link]"
.bottom.textbox config -text "$line"
}
#****f* editor.tcl/anyLeave
# NAME
# anyLeave
# SYNOPSIS
# anyLeave $c
# FUNCTION
# This procedure clears the status line.
# INPUTS
# * c -- tk canvas
#****
proc anyLeave {c} {
global activetool
.bottom.textbox config -text ""
# Boeing
widgetObserveNode $c ""
# nodeHighlights $c "" off ""
# end Boeing
}
#****f* editor.tcl/checkIntRange
# NAME
# checkIntRange -- check integer range
# SYNOPSIS
# set check [checkIntRange $str $low $high]
# FUNCTION
# This procedure checks the input string to see if it is
# an integer between the low and high value.
# INPUTS
# str -- string to check
# low -- the bottom value
# high -- the top value
# RESULT
# * check -- set to 1 if the str is string between low and high
# value, 0 otherwise.
#****
proc checkIntRange { str low high } {
if { $str == "" } {
return 1
}
set str [string trimleft $str 0]
if { $str == "" } {
set str 0
}
if { ![string is integer $str] } {
return 0
}
if { $str < $low || $str > $high } {
return 0
}
return 1
}
proc checkFloatRange { str low high } {
if { $str == "" } {
return 1
}
set str [string trimleft $str 0]
if { $str == "" } {
set str 0
}
if { ![string is double $str] } {
return 0
}
if { $str < $low || $str > $high } {
return 0
}
return 1
}
proc checkHostname { str } {
# per RFC 952 and RFC 1123, any letter, number, or hyphen
return [regexp {^[A-Za-z0-9-]+$} $str]
}
#****f* editor.tcl/focusAndFlash
# NAME
# focusAndFlash -- focus and flash
# SYNOPSIS
# focusAndFlash $W $count
# FUNCTION
# This procedure sets the focus on the bad entry field
# and on this filed it provides an effect of flashing
# for approximately 1 second.
# INPUTS
# * W -- textbox field that caused the bed entry
# * count -- the parameter that causes flashes.
# It can be left blank.
#****
proc focusAndFlash {W {count 9}} {
global badentry
set fg black
set bg white
if { $badentry == -1 } {
return
} else {
set badentry 1
}
focus -force $W
if {$count<1} {
$W configure -foreground $fg -background $bg
set badentry 0
} else {
if {$count%2} {
$W configure -foreground $bg -background $fg
} else {
$W configure -foreground $fg -background $bg
}
after 200 [list focusAndFlash $W [expr {$count - 1}]]
}
}
#****f* editor.tcl/popupConfigDialog
# NAME
# popupConfigDialog -- popup Configuration Dialog Box
# SYNOPSIS
# popupConfigDialog $c
# FUNCTION
# Dynamically creates a popup dialog box for configuring
# links or nodes in IMUNES.
# INPUTS
# * c -- canvas id
#****
proc popupConfigDialog { c } {
global activetool router_model link_color oper_mode
global badentry curcanvas
global node_location systype
global plugin_img_del
set type ""
set wi .popup
if { [winfo exists $wi ] } {
return
}
catch {destroy $wi}
toplevel $wi
wm transient $wi .
wm resizable $wi 1 1
set object_type ""
set tk_type [lindex [$c gettags current] 0]
set target [lindex [$c gettags current] 1]
if { [lsearch {node nodelabel interface} $tk_type] > -1 } {
set object_type node
}
if { [lsearch {link linklabel} $tk_type] > -1 } {
set object_type link
}
if { [lsearch {oval} $tk_type] > -1 } {
set object_type oval
}
if { [lsearch {rectangle} $tk_type] > -1 } {
set object_type rectangle
}
if { [lsearch {text} $tk_type] > -1 } {
set object_type text
}
if { "$object_type" == ""} {
destroy $wi
return
}
if { $object_type == "link" } {
set n0 [lindex [linkPeers $target] 0]
set n1 [lindex [linkPeers $target] 1]
# Boeing: added tunnel check
#if { [nodeType $n0] == "rj45" || [nodeType $n1] == "rj45" || \
# [nodeType $n0] == "tunnel" || [nodeType $n1] == "tunnel" } {
# destroy $wi
# return
#}
}
$c dtag node selected
$c delete -withtags selectmark
switch -exact -- $object_type {
node {
set type [nodeType $target]
if { $type == "pseudo" } {
#
# Hyperlink to another canvas
#
destroy $wi
set curcanvas [getNodeCanvas [getNodeMirror $target]]
switchCanvas none
return
}
set model [getNodeModel $target]
set router_model $model
wm title $wi "$type configuration"
ttk::frame $wi.ftop -borderwidth 4
ttk::entry $wi.ftop.name -width 16 \
-validate focus -invalidcommand "focusAndFlash %W"
if { $type == "rj45" } {
ttk::label $wi.ftop.name_label -text "Physical interface:"
} elseif { $type == "tunnel" } {
ttk::label $wi.ftop.name_label -text "IP address of tunnel peer:"
} else {
ttk::label $wi.ftop.name_label -text "Node name:"
$wi.ftop.name configure -validatecommand {checkHostname %P}
}
$wi.ftop.name insert 0 [getNodeName $target]
set img [getNodeImage $target]
ttk::button $wi.ftop.img -image $img -command "popupCustomImage $target"
if { $type == "rj45" } {
rj45ifclist $wi $target 0
}
# execution server
global exec_servers node_location
set node_location [getNodeLocation $target]
set servers [lsort -dictionary [array names exec_servers]]
set servers "(none) $servers"
if { $node_location == "" } { set node_location "(none)" }
eval tk_optionMenu $wi.ftop.menu node_location $servers
pack $wi.ftop.img $wi.ftop.menu $wi.ftop.name $wi.ftop.name_label \
-side right -padx 4 -pady 4
# end Boeing
pack $wi.ftop -side top
if { $type == "router" || $type == "OVS"} {
ttk::frame $wi.model -borderwidth 4
ttk::label $wi.model.label -text "Type:"
set runstate "disabled"
if { $oper_mode == "edit" } {
eval tk_optionMenu $wi.model.menu router_model \
[getNodeTypeNames]
set runstate "normal"
} else {
tk_optionMenu $wi.model.menu router_model $model
}
# would be nice to update the image upon selection; binding to
# <ButtonRelease> will not work
#tkwait variable router_model "customImageApply $wi $target"
set sock [lindex [getEmulPlugin $target] 2]
ttk::button $wi.model.services -text "Services..." -state $runstate \
-command \
"sendConfRequestMessage $sock $target services 0x1 -1 \"\""
pack $wi.model.services $wi.model.menu $wi.model.label \
-side right -padx 0 -pady 0
pack $wi.model -side top
}
if { $type == "wlan" } {
wlanConfigDialogHelper $wi $target 0
} elseif { $type == "tunnel" } {
#
# tunnel controls
#
ttk::frame $wi.con2
global conntap
set conntap [netconfFetchSection $target "tunnel-tap"]
if { $conntap == "" } { set conntap off }
# TODO: clean this up
ttk::radiobutton $wi.con2.dotap0 \
-variable conntap -value off \
-text "tunnel to another CORE emulation"
ttk::frame $wi.con2.key
ttk::label $wi.con2.key.lab -text "GRE key:"
ttk::entry $wi.con2.key.key -width 6
ttk::radiobutton $wi.con2.dotap1 -state disabled \
-variable conntap -value on \
-text "tunnel to the virtual TAP interface of another system"
pack $wi.con2.key.lab $wi.con2.key.key -side left
pack $wi.con2.dotap0 -side top -anchor w
pack $wi.con2.key -side top
pack $wi.con2.dotap1 -side top -anchor w
pack $wi.con2 -side top
set key [netconfFetchSection $target "tunnel-key"]
if { $key == "" } { set key 1 }
$wi.con2.key.key insert 0 $key
# TODO: clean this up
ttk::frame $wi.conn
ttk::label $wi.conn.label -text "Transport type:"
tk_optionMenu $wi.conn.conntype conntype "UDP" "TCP"
$wi.conn.conntype configure -state disabled
pack $wi.conn.label $wi.conn.conntype -side left -anchor w
pack $wi.conn -side top
global conntype
set conntype [netconfFetchSection $target "tunnel-type"]
if { $conntype == "" } { set conntype "UDP" }
# TODO: clean this up
ttk::frame $wi.linfo
ttk::label $wi.linfo.label -text "Local hook:"
ttk::entry $wi.linfo.local -state disabled
set localhook [netconfFetchSection $target "local-hook"]
if { $localhook == "" || $localhook == "(none)" } {
# automatically generate local hook name
set ifc [lindex [ifcList $target] 0]
if { $ifc != "" } {
set hname [info hostname]
set peer [peerByIfc $target $ifc]
set localhook "$hname$peer"
} else {
set localhook "(none)"
}
}
$wi.linfo.local insert 0 $localhook
pack $wi.linfo.label $wi.linfo.local -side left -anchor w
pack $wi.linfo -side top
ttk::frame $wi.pinfo
ttk::label $wi.pinfo.label -text "Peer hook:"
ttk::entry $wi.pinfo.peer -state disabled
$wi.pinfo.peer insert 0 \
[netconfFetchSection $target "peer-hook"]
pack $wi.pinfo.label $wi.pinfo.peer -side left -anchor w
pack $wi.pinfo -side top
}
# interface list
if { [[typemodel $target].layer] == "NETWORK" } {
# canvas used for scrolling frames for each interface
ttk::frame $wi.ifaces
set height [expr {100 * [llength [ifcList $target]]}]
if { $height > 300 } { set height 300 }
canvas $wi.ifaces.c -height $height -highlightthickness 0 \
-yscrollcommand "$wi.ifaces.scroll set"
scrollbar $wi.ifaces.scroll -command "$wi.ifaces.c yview"
pack $wi.ifaces.c -side left -fill both -expand 1
pack $wi.ifaces.scroll -side right -fill y
pack $wi.ifaces -side top -fill both -expand 1
set y 0
foreach ifc [lsort -ascii [ifcList $target]] {
set fr $wi.ifaces.c.if$ifc
ttk::labelframe $fr -text "Interface $ifc"
$wi.ifaces.c create window 4 $y -window $fr -anchor nw
incr y 100
set peer [peerByIfc $target $ifc]
if { [isEmane $peer] } {
ttk::frame $fr.opts
set caps [getCapabilities $peer "mobmodel"]
set cap [lindex $caps 0]
set cmd "sendConfRequestMessage -1 $target $cap 0x1 -1 \"\""
ttk::button $fr.opts.cfg -command $cmd \
-text "$cap options..."
pack $fr.opts.cfg -side left -padx 4
pack $fr.opts -side top -anchor w
incr y 28
}
ttk::frame $fr.cfg
#
# MAC address
#
ttk::frame $fr.cfg.mac
ttk::label $fr.cfg.mac.addrl -text "MAC address" \
-anchor w
set macaddr [getIfcMacaddr $target $ifc]
global if${ifc}_auto_mac
if { $macaddr == "" } {
set if${ifc}_auto_mac 1
set state disabled
} else {
set if${ifc}_auto_mac 0
set state normal
}
ttk::checkbutton $fr.cfg.mac.auto -text "auto-assign" \
-variable if${ifc}_auto_mac \
-command "macEntryHelper $wi $ifc"
ttk::entry $fr.cfg.mac.addrv -width 15 \
-state $state
$fr.cfg.mac.addrv insert 0 $macaddr
pack $fr.cfg.mac.addrl $fr.cfg.mac.auto \
$fr.cfg.mac.addrv -side left -padx 4
pack $fr.cfg.mac -side top -anchor w
#
# IPv4 address
#
ttk::frame $fr.cfg.ipv4
ttk::label $fr.cfg.ipv4.addrl -text "IPv4 address" \
-anchor w
ttk::entry $fr.cfg.ipv4.addrv -width 30 \
-validate focus -invalidcommand "focusAndFlash %W"
$fr.cfg.ipv4.addrv insert 0 \
[getIfcIPv4addr $target $ifc]
$fr.cfg.ipv4.addrv configure \
-validatecommand {checkIPv4Net %P}
ttk::button $fr.cfg.ipv4.clear -image $plugin_img_del \
-command "$fr.cfg.ipv4.addrv delete 0 end"
pack $fr.cfg.ipv4.addrl $fr.cfg.ipv4.addrv \
$fr.cfg.ipv4.clear -side left
pack $fr.cfg.ipv4 -side top -anchor w -padx 4
#
# IPv6 address
#
ttk::frame $fr.cfg.ipv6
ttk::label $fr.cfg.ipv6.addrl -text "IPv6 address" \
-anchor w
ttk::entry $fr.cfg.ipv6.addrv -width 30 \
-validate focus -invalidcommand "focusAndFlash %W"
$fr.cfg.ipv6.addrv insert 0 \
[getIfcIPv6addr $target $ifc]
$fr.cfg.ipv6.addrv configure -validatecommand {checkIPv6Net %P}
ttk::button $fr.cfg.ipv6.clear -image $plugin_img_del \
-command "$fr.cfg.ipv6.addrv delete 0 end"
pack $fr.cfg.ipv6.addrl $fr.cfg.ipv6.addrv \
$fr.cfg.ipv6.clear -side left
pack $fr.cfg.ipv6 -side top -anchor w -padx 4
pack $fr.cfg -side left
bind $fr.cfg <4> "$wi.ifaces.c yview scroll -1 units"
bind $fr.cfg <5> "$wi.ifaces.c yview scroll 1 units"
} ;# end foreach ifc
$wi.ifaces.c configure -scrollregion "0 0 250 $y"
# mouse wheel bindings for scrolling
foreach ctl [list $wi.ifaces.c $wi.ifaces.scroll] {
bind $ctl <4> "$wi.ifaces.c yview scroll -1 units"
bind $ctl <5> "$wi.ifaces.c yview scroll 1 units"
bind $ctl <Up> "$wi.ifaces.c yview scroll -1 units"
bind $ctl <Down> "$wi.ifaces.c yview scroll 1 units"
}
}
}
oval {
destroy $wi
annotationConfig $c $target
return
}
rectangle {
destroy $wi
annotationConfig $c $target
return
}
text {
destroy $wi
annotationConfig $c $target
return
}
link {
wm title $wi "link configuration"
ttk::frame $wi.ftop -borderwidth 6
set nam0 [getNodeName $n0]
set nam1 [getNodeName $n1]
ttk::label $wi.ftop.name_label -justify left -text \
"Link from $nam0 to $nam1"
pack $wi.ftop.name_label -side right
pack $wi.ftop -side top
set spinbox [getspinbox]
global g_link_config_uni_state
set g_link_config_uni_state "bid"
ttk::frame $wi.preset -borderwidth 4
global link_preset_val
set link_preset_val unlimited
set linkpreMenu [tk_optionMenu $wi.preset.linkpre link_preset_val a]
# unidirectional links not always supported
if { [isUniSupported $n0 $n1] } {
set unistate normal
} else {
set unistate disabled
}
ttk::button $wi.preset.uni -text " >> " -state $unistate \
-command "linkConfigUni $wi"
pack $wi.preset.uni $wi.preset.linkpre -side right
linkPresets $wi $linkpreMenu init
pack $wi.preset -side top -anchor e
ttk::frame $wi.unilabel -borderwidth 4
ttk::label $wi.unilabel.updown -text "Symmetric link effects:"
pack $wi.unilabel.updown -side left -anchor w
pack $wi.unilabel -side top -anchor w
ttk::frame $wi.bandwidth -borderwidth 4
ttk::label $wi.bandwidth.label -anchor e -text "Bandwidth (bps):"
$spinbox $wi.bandwidth.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.bandwidth.value insert 0 [getLinkBandwidth $target]
$wi.bandwidth.value configure \
-validatecommand {checkIntRange %P 0 1000000000} \
-from 0 -to 1000000000 -increment 1000000
pack $wi.bandwidth.value $wi.bandwidth.label -side right
pack $wi.bandwidth -side top -anchor e
ttk::frame $wi.delay -borderwidth 4
ttk::label $wi.delay.label -anchor e -text "Delay (us):"
$spinbox $wi.delay.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.delay.value insert 0 [getLinkDelay $target]
# 274 seconds is maximum netem delay for Linux 3.2.0-60-generic kernel
$wi.delay.value configure \
-validatecommand {checkIntRange %P 0 274000000} \
-from 0 -to 10000000 -increment 5
pack $wi.delay.value $wi.delay.label -side right
pack $wi.delay -side top -anchor e
ttk::frame $wi.jitter -borderwidth 4
ttk::label $wi.jitter.label -anchor e -text "Jitter (us):"
$spinbox $wi.jitter.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.jitter.value insert 0 [getLinkJitter $target]
$wi.jitter.value configure \
-validatecommand {checkIntRange %P 0 10000000} \
-from 0 -to 10000000 -increment 5
pack $wi.jitter.value $wi.jitter.label -side right
pack $wi.jitter -side top -anchor e
ttk::frame $wi.ber -borderwidth 4
if { [lindex $systype 0] == "Linux" } {
set bertext "Loss (%):"
set berinc 0.1
set bermax 100.0
} else { ;# netgraph uses BER
set bertext "BER (1/N):"
set berinc 1000
set bermax 10000000000000
}
ttk::label $wi.ber.label -anchor e -text $bertext
$spinbox $wi.ber.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.ber.value insert 0 [getLinkBER $target]
$wi.ber.value configure \
-validatecommand "checkFloatRange %P 0.0 $bermax" \
-from 0.0 -to $bermax -increment $berinc
pack $wi.ber.value $wi.ber.label -side right
pack $wi.ber -side top -anchor e
ttk::frame $wi.dup -borderwidth 4
ttk::label $wi.dup.label -anchor e -text "Duplicate (%):"
$spinbox $wi.dup.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.dup.value insert 0 [getLinkDup $target]
$wi.dup.value configure \
-validatecommand {checkFloatRange %P 0 50} \
-from 0 -to 50 -increment 1
pack $wi.dup.value $wi.dup.label -side right
pack $wi.dup -side top -anchor e
# Boeing: jitter
# frame $wi.jitter -borderwidth 4
# label $wi.jitter.label -anchor e -text "Jitter (us):"
# spinbox $wi.jitter.value -bg white -justify right -width 10 \
# -validate focus -invalidcommand "focusAndFlash %W"
# $wi.jitter.value insert 0 [getLinkJitter $target]
# $wi.jitter.value configure \
# -validatecommand {checkIntRange %P 0 10000000} \
# -from 0 -to 10000000 -increment 5
# pack $wi.jitter.value $wi.jitter.label -side right
# pack $wi.jitter -side top -anchor e
# end Boeing
ttk::frame $wi.color -borderwidth 4
ttk::label $wi.color.label -anchor e -text "Color:"
set link_color [getLinkColor $target]
tk_optionMenu $wi.color.value link_color \
Red Green Blue Yellow Magenta Cyan Black
$wi.color.value configure -width 8
pack $wi.color.value $wi.color.label -side right
pack $wi.color -side top -anchor e
ttk::frame $wi.width -borderwidth 4
ttk::label $wi.width.label -anchor e -text "Width:"
$spinbox $wi.width.value -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.width.value insert 0 [getLinkWidth $target]
$wi.width.value configure \
-validatecommand {checkIntRange %P 1 8} \
-from 1 -to 8 -increment 1
pack $wi.width.value $wi.width.label -side right
pack $wi.width -side top -anchor e
# auto-expand upstream if values exist
set bw [getLinkBandwidth $target up]
set dl [getLinkDelay $target up]
set jt [getLinkJitter $target up]
set ber [getLinkBER $target up]
set dup [getLinkDup $target up]
if { $bw > 0 || $dl > 0 || $jt > 0 || $ber > 0 || $dup > 0 } {
linkConfigUni $wi
$wi.bandwidth.value2 delete 0 end
$wi.bandwidth.value2 insert 0 $bw
$wi.delay.value2 delete 0 end
$wi.delay.value2 insert 0 $dl
$wi.jitter.value2 delete 0 end
$wi.jitter.value2 insert 0 $jt
$wi.ber.value2 delete 0 end
$wi.ber.value2 insert 0 $ber
$wi.dup.value2 delete 0 end
$wi.dup.value2 insert 0 $dup
}
}
} ;# end switch
ttk::frame $wi.butt -borderwidth 6
# NOTE: plugins.tcl:popupCapabilityConfig may read this command option
ttk::button $wi.butt.apply -text "Apply" -command \
"popupConfigApply $wi $object_type $target 0"
focus $wi.butt.apply
# Boeing: remove range circles upon cancel
if {$type == "wlan"} {
set cancelcmd "set badentry -1 ; destroy $wi;"
set cancelcmd "$cancelcmd updateRangeCircles $target 0"
} else {
set cancelcmd "set badentry -1 ; destroy $wi"
}
ttk::button $wi.butt.cancel -text "Cancel" -command $cancelcmd
#end Boeing
pack $wi.butt.cancel $wi.butt.apply -side right
pack $wi.butt -side bottom
bind $wi <Key-Escape> $cancelcmd
# bind $wi <Key-Return> "popupConfigApply $wi $object_type $target 0"
}
proc linkConfigUni { wi } {
global g_link_config_uni_state
set capt [lindex [$wi.preset.uni configure -text] 4]
if { $capt == " >> " } {
set g_link_config_uni_state "uni"
$wi.preset.uni configure -text " << "
set txt "Asymmetric effects: downstream / upstream"
$wi.unilabel.updown configure -text $txt
set spinbox [getspinbox]
if { ![winfo exists $wi.bandwidth.value2] } {
$spinbox $wi.bandwidth.value2 -justify right \
-width 10 -validate focus -invalidcommand "focusAndFlash %W"
$wi.bandwidth.value2 configure \
-validatecommand {checkIntRange %P 0 1000000000} \
-from 0 -to 1000000000 -increment 1000000
}
$wi.bandwidth.value2 delete 0 end
$wi.bandwidth.value2 insert 0 [$wi.bandwidth.value get]
pack $wi.bandwidth.value2 -side right
pack $wi.bandwidth.value2 -before $wi.bandwidth.value
if { ![winfo exists $wi.delay.value2] } {
$spinbox $wi.delay.value2 -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.delay.value2 configure \
-validatecommand {checkIntRange %P 0 10000000} \
-from 0 -to 10000000 -increment 5
}
$wi.delay.value2 delete 0 end
$wi.delay.value2 insert 0 [$wi.delay.value get]
pack $wi.delay.value2 -side right
pack $wi.delay.value2 -before $wi.delay.value
if { ![winfo exists $wi.jitter.value2] } {
$spinbox $wi.jitter.value2 -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.jitter.value2 configure \
-validatecommand {checkIntRange %P 0 10000000} \
-from 0 -to 10000000 -increment 5
}
$wi.jitter.value2 delete 0 end
$wi.jitter.value2 insert 0 [$wi.jitter.value get]
pack $wi.jitter.value2 -side right
pack $wi.jitter.value2 -before $wi.jitter.value
if { ![winfo exists $wi.ber.value2] } {
$spinbox $wi.ber.value2 -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.ber.value2 configure \
-validatecommand "checkFloatRange %P 0.0 100.0" \
-from 0.0 -to 100.0 -increment 0.1
}
$wi.ber.value2 delete 0 end
$wi.ber.value2 insert 0 [$wi.ber.value get]
pack $wi.ber.value2 -side right
pack $wi.ber.value2 -before $wi.ber.value
if { ![winfo exists $wi.dup.value2] } {
$spinbox $wi.dup.value2 -justify right -width 10 \
-validate focus -invalidcommand "focusAndFlash %W"
$wi.dup.value2 configure \
-validatecommand {checkFloatRange %P 0 50} \
-from 0 -to 50 -increment 1
}
$wi.dup.value2 delete 0 end
$wi.dup.value2 insert 0 [$wi.dup.value get]
pack $wi.dup.value2 -side right
pack $wi.dup.value2 -before $wi.dup.value
} else {
set g_link_config_uni_state "bid"
$wi.preset.uni configure -text " >> "
$wi.unilabel.updown configure -text "Symmetric link effects:"
pack forget $wi.bandwidth.value2
pack forget $wi.delay.value2
pack forget $wi.jitter.value2
pack forget $wi.ber.value2
pack forget $wi.dup.value2
}
}
# unidirectional links are not always supported
proc isUniSupported { n1 n2 } {
set blacklist [list "hub" "lanswitch"]
set type1 [nodeType $n1]
set type2 [nodeType $n2]
# not yet supported for GRE tap device
if { $type1 == "tunnel" || $type2 == "tunnel" } {
return false
}
# unidirectional links are supported between two switches/hubs
if { [lsearch $blacklist $type1] != -1 && \
[lsearch $blacklist $type2] != -1 } {
return true
}
# unidirectional links not supported between hub/switch and something else
if { [lsearch $blacklist $type1] != -1 || \
[lsearch $blacklist $type2] != -1 } {
return false
}
# unidirectional links are supported between routers, rj45s, etc.
# WLANs not included here because they have no link dialog
return true
}
# toggle the state of the mac address entry, and insert MAC address template
proc macEntryHelper { wi ifc } {
set fr $wi.ifaces.c.if$ifc
set ctl $fr.cfg.mac.addrv
set s normal
if { [$ctl cget -state] == $s } { set s disabled }
$ctl configure -state $s
if { [$ctl get] == "" } { $ctl insert 0 "00:00:00:00:00:00" }
}
#****f* editor.tcl/popupConfigApply
# NAME
# popupConfigApply -- popup configuration apply
# SYNOPSIS
# popupConfigApply $w $object_type $target $phase
# FUNCTION
# This procedure is called when the button apply is pressed in
# popup configuration dialog box. It reads different
# configuration parameters depending on the object_type.
# INPUTS
# * w -- widget
# * object_type -- describes the object type that is currently
# configured. It can be either link or node.
# * target -- node id of the configured node or link id of the
# configured link
# * phase -- This procedure is invoked in two diffenet phases
# to enable validation of the entry that was the last made.
# When calling this function always use the phase parameter
# set to 0.
#****
proc popupConfigApply { wi object_type target phase } {
global changed oper_mode router_model link_color badentry
global customEnabled ipsecEnabled
global eid
$wi config -cursor watch
update
if { $phase == 0 } {
set badentry 0
focus .
after 100 "popupConfigApply $wi $object_type $target 1"
return
} elseif { $badentry } {
$wi config -cursor left_ptr
return
}
switch -exact -- $object_type {
#
# Node
#
node {
set type [nodeType $target]
set model [getNodeModel $target]
set name [string trim [$wi.ftop.name get]]
set changed_to_remote 0
global node_location
if { $node_location != [getNodeLocation $target] } {
if { $node_location == "(none)" } { set node_location "" }
setNodeLocation $target $node_location
set changed 1
}
set node_location ""
if { $name != [getNodeName $target] } {
setNodeName $target $name
set changed 1
}
if { $oper_mode == "edit" && $type == "router" && \
$router_model != $model } {
setNodeModel $target $router_model
set changed 1
if { $router_model == "remote" } { set changed_to_remote 1 };#Boeing
}
# Boeing - added wlan, remote, tunnel, ktunnel items
if { $type == "wlan" } {
wlanConfigDialogHelper $wi $target 1
} elseif { $type == "tunnel" } {
#
# apply tunnel items
#
set ipaddr "$name/24" ;# tunnel name == IP address of peer
set oldipaddr [getIfcIPv4addr $target e0]
if { $ipaddr != $oldipaddr } {
setIfcIPv4addr $target e0 $ipaddr
}
global conntype conntap
set oldconntype [netconfFetchSection $target "tunnel-type"]
if { $oldconntype != $conntype } {
netconfInsertSection $target [list "tunnel-type" $conntype]
}
set oldconntap [netconfFetchSection $target "tunnel-tap"]
if { $oldconntap != $conntap } {
netconfInsertSection $target [list "tunnel-tap" $conntap]
}
set oldkey [netconfFetchSection $target "tunnel-key"]
set key [$wi.con2.key.key get]
if { $oldkey != $key } {
netconfInsertSection $target [list "tunnel-key" $key]
}
set oldlocal [netconfFetchSection $target "local-hook"]
set local [$wi.linfo.local get]
if { $oldlocal != $local } {
netconfInsertSection $target [list "local-hook" $local]
}
set oldpeer [netconfFetchSection $target "peer-hook"]
set peer [$wi.pinfo.peer get]
if { $oldpeer != $peer } {
netconfInsertSection $target [list "peer-hook" $peer]
}
} elseif { $type == "ktunnel" } {
#
# apply ktunnel items
#
set oldlocal [netconfFetchSection $target "local-hook"]
set local [$wi.linfo.local get]
if { $oldlocal != $local } {
netconfInsertSection $target [list "local-hook" $local]
}
# Boeing changing to interface name for RJ45
# } elseif { $type == "rj45" } {
# #
# # apply rj45 items
# #
# set ifcName [string trim [$wi.interface.name get]]
# puts "$ifcName\n"
#
} elseif { $type == "router" && [getNodeModel $target] == "remote" } {
if { $changed_to_remote == 0 } {
set i 1
set remoteIP [string trim [$wi.remoteinfo.ip.text get $i.0 $i.end]]
if { $remoteIP != [router.remote.getRemoteIP $target] } {
router.remote.setRemoteIP $target $remoteIP
set changed 1
}
set ifc [string trim [$wi.remoteinfo.ifc.text get $i.0 $i.end]]
if { $ifc != [router.remote.getCInterface $target] } {
router.remote.setCInterface $target $ifc
set changed 1
}
set startcmd [string trim [$wi.remotecommands.start.text get $i.0 $i.end]]
if { $startcmd != [router.remote.getStartCmd $target] } {
router.remote.setStartCmd $target $startcmd
set changed 1
}
set stopcmd [string trim [$wi.remotecommands.stop.text get $i.0 $i.end]]
if { $stopcmd != [router.remote.getStopCmd $target] } {
router.remote.setStopCmd $target $stopcmd
set changed 1
}
}
}
if {[[typemodel $target].layer] == "NETWORK"} {
foreach ifc [ifcList $target] {
set fr $wi.ifaces.c.if$ifc
set macaddr [$fr.cfg.mac.addrv get]
global if${ifc}_auto_mac
if { [set if${ifc}_auto_mac] == 1 } { set macaddr "" }
set oldmacaddr [getIfcMacaddr $target $ifc]
if { $macaddr != $oldmacaddr } {
setIfcMacaddr $target $ifc $macaddr
set changed 1
}
set ipaddr [$fr.cfg.ipv4.addrv get]
set oldipaddr [getIfcIPv4addr $target $ifc]
if { $ipaddr != $oldipaddr } {
setIfcIPv4addr $target $ifc $ipaddr
set changed 1
}
set ipaddr [$fr.cfg.ipv6.addrv get]
set oldipaddr [getIfcIPv6addr $target $ifc]
if { $ipaddr != $oldipaddr } {
setIfcIPv6addr $target $ifc $ipaddr
set changed 1
}
}
}
}
link {
global g_link_config_uni_state
set mirror [getLinkMirror $target]
if { [setIfChanged $target $mirror $wi "bandwidth" "LinkBandwidth"] } {
set changed 1
}
if { [setIfChanged $target $mirror $wi "delay" "LinkDelay"] } {
set changed 1
}
if { [setIfChanged $target $mirror $wi "ber" "LinkBER"] } {
set changed 1
}
if { [setIfChanged $target $mirror $wi "dup" "LinkDup"] } {
set changed 1
}
if { [setIfChanged $target $mirror $wi "jitter" "LinkJitter"] } {
set changed 1
}
if { $link_color != [getLinkColor $target] } {
setLinkColor $target $link_color
if { $mirror != "" } {
setLinkColor $mirror $link_color
}
set changed 1
}
set width [$wi.width.value get]
if { $width != [getLinkWidth $target] } {
setLinkWidth $target $width
if { $mirror != "" } {
setLinkWidth $mirror $width
}
set changed 1
}
if { $changed == 1 && $oper_mode == "exec" } {
execSetLinkParams $eid $target
}
}
}
popdownConfig $wi
}
# helper for Link Config dialog
# ctl must exist as $wi.$ctl.value{2}, and {get,set}$procname must be valid
# returns true when value has changed, false otherwise
proc setIfChanged { target mirror wi ctl procname } {
global g_link_config_uni_state
set val [$wi.$ctl.value get]
if { $g_link_config_uni_state == "uni" } {
set val [list $val [$wi.$ctl.value2 get]]
}
set oldval [get$procname $target]
set oldval2 [get$procname $target "up"]
if { $oldval2 != "" } {
set oldval [list $oldval $oldval2]
}
if { $val != $oldval } {
set$procname $target $val
if { $mirror != "" } {
set$procname $mirror $val
}
return true
}
return false
}
#****f* editor.tcl/printCanvas
# NAME
# printCanvas -- print canvas
# SYNOPSIS
# printCanvas $w
# FUNCTION
# This procedure is called when the print button in
# print dialog box is pressed.
# INPUTS
# * w -- print dialog widget
#****
proc printCanvas { w } {
global sizex sizey
set prncmd [$w.e1 get]
destroy $w
set p [open "|$prncmd" WRONLY]
puts $p [.c postscript -height $sizey -width $sizex -x 0 -y 0 -rotate yes -pageheight 297m -pagewidth 210m]
close $p
}
#****f* editor.tcl/deleteSelection
# NAME
# deleteSelection -- delete selection
# SYNOPSIS
# deleteSelection
# FUNCTION
# By calling this procedure all the selected nodes in imunes will
# be deleted.
#****
proc deleteSelection { } {
global changed
global background
global viewid
catch {unset viewid}
.c config -cursor watch; update
foreach lnode [selectedNodes] {
if { $lnode != "" } {
removeGUINode $lnode
}
set changed 1
}
raiseAll .c
updateUndoLog
.c config -cursor left_ptr
.bottom.textbox config -text ""
}
proc assignSelection { server } {
global changed
.c config -cursor watch; update
foreach node [selectedNodes] {
if { $node != "" } {
setNodeLocation $node $server
}
set changed 1
}
redrawAll
updateUndoLog
.c config -cursor left_ptr
.bottom.textbox config -text ""
}
proc align2grid {} {
global sizex sizey grid zoom changed
set node_objects [.c find withtag node]
if { [llength $node_objects] == 0 } {
return
}
set step [expr {$grid * 4}]
for { set x $step } { $x <= [expr {$sizex - $step}] } { incr x $step } {
for { set y $step } { $y <= [expr {$sizey - $step}] } { incr y $step } {
if { [llength $node_objects] == 0 } {
set changed 1
updateUndoLog
redrawAll
return
}
set node [lindex [.c gettags [lindex $node_objects 0]] 1]
set node_objects [lreplace $node_objects 0 0]
setNodeCoords $node "$x $y"
lassign [getDefaultLabelOffsets [nodeType $node]] dx dy
setNodeLabelCoords $node "[expr {$x + $dx}] [expr {$y + $dy}]"
}
}
}
#****f* editor.tcl/rearrange
# NAME
# rearrange
# SYNOPSIS
# rearrange $mode
# FUNCTION
# This procedure rearranges the position of nodes in imunes.
# It can be used to rearrange all the nodes or only the selected
# nodes.
# INPUTS
# * mode -- when set to "selected" only the selected nodes will be
# rearranged.
#****
proc rearrange { mode } {
global link_list autorearrange_enabled sizex sizey curcanvas zoom activetool
set activetool select
if { $autorearrange_enabled } {
rearrange_off
return
}
set autorearrange_enabled 1
.bottom.mbuf config -text "autorearrange"
if { $mode == "selected" } {
.menubar.tools entryconfigure "Auto rearrange all" -state disabled
.menubar.tools entryconfigure "Auto rearrange all" -indicatoron off
.menubar.tools entryconfigure "Auto rearrange selected" -indicatoron on
set tagmatch "node && selected"
} else {
.menubar.tools entryconfigure "Auto rearrange all" -indicatoron on
.menubar.tools entryconfigure "Auto rearrange selected" -state disabled
.menubar.tools entryconfigure "Auto rearrange selected" -indicatoron off
set tagmatch "node"
}
set otime [clock clicks -milliseconds]
while { $autorearrange_enabled } {
set ntime [clock clicks -milliseconds]
if { $otime == $ntime } {
set dt 0.001
} else {
set dt [expr {($ntime - $otime) * 0.001}]
if { $dt > 0.2 } {
set dt 0.2
}
set otime $ntime
}
set objects [.c find withtag $tagmatch]
set peer_objects [.c find withtag node]
foreach obj $peer_objects {
set node [lindex [.c gettags $obj] 1]
set coords [.c coords $obj]
set x [expr {[lindex $coords 0] / $zoom}]
set y [expr {[lindex $coords 1] / $zoom}]
set x_t($node) $x
set y_t($node) $y
if { $x > 0 } {
set fx [expr {1000 / ($x * $x + 100)}]
} else {
set fx 10
}
set dx [expr {$sizex - $x}]
if { $dx > 0 } {
set fx [expr {$fx - 1000 / ($dx * $dx + 100)}]
} else {
set fx [expr {$fx - 10}]
}
if { $y > 0 } {
set fy [expr {1000 / ($y * $y + 100)}]
} else {
set fy 10
}
set dy [expr {$sizey - $y}]
if { $dy > 0 } {
set fy [expr {$fy - 1000 / ($dy * $dy + 100)}]
} else {
set fy [expr {$fy - 10}]
}
set fx_t($node) $fx
set fy_t($node) $fy
}
foreach obj $objects {
set node [lindex [.c gettags $obj] 1]
set i [lsearch -exact $peer_objects $obj]
set peer_objects [lreplace $peer_objects $i $i]
set x $x_t($node)
set y $y_t($node)
foreach other_obj $peer_objects {
set other [lindex [.c gettags $other_obj] 1]
set o_x $x_t($other)
set o_y $y_t($other)
set dx [expr {$x - $o_x}]
set dy [expr {$y - $o_y}]
set d [expr {hypot($dx, $dy)}]
set d2 [expr {$d * $d}]
set p_fx [expr {1000.0 * $dx / ($d2 * $d + 100)}]
set p_fy [expr {1000.0 * $dy / ($d2 * $d + 100)}]
if {[linkByPeers $node $other] != ""} {
set p_fx [expr {$p_fx - $dx * $d2 * .0000000005}]
set p_fy [expr {$p_fy - $dy * $d2 * .0000000005}]
}
set fx_t($node) [expr {$fx_t($node) + $p_fx}]
set fy_t($node) [expr {$fy_t($node) + $p_fy}]
set fx_t($other) [expr {$fx_t($other) - $p_fx}]
set fy_t($other) [expr {$fy_t($other) - $p_fy}]
}
foreach link $link_list {
set nodes [linkPeers $link]
if { [getNodeCanvas [lindex $nodes 0]] != $curcanvas ||
[getNodeCanvas [lindex $nodes 1]] != $curcanvas ||
[getLinkMirror $link] != "" } {
continue
}
set peers [linkPeers $link]
set coords0 [getNodeCoords [lindex $peers 0]]
set coords1 [getNodeCoords [lindex $peers 1]]
set o_x \
[expr {([lindex $coords0 0] + [lindex $coords1 0]) * .5}]
set o_y \
[expr {([lindex $coords0 1] + [lindex $coords1 1]) * .5}]
set dx [expr {$x - $o_x}]
set dy [expr {$y - $o_y}]
set d [expr {hypot($dx, $dy)}]
set d2 [expr {$d * $d}]
set fx_t($node) \
[expr {$fx_t($node) + 500.0 * $dx / ($d2 * $d + 100)}]
set fy_t($node) \
[expr {$fy_t($node) + 500.0 * $dy / ($d2 * $d + 100)}]
}
}
foreach obj $objects {
set node [lindex [.c gettags $obj] 1]
if { [catch "set v_t($node)" v] } {
set vx 0.0
set vy 0.0
} else {
set vx [lindex $v_t($node) 0]
set vy [lindex $v_t($node) 1]
}
set vx [expr {$vx + 1000.0 * $fx_t($node) * $dt}]
set vy [expr {$vy + 1000.0 * $fy_t($node) * $dt}]
set dampk [expr {0.5 + ($vx * $vx + $vy * $vy) * 0.00001}]
set vx [expr {$vx * exp( - $dampk * $dt)}]
set vy [expr {$vy * exp( - $dampk * $dt)}]
set dx [expr {$vx * $dt}]
set dy [expr {$vy * $dt}]
set x [expr {$x_t($node) + $dx}]
set y [expr {$y_t($node) + $dy}]
set v_t($node) "$vx $vy"
setNodeCoords $node "$x $y"
set e_dx [expr {$dx * $zoom}]
set e_dy [expr {$dy * $zoom}]
.c move $obj $e_dx $e_dy
set img [.c find withtag "selectmark && $node"]
.c move $img $e_dx $e_dy
set img [.c find withtag "nodelabel && $node"]
.c move $img $e_dx $e_dy
set x [expr {[lindex [.c coords $img] 0] / $zoom}]
set y [expr {[lindex [.c coords $img] 1] / $zoom}]
setNodeLabelCoords $node "$x $y"
.c addtag need_redraw withtag "link && $node"
}
foreach link [.c find withtag "link && need_redraw"] {
redrawLink [lindex [.c gettags $link] 1]
}
.c dtag link need_redraw
update
}
rearrange_off
.bottom.mbuf config -text ""
}
proc rearrange_off { } {
global autorearrange_enabled
set autorearrange_enabled 0
.menubar.tools entryconfigure "Auto rearrange all" -state normal
.menubar.tools entryconfigure "Auto rearrange all" -indicatoron off
.menubar.tools entryconfigure "Auto rearrange selected" -state normal
.menubar.tools entryconfigure "Auto rearrange selected" -indicatoron off
}
#****f* editor.tcl/switchCanvas
# NAME
# switchCanvas -- switch canvas
# SYNOPSIS
# switchCanvas $direction
# FUNCTION
# This procedure switches the canvas in one of the defined
# directions (previous, next, first and last).
# INPUTS
# * direction -- the direction of switching canvas. Can be: prev --
# previus, next -- next, first -- first, last -- last.
#****
proc switchCanvas { direction } {
global canvas_list curcanvas
global sizex sizey
set i [lsearch $canvas_list $curcanvas]
switch -exact -- $direction {
prev {
incr i -1
if { $i < 0 } {
set curcanvas [lindex $canvas_list end]
} else {
set curcanvas [lindex $canvas_list $i]
}
}
next {
incr i
if { $i >= [llength $canvas_list] } {
set curcanvas [lindex $canvas_list 0]
} else {
set curcanvas [lindex $canvas_list $i]
}
}
first {
set curcanvas [lindex $canvas_list 0]
}
last {
set curcanvas [lindex $canvas_list end]
}
}
.hframe.t delete all
set x 0
foreach canvas $canvas_list {
set text [.hframe.t create text 0 0 \
-text "[getCanvasName $canvas]" -tags "text $canvas"]
set ox [lindex [.hframe.t bbox $text] 2]
set oy [lindex [.hframe.t bbox $text] 3]
set tab [.hframe.t create polygon $x 0 [expr {$x + 7}] 18 \
[expr {$x + 2 * $ox + 17}] 18 [expr {$x + 2 * $ox + 24}] 0 $x 0 \
-fill gray -tags "tab $canvas"]
set line [.hframe.t create line 0 0 $x 0 [expr {$x + 7}] 18 \
[expr {$x + 2 * $ox + 17}] 18 [expr {$x + 2 * $ox + 24}] 0 999 0 \
-fill #808080 -width 2 -tags "line $canvas"]
.hframe.t coords $text [expr {$x + $ox + 12}] [expr {$oy + 2}]
.hframe.t raise $text
incr x [expr {2 * $ox + 17}]
}
incr x 7
.hframe.t raise "$curcanvas"
.hframe.t itemconfigure "tab && $curcanvas" -fill #e0e0e0
.hframe.t configure -scrollregion "0 0 $x 18"
update
set width [lindex [.hframe.t configure -width] 4]
set lborder [lindex [.hframe.t bbox "tab && $curcanvas"] 0]
set rborder [lindex [.hframe.t bbox "tab && $curcanvas"] 2]
set lmargin [expr {[lindex [.hframe.t xview] 0] * $x - 1}]
set rmargin [expr {[lindex [.hframe.t xview] 1] * $x + 1}]
if { $lborder < $lmargin } {
.hframe.t xview moveto [expr {1.0 * ($lborder - 10) / $x}]
}
if { $rborder > $rmargin } {
.hframe.t xview moveto [expr {1.0 * ($rborder - $width + 10) / $x}]
}
set sizex [lindex [getCanvasSize $curcanvas] 0]
set sizey [lindex [getCanvasSize $curcanvas] 1]
redrawAll
}
proc resizeCanvasPopup {} {
global curcanvas
set w .canvasSizeScaleDialog
catch {destroy $w}
toplevel $w
wm transient $w .
wm title $w "Canvas Size and Scale"
frame $w.buttons
pack $w.buttons -side bottom -fill x -pady 2m
button $w.buttons.print -text "Apply" -command "resizeCanvasApply $w"
button $w.buttons.cancel -text "Cancel" -command "destroy $w"
pack $w.buttons.print $w.buttons.cancel -side left -expand 1
set cursize [getCanvasSize $curcanvas]
set x [lindex $cursize 0]
set y [lindex $cursize 1]
set scale [getCanvasScale $curcanvas]
set refpt [getCanvasRefPoint $curcanvas]
set refx [lindex $refpt 0]
set refy [lindex $refpt 1]
set latitude [lindex $refpt 2]
set longitude [lindex $refpt 3]
set altitude [lindex $refpt 4]
labelframe $w.size -text "Size"
frame $w.size.pixels
pack $w.size $w.size.pixels -side top -padx 4 -pady 4 -fill x
spinbox $w.size.pixels.x -bg white -width 5
$w.size.pixels.x insert 0 $x
$w.size.pixels.x configure -from 300 -to 5000 -increment 2
label $w.size.pixels.label -text "W x"
spinbox $w.size.pixels.y -bg white -width 5
$w.size.pixels.y insert 0 $y
$w.size.pixels.y configure -from 300 -to 5000 -increment 2
label $w.size.pixels.label2 -text "H pixels"
pack $w.size.pixels.x $w.size.pixels.label $w.size.pixels.y \
$w.size.pixels.label2 -side left -pady 2 -padx 2 -fill x
frame $w.size.meters
pack $w.size.meters -side top -padx 4 -pady 4 -fill x
spinbox $w.size.meters.x -bg white -width 7
$w.size.meters.x configure -from 300 -to 10000 -increment 100
label $w.size.meters.label -text "x"
spinbox $w.size.meters.y -bg white -width 7
$w.size.meters.y configure -from 300 -to 10000 -increment 100
label $w.size.meters.label2 -text "meters"
pack $w.size.meters.x $w.size.meters.label $w.size.meters.y \
$w.size.meters.label2 -side left -pady 2 -padx 2 -fill x
labelframe $w.scale -text "Scale"
frame $w.scale.ppm
pack $w.scale $w.scale.ppm -side top -padx 4 -pady 4 -fill x
label $w.scale.ppm.label -text "100 pixels ="
entry $w.scale.ppm.metersper100 -bg white -width 10
$w.scale.ppm.metersper100 insert 0 $scale
label $w.scale.ppm.label2 -text "meters"
pack $w.scale.ppm.label $w.scale.ppm.metersper100 \
$w.scale.ppm.label2 -side left -pady 2 -padx 2 -fill x
bind $w.size.pixels.x <Button> "syncSizeScale $w xp"
bind $w.size.pixels.y <Button> "syncSizeScale $w yp"
bind $w.size.pixels.x <FocusOut> "syncSizeScale $w xp"
bind $w.size.pixels.y <FocusOut> "syncSizeScale $w yp"
bind $w.size.meters.x <FocusOut> "syncSizeScale $w xm"
bind $w.size.meters.y <FocusOut> "syncSizeScale $w ym"
bind $w.size.meters.x <Button> "syncSizeScale $w xm"
bind $w.size.meters.y <Button> "syncSizeScale $w ym"
bind $w.scale.ppm.metersper100 <FocusOut> "syncSizeScale $w scale"
#bind $w.scale.ppm.metersper100 <KeyPress> "syncSizeScale $w"
labelframe $w.ref -text "Reference point"
frame $w.ref.pt
pack $w.ref $w.ref.pt -side top -padx 4 -pady 4 -fill x
set hlp "The default reference point is (0,0), the upper-left corner of"
set hlp "$hlp the canvas."
label $w.ref.pt.help -text $hlp
entry $w.ref.pt.x -bg white -width 4
label $w.ref.pt.label -text "X,"
entry $w.ref.pt.y -bg white -width 4
label $w.ref.pt.label2 -text "Y ="
entry $w.ref.pt.lat -bg white -width 12
label $w.ref.pt.label3 -text "lat,"
entry $w.ref.pt.long -bg white -width 12
label $w.ref.pt.label4 -text "long"
$w.ref.pt.x insert 0 $refx
$w.ref.pt.y insert 0 $refy
$w.ref.pt.lat insert 0 $latitude
$w.ref.pt.long insert 0 $longitude
pack $w.ref.pt.help -side top -anchor w
pack $w.ref.pt.x $w.ref.pt.label $w.ref.pt.y $w.ref.pt.label2 \
$w.ref.pt.lat $w.ref.pt.label3 $w.ref.pt.long $w.ref.pt.label4 \
-side left -pady 2 -padx 2 -fill x
frame $w.ref.alt
pack $w.ref.alt -side top -padx 6 -pady 6 -fill x
label $w.ref.alt.label -text "Altitude:"
entry $w.ref.alt.altitude -bg white -width 10
label $w.ref.alt.label2 -text "meters"
$w.ref.alt.altitude insert 0 $altitude
pack $w.ref.alt.label $w.ref.alt.altitude $w.ref.alt.label2 -side left \
-pady 2 -padx 2 -fill x
global resize_canvas_save_default
set resize_canvas_save_default 0
frame $w.default
checkbutton $w.default.save -text "Save as default" \
-variable resize_canvas_save_default
pack $w.default.save -side left -pady 2 -padx 2 -fill x
pack $w.default -side bottom -fill x
# update the size in meters based on pixels
syncSizeScale $w xp
}
# called when scale or size values change
proc syncSizeScale { w type } {
set xp [$w.size.pixels.x get]
set yp [$w.size.pixels.y get]
set xm [$w.size.meters.x get]
set ym [$w.size.meters.y get]
set scale [$w.scale.ppm.metersper100 get]
set newxp $xp
set newyp $yp
set newxm $xm
set newym $ym
# prevent some math errors
if { ![string is double $scale] } { puts "invalid scale=$scale"; return }
if { $scale == 0 } { puts "zero scale"; return }
switch -exact -- $type {
scale -
xp -
yp {
# changing the scale or size in pixels updates the size in meters
set newxm [expr { $xp * $scale / 100.0 }]
set newym [expr { $yp * $scale / 100.0 }]
}
xm -
ym {
# changing the size in meters updates the size in pixels
set newxp [expr { round(100.0 * $xm / $scale) } ]
set newyp [expr { round(100.0 * $ym / $scale) } ]
}
}
if {$xm != $newxm} {
$w.size.meters.x delete 0 end
$w.size.meters.x insert 0 $newxm
}
if {$ym != $newym} {
$w.size.meters.y delete 0 end
$w.size.meters.y insert 0 $newym
}
if {$xp != $newxp} {
$w.size.pixels.x delete 0 end
$w.size.pixels.x insert 0 $newxp
}
if {$yp != $newyp} {
$w.size.pixels.y delete 0 end
$w.size.pixels.y insert 0 $newyp
}
}
proc resizeCanvasApply { w } {
global curcanvas changed
global g_prefs resize_canvas_save_default
set x [$w.size.pixels.x get]
set y [$w.size.pixels.y get]
set scale [$w.scale.ppm.metersper100 get]
# refpt x,y
# refpt lat, long, alt
set refx [$w.ref.pt.x get]
set refy [$w.ref.pt.y get]
set latitude [$w.ref.pt.lat get]
set longitude [$w.ref.pt.long get]
set altitude [$w.ref.alt.altitude get]
set refpt [list $refx $refy $latitude $longitude $altitude]
if { $resize_canvas_save_default } {
array set g_prefs "gui_canvas_x $x gui_canvas_y $y"
array set g_prefs "gui_canvas_scale $scale"
array set g_prefs [list "gui_canvas_refpt" $refpt]
}
destroy $w
if { "$x $y" != [getCanvasSize $curcanvas] || \
$scale != [getCanvasScale $curcanvas] || \
$refpt != [getCanvasRefPoint $curcanvas] } {
set changed 1
}
setCanvasSize $curcanvas $x $y
setCanvasScale $curcanvas $scale
setCanvasRefPoint $curcanvas $refpt
switchCanvas none
updateUndoLog
}
#****f* editor.tcl/animate
# NAME
# animate
# SYNOPSIS
# animate
# FUNCTION
# This function animates the selectbox. The animation looks
# different for edit and exec mode.
#****
proc animate {} {
global animatephase oper_mode
.c raise -cursor
if { [catch { if { ![winfo exists .c] } { return } }] } {
return ;# user has exited using the window manager
}
.c itemconfigure "selectmark || selectbox" -dashoffset $animatephase
incr animatephase 2
if { $animatephase == 100 } {
set animatephase 0
}
if { $oper_mode == "edit" } {
after 250 animate
} else {
after 1500 animate
}
}
proc zoom { dir } {
global zoom
set stops ".25 .5 .75 1.0 1.5 2.0 4.0"
# set i [lsearch $stops $zoom]
set minzoom [lindex $stops 0]
set maxzoom [lindex $stops [expr [llength $stops] - 1]]
switch -exact -- $dir {
"down" {
if {$zoom > $maxzoom} {
set zoom $maxzoom
} elseif {$zoom < $minzoom} {
; # leave it unchanged
} else {
set newzoom $minzoom
foreach z $stops {
if {$zoom <= $z} {
break
} else {
set newzoom $z
}
}
set zoom $newzoom
}
redrawAll
}
"up" {
if {$zoom < $minzoom} {
set zoom $minzoom
} elseif {$zoom > $maxzoom} {
; # leave it unchanged
} else {
foreach z [lrange $stops 1 end] {
set newzoom $z
if {$zoom < $z} {
break
}
}
set zoom $newzoom
}
redrawAll
}
default {
if { $i < [expr [llength $stops] - 1] } {
set zoom [lindex $stops [expr $i + 1]]
redrawAll
}
}
}
}
#****h* editor.tcl/double1onGrid
# NAME
# double1onGrid.tcl -- called on Double-1 click on grid (bind command)
# SYNOPSIS
# double1onGrid $c %x %y
# FUNCTION
# As grid is layered above annotations this procedure is used to find
# annotation object closest to cursor
#****
proc double1onGrid { c x y } {
set obj [$c find closest $x $y]
set tags [$c gettags $obj]
set node [lindex $tags 1]
if {[lsearch $tags grid] != -1 || [lsearch $tags background] != -1} {
return
}
# Is this really necessary?
set coords [getNodeCoords $node]
set x1 [lindex $coords 0]
set y1 [lindex $coords 1]
set x2 [lindex $coords 2]
set y2 [lindex $coords 3]
if {$x < $x1 || $x > $x2 || $y < $y1 || $y > $y2} {
# cursor is not ON the closest object
return
} else {
annotationConfig $c $node
}
}
proc setZoomApply { w } {
global zoom changed
set newzoom [expr [$w.e1 get] / 100.0]
if { $newzoom != $zoom } {
set zoom $newzoom
redrawAll
}
destroy $w
}
proc selectZoom { x y } {
global curcanvas
global zoom
set stops ".25 .5 .75 1.0 1.5 2.0 4.0"
set w .entry1
catch {destroy $w}
toplevel $w -takefocus 1
if { $x == 0 && $y == 0 } {
set screen [wm maxsize .]
set x [expr {[lindex $screen 0] / 2}]
set y [expr {[lindex $screen 1] / 2}]
} else {
set x [expr {$x + 10}]
set y [expr {$y - 90}]
}
wm geometry $w +$x+$y
wm title $w "Select zoom %"
wm iconname $w "Select zoom %"
frame $w.buttons
pack $w.buttons -side bottom -fill x -pady 2m
button $w.buttons.print -text "Apply" -command "setZoomApply $w"
button $w.buttons.cancel -text "Cancel" -command "destroy $w"
pack $w.buttons.print $w.buttons.cancel -side left -expand 1
bind $w <Key-Escape> "destroy $w"
bind $w <Key-Return> "setZoomApply $w"
entry $w.e1 -bg white
$w.e1 insert 0 [expr {int($zoom * 100)}]
pack $w.e1 -side top -pady 5 -padx 10 -fill x
update
focus $w.e1
grab $w
}
# configure remote servers
# popup a dialog box for editing the remote server list
# results are stored in servers.conf file
proc configRemoteServers {} {
global exec_servers last_server_selected
global plugin_img_add plugin_img_save plugin_img_del
global DEFAULT_API_PORT
set wi .remoteConfig
catch {destroy $wi}
toplevel $wi
wm transient $wi .
wm resizable $wi 0 0
wm title $wi "CORE emulation servers"
set last_server_selected -1
# list of servers
frame $wi.s -borderwidth 4
listbox $wi.s.servers -selectmode single -width 60 \
-yscrollcommand "$wi.s.servers_scroll set" -exportselection 0
scrollbar $wi.s.servers_scroll -command "$wi.s.servers yview"
pack $wi.s.servers $wi.s.servers_scroll -fill both -side left
pack $wi.s -fill both -side top
# add scrollbar
bind $wi.s.servers <<ListboxSelect>> "selectRemoteServer $wi"
# populate the list
foreach server [lsort -dictionary [array names exec_servers]] {
$wi.s.servers insert end $server
}
# controls for editing entries
labelframe $wi.c -text "Server configuration"
frame $wi.c.c -borderwidth 4
label $wi.c.c.namelab -text "Name"
entry $wi.c.c.name -bg white -width 15
bind $wi.c.c.name <KeyPress> "$wi.c.c.add configure -state normal"
label $wi.c.c.iplab -text "IP"
entry $wi.c.c.ip -bg white -width 10
label $wi.c.c.portlab -text "port"
entry $wi.c.c.port -bg white -width 5
pack $wi.c.c.namelab $wi.c.c.name $wi.c.c.iplab $wi.c.c.ip -side left
pack $wi.c.c.portlab $wi.c.c.port -side left
pack $wi.c.c -fill x -side top
$wi.c.c.port insert 0 $DEFAULT_API_PORT
button $wi.c.c.add -image $plugin_img_add \
-command "configRemoteServersHelper $wi 1"
button $wi.c.c.mod -image $plugin_img_save \
-command "configRemoteServersHelper $wi 2"
button $wi.c.c.del -image $plugin_img_del \
-command "configRemoteServersHelper $wi 3"
pack $wi.c.c.add $wi.c.c.mod $wi.c.c.del -side left
pack $wi.c -fill x -side top
# assignment buttons
labelframe $wi.a -borderwidth 4 -text "Assign selected server to:"
button $wi.a.applyall -text "all nodes" -command {
global node_list last_server_selected
set wi .remoteConfig
if { $last_server_selected < 0 } { return }
set server [$wi.s.servers get $last_server_selected]
foreach node $node_list { setNodeLocation $node $server }
$wi.b.cancel configure -text "Close"
highlightAssignedServers $wi
redrawAll
}
button $wi.a.applysel -text "selected nodes" -command {
global last_server_selected
set wi .remoteConfig
if { $last_server_selected < 0 } { return }
set server [$wi.s.servers get $last_server_selected]
set items [.c find withtag "node && selected"]
foreach item $items {
set node [lindex [.c gettags $item] 1]
setNodeLocation $node $server
}
$wi.b.cancel configure -text "Close"
highlightAssignedServers $wi
redrawAll
}
label $wi.a.lab -text "Assigned servers are shown in blue."
pack $wi.a.applyall $wi.a.applysel $wi.a.lab -side left
pack $wi.a -fill x -side top
highlightAssignedServers $wi
# apply/cancel buttons
frame $wi.b -borderwidth 4
button $wi.b.apply -text "Apply" -command \
"writeServersConf; redrawAll; destroy $wi"
button $wi.b.cancel -text "Cancel" -command "loadServersConf; destroy $wi"
pack $wi.b.cancel $wi.b.apply -side right
pack $wi.b -side bottom
focus $wi.b.apply
after 100 { catch { grab .remoteConfig } }
}
# add/modify/remove server in list
proc configRemoteServersHelper { wi action } {
global exec_servers last_server_selected
set index end
set sock -1
# delete from list, array
if { $action > 1 } { ;# delete/modify
if { $last_server_selected < 0 } { return }
set server [$wi.s.servers get $last_server_selected]
$wi.s.servers delete $last_server_selected
set sock [lindex $exec_servers($server) 2]
array unset exec_servers $server
if { $action == 3 } {
$wi.c.c.add configure -state normal
$wi.s.servers selection set $index
set last_server_selected $index
return
}
set index $last_server_selected
}
# update the list
set newserver [$wi.c.c.name get]
$wi.s.servers insert $index $newserver
# update the array
set conf [list [$wi.c.c.ip get] [$wi.c.c.port get]]
array set exec_servers [list $newserver $conf]
$wi.s.servers selection set $index
set last_server_selected $index
$wi.c.c.add configure -state disabled
}
# connects the servers listbox with entry elements
proc selectRemoteServer { wi } {
global exec_servers last_server_selected
set selected [$wi.s.servers curselection]
# clear entries
$wi.c.c.name delete 0 end; $wi.c.c.ip delete 0 end;
$wi.c.c.port delete 0 end
set server [$wi.s.servers get $selected]
if { ![info exists exec_servers($server)] } { return }
$wi.c.c.add configure -state disabled
set last_server_selected $selected
# insert entries from array
$wi.c.c.name insert 0 $server
$wi.c.c.ip insert 0 [lindex $exec_servers($server) 0]
$wi.c.c.port insert 0 [lindex $exec_servers($server) 1]
}
# helper to highlight servers that have been assigned
proc highlightAssignedServers { wi } {
set servers [getAssignedRemoteServers]
set n [$wi.s.servers size]
for { set i 0 } { $i < $n } { incr i } {
set s [$wi.s.servers get $i]
set color blue
if { [lsearch -exact $servers $s] < 0 } { set color black }
$wi.s.servers itemconfigure $i -foreground $color
}
}
# Boeing: custom image dialog box
proc popupCustomImage { node } {
global CORE_DATA_DIR
set wi .customimagedialog
catch {destroy $wi}
toplevel $wi -takefocus 1
wm transient $wi .popup
wm resizable $wi 0 0
wm title $wi "[getNodeName $node] ($node) image"
grab $wi
frame $wi.ftop -borderwidth 4
label $wi.ftop.filelabel -text "Image file:"
entry $wi.ftop.filename -bg white -width 32
set cimg [getCustomImage $node]
$wi.ftop.filename insert 0 $cimg
global configwin
set configwin $wi
button $wi.ftop.filebtn -text "..." -command {
global configwin g_imageFileTypes
set f [tk_getOpenFile -filetypes $g_imageFileTypes \
-initialdir "$CORE_DATA_DIR/icons/normal"]
if { $f != "" } {
set node [string trim [lindex [wm title $configwin] 1] "()"]
$configwin.ftop.filename delete 0 end
$configwin.ftop.filename insert 0 $f
popupCustomImagePreview $configwin $node
}
}
pack $wi.ftop.filebtn $wi.ftop.filename $wi.ftop.filelabel \
-side right -padx 4 -pady 4
pack $wi.ftop -side top
frame $wi.fmid -borderwidth 4
canvas $wi.fmid.c -width 300 -height 100
pack $wi.fmid.c -side top -padx 4 -pady 4
pack $wi.fmid -side top
frame $wi.fbot -borderwidth 4
button $wi.fbot.apply -text "Apply" -command "customImageApply $wi $node"
set msg "Select nodes to apply custom image to:"
set cmd "customImageApplyMultiple $wi"
button $wi.fbot.applym -text "Apply to multiple..." \
-command "popupSelectNodes \"$msg\" $node {$cmd}"
button $wi.fbot.cancel -text "Cancel" -command "destroy $wi"
pack $wi.fbot.cancel $wi.fbot.applym $wi.fbot.apply \
-side right -padx 4 -pady 4
pack $wi.fbot -side bottom
popupCustomImagePreview $wi $node
}
proc popupCustomImagePreview { wi node } {
set coords_save [getNodeCoords $node]
set labelcoords_save [getNodeLabelCoords $node]
set img_save [getCustomImage $node]
set img_new [$wi.ftop.filename get]
setNodeCoords $node "150 50"
setNodeLabelCoords $node "150 78"
if { $img_save != $img_new } { setCustomImage $node $img_new }
$wi.fmid.c delete all
drawNode $wi.fmid.c $node
setNodeCoords $node $coords_save
setNodeLabelCoords $node $labelcoords_save
if { $img_save != $img_new } { setCustomImage $node $img_save }
}
# Boeing: helper for custom image apply button
proc customImageApply { wi node } {
global changed
setCustomImage $node [$wi.ftop.filename get]
set changed 1
# update the custom image button in the parent dialog
set img [getNodeImage $node]
.popup.ftop.img configure -image $img
destroy $wi
}
proc customImageApplyMultiple { wi nodes } {
global changed
set imgfile [$wi.ftop.filename get]
foreach node $nodes {
setCustomImage $node $imgfile
set changed 1
}
destroy $wi
}
# Boeing: create several scaled copies of an image for use with each zoomlevel
proc createScaledImages { img } {
global $img
set w [image width [set $img]]
set h [image height [set $img]]
# we skip 75% and 150% since resulting images are the same (due to int())
foreach size {.25 .5 1.0 2.0 4.0} {
# image will be globally accessible
global $img$size
# create empty photo object
set $img$size [image create photo]
# copy a scaled version
if { $size > 1.0 } {
[set $img$size] copy [set $img] -zoom [expr { int($size) } ]
} else {
[set $img$size] copy [set $img] -subsample \
[expr { int($w / ($w * $size)) }] \
[expr { int($h / ($h * $size)) }]
}
}
}
# Boeing: clear marker drawing
proc clearMarker { } {
.c delete -withtags marker
}
# Boeing: show or hide the marker options palette
proc markerOptions { show } {
global CORE_DATA_DIR markersize markercolor
catch { destroy .left.markeropt }
if { $show == "off" } { return }
frame .left.markeropt
# eraser
set img [image create photo -file $CORE_DATA_DIR/icons/tiny/eraser.gif]
button .left.markeropt.eraser -image $img \
-relief flat -command clearMarker
pack .left.markeropt.eraser -side top -pady 8
# marker sizes
canvas .left.markeropt.sizes -height 40 -width 32
pack .left.markeropt.sizes -side top
bind .left.markeropt.sizes <1> "markerSize %x %y"
drawMarkerSizes .left.markeropt.sizes [expr $markersize / 5]
# color selection buttons
set img [image create photo -file $CORE_DATA_DIR/icons/tiny/blank.gif]
foreach clr { black red yellow blue green } {
radiobutton .left.markeropt.$clr -indicatoron 0 -image $img \
-variable markercolor -value $clr -width 16 -height 16 \
-selectcolor $clr -highlightbackground $clr -background $clr \
-highlightcolor $clr -activebackground $clr
pack .left.markeropt.$clr -side top
}
pack .left.markeropt -side bottom
}
# Boeing: draw the marker sizes tool on a small canvas
proc drawMarkerSizes { c sel } {
# determine the coordinates of the selection box based on value of sel
if { $sel == 1 } { set coords {0 0 16 16}
} elseif { $sel == 2 } { set coords {16 0 32 16}
} elseif { $sel == 3 } { set coords {0 16 16 32}
} else { set coords {16 16 32 32} }
# draw the selection box
$c create rectangle $coords -fill gray -tag square -width 0
# draw each circle
$c create oval 8 8 8 8 -width 2 -fill blue -tag circle
$c create oval 24 8 24 8 -width 5 -fill black -tag circle
$c create oval 8 24 8 24 -width 10 -fill black -tag circl
$c create oval 24 24 24 24 -width 15 -fill black -tag circle
}
# Boeing: receive click from the marker sizes tool
proc markerSize { x y } {
global markersize
# determine which circle was selected, 1-4
if { $x > 16 } {
if { $y > 16 } { set sel 4
} else { set sel 2 }
} else {
if { $y > 16 } { set sel 3
} else { set sel 1 }
}
set markersize [expr {$sel * 5}]
# redraw selection tool
.left.markeropt.sizes delete -withtag "square || circle"
drawMarkerSizes .left.markeropt.sizes $sel
}
# Boeing: set canvas wallpaper
proc wallpaperPopup {} {
global curcanvas
set w .wallpaperDlg
catch {destroy $w}
toplevel $w
wm transient $w .
wm title $w "Set Canvas Wallpaper"
grab $w
# preview
canvas $w.preview -background white -relief sunken -width 200 -height 100 \
-borderwidth 1
pack $w.preview -side top -padx 10 -pady 10
$w.preview create text 100 50 -fill gray -text "(image preview)" \
-justify center -tag "wallpaper"
# file
frame $w.f
label $w.f.lab -text "Image filename:" -justify left
entry $w.f.file
# file browse button
global configwin
set configwin $w
button $w.f.filebtn -text "..." -command {
global configwin showGrid adjustCanvas fileDialogBox_initial
global g_imageFileTypes
# use default conf file path upon first run
if { $fileDialogBox_initial == 0} {
set fileDialogBox_initial 1
set dir $g_prefs(default_conf_path)
set f [tk_getOpenFile -filetypes $g_imageFileTypes -initialdir $dir]
} else {
set f [tk_getOpenFile -filetypes $g_imageFileTypes]
}
if { $f != "" } {
$configwin.f.file delete 0 end
$configwin.f.file insert 0 $f
set showGrid 0
set adjustCanvas 1
}
wallpaperPopupPreview $configwin
raise $configwin
}
# clear wallpaper button
button $w.f.clear -text "clear" -command {
global configwin wallpaperStyle
$configwin.f.file delete 0 end
$configwin.preview delete "wallpaper"
$configwin.preview create text 100 50 -fill gray \
-text "(image preview)" -justify center -tag "wallpaper"
set wallpaperStyle upperleft
raise $configwin
}
set currfile [lindex [getCanvasWallpaper $curcanvas] 0]
set currstyle [lindex [getCanvasWallpaper $curcanvas] 1]
pack $w.f.lab -side top -anchor w
pack $w.f.file $w.f.filebtn $w.f.clear -side left -fill x
pack $w.f -side top
$w.f.file insert 0 $currfile
# wallpaper style
frame $w.style
global wallpaperStyle
if {$currstyle == "" } {
set wallpaperStyle upperleft
} else {
set wallpaperStyle $currstyle
}
radiobutton $w.style.lft -text "upper-left" -variable wallpaperStyle \
-value upperleft -command "wallpaperPopupPreview $w"
radiobutton $w.style.ctr -text "centered" -variable wallpaperStyle \
-value centered -command "wallpaperPopupPreview $w"
radiobutton $w.style.scl -text "scaled" -variable wallpaperStyle \
-value scaled -command "wallpaperPopupPreview $w"
radiobutton $w.style.til -text "tiled" -variable wallpaperStyle \
-value tiled -command "wallpaperPopupPreview $w"
pack $w.style.lft $w.style.ctr -side left
pack $w.style.scl $w.style.til -side left
pack $w.style -side top
# options
frame $w.opts
checkbutton $w.opts.showgrid -text "Show grid" -variable showGrid
checkbutton $w.opts.adjcanvas \
-text "Adjust canvas size to image dimensions" \
-variable adjustCanvas
pack $w.opts.showgrid $w.opts.adjcanvas -side top -anchor w
pack $w.opts -side top
# buttons
frame $w.btns
button $w.btns.apply -text "Apply" -command {
global configwin wallpaperStyle curcanvas adjustCanvas
set f [$configwin.f.file get]
if {$adjustCanvas} {
wallpaperAdjustCanvas $curcanvas $f $wallpaperStyle
}
setCanvasWallpaper $curcanvas $f $wallpaperStyle
redrawAll
destroy $configwin
}
button $w.btns.cancel -text "Cancel" -command "destroy $w"
pack $w.btns.apply $w.btns.cancel -side left -fill x
pack $w.btns -side top
if {$currfile != ""} {
wallpaperPopupPreview $w
}
raise $w
}
# adjust wallpaper dialog preview canvas
proc wallpaperPopupPreview { w } {
global wallpaperStyle
set f [$w.f.file get]
if { $f == "" } {
return
}
drawWallpaper $w.preview $f $wallpaperStyle
}
# auto-adjust the canvas in an intelligent fashion
proc wallpaperAdjustCanvas { c f style } {
set cx [lindex [getCanvasSize $c] 0]
set cy [lindex [getCanvasSize $c] 1]
if {$f==""} { return }
set img [image create photo -file $f]
set imgx [image width $img]
set imgy [image height $img]
#puts -nonewline "wallpaperAdjustCanvas img($imgx, $imgy) $cx, $cy -> "
# For scaled and tiled styles, expand canvas x and y to a multiple of
# imgx, imgy for better stretching. If the image is larger than the canvas,
# just increase the canvas size to accomodate it.
if {$style == "scaled" || $style == "tiled"} {
if {$cx > $imgx} {
if { [expr { $cx % $imgx }] > 0} {
set cx [expr { (1+int($cx/$imgx)) * $imgx }]
}
} elseif { $cx < $imgx } {
set cx $imgx
}
if {$cy > $imgy} {
if { [expr { $cy % $imgy }] > 0} {
# there is a fractional part, round up
set cy [expr { (1+int($cy/$imgy)) * $imgy }]
}
} elseif { $cy < $imgy } {
set cy $imgy
}
# For topleft and centered, resize the canvas to fit the image
# if the size difference isn't too large
} elseif { $style == "topleft" || $style == "centered" } {
if { [expr {abs($cx - $imgx)} ] < 300 } {
set cx $imgx
}
if { [expr {abs($cy - $imgy)} ] < 300 } {
set cy $imgy
}
}
#puts "$cx, $cy"
setCanvasSize $c $cx $cy
switchCanvas none
updateUndoLog
}
# draw the image from filename f onto the wallpaper c in the specified style
proc drawWallpaper { c f style } {
global $c
# clear the canvas
$c delete "wallpaper"
if { $f == "" } {
return
}
if { $c == ".wallpaperDlg.preview" } {
set cx [expr [$c cget -width]-2]
set cy [expr [$c cget -height]-2]
} else {
global curcanvas
# subtract 2 for canvas border
set cx [expr [lindex [getCanvasSize $curcanvas] 0]-2]
set cy [expr [lindex [getCanvasSize $curcanvas] 1]-2]
}
set f [absPathname $f]
if { [ catch { set img [image create photo -file $f] } e ] } {
puts "Error: couldn't open wallpaper file $f: $e"
return
}
set imgx [image width $img]
set imgy [image height $img]
# scaled: grow/shrink the image to fit the canvas size
if { $style == "scaled" } {
set img2 [image create photo -width $cx -height $cy]
# grow image
if { $cx >= $imgx || $cy > $imgy } {
set x [expr 1+($cx / $imgx)]
set y [expr 1+($cy / $imgy)]
$img2 copy $img -zoom $x $y
# shrink image
} else {
$img2 copy $img -subsample \
[expr { int($imgx / $cx) }] \
[expr { int($imgy / $cy) }]
}
$c create image [expr 1+$cx/2] [expr 1+$cy/2] -image $img2 \
-tags "background wallpaper"
# centered: center of image at center of canvas
} elseif { $style == "centered" } {
$c create image [expr $cx/2] [expr $cy/2] -image $img \
-tags "background wallpaper"
# tiled: repeat image several times
} elseif { $style == "tiled" } {
for {set y [expr $imgy/2]} {$y < $cy} {incr y $imgy} {
for {set x [expr $imgx/2]} {$x < $cx} {incr x $imgx} {
$c create image $x $y -image $img -tags "background wallpaper"
}
}
# upper-left: top left corner of image at 0,0
} else {
set img2 [image create photo -width $cx -height $cy]
$img2 copy $img -shrink
$c create image [expr 1+$cx/2] [expr 1+$cy/2] -image $img2 \
-tags "background wallpaper"
}
raiseAll $c
}
# helper for close/cancel buttons
proc popdownConfig { w } {
global changed
if { $changed == 1 } {
redrawAll
updateUndoLog
}
destroy $w
}
# helper for rj45 config dialog
proc rj45ifclist { wi node wasclicked } {
# user has double-clicked an entry
if { $wasclicked } {
set selected [$wi.ftop.ifc.ifc_list curselection]
set chosen [$wi.ftop.ifc.ifc_list get $selected]
set ifname [lindex [split $chosen] 0]
$wi.ftop.name delete 0 end
$wi.ftop.name insert 0 $ifname
return
}
# build a list of interfaces
frame $wi.ftop.ifc
listbox $wi.ftop.ifc.ifc_list -height 4 -width 30 \
-selectmode browse -yscrollcommand "$wi.ftop.ifc.ifc_scroll set"
scrollbar $wi.ftop.ifc.ifc_scroll \
-command "$wi.ftop.ifc.ifc_list yview"
set ifname ""
set ifip ""
# this handles differences between ifconfig
foreach line [split [nexec localnode ifconfig -a] "\n"] {
set char [string index $line 0]
if { $char != " " && $char != " " } {
if { $ifname != "" } {
$wi.ftop.ifc.ifc_list insert end "$ifname ($ifip)"
set ifname ""
set ifip ""
}
if { [string match "*Link encap:*" $line] } {
set ifname [lindex [split $line " "] 0]
} else {
set ifname [lindex [split $line :] 0]
}
} elseif { [string match "*inet addr:*" $line] } {
set inetidx [string first i $line]
set t [lindex [split [string range $line $inetidx end]] 1]
set ifip [lindex [split $t ":"] 1]
} elseif { [string match " inet *" $line] } {
set ifip [lindex [split $line] 2]
}
}
if { $ifname != "" } {
$wi.ftop.ifc.ifc_list insert end "$ifname ($ifip)"
}
bind $wi.ftop.ifc.ifc_list <Double-1> "rj45ifclist $wi $node 1"
bind $wi.ftop.ifc.ifc_list <<ListboxSelect>> "rj45ifclist $wi $node 1"
pack $wi.ftop.ifc.ifc_list $wi.ftop.ifc.ifc_scroll -side left -fill y
pack $wi.ftop.ifc -side bottom -padx 4 -pady 4
}
# link preset values - bandwidth delay ber duplicate
array set link_presets {
"unlimited" { 0 0 0 0 0 }
"1000M" { 1000000000 100 0 0.0 0.0}
"100M" { 100000000 110 0 0.0 0.0}
"10M" { 10000000 160 0 0.0 0.0}
"512kbps" { 512000 50000 0 0.0 0.0}
"256kbps" { 256000 75000 0 0.0 0.0}
"64kbps" { 64000 80000 0 0.0 0.0}
}
# link presets
proc linkPresets { wi linkpreMenu cmd } {
global link_presets link_preset_val
global g_link_config_uni_state
if { $cmd == "init" } { ;# populate the list with presets and exit
$linkpreMenu delete 0
foreach p [lsort [array names link_presets]] {
$linkpreMenu add radiobutton -label $p -value $p \
-variable link_preset_val \
-command "linkPresets $wi $linkpreMenu set"
}
return
}
# set the selected link presets
set params $link_presets($link_preset_val)
$wi.bandwidth.value delete 0 end
$wi.delay.value delete 0 end
$wi.jitter.value delete 0 end
$wi.ber.value delete 0 end
$wi.dup.value delete 0 end
$wi.bandwidth.value insert 0 [lindex $params 0]
$wi.delay.value insert 0 [lindex $params 1]
$wi.jitter.value insert 0 [lindex $params 2]
$wi.ber.value insert 0 [lindex $params 3]
$wi.dup.value insert 0 [lindex $params 4]
if { $g_link_config_uni_state == "uni" } {
$wi.bandwidth.value2 delete 0 end
$wi.delay.value2 delete 0 end
$wi.jitter.value2 delete 0 end
$wi.ber.value2 delete 0 end
$wi.dup.value2 delete 0 end
$wi.bandwidth.value2 insert 0 [lindex $params 0]
$wi.delay.value2 insert 0 [lindex $params 1]
$wi.jitter.value2 insert 0 [lindex $params 2]
$wi.ber.value2 insert 0 [lindex $params 3]
$wi.dup.value2 insert 0 [lindex $params 4]
}
}
set last_nodeHighlights [clock clicks -milliseconds]
proc nodeHighlights { c node onoff color } {
global execMode zoom
if { $execMode != "interactive"} { return } ; # batch mode
#puts "nodeHighlights $c $node $onoff $color"
$c delete -withtags "highlight && $node"
if { $onoff == "off" } {
if { $node == "" } { ;# remove all highlights
$c delete -withtags highlight
}
return
} elseif { $onoff == "single" } {
# this was called from nodeEnter binding, perform rate limiting
set now [clock clicks -milliseconds]
global last_nodeHighlights
if { [expr $now - $last_nodeHighlights] < 100 } { return }
set last_nodeHighlights $now
}
# this could be improved to draw hidden items if not on current canvas,
# then properly unhide/hide when switching canvases
global curcanvas
if { [getNodeCanvas $node] != $curcanvas } { return }
set coords [getNodeCoords $node]
set x [lindex $coords 0]
set y [lindex $coords 1]
set wd 4; # line width
set d 35; # box size
set w [expr {50 * $zoom}]; # corner size
set x0 [expr {($x - $d) * $zoom}]
set y0 [expr {($y - $d) * $zoom}]
set x1 [expr {($x + $d) * $zoom}]
set y1 [expr {($y + $d) * $zoom}]
# upper left
$c create line $x0 $y0 [expr {$x1-$w}] $y0 \
-tags "marker highlight $node" -width $wd -fill $color
$c create line $x0 $y0 $x0 [expr {$y1-$w}] \
-tags "marker highlight $node" -width $wd -fill $color
# upper right
$c create line $x1 $y0 [expr {$x0+$w}] $y0 \
-tags "marker highlight $node" -width $wd -fill $color
$c create line $x1 $y0 $x1 [expr {$y1-$w}] \
-tags "marker highlight $node" -width $wd -fill $color
# lower left
$c create line $x0 $y1 [expr {$x1-$w}] $y1 \
-tags "marker highlight $node" -width $wd -fill $color
$c create line $x0 $y1 $x0 [expr {$y0+$w}] \
-tags "marker highlight $node" -width $wd -fill $color
# lower right
$c create line $x1 $y1 [expr {$x0+$w}] $y1 \
-tags "marker highlight $node" -width $wd -fill $color
$c create line $x1 $y1 $x1 [expr {$y0+$w}] \
-tags "marker highlight $node" -width $wd -fill $color
}
# show the hook scripts dialog for editing session hooks
proc popupHooksConfig {} {
global plugin_img_add plugin_img_edit plugin_img_del
global oper_mode
set wi .hooks
catch {destroy $wi}
toplevel $wi
wm transient $wi .
wm resizable $wi 0 0
wm title $wi "CORE Session Hooks"
labelframe $wi.f -text "Hooks"
listbox $wi.f.hooks -selectmode extended -width 50 -exportselection 0 \
-yscrollcommand "$wi.f.hooks_scroll set" -height 5
scrollbar $wi.f.hooks_scroll -command "$wi.f.hooks yview"
pack $wi.f.hooks $wi.f.hooks_scroll -pady 4 -fill both -side left
pack $wi.f -padx 4 -pady 4 -fill both -side top
bind $wi.f.hooks <Double-Button-1> "hooksHelper $wi edit"
frame $wi.bbar
button $wi.bbar.new -image $plugin_img_add -command "hooksHelper $wi new"
button $wi.bbar.save -image $plugin_img_edit \
-command "hooksHelper $wi edit"
button $wi.bbar.del -image $plugin_img_del -command "hooksHelper $wi del"
label $wi.bbar.help -text "Press the new button to create a hook script."
pack $wi.bbar.new $wi.bbar.save $wi.bbar.del -side left
pack $wi.bbar.help -padx 8 -side left
pack $wi.bbar -padx 4 -pady 4 -fill both -side top
frame $wi.b -borderwidth 4
button $wi.b.close -text "Close" -command "destroy $wi"
pack $wi.b.close -side bottom
pack $wi.b -side bottom
refreshHooksList $wi
}
proc hooksHelper { wi cmd } {
global g_hook_scripts
set selected [lindex [$wi.f.hooks curselection] 0]
set name ""
if { $selected != "" } { set name [$wi.f.hooks get $selected] }
# start/stop/delete selected
if { $cmd == "del" } {
removeHook $name
refreshHooksList $wi
return
}
if { $cmd == "edit" && $name == "" } { return }
if { $cmd == "new" } {
set name ""
}
popupHookScript $name
}
proc refreshHooksList { wi } {
global g_hook_scripts
$wi.f.hooks delete 0 end
if { ![info exists g_hook_scripts] } { set g_hook_scripts "" }
foreach hook $g_hook_scripts {
set name [lindex $hook 0]
$wi.f.hooks insert end $name
}
}
proc removeHook { name } {
global g_hook_scripts
for { set i 0 } { $i < [llength $g_hook_scripts] } { incr i } {
set flow [lindex $g_hook_scripts $i]
if { [lindex $flow 0] == $name } {
set g_hook_scripts [lreplace $g_hook_scripts $i $i]
return $i
}
}
return end
}
# show the script config dialog, for specifying an optional global session
# startup script that is run on the host after the emulation has been started
proc popupHookScript { name } {
global g_hook_scripts CORE_STATES plugin_img_open plugin_img_save
set wi .scriptConfig
catch {destroy $wi}
if { ![info exists g_hook_scripts] } { set g_hook_scripts "" }
toplevel $wi
wm transient $wi .hooks
wm resizable $wi 1 1
wm title $wi "CORE Hook Script"
# help text at top
ttk::frame $wi.top
set helptext "This is an optional script that is run"
set helptext "$helptext on the host when the\n emulation reaches the"
set helptext "$helptext specified state. It is saved with the config file."
ttk::label $wi.top.help -text $helptext
pack $wi.top.help -side top -fill both -expand true
pack $wi.top -padx 4 -pady 4 -side top
ttk::frame $wi.n
ttk::label $wi.n.lab -text "Hook script name:"
ttk::entry $wi.n.name -width 35
foreach c [list open save] {
ttk::button $wi.n.$c -image [set plugin_img_$c] -command \
"genericOpenSaveButtonPress $c $wi.mid.script $wi.n.name"
}
ttk::combobox $wi.n.state -width 15 -state readonly -exportselection 0 \
-values $CORE_STATES
pack $wi.n.lab $wi.n.name -padx 4 -pady 4 -side left
pack $wi.n.open $wi.n.save -pady 4 -side left
pack $wi.n.state -padx 4 -pady 4 -side left
pack $wi.n -padx 4 -pady 4 -side top -anchor w
bind $wi.n.state <<ComboboxSelected>> "setHookName $wi"
set hook ""
if { $name == "" } {
$wi.n.state current 4
setHookName $wi
} else {
$wi.n.name insert 0 $name
foreach hook $g_hook_scripts {
if { [lindex $hook 0] == $name } {
$wi.n.state current [lindex $hook 1]
break
}
}
}
# text box for script entry with scroll bar
ttk::frame $wi.mid
text $wi.mid.script -relief sunken -bd 2 \
-yscrollcommand "$wi.mid.scroll set" -setgrid 1 -height 30 -undo 1 \
-autosep 1 -background white
ttk::scrollbar $wi.mid.scroll -command "$wi.mid.script yview"
pack $wi.mid.script -side left -fill both -expand true
pack $wi.mid.scroll -side right -fill y
pack $wi.mid -side top -fill both -expand true
# load any existing script text
if { $hook == "" } { ;# some default text
$wi.mid.script insert end "#!/bin/sh\n"
$wi.mid.script insert end "# session hook script; write commands here to execute on the host at the\n# specified state\n"
} else {
$wi.mid.script insert end [lindex $hook 2]
}
# buttons on the bottom
ttk::frame $wi.btm
ttk::button $wi.btm.apply -text "Apply" -command \
"popupHookScriptApply $wi \"$name\""
ttk::button $wi.btm.cancel -text "Cancel" -command "destroy $wi"
pack $wi.btm.apply $wi.btm.cancel -side left
pack $wi.btm
focus $wi.mid.script
}
proc popupHookScriptApply { wi oldname } {
global g_hook_scripts CORE_STATES
set name [$wi.n.name get]
set state [$wi.n.state get]
# convert state to a number
for { set i 0 } { $i < [llength $CORE_STATES] } { incr i } {
if {[lindex $CORE_STATES $i] == $state } {
set state $i
break
}
}
set script [string trim [$wi.mid.script get 0.0 end-1c]]
set hook [list $name $state $script]
set i end
if { $oldname != "" } { set i [removeHook $oldname] }
set g_hook_scripts [linsert $g_hook_scripts $i $hook]
refreshHooksList .hooks
destroy $wi
}
proc setHookName { wi } {
global g_hook_scripts
set state [string tolower [$wi.n.state get]]
set name "${state}_hook.sh"
set n 1
set names ""
foreach hook $g_hook_scripts {
lappend names [lindex $hook 0]
}
while { [lsearch $names $name] >= 0 } {
incr n
set name "${state}${n}_hook.sh"
}
$wi.n.name delete 0 end
$wi.n.name insert 0 $name
}
# show the comments dialog for adding comments to a scenario
proc popupCommentsConfig {} {
global g_comments
set wi .commentsConfig
catch {destroy $wi}
if { ![info exists g_comments] } { set g_comments "" }
toplevel $wi
wm transient $wi .
wm resizable $wi 1 1
wm title $wi "CORE Session Comments"
# help text at top
frame $wi.top
set helptext "Optional text comments associated with this scenario may"
set helptext "$helptext be entered below and saved with the config file."
label $wi.top.help -text $helptext
pack $wi.top.help -side top -fill both -expand true
pack $wi.top -padx 4 -pady 4 -side top
# text box for comment entry with scroll bar
frame $wi.mid
text $wi.mid.comments -relief sunken -bd 2 \
-yscrollcommand "$wi.mid.scroll set" -setgrid 1 -height 30 -undo 1 \
-autosep 1 -background white
scrollbar $wi.mid.scroll -command "$wi.mid.comments yview"
pack $wi.mid.comments -side left -fill both -expand true
pack $wi.mid.scroll -side right -fill y
pack $wi.mid -side top -fill both -expand true
# load any existing comment text
if { $g_comments != "" } {
$wi.mid.comments insert end $g_comments
}
# buttons on the bottom
frame $wi.btm
button $wi.btm.apply -text "Apply" -command {
set wi .commentsConfig
global g_comments
set g_comments [string trim [$wi.mid.comments get 0.0 end-1c]]
destroy $wi
}
button $wi.btm.cancel -text "Cancel" -command "destroy $wi"
pack $wi.btm.apply $wi.btm.cancel -side left
pack $wi.btm
focus $wi.mid.comments
}
# show the contents of a file
proc popupFileView { pathname } {
set wi .fileview
catch {destroy $wi}
toplevel $wi
wm transient $wi .
wm resizable $wi 1 1
wm title $wi "File: $pathname"
ttk::frame $wi.top
ttk::label $wi.top.fnl -text "File:"
ttk::entry $wi.top.fn
#ttk::entry $wi.top.fn -state readonly
pack $wi.top.fnl -padx 4 -side left
pack $wi.top.fn -padx 4 -side left -fill both -expand true
pack $wi.top -padx 4 -pady 4 -side top -fill both -expand true
$wi.top.fn insert 0 $pathname
$wi.top.fn state readonly
ttk::frame $wi.mid
text $wi.mid.contents -relief sunken -bd 2 \
-yscrollcommand "$wi.mid.scroll set" -setgrid 1 -height 30 -undo 1 \
-autosep 1 -background white
ttk::scrollbar $wi.mid.scroll -command "$wi.mid.contents yview"
pack $wi.mid.contents -side left -fill both -expand true
pack $wi.mid.scroll -side right -fill y
pack $wi.mid -side top -fill both -expand true
if { [catch { set f [open $pathname r] } e] } {
$wi.mid.contents insert end "error: $e"
} else {
while { [ gets $f line] >= 0 } {
$wi.mid.contents insert end "$line\n"
}
close $f
}
# buttons on the bottom
ttk::frame $wi.btm
ttk::button $wi.btm.close -text "Close" -command "destroy $wi"
pack $wi.btm.close -side left
pack $wi.btm
$wi.mid.contents see end
focus $wi.mid.contents
}
# helper for "..." buttons for browsing for files
# ctl is the text entry to populate
proc fileButtonPopup { ctl initial } {
set f [tk_getOpenFile -initialdir $initial]
if { $f != "" } {
$ctl delete 0 end
$ctl insert 0 $f
}
}
# helper to get the name of the image representing a node; first, use any
# custom image defined, then customizable node type image, then finally the
# node's type name
proc getNodeImage { node } {
set type [nodeType $node]
set model [getNodeModel $node]
set imgname [getNodeTypeImage $model normal]
set cimg [absPathname [getCustomImage $node]]
if { $cimg != "" } { set imgname $cimg }
set imgname [file tail $imgname]
if { $imgname == "" } { set imgname $type}
global $imgname
return [set $imgname]
}
proc hideSelected { } {
foreach node [selectedNodes] { hideNode $node }
.c delete -withtags selectmark
}
proc hideNode { node } {
set c .c
setNodeHidden $node 1
$c itemconfigure "node && $node" -state hidden
$c itemconfigure "nodelabel && $node" -state hidden
$c itemconfigure "highlight && $node" -state hidden
$c itemconfigure "$node && antenna" -state hidden
$c itemconfigure "$node && link" -state hidden
$c itemconfigure "$node && interface" -state hidden
foreach l [$c find withtag "$node && link"] {
set link [lindex [$c gettags $l] 1]
$c itemconfigure "linklabel && $link" -state hidden
}
}
# this is a helper to save/restore the (green) WLAN wireless links used with
# the basic range model, because they are not saved on the global link_list
# called from proc redrawAll
proc saveRestoreWlanLinks { c cmd } {
global wlink_list
if { $cmd == "save" } {
set wlink_list {}
foreach item [$c find withtag "wlanlink"] {
set tags [$c gettags $item] ;# tags = "wlanlink n1 n2 wlan need_r"
lappend wlink_list [lrange $tags 1 3]
}
} elseif { $cmd == "restore" } {
if { ![info exists wlink_list] } {
return
}
foreach wlink $wlink_list {
lassign $wlink node1 node2 wlan
drawWlanLink $node1 $node2 $wlan
}
}
}
proc cutSelection {} {
editCopy
deleteSelection
}
proc copySelection {} {
global clipboard
set clipboard {}
set c .c
set copied ""
foreach img [$c find withtag "selected"] {
set tags [$c gettags $img]
set objtype [lindex $tags 0]
set objname [lindex $tags 1]
# some objects (e.g. oval) consist of multiple canvas objects
if { [lsearch $copied $objname] != -1 } { continue}
global $objname
if { ![info exists $objname] } { continue }
set item [list $tags [set $objname]]
lappend clipboard $item
lappend copied $objname
}
}
proc pasteSelection {} {
global clipboard
global node_list link_list annotation_list curcanvas
array set node_map ""
set new_nodes ""
set new_annotations ""
set v4blacklist ""
set v6blacklist ""
set dx 75; set dy 50 ;# paste offset
if { ![info exists clipboard] } { return }
# pass 1 - make new nodes
foreach item $clipboard {
set tags [lindex $item 0]
set olddata [lindex $item 1] ;# allows copy, change, paste
set old [lindex $tags 1]
# annotations
set type [nodeType $old]
if { [lsearch -exact "oval rectangle text" $type] != -1 } {
set new [newObjectId annotation]
global $new
set $new $olddata
lappend annotation_list $new
lappend new_annotations $new
moveAnnotation $new $dx $dy
continue
}
if { $type == "pseudo" } { continue }
set new [newObjectId node]
set node_map($old) $new
global $new
# set $new [set $old] would copy the current node's data, but using
# $olddata instead will copy node data at the time "copy" was invoked
set $new $olddata
lappend node_list $new
lappend new_nodes $new
if { [getNodeName $old] != $old } {
setNodeName $new [getNodeName $old] ;# preserve textual names
} else {
setNodeName $new $new
}
setNodeCanvas $new $curcanvas
if { [nodeType $new] == "wlan" } {
setIfcIPv4addr $new wireless "[findFreeIPv4Net 24].0/32"
setIfcIPv6addr $new wireless "[findFreeIPv6Net 64]::0/128"
continue
}
# remove existing addresses, generate new ones later
if { [[typemodel $new].layer] != "NETWORK" } { continue }
foreach ifc [ifcList $new] {
if { [getIfcIPv4addr $new $ifc] == "" } {
lappend v4blacklist ${new}_${ifc} ;# preserve empty addrs
} else {
setIfcIPv4addr $new $ifc ""
}
if { [getIfcIPv6addr $new $ifc] == "" } {
lappend v6blacklist ${new}_${ifc} ;# preserve empty addrs
} else {
setIfcIPv6addr $new $ifc ""
}
}
}
# pass 2 update interfaces and coordinates
foreach item $clipboard {
set tags [lindex $item 0]
set old [lindex $tags 1]
set type [nodeType $old]
if { [lsearch -exact "oval rectangle text pseudo" $type] != -1 } {
continue
}
set new $node_map($old)
# update coordinates, shifting by <dx, dy>
lassign [getNodeCoords $new] x y
setNodeCoords $new "[expr $x + $dx] [expr $y + $dy]"
lassign [getNodeLabelCoords $new] x y
setNodeLabelCoords $new "[expr $x + $dx] [expr $y + $dy]"
foreach ifc [ifcList $new] {
set old_peer [peerByIfc $new $ifc]
set i [lsearch [set $new] "interface-peer {$ifc $old_peer}"]
set logical [logicalPeerByIfc $new $ifc]
if { $logical != $old_peer } { ;# prune links to other canvases
set $new [lreplace [set $new] $i $i]; continue;
}
if { [lindex [array get node_map $old_peer] 1] != "" } {
set peer $node_map($old_peer)
set $new [lreplace [set $new] $i $i \
"interface-peer {$ifc $peer}"]
} else {
# old peer is not being copied, create a new interface
set peer $old_peer
set peer_ifc [newIfc [chooseIfName $peer $new] $peer]
global $old_peer
lappend $old_peer "interface-peer {$peer_ifc $new}"
if {[[typemodel $peer].layer] == "NETWORK"} {
autoIPv4addr $peer $peer_ifc
autoIPv6addr $peer $peer_ifc
}
}
# a new link already has been created (when peer interfaces were
# iterated)
if { [linkByPeers $new $peer] != "" } { continue }
set oldlink [linkByPeers $old $old_peer]
global $oldlink
if { [lindex [linkPeers $oldlink] 0] == $old } {
set newpeers "$new $peer"
} else {
set newpeers "$peer $new"
}
set newlink [newObjectId link]
global $newlink
set $newlink [set $oldlink] ;# copies all attributes
set i [lsearch [set $oldlink] "nodes {*}"]
set $newlink [lreplace [set $newlink] $i $i "nodes {$newpeers}"]
lappend link_list $newlink
}
}
# pass 3 - readdress (must occur after all links are updated above)
foreach new $new_nodes {
if {[[typemodel $new].layer] != "NETWORK"} { continue }
foreach ifc [ifcList $new] {
if { [lsearch -exact $v4blacklist ${new}_${ifc}] == -1 } {
autoIPv4addr $new $ifc
}
if { [lsearch -exact $v6blacklist ${new}_${ifc}] == -1 } {
autoIPv6addr $new $ifc
}
}
}
set changed 1
updateUndoLog
redrawAll
selectNodes $new_nodes
foreach a $new_annotations { selectNode .c $a }
}