#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" -- ${1+"$@"}

# Copyright: 2018-2022 Paul Obermeier (obermeier@tcl3d.org)
# Distributed under BSD license.

namespace eval BawtLogViewer {
    variable ns [namespace current]

    namespace export ShowMainWin
    namespace export AppendLib
    namespace export SetLibWarning
    namespace export SetLibStatus
    namespace export SetLibStages
    namespace export SetLibUpdateCause
    namespace export AppendLogMsg
    namespace export SetStatusMsg
    namespace export UpdateTitle
    namespace export ReadFile
    namespace export AutoUpdate
    namespace export SetLogFile
    namespace export LoadSettings
    namespace export SaveSettings
    namespace export ViewSettingsDir

    variable sTopLevel ".bawt"
    variable sCurLib   ""
    variable sSummarySection false

    proc SetAutosaveOnExit { onOff } {
        variable sGui

        set sGui(autosaveOnExit) $onOff
    }

    proc GetAutosaveOnExit {} {
        variable sGui

        return [list $sGui(autosaveOnExit)]
    }

    proc SetWindowPos { x y { w -1 } { h -1 } } {
        variable sGui

        set sGui(MainWin,x) $x
        set sGui(MainWin,y) $y
        set sGui(MainWin,w) $w
        set sGui(MainWin,h) $h
    }

    proc GetWindowPos {} {
        variable sTopLevel
        variable sGui

        if { [info exists sTopLevel] && \
            [winfo exists $sTopLevel] } {
            scan [wm geometry $sTopLevel] "%dx%d+%d+%d" w h x y
        } else {
            set x $sGui(MainWin,x)
            set y $sGui(MainWin,y)
            set w $sGui(MainWin,w)
            set h $sGui(MainWin,h)
        }
        return [list $x $y $w $h]
    }

    proc SetLastDir { dir } {
        variable sGui

        set sGui(LastDir) $dir
    }

    proc GetLastDir {} {
        variable sGui

        return [list $sGui(LastDir)]
    }

    proc SetDeadlockThreshold { thres } {
        variable sGui

        set sGui(Deadlock,Threshold) $thres
    }

    proc GetDeadlockThreshold {} {
        variable sGui

        return [list $sGui(Deadlock,Threshold)]
    }

    proc SetDeadlockBeep { useBeep } {
        variable sGui

        set sGui(Deadlock,UseBeep) $useBeep
    }

    proc GetDeadlockBeep {} {
        variable sGui

        return [list $sGui(Deadlock,UseBeep)]
    }

    proc SetUseEditorEnvVar { useEditorEnvVar } {
        variable sGui

        set sGui(Text,UseEditorEnvVar) $useEditorEnvVar
    }

    proc GetUseEditorEnvVar {} {
        variable sGui

        return [list $sGui(Text,UseEditorEnvVar)]
    }

    proc SetLineWrap { useWrap } {
        variable sGui

        set sGui(Text,LineWrap) $useWrap
    }

    proc GetLineWrap {} {
        variable sGui

        return [list $sGui(Text,LineWrap)]
    }

    proc _ToggleLineWrap {} {
        variable sGui

        if { $sGui(Text,LineWrap) } {
            $sGui(LogId) configure -wrap word
        } else {
            $sGui(LogId) configure -wrap none
        }
    }

    proc LoadSettings {} {
        SetWindowPos 10 30
        SetLastDir   [pwd]
        SetDeadlockThreshold 0.25
        SetDeadlockBeep    1
        SetUseEditorEnvVar 0
        SetLineWrap        0
        SetAutosaveOnExit  1

        set cfgFile [file normalize [poCfgFile GetCfgFilename [GetAppName] [GetCfgDir]]]
        if { [file readable $cfgFile] } {
            source $cfgFile
        }
        set cfgFile [file normalize [poCfgFile GetCfgFilename "EstimatedBuildTimes" [GetCfgDir]]]
        if { [file readable $cfgFile] } {
            source $cfgFile
        }
    }

    proc PrintCmd { fp cmdName { ns "" } } {
        puts $fp "\n# Set${cmdName} [info args ${ns}::Set${cmdName}]"
        puts $fp "catch {${ns}::Set${cmdName} [${ns}::Get${cmdName}]}"
    }

    proc SaveSettings {} {
        variable ns
        variable sEstimatedTimes

        set cfgFile [file normalize [poCfgFile GetCfgFilename [GetAppName] [GetCfgDir]]]
        poCfgFile CreateBackupFile $cfgFile
        set retVal [catch {open $cfgFile w} fp]
        if { $retVal == 0 } {
            puts $fp "\n# SetWindowPos [info args SetWindowPos]"
            puts $fp "catch {${ns}::SetWindowPos [GetWindowPos]}"

            PrintCmd $fp "LastDir"           "$ns"
            PrintCmd $fp "DeadlockThreshold" "$ns"
            PrintCmd $fp "DeadlockBeep"      "$ns"
            PrintCmd $fp "UseEditorEnvVar"   "$ns"
            PrintCmd $fp "LineWrap"          "$ns"

            PrintCmd $fp "RecentFileList" "::poAppearance"

            close $fp
            SetStatusMsg "Settings stored in file $cfgFile" "Ok"
        }

        set cfgFile [file normalize [poCfgFile GetCfgFilename "EstimatedBuildTimes" [GetCfgDir]]]
        poCfgFile CreateBackupFile $cfgFile
        set retVal [catch {open $cfgFile w} fp]
        if { $retVal == 0 } {
            if { [info exists sEstimatedTimes] } {
                puts $fp "\n# SetEstimatedBuildTime [info args SetEstimatedBuildTime]"
                foreach key [lsort -dictionary [array names sEstimatedTimes]] {
                    set libName     [lindex [split $key ","] 0]
                    set libCompiler [lindex [split $key ","] 1]
                    set estimatedTime [GetEstimatedBuildTime $libName $libCompiler]
                    puts $fp "catch {SetEstimatedBuildTime $libName $estimatedTime $libCompiler}"
                }
            }

            close $fp
            SetStatusMsg "Settings stored in file $cfgFile" "Ok"
        }
    }

    proc ViewSettingsDir {} {
        _StartFileBrowser [GetCfgDir]
    }

