
#-------------------------------------------------------------------------------
# Module:     menuLayout.tk
# Activation: Loaded during GOBLET startup
# Scope:      Layout menu item, bindings, some small dialogs
#             Graph display and navigator functionality
#-------------------------------------------------------------------------------



proc MakeLayoutMenu {} {
    global ThisGraph
    
    destroy .mbar.layout.menu
    
    menu .mbar.layout.menu

    if {[$ThisGraph is mip]} {
        .mbar.layout.menu add command -label "Display Options..." \
            -accelerator Shift+Ctrl+D -command SetLpDisplayOptions
        bind . <Control-D> {SetLpDisplayOptions}
        return

        .mbar.layout.menu add check -label "Show LP Instance" -underline 5 \
            -variable x1
        .mbar.layout.menu add check -label "Show Integers" -underline 5 \
            -variable x2
        .mbar.layout.menu add check -label "Show Basis" -underline 5 \
            -variable x3
        .mbar.layout.menu add check -label "Show Primal" -underline 5 \
            -variable x4
        .mbar.layout.menu add check -label "Show Dual" -underline 5 \
            -variable x5
        .mbar.layout.menu add check -label "Show Slacks" -underline 5 \
            -variable x6
        .mbar.layout.menu add check -label "Show Fixed" -underline 5 \
            -variable x7
    }

    .mbar.layout.menu add command -label "Strip Geometry" -underline 6 \
        -command StripGraph
    .mbar.layout.menu add command -label "Scale Geometry..." -underline 0 \
        -command ScaleGraph
    .mbar.layout.menu add command -label "Spring Embedding" -underline 0 \
        -command SpringEmbedding
    .mbar.layout.menu add command -label "Align Arcs" -underline 0 \
        -command AutoArcAlignment
    .mbar.layout.menu add command -label "Test Planarity" -underline 5 \
        -command CheckPlanarity -state disabled
    .mbar.layout.menu add command -label "Layout Style..." \
        -accelerator Shift+Ctrl+F -command SourceLayoutStyle
    .mbar.layout.menu add separator
    .mbar.layout.menu add command -label "Arc Display..." \
        -accelerator Shift+Ctrl+A -command SourceArcDisplay
    .mbar.layout.menu add command -label "Node Display..." \
        -accelerator Shift+Ctrl+N -command SourceNodeDisplay
    .mbar.layout.menu add command -label "Layout Options..." \
        -accelerator Shift+Ctrl+W -command SourceLayoutOptions
    .mbar.layout.menu add command -label "Fit Into Window" \
        -accelerator Ctrl+W -command FitIntoWindow
    .mbar.layout.menu add command -label "Zoom In" \
        -accelerator Ctrl+ -command ZoomIn
    .mbar.layout.menu add command -label "Zoom Out" \
        -accelerator Ctrl- -command ZoomOut

    bind . <Control-t> {DisplayNewLayout}
    bind . <Control-w> {FitIntoWindow}
    bind . <Control-F> {SourceLayoutStyle}
    bind . <Control-A> {SourceArcDisplay}
    bind . <Control-N> {SourceNodeDisplay}
    bind . <Control-W> {SourceLayoutOptions}
    bind . <Control-plus> {ZoomIn}
    bind . <Control-minus> {ZoomOut}
}

proc InitDisplay {} {
    global Mode FileName ThisBackup
    set Mode ""
    if {[goblin solver idle]} {ToggleBrowser} else {
        set state [catch {goblin messenger filename} traceFile]
        if {!$state} {
            TraceObject [file rootname $traceFile]
        } else {
            TraceObject $FileName.backup$ThisBackup
        }
    }
}

proc DisplayNewLayout {} {
    global ThisGraph FileCounter goblinTraceCounter Mode goblinCommLevel

    if {[$ThisGraph is mip] || [$ThisGraph max cx]>0 || [$ThisGraph max cy]>0} {
        set savedLevel $goblinCommLevel
        set goblinCommLevel 0
        $ThisGraph trace
        set goblinCommLevel $savedLevel
    }
    set FileCounter $goblinTraceCounter
    set Mode "display object"
    InitNavigator
}

proc InitNavigator {} {
    global goblinTraceCounter Mode

    if {$goblinTraceCounter>0} {
        ResetMode "navigate"
        Navigate
    } else {
        DisplayObject
    }
}

proc DisplayCanvas {fileName} {
    global c

    goblin export goblet $fileName.gob $fileName.tk
    InitCanvasRegion
    set goblinCanvas $c
    source $fileName.tk
    BackupScrollPos
    file delete $fileName.tk
}

