# Copyright:      2010-2014 Paul Obermeier (obermeier@tcl3d.org)
#
#                 See the file "Tcl3D_License.txt" for information on
#                 usage and redistribution of this file, and for a
#                 DISCLAIMER OF ALL WARRANTIES.
#
# Module:         Tcl3D
# Filename:       OglInfo.tcl
#
# Author:         Paul Obermeier
#
# Description:    Tcl script to display OpenGL related information.
#                 When called without arguments, a window is opened with
#                 buttons to display OpenGL information for the following
#                 categories:
#                 - General information                           (-info)
#                 - Available OpenGL commands in Tcl              (-cmd)
#                 - Available OpenGL enumerations in Tcl          (-enum)
#
#                 The information texts can also be printed to stdout
#                 whithout opening a GUI, if calling this Tcl script
#                 with any of the above listed command line options.
#                 To display all three categories, the option "-all"
#                 can be used.
#
#                 Note: To retrieve all necessary information, an OpenGL
#                       context has to be established. So the batch mode
#                       needs a DISPLAY, too.

package require tablelist
package require tcl3dogl

tcl3dAddEvents

proc bgerror { msg } {
    tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo"
    exit
}

proc AddMenuCmd { menu label acc cmd args } {
    eval {$menu add command -label $label -accelerator $acc -command $cmd} $args
}

proc AddMenuRadio { menu label acc var val cmd args } {
    eval {$menu add radiobutton -label $label -accelerator $acc \
                                -variable $var -value $val -command $cmd} $args
}

proc AddMenuCheck { menu label acc var cmd args } {
    eval {$menu add checkbutton -label $label -accelerator $acc \
                                -variable $var -command $cmd} $args
}

proc ChangeTab { tabInd } {
    global gPo

    $gPo(notebookId) select $tabInd
    set gPo(curTab) $tabInd
}

proc ChangedTab { notebookId } {
    global gPo

    set gPo(curTab) [$notebookId index [$notebookId select]]
}

proc UpdateCombo { comboId typeList { showInd -1 } } {
    $comboId configure -values $typeList
    if { $showInd >= 0 } {
        $comboId current $showInd
    }
}

proc ExecFromHistory { comboId } {
    global gPo

    set ind [$comboId current]
    set item [lindex $gPo(historyView) $ind]
    set cmd [lindex $gPo(historyCmds) $ind]
    eval $cmd
    SelectEntry $item $gPo(selTableId,$gPo(curTab)) 0
}

proc AskOpenFile { textId } {
    global gPo

    set fileTypes {
        {{All files}   *}
        {{Tcl files}   {.tcl}}
        {{C/C++ files} {.c .cpp}}
        {{Text files}  {.txt}}
    }

    if { ! [file isdirectory $gPo(lastDir)] } {
        set gPo(lastDir) [pwd]
    }
    set fileName [tk_getOpenFile -filetypes $fileTypes \
                                 -initialdir $gPo(lastDir)]
    if { $fileName ne "" } {
        set gPo(lastDir) [file dirname $fileName]
    }
}

proc LoadFileIntoTextWidget { fileName textId } {
    global gPo

    set retVal [catch {open $fileName r} fp]
    if { $retVal != 0 } {
        error "Could not open file $fileName for reading."
    }

    set curState [$textId cget -state]
    $textId configure -state normal
    $textId delete 1.0 end
    while { ![eof $fp] } {
        $textId insert end [read $fp 2048]
    }
    $textId configure -state $curState
    close $fp
    set gPo(curCheckFile) [file tail $fileName]
}

proc CheckFile { textId cmdTableId enumTableId } {
    global gPo gOpts gCmdInd gEnumInd

    catch { unset gCmdInd }
    catch { unset gEnumInd }

    scan [$textId index end] %d noLines
    for { set i 1 } { $i < $noLines } { incr i } {
        set line [$textId get $i.0 $i.end]
        set ind 0
        set len [string length $line]
        set retVal 1
        while { $retVal && $ind < $len } {
            set retVal [regexp -start $ind -indices {gl([A-z,0-9]+)} $line total indList]
            if { $retVal } {
                set ws [expr {[lindex $indList 0] -2}]
                set we [lindex $indList 1]
                set func [string range $line $ws $we]
                set found [tcl3dOglIsFuncWrapped $func]
                if { $found } {
                    lappend gCmdInd($func) "$i.$ws"
                    $textId tag add "GLCmd" $i.$ws "$i.$ws + [string length $func] chars"
                }
                set ind [expr {$we + 1}]
            }
            set retVal [regexp -start $ind -indices {GL_([A-z,0-9,_]+)} $line total indList]
            if { $retVal } {
                set ws [expr {[lindex $indList 0] -3}]
                set we [lindex $indList 1]
                set enum [string range $line $ws $we]
                set found [tcl3dOglGetEnumVersion $enum]
                if { $found ne "" } {
                    lappend gEnumInd($enum) "$i.$ws"
                    $textId tag add "GLEnum" $i.$ws "$i.$ws + [string length $enum] chars"
                }
                set ind [expr {$we + 1}]
            }
        }
    }
    $textId tag configure "GLCmd"  -background $gOpts(cmdColor)
    $textId tag configure "GLEnum" -background $gOpts(enumColor)
    set funcList [lsort [array names gCmdInd]]
    _CmdInfo $gPo(chkTabInd) $gPo(cmdTableId,$gPo(chkTabInd)) $funcList \
             "Commands of $gPo(curCheckFile)" "ShowGLCommands"
    set enumList [lsort [array names gEnumInd]]
    _EnumInfo $gPo(chkTabInd) $gPo(enumTableId,$gPo(chkTabInd)) $enumList \
              "Enums of $gPo(curCheckFile)" "ShowGLEnums" true
}