    proc _SaveBuildTime { tableId libName } {
        variable sGui

        set row [_GetSelectedRowNum $tableId]
        if { $row < 0 } {
            SetStatusMsg "No row selected." "Warning"
            return
        }
        set buildTime [poTablelistUtil GetCellValue $sGui(TableId) $row $sGui(Col,BuildTime)]
        if { $buildTime ne "" && $buildTime > 0.0 } {
            poTablelistUtil SetCellValue $sGui(TableId) $row $sGui(Col,EstimatedTime) $buildTime
            set libCompiler [poTablelistUtil GetCellValue $tableId $row $sGui(Col,LibCompiler)]
            SetEstimatedBuildTime $libName $buildTime $libCompiler
        }
    }

    proc SaveBuildTimes {} {
        variable sGui
        variable sEstimatedTimes

        set tableId $sGui(TableId)
        for { set row 0 } { $row < [$tableId size] } { incr row } {
            set libName      [poTablelistUtil GetCellValue $tableId $row $sGui(Col,LibName)]
            set buildTimeStr [poTablelistUtil GetCellValue $tableId $row $sGui(Col,BuildTime)]
            set libCompiler  [poTablelistUtil GetCellValue $tableId $row $sGui(Col,LibCompiler)]
            set res [scan $buildTimeStr "%f" buildTime]
            if { $res == 1 && [string is double -strict $buildTime] && $buildTime > 0.0 } {
                if { $libCompiler ne "" && ! [info exists sEstimatedTimes($libName,$libCompiler)] } {
                    SetEstimatedBuildTime $libName $buildTime $libCompiler
                } elseif { $buildTime > [GetEstimatedBuildTime $libName $libCompiler] } {
                    SetEstimatedBuildTime $libName $buildTime $libCompiler
                }
            }
        }
        SaveSettings
    }

    proc GetEstimatedBuildTime { libName { libCompiler "" } } {
        variable sEstimatedTimes

        if { $libCompiler ne "" } {
            if { [info exists sEstimatedTimes($libName,$libCompiler)] } {
                return $sEstimatedTimes($libName,$libCompiler)
            }
        }
        if { [info exists sEstimatedTimes($libName)] } {
            return $sEstimatedTimes($libName)
        } else {
            return 0.0
        }
    }

    proc SetEstimatedBuildTime { libName timeInMinutes { libCompiler "" } } {
        variable sEstimatedTimes

        set t [expr {double($timeInMinutes)}]
        if { $libCompiler ne "" } {
            set sEstimatedTimes($libName,$libCompiler) $t
        } else {
            set sEstimatedTimes($libName) $t
        }
    }

    proc ViewEstimatedBuildTimes {} {
        variable sTopLevel
        variable sEstimatedTimes

        set top $sTopLevel.view
        set fr  $top.fr

        toplevel $top
        ttk::frame $fr
        pack $fr -expand true -fill both
        wm title $top "BAWT - Estimated Build Times"

        set tableId [poWin CreateScrolledTablelist $fr true "Estimated build times" \
            -height 20 -width 100 \
            -exportselection false \
            -columns { 3 "#"            "right"
                      15 "Library Name" "left"
                       0 "None"         "right"
                       0 "gcc"          "right"
                       0 "vs2008"       "right"
                       0 "vs2010"       "right"
                       0 "vs2013"       "right"
                       0 "vs2015"       "right"
                       0 "vs2017"       "right"
                       0 "vs2019"       "right"
                       0 "vs2022"       "right" } \
            -setfocus 1 \
            -stretch 1  \
            -stripebackground #e0e8f0 \
            -selectmode single \
            -showseparators yes]
        $tableId columnconfigure 0 -showlinenumbers true
        set bodyTag [$tableId bodytag]
        $tableId configure -labelcommand tablelist::sortByColumn
        $tableId columnconfigure  1 -sortmode dictionary
        $tableId columnconfigure  2 -sortmode dictionary
        $tableId columnconfigure  3 -sortmode dictionary
        $tableId columnconfigure  4 -sortmode dictionary
        $tableId columnconfigure  5 -sortmode dictionary
        $tableId columnconfigure  6 -sortmode dictionary
        $tableId columnconfigure  7 -sortmode dictionary
        $tableId columnconfigure  8 -sortmode dictionary
        $tableId columnconfigure  9 -sortmode dictionary

        set col 2
        foreach type { None gcc vs2008 vs2010 vs2013 vs2015 vs2017 vs2019 vs2022 } {
            set cols($type) $col
            incr col
        }

        if { [info exists sEstimatedTimes] } {
            set curLib ""
            foreach key [lsort -dictionary [array names sEstimatedTimes]] {
                set libName     [lindex [split $key ","] 0]
                set libCompiler [lindex [split $key ","] 1]
                set estimatedTime [GetEstimatedBuildTime $libName $libCompiler]
                if { $libName ne $curLib } {
                    if { [info exists rowCont] } {
                        $tableId insert end $rowCont
                    }
                    # Got new libName
                    set rowCont [list "" $libName "" "" "" "" "" "" ""]
                    set curLib $libName
                }
                set colName $libCompiler
                if { $colName eq "" } {
                    set colName None
                }
                if { ! [string is double $estimatedTime] } {
                    set estimatedTime 0.0
                }
                lset rowCont $cols($colName) [format "%.2f" $estimatedTime]
                puts "$libName $estimatedTime $libCompiler"

            }
        }
    }

    proc _GetLibNames { tableId } {
        variable sGui

        return [$tableId getcolumns [list $sGui(Col,LibName)]]
    }

    proc _GetLibRow { tableId libName } {
        return [lsearch -exact [_GetLibNames $tableId] $libName]
    }

    proc _GetSelectedRowNum { tableId } {
        variable sGui

        set indList [$tableId curselection]
        if { [llength $indList] == 0 } {
            return -1
        }
        return [lindex $indList 0]
    }

    proc _GetLibNameFromTable { tableId } {
        variable sGui

        set indList [$tableId curselection]
        if { [llength $indList] == 0 } {
            return ""
        }
        set row [lindex $indList 0]
        set libName [poTablelistUtil GetCellValue $tableId $row $sGui(Col,LibName)]
        return $libName
    }

    proc _StartEditor { fileName } {
        variable sGui
        global env

        if { $sGui(Text,UseEditorEnvVar) && [info exists env(EDITOR)] } {
            set prog $env(EDITOR)
            if { [auto_execok $prog] ne "" } {
                eval exec $prog [list $fileName] &
                return
            }
            SetStatusMsg "Program in EDITOR environment variable ($prog) cannot be executed." "Warning"
        }
        if { $sGui(Text,UseEditorEnvVar) && ! [info exists env(EDITOR)] } {
            SetStatusMsg "No EDITOR environment variable specified." "Warning"
        }
        poExtProg ShowSimpleTextEdit $fileName "" true -undo true
    }