proc DisplayTextFile {fileName} {
    global goblinLPDisplay
    InitCanvasRegion 1
    goblin export ascii $fileName.gob $fileName.ascii $goblinLPDisplay
    LoadTextForm $fileName.ascii
    file delete $fileName.ascii
}

proc DisplayNothing {} {
    global StatusInfo
    DestroyCanvasRegion
    set StatusInfo "no visual information available"
}

proc DisplayCanvasOrTextFile {fileName} {
    global ThisGraph

    if {[$ThisGraph is sparse] && [$ThisGraph #arcs]>5000} {
        DisplayNothing
        return 0
    } else {
        if {[$ThisGraph is mip]} {
            DisplayTextFile $fileName
        } else {
            if {[$ThisGraph max cx]<=0 && [$ThisGraph max cy]<=0} {
                if {[$ThisGraph #nodes]==0 || \
                    (![$ThisGraph is sparse] && [$ThisGraph #arcs]>5000)} {
                    DisplayNothing
                    return 0
                } else {
                    DisplayTextFile $fileName
                }
            } else {
                DisplayCanvas $fileName
            }
        }
    }
    
    return 1
}

proc DisplayObject {} {
    global ThisGraph FileName OrigFileName ThisBackup StatusInfo

    set StatusInfo "$OrigFileName"

    if {[$ThisGraph is mip] || [$ThisGraph #nodes]>0} {
        $ThisGraph write $FileName.backup$ThisBackup.gob
    }

    if {![DisplayCanvasOrTextFile $FileName.backup$ThisBackup]} {return}

    if {[goblin solver idle]} {
        MakeEditorBar
        ResetMode "display object"
    } else {
        MakeTracingBar
        ResetMode "trace"
    }
}

proc Navigate {{direction "plus"}} {
    global FileName FileCounter goblinTraceCounter StatusInfo
    
    if {$direction=="plus"} {
        while {![file exists "$FileName.$FileCounter.gob"] &&   
            $FileCounter<=$goblinTraceCounter} {incr FileCounter}
        
        if {$FileCounter>$goblinTraceCounter} {
            while {![file exists "$FileName.$FileCounter.gob"] &&   
                $FileCounter>0} {incr FileCounter -1}
        }
        
        if {$FileCounter==0} {
            set goblinTraceCounter 0
            ResetMode "display object"
            InitDisplay
            return
        }
    } else {
        while {![file exists "$FileName.$FileCounter.gob"] &&   
            $FileCounter>0} {incr FileCounter -1}
        
        if {$FileCounter==0} {
            while {![file exists "$FileName.$FileCounter.gob"] &&   
                $FileCounter<=$goblinTraceCounter} {incr FileCounter}
        }
        
        if {$FileCounter>$goblinTraceCounter} {
            set goblinTraceCounter 0
            return
            ResetMode "display object"
            InitDisplay
        }
    }
        
    set StatusInfo "trace file #$FileCounter of $goblinTraceCounter"

    set traceFile $FileName.$FileCounter
    
    DisplayCanvasOrTextFile $traceFile
    
    MakeNavigationBar
}

proc TraceObject {traceFile} {
    global c ThisGraph StatusInfo FileName ThisBackup
    
    if {[$ThisGraph is mip] || [$ThisGraph #nodes]>0} {
        $ThisGraph write $FileName.backup$ThisBackup.gob
    }

    DisplayCanvasOrTextFile $traceFile
    
    ResetMode "trace"
    MakeTracingBar
    set StatusInfo "$traceFile"
}

proc LoadTextForm {textFile} {
    global c GoblinTclPath StatusInfo
    
    if [catch {open "| cat < \"$textFile\""} textStream] {
        set StatusInfo "Could not open textfile"
        return
    }
    
    $c configure -state normal
    $c delete 1.0 end

    while {[gets $textStream thisLine]>=0} {
        $c insert end $thisLine\n
    }
    close $textStream
    $c see 1.0
    $c configure -state disabled
}

proc SetLpDisplayOptions {} {
    global GoblinTclPath
    source [file join $GoblinTclPath layoutLPOpt.tk]
}

proc StripGraph {} {
    global ThisGraph
    
    $ThisGraph layout strip
    PropagateModifications
}

proc ScaleGraph {} {
    global ThisGraph GoCommand UploadCommand WDInput InternalWindowing

    global dx dy
    set dx 1.00
    set dy 1.00
    
    set GoCommand {
        $ThisGraph layout scale $dx $dy
        PropagateModifications
        if {$InternalWindowing} {
            DestroyActiveDialog
        } else {
            destroy $WDInput
        }
    }
    set UploadCommand {}
    
    DisplayInputDialog "Scale Geometry" "ScaleGraph" "Scale" "Cancel" {
        label $WDInput.frame.xText -text "Horizontal Magnification:" -anchor w
        grid $WDInput.frame.xText -in $WDInput.frame -row 0 -column 0 \
            -rowspan 1 -columnspan 1 -sticky news -padx 1m -pady 1m
        entry $WDInput.frame.x -textvariable dx -bg white -width 5
        grid $WDInput.frame.x -in $WDInput.frame -row 0 -column 1 \
            -rowspan 1 -columnspan 1 -sticky news -padx 1m -pady 1m
    
        label $WDInput.frame.yText -text "Vertical Magnification:" -anchor w
        grid $WDInput.frame.yText -in $WDInput.frame -row 1 -column 0 \
            -rowspan 1 -columnspan 1 -sticky news -padx 1m -pady 1m
        entry $WDInput.frame.y -textvariable dy -bg white -width 5
        grid $WDInput.frame.y -in $WDInput.frame -row 1 -column 1 \
            -rowspan 1 -columnspan 1 -sticky news -padx 1m -pady 1m

        grid rowconfig    $WDInput.frame 0 -weight 1 -minsize 0
        grid columnconfig $WDInput.frame 0 -weight 1 -minsize 0

        bind $WDInput.frame.x <Return> {focus $WDInput.frame.y}
        bind $WDInput.frame.y <Return> $GoCommand
    }
    
    focus $WDInput.frame.x
}

proc SpringEmbedding {} {
    global ThisGraph LastCommand

    set LastCommand "$ThisGraph layout spring"
    CallSolver "Spring Embedding" $LastCommand
}

proc AutoArcAlignment {} {
    global ThisGraph goblinXZoom goblinYZoom
    
    $ThisGraph layout align
    PropagateModifications
}

proc CheckPlanarity {} {
    global ThisGraph LastCommand
    
    set LastCommand "$ThisGraph is planar"
    CallSolver "Check planarity" $LastCommand
}

proc SourceLayoutStyle {} {
    global GoblinTclPath
    source [file join $GoblinTclPath layoutStyle.tk]
}

proc FitIntoWindow {} {
    global ThisGraph goblinXZoom goblinYZoom goblinXShift goblinYShift
    global goblinLegenda goblinNodeSize Zoom c

    set xSize [winfo width $c]
    set ySize [winfo height $c]
        
    InitCanvasRegion

    set xMax [$ThisGraph node 0 cx]
    set yMax [$ThisGraph node 0 cy]
    set xMin $xMax
    set yMin $yMax
    for {set v 0} {$v<[$ThisGraph #nodes]+[$ThisGraph #artificial]} {incr v} {
        if {[$ThisGraph node $v cx]<$xMin} {set xMin [$ThisGraph node $v cx]}
        if {[$ThisGraph node $v cy]<$yMin} {set yMin [$ThisGraph node $v cy]}
        if {[$ThisGraph node $v cx]>$xMax} {set xMax [$ThisGraph node $v cx]}
        if {[$ThisGraph node $v cy]>$yMax} {set yMax [$ThisGraph node $v cy]}
    }
    set margin [expr round(2*$goblinNodeSize*$Zoom)]
    set goblinXZoom [expr ($xSize-2*$margin)/($Zoom*($xMax-$xMin))]
    set goblinYZoom \
        [expr ($ySize-2*$margin)/($Zoom*($yMax-$yMin+$goblinLegenda))]
    set goblinXShift [expr round(($margin-$xMin*$Zoom*$goblinXZoom)/$Zoom)]
    set goblinYShift [expr round(($margin-$yMin*$Zoom*$goblinYZoom)/$Zoom)]
            
    InitDisplay
}

proc ZoomIn {} {
    global goblinXZoom goblinYZoom
    set goblinXZoom [expr {round($goblinXZoom*1.25)}]
    set goblinYZoom [expr {round($goblinYZoom*1.25)}]
    InitDisplay
}

proc ZoomOut {} {
    global goblinXZoom goblinYZoom
    set goblinXZoom [expr {round($goblinXZoom*0.8)}]
    set goblinYZoom [expr {round($goblinYZoom*0.8)}]
    InitDisplay
}

proc SourceArcDisplay {} {
    global GoblinTclPath
    source [file join $GoblinTclPath layoutArcs.tk]
}

proc SourceNodeDisplay {} {
    global GoblinTclPath
    source [file join $GoblinTclPath layoutNodes.tk]
}

proc SourceLayoutOptions {} {
    global GoblinTclPath
    source [file join $GoblinTclPath layoutMeasures.tk]
}