proc GetComboValue { comboId textId } {
    global gPo

    set fileName [tcl3dDirSelect::GetValue $comboId]
    LoadFileIntoTextWidget $fileName $textId
    CheckFile $textId $gPo(cmdTableId,$gPo(chkTabInd)) $gPo(enumTableId,$gPo(chkTabInd))
}

proc ShowMainWin {title} {
    global gPo

    # Create the windows title.
    wm title . $title

    ttk::frame .fr -relief sunken -borderwidth 1
    pack .fr -side top -fill both -expand 1

    ttk::frame .fr.toolfr
    ttk::frame .fr.workfr
    ttk::frame .fr.infofr
    ttk::frame .fr.toglfr

    grid .fr.toolfr -row 0 -column 0 -sticky nwse
    grid .fr.workfr -row 1 -column 0 -sticky nwse
    grid .fr.infofr -row 2 -column 0 -sticky nwse
    grid .fr.toglfr -row 3 -column 0 -sticky nwse
    grid rowconfigure .fr 1 -weight 1
    grid columnconfigure .fr 0 -weight 1

    # Create menus File and Help
    set hMenu .fr.menufr
    menu $hMenu -borderwidth 2 -relief sunken
    if { $::tcl_platform(os) eq "Darwin" } {
        $hMenu add cascade -menu $hMenu.apple -label $gPo(appName)
        set appleMenu  $hMenu.apple
        menu $appleMenu -tearoff 0
        AddMenuCmd $appleMenu "About $gPo(appName) ..." ""  HelpAbout
    }
    $hMenu add cascade -menu $hMenu.file -label "File" -underline 0
    $hMenu add cascade -menu $hMenu.help -label "Help" -underline 0

    set fileMenu $hMenu.file
    menu $fileMenu -tearoff 0
    AddMenuCmd $fileMenu "Export as CSV ..."   "Ctrl+S" AskExport
    if { $::tcl_platform(os) ne "Darwin" } {
        AddMenuCmd $fileMenu "Quit" "Ctrl+Q" ExitProg
    }
    bind . <Control-s>  AskExport
    bind . <Control-q>  ExitProg
    wm protocol . WM_DELETE_WINDOW "ExitProg"

    set helpMenu $hMenu.help
    menu $helpMenu -tearoff 0
    AddMenuCmd $helpMenu "Help ..."                "F1" HelpCont
    AddMenuCmd $helpMenu "Open reference link"     "F2" HelpLinks
    AddMenuCmd $helpMenu "About $gPo(appName) ..." ""   HelpAbout
    bind . <Key-F1> HelpCont
    bind . <Key-F2> HelpLinks

    . configure -menu $hMenu

    # Create toolbar.
    set toolfr .fr.toolfr
    ttk::label $toolfr.lSearch -text "Search:"
    set gPo(searchLabel) $toolfr.searchLabel
    ttk::label $gPo(searchLabel) -width 8 -textvariable gPo(searchKey)
    pack $toolfr.lSearch $gPo(searchLabel) -side left

    ttk::label $toolfr.lHistory -text "History:"
    set gPo(historyCombo) $toolfr.comboHistory
    ttk::combobox $gPo(historyCombo) -width 30 -state readonly
    UpdateCombo $gPo(historyCombo) $gPo(historyCmds)
    bind $gPo(historyCombo) <<ComboboxSelected>> "ExecFromHistory %W"
    pack $toolfr.lHistory $gPo(historyCombo) -side left

    # Create the notebook and tabs for general infos, version infos and file checking.
    set nb .fr.workfr.nb
    set gPo(notebookId) $nb
    ttk::notebook $nb
    pack $nb -fill both -expand 1 -padx 2 -pady 3
    ttk::notebook::enableTraversal $nb
    bind $nb <<NotebookTabChanged>> "ChangedTab %W"

    set genTab $nb.generalFr
    set genTabInd 0
    set gPo(genTabInd) $genTabInd
    ttk::frame $genTab
    $nb add $genTab -text "General infos" -underline 0 -padding 2
    ttk::panedwindow $genTab.pane -orient horizontal
    pack $genTab.pane -side top -expand 1 -fill both
    ttk::frame $genTab.pane.catfr
    ttk::frame $genTab.pane.resfr
    pack $genTab.pane.catfr -expand 1 -fill both -side left
    pack $genTab.pane.resfr -expand 1 -fill both -side left
    $genTab.pane add $genTab.pane.catfr
    $genTab.pane add $genTab.pane.resfr

    set verTab $nb.versionFr
    set verTabInd 1
    set gPo(verTabInd) $verTabInd
    ttk::frame $verTab
    $nb add $verTab -text "Version infos" -underline 0 -padding 2
    ttk::panedwindow $verTab.pane -orient horizontal
    pack $verTab.pane -side top -expand 1 -fill both
    ttk::frame $verTab.pane.catfr
    ttk::frame $verTab.pane.resfr
    pack $verTab.pane.catfr -expand 1 -fill both -side left
    pack $verTab.pane.resfr -expand 1 -fill both -side left
    $verTab.pane add $verTab.pane.catfr
    $verTab.pane add $verTab.pane.resfr

    set chkTab $nb.checkFr
    set chkTabInd 2
    set gPo(chkTabInd) $chkTabInd
    ttk::frame $chkTab
    $nb add $chkTab -text "Check file" -underline 0 -padding 2
    ttk::panedwindow $chkTab.pane -orient horizontal
    pack $chkTab.pane -side top -expand 1 -fill both
    ttk::frame $chkTab.pane.catfr
    ttk::frame $chkTab.pane.resfr
    pack $chkTab.pane.catfr -expand 1 -fill both -side left
    pack $chkTab.pane.resfr -expand 1 -fill both -side left
    $chkTab.pane add $chkTab.pane.catfr
    $chkTab.pane add $chkTab.pane.resfr

    # Fill the General info notebook tab.
    set gPo(selTableId,$genTabInd) [tcl3dCreateScrolledTablelist $genTab.pane.catfr "Categories" \
                  -stripebackground #e0e8f0 \
                  -exportselection false \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -showlabels false -stretch all]
    bind $gPo(selTableId,$genTabInd) <<TablelistSelect>> "ExecCmd $gPo(selTableId,$genTabInd)"
    bind $gPo(selTableId,$genTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(selTableId,$genTabInd)"
    $gPo(selTableId,$genTabInd) configure -columns {0 "Keywords" "left"
                                                 0 "Command"  "left"}
    $gPo(selTableId,$genTabInd) columnconfigure 0 -sortmode ascii
    $gPo(selTableId,$genTabInd) columnconfigure 1 -hide true

    set gPo(cmdTableId,$genTabInd) [tcl3dCreateScrolledTablelist $genTab.pane.resfr "Result window" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 20]
    bind $gPo(cmdTableId,$genTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$genTabInd)"
    bind $gPo(cmdTableId,$genTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$genTabInd)"

    set tableId $gPo(cmdTableId,$genTabInd)
    set cmdList [list \
        [list "GL information" "ShowGLInfo      \"GL information\" $gPo(genTabInd) $tableId" ] \
        [list "GL extensions"  "ShowExtensions  \"GL extensions\"  $gPo(genTabInd) $tableId" ] \
        [list "GL commands"    "ShowGLCommands  \"GL commands\"    $gPo(genTabInd) $tableId" ] \
        [list "GLU commands"   "ShowGLUCommands \"GLU commands\"   $gPo(genTabInd) $tableId" ] \
        [list "GL enums"       "ShowGLEnums     \"GL enums\"       $gPo(genTabInd) $tableId" ] \
        [list "GLU enums"      "ShowGLUEnums    \"GLU enums\"      $gPo(genTabInd) $tableId" ] \
    ]

    $gPo(selTableId,$genTabInd) delete 0 end
    foreach cmdInfo $cmdList {
        $gPo(selTableId,$genTabInd) insert end $cmdInfo
    }

    set gPo(curTab)   $genTabInd
    set gPo(curTable) $gPo(cmdTableId,$gPo(curTab))

    # Fill the Version info notebook tab.
    set gPo(selTableId,$verTabInd) [tcl3dCreateScrolledTablelist $verTab.pane.catfr "Versions" \
                  -stripebackground #e0e8f0 \
                  -exportselection false \
                  -labelcommand tablelist::sortByColumn \
                  -selectmode single \
                  -showseparators yes -showlabels false -stretch all]
    bind $gPo(selTableId,$verTabInd) <<TablelistSelect>> "ExecCmd $gPo(selTableId,$verTabInd)"
    bind $gPo(selTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(selTableId,$verTabInd)"
    $gPo(selTableId,$verTabInd) configure -columns {0 "Keywords" "left"
                                                    0 "Command"  "left"}
    $gPo(selTableId,$verTabInd) columnconfigure 0 -sortmode ascii
    $gPo(selTableId,$verTabInd) columnconfigure 1 -hide true

    set pane $verTab.pane.resfr.pane
    ttk::panedwindow $pane -orient vertical
    pack $pane -side top -expand 1 -fill both
    ttk::frame $pane.cmdfr
    ttk::frame $pane.enumfr
    pack $pane.cmdfr  -expand 1 -fill both -side left
    pack $pane.enumfr -expand 1 -fill both -side left
    $pane add $pane.cmdfr
    $pane add $pane.enumfr

    set gPo(cmdTableId,$verTabInd) [tcl3dCreateScrolledTablelist $pane.cmdfr "Command list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 10]
    bind $gPo(cmdTableId,$verTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$verTabInd)"
    bind $gPo(cmdTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$verTabInd)"

    set gPo(enumTableId,$verTabInd) [tcl3dCreateScrolledTablelist $pane.enumfr "Enum list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 100 -height 10]
    bind $gPo(enumTableId,$verTabInd) <<TablelistSelect>> "ShowDetail $gPo(enumTableId,$verTabInd)"
    bind $gPo(enumTableId,$verTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(enumTableId,$verTabInd)"

    set cmdTableId  $gPo(cmdTableId,$verTabInd)
    set enumTableId $gPo(enumTableId,$verTabInd)
    $gPo(selTableId,$verTabInd) delete 0 end
    foreach extVers [tcl3dOglGetVersionList] {
        $gPo(selTableId,$verTabInd) insert end \
            [list $extVers "ShowVersionInfo $extVers $gPo(verTabInd) $cmdTableId $enumTableId"]
    }

    bind [$gPo(cmdTableId,$genTabInd) bodypath] <<RightMousePress>> \
         "OpenContextMenu $gPo(selTableId,$verTabInd) $gPo(cmdTableId,$genTabInd) %X %Y"

    # Fill the Check file notebook tab.
    ttk::frame $chkTab.pane.catfr.btnfr
    ttk::frame $chkTab.pane.catfr.txtfr
    pack $chkTab.pane.catfr.btnfr -expand 1 -fill x -side top
    pack $chkTab.pane.catfr.txtfr -expand 1 -fill both -side top

    set gPo(selTextId,$chkTabInd) [tcl3dCreateScrolledText $chkTab.pane.catfr.txtfr "" \
                                   -wrap none]

    set pane $chkTab.pane.resfr.pane
    ttk::panedwindow $pane -orient vertical
    pack $pane -side top -expand 1 -fill both
    ttk::frame $pane.cmdfr
    ttk::frame $pane.enumfr
    pack $pane.cmdfr  -expand 1 -fill both -side left
    pack $pane.enumfr -expand 1 -fill both -side left
    $pane add $pane.cmdfr
    $pane add $pane.enumfr

    set gPo(cmdTableId,$chkTabInd) [tcl3dCreateScrolledTablelist $pane.cmdfr "Command list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 50 -height 10]
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistSelect>> "ShowDetail $gPo(cmdTableId,$chkTabInd)"
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistSelect>> "+ShowCmdInText $gPo(cmdTableId,$chkTabInd) $gPo(selTextId,$chkTabInd)"
    bind $gPo(cmdTableId,$chkTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(cmdTableId,$chkTabInd)"

    set gPo(enumTableId,$chkTabInd) [tcl3dCreateScrolledTablelist $pane.enumfr "Enum list" \
                  -stripebackground #e0e8f0 \
                  -labelcommand tablelist::sortByColumn \
                  -showseparators yes -width 50 -height 10]
    bind $gPo(enumTableId,$chkTabInd) <<TablelistSelect>> "ShowDetail $gPo(enumTableId,$chkTabInd)"
    bind $gPo(enumTableId,$chkTabInd) <<TablelistSelect>> "+ShowEnumInText $gPo(enumTableId,$chkTabInd) $gPo(selTextId,$chkTabInd)"
    bind $gPo(enumTableId,$chkTabInd) <<TablelistColumnSorted>> "AddSortToHistory $gPo(enumTableId,$chkTabInd)"


    set gPo(fileCombo) [tcl3dDirSelect::CreateFileSelect $chkTab.pane.catfr.btnfr \
                        $gPo(lastDir) "open" "Open ..."]
    set fileTypes {
        {{All files}   *}
        {{Tcl files}   {.tcl}}
        {{C/C++ files} {.c .cpp}}
        {{Text files}  {.txt}}
    }
    tcl3dDirSelect::SetFileTypes $gPo(fileCombo) $fileTypes
    bind $gPo(fileCombo) <Key-Return>     "GetComboValue $gPo(fileCombo) $gPo(selTextId,$chkTabInd)"
    bind $gPo(fileCombo) <<FileSelected>> "GetComboValue $gPo(fileCombo) $gPo(selTextId,$chkTabInd)"

    ttk::button $chkTab.pane.catfr.btnfr.chk  -text "Check" -command \
        "CheckFile $gPo(selTextId,$chkTabInd) $gPo(cmdTableId,$chkTabInd) $gPo(enumTableId,$chkTabInd)"
    pack $chkTab.pane.catfr.btnfr.chk  -side left

    # Status frame.
    ttk::frame .fr.infofr.fr
    pack .fr.infofr.fr -expand true -fill both

    # Dummy Togl window to get graphics context.
    togl .fr.toglfr.togl -width 1 -height 1
    pack .fr.toglfr.togl

    set gPo(initDir) [pwd]
    wm title . $title
}

# proc ::tk::mac::ShowPreferences {} {
#    ShowSpecificNotebookWin
# }

proc AskExport {} {
    global gPo

    set fileTypes {
        {{CSV files} {.csv}}
    }
    set fileName [tk_getSaveFile -filetypes $fileTypes \
                  -initialfile "table.csv" -initialdir $gPo(lastDir) \
                  -title "Save table as CSV file"]
    if { $fileName ne "" } {
        Export $fileName $gPo(curTable)
    }
}

proc Export { fileName tableId } {
    set retVal [catch {open $fileName w} fp]
    if { $retVal != 0 } {
        error "Could not open file $fileName for writing."
    }
    set numCols [$tableId columncount]
    for { set col 0 } { $col < $numCols } { incr col } {
        lappend colList [$tableId columncget $col -title]
    }
    set colStr [join [lrange $colList 1 end] ";"]
    puts $fp "$colStr"

    set numRows [$tableId size]
    for { set row 0 } { $row < $numRows } { incr row } {
        set rowList [$tableId get $row]
        set rowStr [join [lrange $rowList 1 end] ";"]
        puts $fp "$rowStr"
    }
    close $fp
}

proc WriteEntryValue { index msg } {
    .fr.infofr.fr.entry_$index delete 0 end
    .fr.infofr.fr.entry_$index insert 0 $msg
}

proc Cleanup {} {
    uplevel #0 unset gPo
}

proc ExitProg {} {
    exit
}

proc AddCmdToHistory { view cmd  { addToHistory true } } {
    global gPo

    if { $addToHistory } {
        # If cmd is already in the history list, remove it. Append cmd at the front of the list.
        set ind [lsearch -exact $gPo(historyCmds) $cmd]
        if { $ind >= 0 } {
            set gPo(historyCmds) [lreplace $gPo(historyCmds) $ind $ind]
            set gPo(historyView) [lreplace $gPo(historyView) $ind $ind]
        }
        set gPo(historyCmds) [linsert $gPo(historyCmds) 0 $cmd]
        set gPo(historyView) [linsert $gPo(historyView) 0 $view]
        UpdateCombo $gPo(historyCombo) $gPo(historyView) 0
    }
}

proc AddSortToHistory { tableId } {
    global gPo

    set sortColumn [$tableId sortcolumn]
    set sortOrder  [$tableId sortorder]
    lappend gPo(historyCmds) "$tableId sortbycolumn $sortColumn -$sortOrder"
}

proc OpenURL { url } {
    global env tcl_platform

    if { $tcl_platform(platform) eq "windows" } {
        if {[file exists $env(COMSPEC)]} {
            eval exec [list $env(COMSPEC)] /c start [list $url] &
        } else {
            eval exec command /c start [list $url] &
        }
    } elseif { $tcl_platform(os) eq "Darwin" } {
        eval exec open [list $url] &
    } elseif { $tcl_platform(os) eq "Linux" } {
        eval exec xdg-open [list $url] &
    } else {
        puts "Unknown OS"
        eval exec [list $url] &
    }
}

proc GetURL { item } {
    return [tcl3dOglGetUrl $item]
}

proc GetSelectedTableItem { tableId column } {
    if { [$tableId size] == 0 } {
        return ""
    }
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    return [lindex $rowCont $column]
}

proc ShowCmdInText { tableId textId } {
    global gCmdInd

    set cmd [GetSelectedTableItem $tableId 1]
    if { $cmd ne "" && [info exists gCmdInd($cmd)] } {
        $textId see [lindex $gCmdInd($cmd) 0]
    }
}

proc ShowEnumInText { tableId textId } {
    global gEnumInd

    set enum [GetSelectedTableItem $tableId 1]
    if { $enum ne "" && [info exists gEnumInd($enum)] } {
        $textId see [lindex $gEnumInd($enum) 0]
    }
}

proc ShowDetail { tableId } {
    global gPo

    set gPo(curTable) $tableId

    if { [$tableId size] == 0 } {
        return
    }
    DrawDetail $tableId
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    set item [lindex $rowCont 1]
    set url [GetURL $item]
    set numCols [llength $rowCont]
    for { set i 1 } { $i < $numCols } { incr i } {
        set value [lindex $rowCont $i]
        WriteEntryValue $i "$value"
    }
    WriteEntryValue $i "$url"
}

proc HelpLinks {} {
    global gPo

    set rowIndList [$gPo(curTable) curselection]
    if { $rowIndList eq "" } {
        tk_messageBox -title "Reference links" \
                      -message "No item of the table selected" \
                      -type ok -icon error
        return
    }

    set rowCont [$gPo(curTable) get [lindex $rowIndList 0]]
    set item [lindex $rowCont 1]
    set url [GetURL $item]
    OpenURL $url
}

proc HelpCont {} {
    set helpStr \
"Sorry, no serious documentation available yet."

    tk_messageBox -title "Online help" \
                  -message $helpStr \
                  -type ok -icon info
}

proc HelpAbout {} {
    tcl3dLogoShowPoSoft "OglInfo Version 0.6.0" \
        "Copyright 2010-2014 Paul Obermeier" ""
}

proc DrawTitle { tableId titleStr } {
    tcl3dSetScrolledTitle $tableId $titleStr
}

proc OpenContextMenu { selTableId resTableId x y } {
    global gPo

    set w .tcl3dInfo_ContextMenu
    catch { destroy $w }
    menu $w -tearoff false -disabledforeground white

    set rowIndList [$resTableId curselection]
    set noSel [llength $rowIndList]
    if { $noSel != 0 } {
        set rowCont [$resTableId get [lindex $rowIndList 0]]
        if { $gPo($resTableId,infoType) eq "ShowExtensions" || \
             $gPo($resTableId,infoType) eq "ShowGLCommands" || \
             $gPo($resTableId,infoType) eq "ShowGLEnums" } {
            if { $gPo($resTableId,infoType) eq "ShowExtensions" } {
                set item [lindex $rowCont 1]
            }
            if { $gPo($resTableId,infoType) eq "ShowGLCommands" } {
                set item [lindex $rowCont 3]
            }
            if { $gPo($resTableId,infoType) eq "ShowGLEnums" } {
                set item [lindex $rowCont 4]
            }
            $w add command -label "Show all enums and commands of $item" \
                           -command "SelectVersionInfo $selTableId $item"
        } else {
            set menuTitle "No additional information"
            $w add command -label "$menuTitle" -state disabled \
                           -background "#303030"
        }
    } else {
        set menuTitle "Nothing selected"
        $w add command -label "$menuTitle" -state disabled -background "#303030"
    }
    tk_popup $w $x $y
}

proc Search { searchKey resTableId column } {
    global gPo

    # puts "Search $searchKey $resTableId $column"
    set gPo(searchKey) $searchKey
    set keyLen [expr {[string length $searchKey] -1}]
    set ind 0
    foreach line [$resTableId get 0 end] {
        set key [string range [lindex $line $column] 0 $keyLen]
        if { $searchKey eq [string tolower $key] } {
            $resTableId see $ind
            $resTableId selection clear 0 end
            $resTableId selection set $ind $ind
            $resTableId activate $ind
            break
        }
        incr ind
    }
}

proc SetSearchOff {} {
    global gPo

    $gPo(searchLabel) configure -text "" -state disabled
}

proc SetSearchKey { infoType resTableId column } {
    global gPo

    if { ! [info exists gPo(lastKeys,$infoType)] } {
        set gPo(lastKeys,$infoType) ""
    }
    $gPo(searchLabel) configure -text $gPo(lastKeys,$infoType) -state normal
    Search $gPo(lastKeys,$infoType) $resTableId $column
}

proc SearchKey { infoType resTableId column key sym } {
    global gPo

    # puts "SearchKey $infoType"
    if { ! [info exists gPo(lastKeys,$infoType)] } {
        set gPo(lastKeys,$infoType) ""
    }

    if { $sym eq "BackSpace" } {
        set gPo(lastKeys,$infoType) [string range $gPo(lastKeys,$infoType) 0 end-1]
    } else {
        if { [string length $key] == 0 } {
            return
        }
        if { $sym ne "Return" } {
            append gPo(lastKeys,$infoType) [string tolower $key]
        }
    }
    Search $gPo(lastKeys,$infoType) $resTableId $column
}

proc GetCmdInfo { cmd } {
    if { [tcl3dOglIsFuncWrapped $cmd] } {
        set avail  [tcl3dOglHaveFunc $cmd]
        set vers   [tcl3dOglGetFuncVersion $cmd]
        set depr   [tcl3dOglGetFuncDeprecated $cmd]
        set cSig   [tcl3dOglGetFuncSignature $cmd]
        set tclSig [tcl3dOglGetFuncSignature $cmd "tcl"]
        return [list "" $cmd $avail $vers $depr $cSig $tclSig]
    } else {
        set avail [tcl3dOglHaveFunc $cmd]
        # puts "$cmd" ; flush stdout
        set retVal [catch {eval $cmd} errMsg]
        set versionStr ""
        set helpStr  ""
        if { $retVal == 0 } {
            set helpStr "$cmd"
        } else {
            set startInd [string first $cmd $errMsg]
            set endInd [expr [string last "argument" $errMsg] -1]
            if { $endInd < 0 } {
                set endInd end
            }
            set helpStr [string trim [string range $errMsg $startInd $endInd] " \""]
        }
        return [list "" $cmd $avail $versionStr "" $helpStr $helpStr]
    }
}

proc ResetTablelist { tableId } {
    $tableId delete 0 end
}

proc ExecCmd { tableId } {
    set rowIndList [$tableId curselection]
    set rowCont [$tableId get [lindex $rowIndList 0]]
    set cmd [lindex $rowCont 1]
    eval $cmd
}

# Draw label and entry widgets below the table for detailled
# viewing of a table row.
proc DrawDetail { tableId } {
    global gPo

    if { $gPo(detailInfoType) eq $gPo($tableId,infoType) } {
        return
    }

    catch { eval destroy [winfo children .fr.infofr.fr] }

    set numCols [$tableId columncount]
    # Start at column 1, as the first column is just the row numbering.
    for { set i 1 } { $i < $numCols } { incr i } {
        set key [$tableId columncget $i -title]
        ttk::label .fr.infofr.fr.label_$i -anchor nw -text "$key"
        ttk::entry .fr.infofr.fr.entry_$i -exportselection false
        grid .fr.infofr.fr.label_$i -row [expr $i-1] -column 0 -sticky w
        grid .fr.infofr.fr.entry_$i -row [expr $i-1] -column 1 -sticky news
    }
    ttk::label .fr.infofr.fr.label_$i -anchor nw -text "Reference link"
    ttk::entry .fr.infofr.fr.entry_$i -exportselection false
    grid .fr.infofr.fr.label_$i -row [expr $i-1] -column 0 -sticky w
    grid .fr.infofr.fr.entry_$i -row [expr $i-1] -column 1 -sticky news
    grid columnconfigure .fr.infofr.fr 1 -weight 1
    set gPo(detailInfoType) $gPo($tableId,infoType)
}

proc ShowGLInfo { displayName tabInd tableId { addToHistory true } } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $tableId
    $tableId configure -columns {0 "#"          "right"
                                 0 "Identifier" "left"
                                 0 "Value"      "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode ascii

    foreach glInfo [tcl3dOglGetVersions] {
        $tableId insert end [linsert $glInfo 0 ""]
    }
    $tableId insert end [list "" TCL3D_VERSION [package present tcl3dogl]]
    set gPo($tableId,infoType) "ShowGLInfo"
    DrawDetail $tableId
    DrawTitle $tableId "OpenGL information"
    AddCmdToHistory $displayName "ShowGLInfo \"$displayName\" $tabInd $tableId" $addToHistory
    $tableId selection set 0 0
    bind [$tableId bodypath] <KeyPress> {}
    SetSearchOff
    focus $tableId
}

proc ShowExtensions { displayName tabInd tableId { addToHistory true } } {
    global gPo gOpts

    ChangeTab $tabInd
    ResetTablelist $tableId

    $tableId configure -columns {0 "#"         "right"
                                 0 "Extension" "left"
                                 0 "Avail"     "left"
                                 0 "GLEW"      "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode integer
    $tableId columnconfigure 3 -sortmode integer

    foreach ext [tcl3dOglGetExtensionList] {
        set extArr($ext,avail)   0
        set extArr($ext,wrapped) 0
    }

    foreach glInfo [tcl3dOglGetExtensions] {
        foreach ext [lindex $glInfo 1] {
            set extArr($ext,avail)   1
            set extArr($ext,wrapped) 0
        }
    }

    foreach ext [tcl3dOglGetVersionList] {
        if { ! [string match "GL_VERSION_*" $ext] } {
            if { ! [info exists extArr($ext,avail)] } {
                puts "Extension $ext is listed in GLEW, but not in official list."
                set extArr($ext,avail) 0
            }
            set extArr($ext,wrapped) 1
        }
    }

    set numExt   0
    set numAvail 0
    foreach keys [lsort -dictionary [array names extArr "*,avail"]] {
        set ext [lindex [split $keys ","] 0]
        set wrapped $extArr($ext,wrapped)
        set avail   $extArr($ext,avail)
        $tableId insert end [list "" $ext $avail $wrapped]
        if { $avail } {
            $tableId rowconfigure end -background $gOpts(availColor)
            incr numAvail
        }
        incr numExt
    }

    set titleStr "OpenGL Extensions ($numAvail of $numExt available)"
    set gPo($tableId,infoType) "ShowExtensions"
    DrawDetail $tableId
    DrawTitle $tableId $titleStr
    AddCmdToHistory $displayName "ShowExtensions \"$displayName\" $tabInd $tableId" $addToHistory

    bind [$tableId bodypath] <KeyPress> \
        "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"
    SetSearchKey $gPo($tableId,infoType) $tableId 1
    focus $tableId
}

proc _CmdInfo { tabInd tableId cmdList title cmdType } {
    global gPo gOpts

    ChangeTab $tabInd
    ResetTablelist $tableId
    $tableId configure -columns {0 "#"             "right"
                                 0 "Command"       "left"
                                 0 "Avail"         "left"
                                 0 "OGL/Ext"       "left"
                                 0 "Deprecated"    "left"
                                 0 "C signature"   "left"
                                 0 "Tcl signature" "left"}
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode integer
    $tableId columnconfigure 3 -sortmode ascii
    $tableId columnconfigure 4 -sortmode ascii
    $tableId columnconfigure 5 -sortmode ascii
    $tableId columnconfigure 6 -sortmode ascii

    set numCmds [llength $cmdList]
    set numAvail 0
    foreach cmd [lsort $cmdList] {
        set cmdInfoList [GetCmdInfo $cmd]
        $tableId insert end $cmdInfoList
        if { [lindex $cmdInfoList 2] } {
            $tableId rowconfigure end -background $gOpts(availColor)
            incr numAvail
        }
    }
    set titleStr "$title ($numAvail of $numCmds available)"
    set gPo($tableId,infoType) $cmdType
    DrawTitle $tableId $titleStr
    DrawDetail $tableId

    bind [$tableId bodypath] <KeyPress> "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"
    SetSearchKey $gPo($tableId,infoType) $tableId 1
    focus $tableId
}

proc SelectEntry { item tableId column } {
    set ind -1
    # puts "SelectEntry $item $tableId $column"
    for { set row 0 } { $row < [$tableId size] } { incr row } {
        set rowCont [$tableId get $row]
        set key [lindex $rowCont $column]
        # puts "Row $row : Comparing <$key> with <$item>"
        if { $key eq $item } {
            set ind $row
            break
        }
    }
    if { $ind < 0 } {
        tk_messageBox -title "Wrapping info" \
                      -message "No detail information available for $item" \
                      -type ok -icon info
        return false
    }
    $tableId selection clear 0 end
    $tableId selection set $ind $ind
    $tableId see $ind
    return true
}

proc SelectVersionInfo { selTableId item } {
    global gPo

    if { [SelectEntry $item $selTableId 0] } {
        ChangeTab $gPo(verTabInd)
        ExecCmd $selTableId
    }
}

proc ShowVersionInfo { item tabInd cmdTableId enumTableId { addToHistory true } } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $cmdTableId
    ResetTablelist $enumTableId

    set funcList [tcl3dOglGetVersionFuncs $item]
    _CmdInfo $tabInd $cmdTableId $funcList "Commands of $item" "ShowGLCommands"

    set enumList [tcl3dOglGetVersionEnums $item]
    _EnumInfo $tabInd $enumTableId $enumList "Enums of $item" "ShowGLEnums" true

    AddCmdToHistory "$item" "ShowVersionInfo $item $tabInd $cmdTableId $enumTableId" $addToHistory
}

proc _EnumInfo { tabInd tableId enumList title enumType showVersionColumn } {
    global gPo

    ChangeTab $tabInd
    ResetTablelist $tableId
    set columns {0 "#"            "right"
                 0 "Enumeration"  "left"
                 0 "Value (Dez.)" "right"
                 0 "Value (Hex.)" "right"}
    if { $showVersionColumn } {
        lappend columns 0 "OGL/Ext" "left"
    }
    $tableId configure -columns $columns
    $tableId columnconfigure 0 -showlinenumbers true
    $tableId columnconfigure 1 -sortmode ascii
    $tableId columnconfigure 2 -sortmode integer
    if { $showVersionColumn } {
        $tableId columnconfigure 3 -sortmode ascii
    }

    set numEnums [llength $enumList]
    set tblList [list]
    foreach cmd [lsort $enumList] {
        set cmdStr [format "::%s" $cmd]
        set decVal [set $cmdStr]
        if { [string is integer $decVal] } {
            set hexVal [format "%0X" $decVal]
        } else {
            set hexVal $decVal
        }
        set version [tcl3dOglGetEnumVersion $cmd]
        lappend tblList [list "" $cmd $decVal $hexVal $version]
    }
    $tableId insertlist end $tblList
    set titleStr "$title ($numEnums enumerations)"
    set gPo($tableId,infoType) $enumType
    DrawDetail $tableId
    DrawTitle $tableId $titleStr

    bind [$tableId bodypath] <KeyPress> "SearchKey $gPo($tableId,infoType) $tableId 1 %A %K"
    SetSearchKey $gPo($tableId,infoType) $tableId 1
    focus $tableId
}

proc ShowGLCommands { displayName tabInd tableId { addToHistory true } } {
    global gPo

    _CmdInfo $tabInd $tableId $gPo(glCmds) "OpenGL commands" "ShowGLCommands"
    AddCmdToHistory $displayName "ShowGLCommands \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLUCommands { displayName tabInd tableId { addToHistory true } } {
    global gPo

    _CmdInfo $tabInd $tableId $gPo(gluCmds) "OpenGL utility commands" "ShowGLUCommands"
    AddCmdToHistory $displayName "ShowGLUCommands \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLEnums { displayName tabInd tableId { addToHistory true } } {
    _EnumInfo $tabInd $tableId [info globals GL_*] "OpenGL enums" "ShowGLEnums" true
    AddCmdToHistory $displayName "ShowGLEnums \"$displayName\" $tabInd $tableId" $addToHistory
}

proc ShowGLUEnums { displayName tabInd tableId { addToHistory true } } {
    _EnumInfo $tabInd $tableId [info globals GLU_*] "OpenGL utility enums" "ShowGLUEnums" false
    AddCmdToHistory $displayName "ShowGLUEnums \"$displayName\" $tabInd $tableId" $addToHistory
}

set gPo(appName) "OglInfo"

set gPo(detailInfoType) ""
set gPo(searchKey)      ""
set gPo(historyCmds)    ""
set gPo(historyView)    ""
set gPo(lastDir)        [pwd]
set gPo(curCheckFile)   "Clipboard"

set gOpts(availColor) "lightgreen"
set gOpts(cmdColor)   "green"
set gOpts(enumColor)  "yellow"

# Pre-compute some command lists.
set gPo(glCmds)  [tcl3dOglGetFuncList "gl"]
set gPo(gluCmds) [tcl3dOglGetFuncList "glu"]

ShowMainWin "$gPo(appName): OpenGL Information Center"

set inFile ""
set inDir  ""
if { $argc > 0 } {
    set inFileOrDir [lindex $argv 0]
    if { [file isdirectory $inFileOrDir] } {
        set inDir [file normalize $inFileOrDir]
    } elseif { [file isfile $inFileOrDir] } {
        set inFile [file normalize $inFileOrDir]
    }
}

if { $inFile ne "" } {
    set tabInd $gPo(chkTabInd)
    ChangeTab $tabInd
    LoadFileIntoTextWidget $inFile $gPo(selTextId,$tabInd)
    tcl3dDirSelect::SetValue $gPo(fileCombo) $inFile
    CheckFile $gPo(selTextId,$tabInd) $gPo(cmdTableId,$tabInd) $gPo(enumTableId,$tabInd)
} elseif { $inDir ne "" } {
    puts "Not yet implemented"
} else {
    SelectEntry "GL information" $gPo(selTableId,$gPo(genTabInd)) 0
    ExecCmd $gPo(selTableId,$gPo(genTabInd))
}