    proc ViewLibLogFile { tableId } {
        variable sGui

        set libName [_GetLibNameFromTable $tableId]
        set libLogFile [_GetLibLogFile $libName]
        if { $libLogFile ne "" } {
            _StartEditor $libLogFile
        } else {
            SetStatusMsg "Log file for library $libName not existent" "Error"
        }
    }

    proc ShowLib { tableId } {
        variable sGui
        variable sData

        set libName [_GetLibNameFromTable $tableId]
        if { $libName eq "" } {
            return
        }

        foreach tmpLibName [_GetLibNames $tableId] {
            $sGui(LogId) tag configure $tmpLibName -background white
        }

        if { [info exists sData(Tag,Start,$libName)] } {
            $sGui(LogId) see $sData(Tag,Start,$libName)
            $sGui(LogId) tag configure $libName -background yellow
        }
        $sGui(LogId) tag configure "Warning" -foreground red
    }

    proc AskOpenFile {} {
        variable sGui

        set fileName [tk_getOpenFile  \
                     -initialdir $sGui(LastDir) \
                     -title "Select file"]
        if { $fileName ne "" } {
            ClearGui
            ReadFile $fileName
        }
    }

    proc ExitApp { retVal } {
        variable sGui

        if { [info exists sGui(autosaveOnExit)] && $sGui(autosaveOnExit) } {
            SaveSettings
        }
        poMisc CleanTclkitDirs
        exit $retVal
    }

    proc UpdateTitle { setupFile { compilerVersions "" } } {
        variable sTopLevel

        if { $compilerVersions eq "" } {
            wm title $sTopLevel "BAWT - Setup file $setupFile"
        } else {
            wm title $sTopLevel "BAWT - Setup file $setupFile ($compilerVersions)"
        }
    }

    proc GetCopyright {} {
        return "Copyright 2018-2022 Paul Obermeier"
    }

    proc GetAppName {} {
        return "BawtLogViewer"
    }

    proc GetCfgDir {} {
        return [file join "~" ".[GetAppName]"]
    }

    proc GetVersionNumber {} {
        return "2.2.0"
    }

    proc GetBuildInfo {} {
        set buildNumber "Developer"
        set buildDate   "N/A"
        if { [info procs GetBuildNumber] ne "" } {
            set buildNumber [GetBuildNumber]
        }
        if { [info procs GetBuildDate] ne "" } {
            set buildDate [GetBuildDate]
        }
        return [format "Build: %s (Date: %s)" $buildNumber $buildDate]
    }

    proc GetVersion {} {
        return "[GetAppName] [GetVersionNumber] ([poMisc GetOSBitsStr])"
    }

    proc HelpProg {} {
        poSoftLogo ShowLogo [GetVersion] [GetBuildInfo] [GetCopyright]
    }

    proc _StartFileBrowser { dir } {
        set browserProg ""
        if { $::tcl_platform(platform) eq "windows" } {
            set browserProg "explorer"
        } elseif { $::tcl_platform(os) eq "Linux" } {
            foreach prog [list "dolphin" "konqueror" "nautilus"] {
                if { [auto_execok $prog] ne "" } {
                    set browserProg $prog
                    break
                }
            }
            if { $browserProg eq "" } {
                error "No file browser found. Looked for dolphin, konqueror and nautilus."
            }
        } elseif { $::tcl_platform(os) eq "Darwin" } {
            set browserProg "open"
        }
        if { [file isdirectory $dir] } {
            eval exec $browserProg [list [file nativename $dir]] &
        }
    }

    proc StartShell { type dir } {
        variable sGui

        set cwd [pwd]
        cd $dir
        if { $type eq "dos" || $type eq "msys" } {
            if { $type eq "dos" } {
                eval exec cmd /c start &
            } else {
                if { ! [info exists sGui(MSysShell)] } {
                    error "No MSys shell found. You might be using an old BAWT log file."
                }
                set batchFile [file join $dir "_Bawt_StartMSysConsole.bat"]
                if { ! [file exists $batchFile] } {
                    set retVal [catch {open $batchFile "w"} fp]
                    if { $retVal != 0 } {
                        error "Cannot write to file $batchFile"
                    }
                    puts $fp "SET HOME=[file nativename $dir]"
                    puts $fp "$sGui(MSysShell)"
                    close $fp
                }
                eval exec $batchFile & 
            }
        } elseif { $type eq "Linux" } {
            set shellProg ""
            foreach prog [list "konsole" "gnome-terminal" "xterm"] {
                if { [auto_execok $prog] ne "" } {
                    set shellProg $prog
                    break
                }
            }
            if { $shellProg eq "" } {
                error "No shell found. Looked for konsole, gnome-terminal and xterm."
            }
            eval exec $shellProg &
        } elseif { $type eq "Darwin" } {
            eval exec "open" "-a Terminal" [list $dir] &
        }
        cd $cwd
    }

    proc _ShowLibByKey { tableId key } {
        variable sCurLib

        set indList [$tableId curselection]
        if { [llength $indList] == 0 } {
            set row 0
        } else {
            set row [expr { [lindex $indList 0] + 1 }]
        }
        set libList [_GetLibNames $tableId]
        set numLibs [llength $libList]
        set key [string tolower $key]
        for { set i 0 } { $i < $numLibs } { incr i } {
            set libName [lindex $libList $row]
            if { ( [string tolower [string index $libName 0]] eq $key ) || \
                 ( $sCurLib ne "" && $key eq "return" && $libName eq $sCurLib ) } {
                $tableId selection clear 0 end
                $tableId selection set $row $row
                event generate $tableId <<TablelistSelect>>
                $tableId see $row
                break
            }
            set row [expr {( $row + 1 ) % $numLibs }]
        }
    }

