summaryrefslogtreecommitdiff
path: root/Master/tlpkg/TeXLive/tltcl.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'Master/tlpkg/TeXLive/tltcl.tcl')
-rw-r--r--Master/tlpkg/TeXLive/tltcl.tcl434
1 files changed, 434 insertions, 0 deletions
diff --git a/Master/tlpkg/TeXLive/tltcl.tcl b/Master/tlpkg/TeXLive/tltcl.tcl
new file mode 100644
index 00000000000..0a00a65779a
--- /dev/null
+++ b/Master/tlpkg/TeXLive/tltcl.tcl
@@ -0,0 +1,434 @@
+#!/usr/bin/env wish
+
+# Copyright 2018 Siep Kroonenberg
+
+# This file is licensed under the GNU General Public License version 2
+# or any later version.
+
+# common declarations for tlshell.tcl and install-tl-gui.tcl
+
+set ::plain_unix 0
+if {$::tcl_platform(platform) eq "unix" && $::tcl_platform(os) ne "Darwin"} {
+ set ::plain_unix 1
+}
+
+# process ID of the perl program that will run in the background
+set ::perlpid 0
+
+set any_mirror "http://mirror.ctan.org/systems/texlive/tlnet"
+
+proc get_stacktrace {} {
+ set level [info level]
+ set s ""
+ for {set i 1} {$i < $level} {incr i} {
+ append s [format "Level %u: %s\n" $i [info level $i]]
+ }
+ return $s
+} ; # get_stacktrace
+
+# what exit procs do we need?
+# - plain error exit with messagebox and stacktrace
+# - plain messagebox exit
+# - showing log output, maybe with appended message,
+# use log toplevel for lengthy output
+# is closing the pipe $::inst guaranteed to kill perl? It should be
+
+proc err_exit {{mess ""}} {
+ if {$mess eq ""} {set mess "Error"}
+ append mess "\n" [get_stacktrace]
+ if $::plain_unix {
+ # plain_unix: avoid a RenderBadPicture error on quitting.
+ # 'send' changes the shutdown sequence,
+ # which avoids triggering the bug.
+ # 'tk appname <something>' restores 'send' and avoids the bug
+ bind . <Destroy> {
+ catch {tk appname appname}
+ }
+ }
+ tk_messageBox -icon error -message $mess
+ # kill perl process, just in case
+ if $::perlpid {
+ catch {
+ if {$::tcl_platform(platform) eq "unix"} {
+ exec -ignorestderr "kill" $::perlpid
+ } else {
+ exec -ignorestderr "taskkill" "/pid" $::perlpid
+ }
+ }
+ }
+ exit
+} ; # err_exit
+
+# localization support
+# tcl 8.5 observes LANG on Unix, but is overruled by LC_ALL.
+# for 8.5 or on Mac OS, locale setting from within tcl may not work.
+# so let the shell wrapper handle a language option by setting LANG
+# and unsetting LC_ALL.
+
+# $TEXMFCONFIG/tlmgr/config can have a setting for gui-lang.
+# replace with shell script / batchfile which is sourced?
+
+# for windows there is a bundled 8.6 in which locale can be set on-the-fly.
+# consult registry for default locale if LANG is not set.
+# The wrapper already does this, but here we do it again
+# in case e.g. company policy blocked reg.exe.
+
+# check the command-line for a lang parameter
+unset -nocomplain lang
+set i 0
+while {$i < $::argc} {
+ set p [lindex $::argv $i]
+ incr i
+ if {$p eq "-lang" || $p eq "--lang"} {
+ if {$i < $::argc} {
+ set lang [lindex $::argv $i]
+ break
+ }
+ } elseif {[string range $p 0 5] eq "-lang="} {
+ set lang [string range $p 6 end]
+ break
+ } elseif {[string range $p 0 6] eq "--lang="} {
+ set lang [string range $p 7 end]
+ break
+ }
+}
+unset i
+
+# first fallback: check config file if available
+if {! [info exists lang] || $lang eq ""} {
+ foreach tmf {"TEXMFCONFIG" "TEXMFSYSCONFIG"} {
+ if [catch {exec kpsewhich -var-value $tmf} d] {
+ break; # apparently there is not yet a TL installation
+ }
+ if [catch {open [file join $d "tlmgr" "config"] r} fid] continue
+ while 1 {
+ if [catch {chan gets $fid} l] break
+ if {[regexp {^gui-lang\s*=\s*(\S+)$} $l m lang]} {
+ chan close $fid
+ break
+ }
+ }
+ if {[info exists lang] && $lang ne ""} break
+ }
+}
+
+# second fallback: check environment for LC_ALL, LC_MESSAGES and LANG
+if {! [info exists lang] || $lang eq ""} {
+ foreach lng {"LC_ALL" "LC_MESSAGES" "LANG"} {
+ if {[info exists ::env($lng)] && $::env($lng) ne ""} {
+ set lang $::env($lng)
+ break
+ }
+ }
+}
+
+# third fallback, windows-only: check registry
+if {(! [info exists lang] || $lang eq "") && \
+ $::tcl_platform(platform) eq "windows"} {
+ if {! [catch {package require registry}]} {
+ set regpath [join {HKEY_LOCAL_MACHINE system currentcontrolset \
+ control nls language} "\\"]
+ if {! [catch {registry get $regpath "Installlanguage"} lcode]} {
+ set regpath [join {HKEY_CLASSES_ROOT mime database rfc1766} "\\"]
+ if {! [catch {registry get $regpath $lcode} lng]} {
+ set l [string first ";" $lng]
+ if {$l > 0} {
+ incr l -1
+ set lng [string range $lng 0 $l]
+ }
+ set lang $lng
+ #tk_messageBox -message "Language $lang in registry found"
+ }
+ }
+ }
+}
+
+# load the message catalog
+package require msgcat
+if {[info exists lang] && $lang ne ""} {
+ ::msgcat::mclocale $lang
+ #unset lang
+}
+::msgcat::mcload [file join $::instroot "tlpkg" "translations"]
+
+proc __ {s args} {return [::msgcat::mc $s {*}$args]}
+
+# string representation of booleans
+proc yes_no {b} {
+ if $b {
+ set ans [::msgcat::mc "Yes"]
+ } else {
+ set ans [::msgcat::mc "No"]
+ }
+ return $ans
+}
+
+if {$::tcl_platform(os) eq "Darwin"} {
+ # avoid warnings 'tar: Failed to set default locale'
+ set ::env(LC_ALL) "en_US.UTF-8"
+}
+
+### fonts ###
+
+# no bold text for messages; `userDefault' indicates priority
+option add *Dialog.msg.font TkDefaultFont userDefault
+
+# larger fonts: lfont and bold titlefont
+font create lfont {*}[font configure TkDefaultFont]
+font configure lfont -size [expr {round(1.2 * [font actual lfont -size])}]
+font create bfont {*}[font configure TkDefaultFont]
+font configure bfont -weight bold
+font create hfont {*}[font configure lfont]
+font configure hfont -weight bold
+font create titlefont {*}[font configure TkDefaultFont]
+font configure titlefont -weight bold \
+ -size [expr {round(1.5 * [font actual titlefont -size])}]
+
+## italicized items; not used
+#font create it_font {*}[font configure TkDefaultFont]
+#font configure it_font -slant italic
+
+# width of '0', as a very rough estimate of average character width
+# assume height == width*2
+set ::cw [font measure TkTextFont "0"]
+
+# default foreground color and disabled foreground color
+# may not be black in e.g. dark color schemes
+set blk [ttk::style lookup TButton -foreground]
+set gry [ttk::style lookup TButton -foreground disabled]
+
+# 'default' padding
+
+proc ppack {wdg args} { ; # pack command with padding
+ pack $wdg {*}$args -padx 3 -pady 3
+}
+
+proc pgrid {wdg args} { ; # grid command with padding
+ grid $wdg {*}$args -padx 3 -pady 3
+}
+
+# unicode symbols as fake checkboxes in ttk::treeview widgets
+
+proc mark_sym {mrk} {
+ if $mrk {
+ return "\u25A3" ; # 'white square containing black small square'
+ } else {
+ return "\u25A1" ; # 'white square'
+ }
+} ; # mark_sym
+
+# for help output
+set ::env(NOPERLDOC) 1
+
+##### dialog support #####
+
+# for example code, look at dialog.tcl, part of Tk itself
+
+# global variable for dialog return value, in case the outcome
+# must be handled by the caller rather than by the dialog itself
+
+set ::dialog_ans {}
+
+# start new toplevel with settings appropriate for a dialog
+proc create_dlg {wnd {p .}} {
+ catch {destroy $wnd} ; # no error if it does not exist
+ toplevel $wnd -class Dialog
+ wm withdraw $wnd
+ if [winfo viewable $p] {wm transient $wnd $p}
+ if $::plain_unix {wm attributes $wnd -type dialog}
+ wm protocol $wnd WM_DELETE_WINDOW {destroy $wnd}
+}
+
+# Place a dialog centered wrt its parent.
+# If its geometry is somehow not yet available,
+# its upperleft corner will be centered.
+
+proc place_dlg {wnd {p "."}} {
+ set g [wm geometry $p]
+ scan $g "%dx%d+%d+%d" pw ph px py
+ set hcenter [expr {$px + $pw / 2}]
+ set vcenter [expr {$py + $ph / 2}]
+ set g [wm geometry $wnd]
+ set wh [winfo reqheight $wnd]
+ set ww [winfo reqwidth $wnd]
+ set wx [expr {$hcenter - $ww / 2}]
+ if {$wx < 0} { set wx 0}
+ set wy [expr {$vcenter - $wh / 2}]
+ if {$wy < 0} { set wy 0}
+ wm geometry $wnd [format "+%d+%d" $wx $wy]
+ wm state $wnd normal
+ wm attributes $wnd -topmost
+ raise $wnd $p
+ tkwait visibility $wnd
+ focus $wnd
+ grab set $wnd
+} ; # place_dlg
+
+# place dialog answer in ::dialog_ans, raise parent, close dialog
+proc end_dlg {ans wnd} {
+ set ::dialog_ans $ans
+ set p [winfo parent $wnd]
+ if {$p eq ""} {set p "."}
+ raise $p
+ destroy $wnd
+} ; # end_dlg
+
+##### directories #####
+
+# slash flipping
+proc forward_slashify {s} {
+ regsub -all {\\} $s {/} r
+ return $r
+}
+proc native_slashify {s} {
+ if {$::tcl_platform(platform) eq "windows"} {
+ regsub -all {/} $s {\\} r
+ } else {
+ regsub -all {\\} $s {/} r
+ }
+ return $r
+}
+
+# unix: choose_dir replacing native directory browser
+
+if {$::tcl_platform(platform) eq "unix"} {
+
+ # Based on the tcl/tk widget demo.
+ # Also for MacOS, because we want to see /usr.
+ # For windows, the native browser widget is better.
+
+ ## Code to populate a single directory node
+ proc populateTree {tree node} {
+ if {[$tree set $node type] ne "directory"} {
+ set type [$tree set $node type]
+ return
+ }
+ $tree delete [$tree children $node]
+ foreach f [lsort [glob -nocomplain -directory $node *]] {
+ set type [file type $f]
+ if {$type eq "directory"} {
+ $tree insert $node end \
+ -id $f -text [file tail $f] -values [list $type]
+ # Need at least one child to make this node openable,
+ # will be deleted when actually populating this node
+ $tree insert $f 0 -text "dummy"
+ }
+ }
+ # Stop this code from rerunning on the current node
+ $tree set $node type processedDirectory
+ }
+
+ proc choose_dir {initdir {parent .}} {
+
+ create_dlg .browser $parent
+ wm title .browser [__ "Browse..."]
+
+ # wallpaper
+ pack [ttk::frame .browser.bg -padding 3] -fill both -expand 1
+ ## Create the tree and set it up
+ pack [ttk::frame .browser.fr0] -in .browser.bg -fill both -expand 1
+ set tree [ttk::treeview .browser.tree \
+ -columns {type} -displaycolumns {} -selectmode browse \
+ -yscroll ".browser.vsb set"]
+ .browser.tree column 0 -minwidth 500 -stretch 0
+ ttk::scrollbar .browser.vsb -orient vertical -command "$tree yview"
+ # hor. scrolling does not work, but toplevel and widget are resizable
+ $tree heading \#0 -text "/"
+ $tree insert {} end -id "/" -text "/" -values [list "directory"]
+
+ populateTree $tree "/"
+ bind $tree <<TreeviewOpen>> {
+ populateTree %W [%W focus]
+ }
+ bind $tree <ButtonRelease-1> {
+ .browser.tree heading \#0 -text [%W focus]
+ }
+
+ ## Arrange the tree and its scrollbar in the toplevel
+ # horizontal scrolling does not work.
+ # possible solution: very wide treeview in smaller paned window
+ # (may as well use pack in the absence of a horizontal scrollbar)
+ grid $tree .browser.vsb -sticky nsew -in .browser.fr0
+ grid columnconfigure .browser.fr0 0 -weight 1
+ grid rowconfigure .browser.fr0 0 -weight 1
+
+ # ok and cancel buttons
+ pack [ttk::frame .browser.fr1] -in .browser.bg -fill x -expand 1
+ ppack [ttk::button .browser.ok -text [__ "Ok"]] \
+ -in .browser.fr1 -side right
+ ppack [ttk::button .browser.cancel -text [__ "Cancel"]] \
+ -in .browser.fr1 -side right
+ .browser.ok configure -command {
+ set ::dialog_ans [.browser.tree focus]
+ destroy .browser
+ }
+ .browser.cancel configure -command {
+ set ::dialog_ans ""
+ destroy .browser
+ }
+ unset -nocomplain ::dialog_ans
+
+ # navigate tree to $initdir
+ set chosenDir {}
+ foreach d [file split [file normalize $initdir]] {
+ set nextdir [file join $chosenDir $d]
+ if [file isdirectory $nextdir] {
+ if {! [$tree exists $nextdir]} {
+ $tree insert $chosenDir end -id $nextdir \
+ -text $d -values [list "directory"]
+ }
+ populateTree $tree $nextdir
+ set chosenDir $nextdir
+ } else {
+ break
+ }
+ }
+ $tree see $chosenDir
+ $tree selection set [list $chosenDir]
+ $tree focus $chosenDir
+ $tree heading \#0 -text $chosenDir
+
+ place_dlg .browser $parent
+ tkwait window .browser
+ return $::dialog_ans
+ }; # choose_dir
+
+}; # if unix
+
+proc browse4dir {inidir {parent .}} {
+ if {$::tcl_platform(platform) eq "unix"} {
+ return [choose_dir $inidir $parent]
+ } else {
+ return [tk_chooseDirectory \
+ -initialdir $inidir -title [__ "Select or type"] -parent $parent]
+ }
+} ; # browse4dir
+
+# browse for a directory and store in entry- or label widget $w
+proc dirbrowser2widget {w} {
+ set wclass [winfo class $w]
+ if {$wclass eq "Entry" || $wclass eq "TEntry"} {
+ set is_entry 1
+ } elseif {$wclass eq "Label" || $wclass eq "TLabel"} {
+ set is_entry 0
+ } else {
+ err_exit "browse2widget invoked with unsupported widget class $wclass"
+ }
+ if $is_entry {
+ set retval [$w get]
+ } else {
+ set retval [$w cget -text]
+ }
+ set retval [browse4dir $retval [winfo parent $w]]
+ if {$retval eq ""} {
+ return 0
+ } else {
+ if {$wclass eq "Entry" || $wclass eq "TEntry"} {
+ $w delete 0 end
+ $w insert 0 $retval
+ } else {
+ $w configure -text $retval
+ }
+ return 1
+ }
+}