    proc _OpenContextMenu { tableId x y } {
        variable ns
        variable sGui

        if { ! [info exists sGui(SetupFile)] } {
            return
        }
        set w .bawtLogViewer:contextMenu
        catch { destroy $w }
        menu $w -tearoff false -disabledforeground white

        set libName [_GetLibNameFromTable $tableId]
        if { $libName eq "" } {
            set menuTitle "Nothing selected"
        } else {
            set menuTitle "$libName selected"
        }
        $w add command -label "$menuTitle" -state disabled -background "#303030"

        if { $libName ne "" } {
            $w add command -label "Save build time" \
               -command "${ns}::_SaveBuildTime $sGui(TableId) $libName"
        }

        set logDir       [_GetLogDir]
        set devDir       [_GetDevDir]
        set buildDirList [_GetLibDirs $libName "Build"]
        set instDirList  [_GetLibDirs $libName "Install"]

        if { $logDir ne "" || $devDir ne "" || \
             [llength $buildDirList] >= 1 || [llength $instDirList] >= 1 } { 
            $w add cascade -label "Open directory" -menu $w.open
            menu $w.open -tearoff 0
        }
        if { $logDir ne "" } {
            $w.open add command -label "Logs" \
                    -command "${ns}::_StartFileBrowser $logDir"
        }
        if { $devDir ne "" } {
            $w.open add command -label "Development" \
                    -command "${ns}::_StartFileBrowser $devDir"
        }

        if { $libName ne "" } {
            if { [llength $buildDirList] >= 1 || [llength $instDirList] >= 1 } {
                $w.open add separator
            }
            foreach dir [concat $buildDirList $instDirList] {
                set pathList [lrange [file split $dir] end-2 end-1]
                set dirName  [join $pathList "/"]
                $w.open add command -label $dirName -command "${ns}::_StartFileBrowser $dir"
            }
        }

        $w add cascade -label "View file" -menu $w.view
        menu $w.view -tearoff 0

        $w.view add command -label "BAWT Setup" \
                -command [list ${ns}::_StartEditor $sGui(SetupFile)]
        $w.view add command -label "BAWT log" \
                -command [list ${ns}::_StartEditor $sGui(LogFile)]

        if { $libName ne "" } {
            set libLogFile [_GetLibLogFile $libName]

            if { $libLogFile ne "" } {
                $w.view add separator
                $w.view add command -label "Library log" \
                        -command [list ${ns}::_StartEditor $libLogFile]
            }
            if { [info exists sGui(BuildFile,$libName)] && \
                 [file exists $sGui(BuildFile,$libName)] } {
                $w.view add command -label "Library build" \
                        -command [list ${ns}::_StartEditor $sGui(BuildFile,$libName)]
            }
            if { [info exists sGui(UserBuildFile,$libName)] && \
                 [file exists $sGui(UserBuildFile,$libName)] } {
                $w.view add command -label "Library user build" \
                        -command [list ${ns}::_StartEditor $sGui(UserBuildFile,$libName)]
            }

            if { [llength $buildDirList] >= 1 } {
                $w add cascade -label "Open shell" -menu $w.shell
                menu $w.shell -tearoff 0

                foreach dir $buildDirList {
                    set pathList [lrange [file split $dir] end-2 end-1]
                    set dirName  [join $pathList "/"]
                    if { $::tcl_platform(platform) eq "windows" } {
                        $w.shell add command -label "${dirName}/DOS"  -command "${ns}::StartShell dos $dir"
                        $w.shell add command -label "${dirName}/MSys" -command "${ns}::StartShell msys $dir"
                    } elseif { $::tcl_platform(os) eq "Linux" } {
                        $w.shell add command -label "${dirName}" -command "${ns}::StartShell Linux $dir"
                    } elseif { $::tcl_platform(os) eq "Darwin" } {
                        $w.shell add command -label "${dirName}" -command "${ns}::StartShell Darwin $dir"
                    }
                }
            }
        }

        tk_popup $w $x $y
    }

    proc AddRecentFiles { menuId } {
        variable ns

        poMenu DeleteMenuEntries $menuId 3
        poMenu AddRecentFileList $menuId ${ns}::LoadRecentFile
    }

    proc ShowMainWin { { setupFile "None" } } {
        variable ns
        variable sTopLevel
        variable sGui
        variable sData

        if { [winfo exists $sTopLevel] } {
            wm deiconify $sTopLevel
            return
        }

        # Try to load settings file.
        if { ! [file isdirectory [GetCfgDir]] } {
            file mkdir [GetCfgDir]
        }
        LoadSettings

        if { [info exists sTopLevel] && [winfo exists $sTopLevel] } {
            destroy $sTopLevel
        }

        # If Tk could be loaded, create a toplevel window and generate
        # the master frame, where other widgets can be added dynamically.
        set sData(NumLibs) 0

        toplevel $sTopLevel
        wm withdraw .
        UpdateTitle $setupFile

        if { $sGui(MainWin,w) <= 0 || $sGui(MainWin,h) <= 0 } {
            wm geometry $sTopLevel [format "+%d+%d" $sGui(MainWin,x) $sGui(MainWin,y)]
        } else {
            wm geometry $sTopLevel [format "%dx%d+%d+%d" \
                        $sGui(MainWin,w) $sGui(MainWin,h) \
                        $sGui(MainWin,x) $sGui(MainWin,y)]
        }

        set sGui(MasterFr) $sTopLevel.fr
        ttk::frame $sGui(MasterFr)
        pack $sGui(MasterFr) -side top -fill both -expand 1

        set workFr   $sGui(MasterFr).workFr
        set statusFr $sGui(MasterFr).statusFr
        ttk::frame $workFr
        ttk::frame $statusFr
        grid $workFr   -row 0 -column 0 -sticky news
        grid $statusFr -row 1 -column 0 -sticky news
        grid rowconfigure    $sGui(MasterFr) 0 -weight 1
        grid columnconfigure $sGui(MasterFr) 0 -weight 1

        set paneId $workFr.paneFr
        ttk::panedwindow $paneId -orient vertical
        pack $paneId -side top -expand 1 -fill both

        set tableFr $paneId.tableFr
        set logFr   $paneId.logFr
        ttk::frame $tableFr
        ttk::frame $logFr
        grid $tableFr -row 0 -column 0 -sticky news
        grid $logFr   -row 1 -column 0 -sticky news
        grid rowconfigure    $workFr 0 -weight 1
        grid rowconfigure    $workFr 1 -weight 1
        grid columnconfigure $workFr 0 -weight 1

        $paneId add $tableFr
        $paneId add $logFr

        set sGui(Col,LibName)        2
        set sGui(Col,UserBuild)      3
        set sGui(Col,LibCompiler)    5
        set sGui(Col,BuildTime)      6
        set sGui(Col,EstimatedTime)  7
        set sGui(Col,ModTime)        8
        set sGui(Col,UpdateCause)    9
        set sGui(Col,Stages)        10

        set sGui(TableId) [poWin CreateScrolledTablelist $tableFr true "Library list" \
            -height 10 -width 100 \
            -exportselection false \
            -columns { 3 "#"            "right"
                       8 "Build-#"      "right"
                      15 "Library Name" "left"
                       4 "User"         "left"
                       8 "Version"      "left"
                       9 "Compiler"     "left"
                       0 "Build time"   "right"
                       0 "Est. time"    "right"
                       0 "Mod. time"    "left"
                       0 "Update cause" "left"
                       0 "Stages"       "left" } \
            -setfocus 1 \
            -stretch $sGui(Col,Stages)  \
            -stripebackground #e0e8f0 \
            -selectmode single \
            -showseparators yes]
        $sGui(TableId) columnconfigure 0 -showlinenumbers true
        bind $sGui(TableId) <<TablelistSelect>> "${ns}::ShowLib $sGui(TableId)"
        set bodyTag [$sGui(TableId) bodytag]
        bind $bodyTag <Double-1> "${ns}::ViewLibLogFile $sGui(TableId)"
        bind $bodyTag <<RightButtonPress>> "${ns}::_OpenContextMenu $sGui(TableId) %X %Y"
        bind $bodyTag <Any-KeyRelease> "${ns}::_ShowLibByKey $sGui(TableId) %K" 
        $sGui(TableId) columnconfigure  1 -sortmode integer
        $sGui(TableId) columnconfigure  2 -sortmode dictionary
        $sGui(TableId) columnconfigure  3 -sortmode dictionary
        $sGui(TableId) columnconfigure  4 -sortmode dictionary
        $sGui(TableId) columnconfigure  5 -sortmode dictionary
        $sGui(TableId) columnconfigure  6 -sortmode dictionary
        $sGui(TableId) columnconfigure  7 -sortmode dictionary
        $sGui(TableId) columnconfigure  8 -sortmode dictionary
        $sGui(TableId) columnconfigure  9 -sortmode dictionary
        $sGui(TableId) columnconfigure 10 -sortmode dictionary

        set sGui(LogId) [poWin CreateScrolledText $logFr true "Log messages" \
            -height 20 -width 100]
        _ToggleLineWrap

        if { $sGui(Have,tkdnd) } {
            poDragAndDrop AddCanvasBinding $sGui(TableId) "${ns}::LoadFile"
            poDragAndDrop AddCanvasBinding $sGui(LogId)   "${ns}::LoadFile"
        }

        # Create widget for status messages with progress bar.
        set sGui(StatusWidget) [poWin CreateStatusWidget $statusFr true]

        # Create menu.
        set hMenu $sTopLevel.menufr
        menu $hMenu -borderwidth 2 -relief sunken

        if { $::tcl_platform(os) eq "Darwin" } {
            $hMenu add cascade -menu $hMenu.apple -label [GetAppName]
            set appleMenu $hMenu.apple
            menu $appleMenu -tearoff 0
            poMenu AddCommand $appleMenu "About [GetAppName] ..."  ""   ${ns}::HelpProg
        }

        set fileMenu $hMenu.file
        set settMenu $hMenu.sett
        set helpMenu $hMenu.help
        $hMenu add cascade -menu $fileMenu -label "File"     -underline 0
        $hMenu add cascade -menu $settMenu -label "Settings" -underline 0
        $hMenu add cascade -menu $helpMenu -label "Help"     -underline 0

        # Menu File
        set sGui(AutoUpdate) 1
        menu $fileMenu -tearoff 0

        set openMenu $fileMenu.open
        $fileMenu add cascade -label "Open log file" -menu $openMenu

        menu $openMenu -tearoff 0 -postcommand "${ns}::AddRecentFiles $openMenu"
        poMenu AddCommand $openMenu "Select ..."    "Ctrl+O" ${ns}::AskOpenFile
        poMenu AddCommand $openMenu "Edit list ..." ""       "poAppearance EditRecentList recentFileList"
        $openMenu add separator

        poMenu AddCommand $fileMenu "Reload log file"  "F5" ${ns}::ReloadFile
        poMenu AddCheck   $fileMenu "Auto Update"      "F6" ${ns}::sGui(AutoUpdate) ""
        $fileMenu add separator

        poMenu AddCommand $fileMenu "Save build times" ""   ${ns}::SaveBuildTimes
        poMenu AddCommand $fileMenu "View est. times" ""    ${ns}::ViewEstimatedBuildTimes
        $fileMenu add separator

        if { $::tcl_platform(os) ne "Darwin" } {
            poMenu AddCommand $fileMenu "Quit" "Ctrl+Q" "${ns}::ExitApp 0"
        }

        bind $sTopLevel <Control-o> ${ns}::AskOpenFile
        bind $sTopLevel <F5>        ${ns}::ReloadFile
        bind $sTopLevel <F6>        ${ns}::ToggleAutoUpdate
        bind $sTopLevel <Control-q> "${ns}::ExitApp 0"
        if { $::tcl_platform(platform) eq "windows" } {
            bind $sTopLevel <Alt-F4> "${ns}::ExitApp 0"
        }

        wm protocol $sTopLevel WM_DELETE_WINDOW "${ns}::ExitApp 0"

        # Menu Settings
        menu $settMenu -tearoff 0

        set thresMenu $fileMenu.thres
        $settMenu add cascade -label "Deadlock threshold" -menu $thresMenu
        menu $thresMenu -tearoff 0
        poMenu AddRadio $thresMenu "  0%"  "" ${ns}::sGui(Deadlock,Threshold) 0.0  ""
        poMenu AddRadio $thresMenu " 25%"  "" ${ns}::sGui(Deadlock,Threshold) 0.25 ""
        poMenu AddRadio $thresMenu " 50%"  "" ${ns}::sGui(Deadlock,Threshold) 0.5  ""
        poMenu AddRadio $thresMenu " 75%"  "" ${ns}::sGui(Deadlock,Threshold) 0.75 ""
        poMenu AddRadio $thresMenu "100%"  "" ${ns}::sGui(Deadlock,Threshold) 1.0  ""

        poMenu AddCheck $settMenu "Deadlock beep" "" ${ns}::sGui(Deadlock,UseBeep) ""

        $settMenu add separator
        poMenu AddCheck $settMenu "Use EDITOR variable" "" ${ns}::sGui(Text,UseEditorEnvVar) ""
        poMenu AddCheck $settMenu "Line wrap"  "" ${ns}::sGui(Text,LineWrap) ${ns}::_ToggleLineWrap

        $settMenu add separator
        poMenu AddCheck   $settMenu "Save on exit"       "" ${ns}::sGui(autosaveOnExit) ""
        poMenu AddCommand $settMenu "View setting files" "" ${ns}::ViewSettingsDir
        poMenu AddCommand $settMenu "Save settings"      "" ${ns}::SaveSettings

        # Menu Help
        menu $helpMenu -tearoff 0
        if { $::tcl_platform(os) ne "Darwin" } {
            poMenu AddCommand $helpMenu "About [GetAppName] ..." "" ${ns}::HelpProg
        }

        $sTopLevel configure -menu $hMenu

        update
    }

    proc ClearGui {} {
        variable sGui
        variable sData

        $sGui(LogId) delete 0.1 end
        $sGui(TableId) delete 0 end
        catch { unset sData }
        set sData(NumLibs) 0
    }

    proc _GetLogDir {} {
        variable sGui

        if { [info exists sGui(LogFile)] } {
            set dir [file dirname $sGui(LogFile)]
            if { [file isdirectory $dir] } {
                return $dir
            }
        }
        return ""
    }

    proc _GetRootDir {} {
        set logDir  [_GetLogDir]
        if { $logDir ne "" } {
            set rootDir [file dirname $logDir]
            if { [file isdirectory $rootDir] } {
                return $rootDir
            }
        }
        return ""
    }

    proc _GetDevDir {} {
        set rootDir [_GetRootDir]
        if { $rootDir ne "" } {
            set dir [file join $rootDir "Development"]
            if { $dir ne "" && [file isdirectory $dir] } {
                return $dir
            }
        }
        return ""
    }

    proc _GetLibLogFile { libName } {
        variable sGui

        set libLogFile ""
        set logDir [_GetLogDir]
        if { $libName ne "" && $logDir ne "" } {
            set fileName [file join $logDir ${libName}.log]
            if { [file exists $fileName] } {
                set libLogFile $fileName
            }
        }
        return $libLogFile
    }

    proc _GetLibDirs { libName dirType } {
        set rootDir [_GetRootDir]

        set dirList [list]
        if { $rootDir ne "" } {
            foreach buildType { "Release" "Debug" } {
                set dir ""
                switch $dirType {
                    "Build"   { set dir [file join $rootDir $buildType "Build"   $libName] }
                    "Install" { set dir [file join $rootDir $buildType "Install" $libName] }
                }
                if { $dir ne "" && [file isdirectory $dir] } {
                    lappend dirList $dir
                }
            }
        }
        return $dirList
    }

    proc SetLibCompiler { libName libCompiler } {
        variable sLibCompiler

        set sLibCompiler($libName) $libCompiler
    }

    proc GetLibCompiler { libName } {
        variable sLibCompiler

        set libCompiler ""
        if { [info exists sLibCompiler($libName)] } {
            set libCompiler $sLibCompiler($libName)
        }
        return $libCompiler
    }

    proc AppendLib { libName libVersion libCompiler } {
        variable ns
        variable sGui
        variable sData

        set libLogFile [_GetLibLogFile $libName]
        set timeStr ""
        if { $libLogFile ne "" } {
            set timeStr [clock format [file mtime $libLogFile] -format "%Y-%m-%d %H:%M:%S"]
        }
        set estimatedTimeStr ""
        set estimatedTime [GetEstimatedBuildTime $libName $libCompiler]
        if { $estimatedTime > 0.0 } {
            set estimatedTimeStr [format "%.2f" $estimatedTime]
        }
        SetLibCompiler $libName $libCompiler
        set userBuild ""
        if { [info exists sGui(UserBuildFile,$libName)] } {
            set userBuild "Yes"
        }
        $sGui(TableId) insert end [list "" [expr {$sData(NumLibs) + 1}] $libName $userBuild $libVersion $libCompiler "" $estimatedTimeStr $timeStr ""]
        incr sData(NumLibs)
        poWin SetScrolledTitle $sGui(TableId) [format "Setup contains %d libraries" $sData(NumLibs)]
    }

    proc SetLibWarning { libName } {
        variable sGui

        set row [_GetLibRow $sGui(TableId) $libName]
        if { $row >= 0 } {
            $sGui(TableId) rowconfigure $row -foreground "red"
        }
	set tag [$sGui(LogId) index end]
	$sGui(LogId) tag add "Warning" "$tag - 2 lines" "$tag - 1 lines"
    }

    proc SetLibStatus { libName status msg } {
        variable sGui
        variable sData

        set col $sGui(Col,BuildTime)
        set row [_GetLibRow $sGui(TableId) $libName]
        if { $row >= 0 } {
            set color "grey"
            if { $status eq "Started" } {
                set color "yellow"
                set sData(Tag,Start,$libName) [$sGui(LogId) index end]
            } elseif { $status eq "Excluded" } {
                set col $sGui(Col,UpdateCause)
                set color "cyan"
                set sData(Tag,End,$libName) [$sGui(LogId) index end]
                $sGui(LogId) tag add $libName "$sData(Tag,Start,$libName) - 2 lines" "$sData(Tag,End,$libName) - 1 lines"
                poTablelistUtil SetCellValue $sGui(TableId) $row $sGui(Col,BuildTime) ""
            } elseif { $status eq "Finished" } {
                set color "lightgreen"
                set sData(Tag,End,$libName) [$sGui(LogId) index end]
                $sGui(LogId) tag add $libName "$sData(Tag,Start,$libName) - 2 lines" "$sData(Tag,End,$libName) - 1 lines"
            }
            $sGui(TableId) rowconfigure $row -background $color
            poTablelistUtil SetCellValue $sGui(TableId) $row $col $msg
            $sGui(TableId) see $row
            set sGui(SeeRow) $row
        }
    }

    proc SetLibStages { libName stages } {
        variable sGui

        set row [_GetLibRow $sGui(TableId) $libName]
        if { $row >= 0 } {
            poTablelistUtil SetCellValue $sGui(TableId) $row $sGui(Col,Stages) $stages
        }
    }

    proc SetLibUpdateCause { libName cause } {
        variable sGui

        set row [_GetLibRow $sGui(TableId) $libName]
        if { $row >= 0 } {
            poTablelistUtil SetCellValue $sGui(TableId) $row $sGui(Col,UpdateCause) $cause
        }
    }

    proc AppendLogMsg { logMsg } {
        variable sGui

        $sGui(LogId) insert end "$logMsg\n"
    }

    proc SetStatusMsg { statusMsg { icon "None" } } {
        variable sGui

        if { [info exists sGui(StatusWidget)] } {
            poWin WriteStatusMsg $sGui(StatusWidget) $statusMsg $icon
        }
    }

    proc SetLogFile { fileName } {
        variable sGui

        poWin SetScrolledTitle $sGui(LogId) [format "Log file %s" $fileName]
        set sGui(LogFile) $fileName
        set sGui(LastDir) [file dirname $fileName]
    }

    proc InitStatusProgress { par maxValue { mode "determinate" } } {
        # Copy of procedure from poTklib/poWin with call of update idletasks.
        if { [winfo exists $par.progress] } {
            $par.progress configure -maximum $maxValue -mode $mode
        }
    }

    proc ParseLine { line } {
        variable sGui
        variable sCurLib
        variable sSummarySection

        AppendLogMsg $line
        if { [string match "* > Setup (File*" $line] } {
            set setupStr [string trim [lindex [split $line "("] 1] " )"]
            # Remove word "File"
            set sGui(SetupFile) [string trim [string range $setupStr 4 end]]
            UpdateTitle $sGui(SetupFile)
        } elseif { [string match "*Build file*:*" $line] } {
            set sGui(BuildFile,$sCurLib) [lindex [split [string trim $line]] end]
        } elseif { [string match "*User file*:*" $line] } {
            set sGui(UserBuildFile,$sCurLib) [lindex [split [string trim $line]] end]
        } elseif { [string match "* SetCompilerVersions *" $line] } {
            set sGui(CompilerVersions) [lrange [split [string trim $line]] 1 end]
            UpdateTitle $sGui(SetupFile) $sGui(CompilerVersions)
        } elseif { [string match "* WorkingSet *" $line] } {
            set libInfo [string trim [lindex [split $line ":"] 1]]
            set libName     $libInfo
            set libVersion  ""
            set libCompiler ""
            if { [llength $libInfo] == 3 } {
                lassign $libInfo libName libVersion libCompiler
            } elseif { [llength $libInfo] == 2 } {
                lassign $libInfo libName libVersion
            }
            AppendLib $libName $libVersion $libCompiler
        } elseif { [string match "* > Start *" $line] } {
            set libInfo [string trim [lindex [split $line ">"] 1]]
            lassign $libInfo dummy libName
            SetLibStatus $libName "Started" "Running"
            set sCurLib $libName
            set timeStr [string trim [lindex [split $line ">"] 0]]
            if { $timeStr ne "" } {
                set sGui($libName,StartTime) [clock scan $timeStr -format "%H:%M:%S"]
            } else {
                set sGui($libName,StartTime) [clock seconds]
            }
            if { $sGui(AutoUpdate) } {
                set estimatedTime [GetEstimatedBuildTime $sCurLib [GetLibCompiler $sCurLib]]
                if { $estimatedTime > 0.0 } {
                    InitStatusProgress $sGui(StatusWidget) [expr {int($estimatedTime * 100)}] "determinate"
                } else {
                    InitStatusProgress $sGui(StatusWidget) 30 "indeterminate"
                }
            }
        } elseif { [string match "* > End *" $line] } {
            set libInfo [string trim [lindex [split $line ">"] 1]]
            lassign $libInfo dummy libName
            set libName [string trim $libName " :"]
            set status [string trim [lindex [split $libInfo ":"] 1]]
            if { [string match "Excluded*" $status] } {
                SetLibStatus $libName "Excluded" $status
            } else {
                set res [scan $status "%f minutes" buildTime]
                if { $res == 1 && [string is double -strict $buildTime] } {
                    SetLibStatus $libName "Finished" [format "%.2f" $buildTime]
                } else {
                    SetLibStatus $libName "Finished" $status
                }
            }
            set sCurLib ""
        } elseif { [string match "*>*Setup *" $line] } {
            set sCurLib [lindex [split [string trim $line]] end]
        } elseif { [string match "*Warning > *" $line] || \
                   [string match "*Error > *" $line] || \
                   [string match "*FATAL > *" $line] } {
            SetLibWarning $sCurLib
        } elseif { [string match "* LogFile *" $line] } {
            set logFile [lindex [split [string trim $line]] 1]
            SetLogFile $logFile
        } elseif { [string match "* #  : Library *" $line] } {
            set sSummarySection true
        } elseif { [string match "* Total: *" $line] } {
            set sSummarySection false
            set sGui(AutoUpdate) 0
        } elseif { [string match "*Update cause: *" $line] } {
            set cause [string trim [lindex [split $line ":"] 1]]
            SetLibUpdateCause $sCurLib $cause
        } elseif { [string match "*MSys/MinGW available:*" $line] || \
                   [string match "*MinGW available:*" $line] } {
            set colon [expr [string first ":" $line] + 1]
            set sGui(MinGW) [string trim [string range $line $colon end]]
        } elseif { [string match "*MSys available:*" $line] } {
            set colon [expr [string first ":" $line] + 1]
            set sGui(MSysShell) [string trim [string range $line $colon end]]
        } elseif { [string match "*: *" $line] && $sSummarySection } {
            set libInfo [string trim [lindex [split $line ":"] 1]]
            set libName [lindex $libInfo 0]
            set mappedLibInfo [string map { minutes : Excluded : } $libInfo]
            set stages [string trim [lindex [split $mappedLibInfo ":"] 1]]
            SetLibStages $libName $stages
        }
    }

    proc _AdjustTableView {} {
        variable sGui

        if { [info exists sGui(SeeRow)] && $sGui(SeeRow) > 0 } {
            $sGui(TableId) yview scroll 1 units
        }
    }

    proc ReadFile { fileName } {
        variable sGui

        SetStatusMsg "Reading log file $fileName ..." "Watch"
        update
        set fp [open $fileName "r"]
        while { [gets $fp line] >= 0 } {
            ParseLine $line
        }
        close $fp
        $sGui(LogId) tag configure "Warning" -foreground red
        $sGui(LogId) see end
        SetStatusMsg ""
        $sGui(TableId) configure -labelcommand tablelist::sortByColumn
        set sGui(LastUpdateSize) [file size $fileName]
        set sGui(LastDir)  [file dirname $fileName]
        set sGui(LastFile) $fileName
        poAppearance AddToRecentFileList $fileName
        _AdjustTableView
    }

    proc LoadFile { w fileList } {
        variable sGui

        if { [llength $fileList] > 0 } {
            set fileName [lindex $fileList 0]
            ClearGui
            ReadFile $fileName
        }
    }

    proc LoadRecentFile { fileName } {
        ClearGui
        ReadFile $fileName
    }

    proc ReloadFile {} {
        variable sGui

        if { [info exists sGui(LastFile)] } {
            ClearGui
            ReadFile $sGui(LastFile)
        }
    }

    proc AutoUpdate {} {
        variable sGui
        variable sCurLib
        variable sData

        if { $sGui(AutoUpdate) } {
            set diffTimeStr ""
            if { [info exists sGui($sCurLib,StartTime)] } {
                set curTime [clock seconds]
                set diffTime [expr ($curTime - $sGui($sCurLib,StartTime)) / 60.0]
                set estTimeStr ""
                set estimatedTime [GetEstimatedBuildTime $sCurLib [GetLibCompiler $sCurLib]]
                set row [_GetLibRow $sGui(TableId) $sCurLib]
                if { $row >= 0 } {
                    set remainTime 0.0
                    set remainLibs 0
                    for { set r $row } { $r < [poTablelistUtil GetNumRows $sGui(TableId)] } { incr r } {
                        set cellValue [poTablelistUtil GetCellValue $sGui(TableId) $r $sGui(Col,EstimatedTime)]
                        if { $cellValue eq "" } {
                            set cellValue 0.0
                        }
                        set remainTime [expr { $remainTime + $cellValue }]
                        incr remainLibs
                    }
                    set remainTimeStr ""
                    if { $remainTime > 0.0 } {
                        set remainTime [expr { $remainTime - $diffTime }]
                        set remainTimeStr [format "Remaining estimated build time: %.2f minutes." $remainTime]
                    }
                    poWin SetScrolledTitle $sGui(TableId) \
                          [format "Setup contains %d libraries. Remaining %d libraries. %s" \
                          $sData(NumLibs) $remainLibs $remainTimeStr]
                    if { $estimatedTime > 0.0 } {
                        set estTimeStr [format ". Estimated build time %.2f minutes" $estimatedTime]
                        if { $diffTime > [expr {$estimatedTime + $estimatedTime * $sGui(Deadlock,Threshold)}] } {
                            $sGui(TableId) rowconfigure $row -background orange
                            if { $sGui(Deadlock,UseBeep) } {
                                bell
                            }
                        } else {
                            $sGui(TableId) rowconfigure $row -background yellow
                        }
                    }
                    poTablelistUtil SetCellValue $sGui(TableId) $row $sGui(Col,BuildTime) [format "%.2f" $diffTime]
                }
                set diffTimeStr [format "(Library %s running since %.2f minutes%s)" \
                                        $sCurLib $diffTime $estTimeStr]
                poWin UpdateStatusProgress $sGui(StatusWidget) [expr {int($diffTime * 100.0) }]
            }
            SetStatusMsg "Auto Update: ON $diffTimeStr"
        } else {
            SetStatusMsg "Auto Update: OFF"
            poWin InitStatusProgress $sGui(StatusWidget) 0
            poWin UpdateStatusProgress $sGui(StatusWidget) 0
        }
        if { $sGui(AutoUpdate) && \
             [info exists sGui(LastUpdateSize)] && \
             [info exists sGui(LastFile)] } {
            if { [file size $sGui(LastFile)] > $sGui(LastUpdateSize) } {
                ReloadFile
            }
        }
        after 3000 AutoUpdate
    }

    proc ToggleAutoUpdate {} {
        variable sGui

        if { $::tcl_platform(os) ne "Darwin" } {
            set sGui(AutoUpdate) [expr ! $sGui(AutoUpdate)]
        }
    }
}

# Start of main program.

namespace import BawtLogViewer::*

set retVal [catch {package require Tk} version]
if { $retVal != 0 } {
    puts "Tk could not be loaded. No GUI possible."
    ExitApp 1
}
set retVal [catch {package require tablelist} version]
if { $retVal != 0 } {
    puts "tablelist could not be loaded. No GUI possible."
    ExitApp 1
}
set retVal [catch {package require tkdnd} version]
set BawtLogViewer::sGui(Have,tkdnd) true
if { $retVal != 0 } {
    set BawtLogViewer::sGui(Have,tkdnd) false
}
set retVal [catch {package require poTcllib} version]
if { $retVal != 0 } {
    puts "poTcllib could not be loaded. No GUI possible."
    ExitApp 1
}
set retVal [catch {package require poTklib} version]
if { $retVal != 0 } {
    puts "poTklib could not be loaded. No GUI possible."
    ExitApp 1
}

set optNoSaveOnExit false
set fileName ""

set curArg 0
while { $curArg < $argc } {
    set curParam [lindex $argv $curArg]
    if { [string compare -length 1 $curParam "-"]  == 0 || \
         [string compare -length 2 $curParam "--"] == 0 } {
        set curOpt [string tolower [string trimleft $curParam "-"]]
        if { $curOpt eq "nosaveonexit" } {
            set optNoSaveOnExit 1
        } elseif { [string first "-psn" $curParam] == 0 } {
            # Ignore this option. This parameter is supplied automatically
            # by OS X, when using this program as a starpack in a Mac APP.
        }
    } else {
        set fileName $curParam
    }
    incr curArg
}

if { $fileName ne "" } {
    if { ! [file isfile $fileName] } {
        puts "File $fileName not existent"
        ExitApp 1
    }
}

if { $::tcl_platform(os) eq "Darwin" } {
    proc ::tk::mac::Quit {} {
        BawtLogViewer::ExitApp 0
    }
}

event add <<LeftButtonPress>> <ButtonPress-1>
if { $::tcl_platform(os) eq "Darwin" } {
    event add <<MiddleButtonPress>> <ButtonPress-3>
    event add <<RightButtonPress>>  <ButtonPress-2>
    event add <<RightButtonPress>>  <Control-ButtonPress-1>
} else {
    event add <<MiddleButtonPress>> <ButtonPress-2>
    event add <<RightButtonPress>>  <ButtonPress-3>
}

ShowMainWin

if { $optNoSaveOnExit } {
    BawtLogViewer::SetAutosaveOnExit 0
}

if { [file readable $fileName] } {
    ReadFile $fileName
}

poAppearance UpdateRecentCaches

AutoUpdate

