#!/bin/sh # -*-Mode: TCL;-*- # SPDX-License-Identifier: GPL-2.0-only #-------------------------------------------------------------- # BUILD.TK # # A cheesy graphical front-end for building and installing REMIND. # # This file is part of REMIND. # Copyright (C) 1992-2026 Dianne Skoll # #-------------------------------------------------------------- # the next line restarts using wish \ exec wish "$0" "$@" global RemindExecutable #*********************************************************************** # %PROCEDURE: SetConfigDefaults # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Sets up default values for various parameters. #*********************************************************************** proc SetConfigDefaults {} { global Config set Config(LAT_DEG) 45 set Config(LAT_MIN) 25 set Config(LAT_SEC) 14 set Config(LON_DEG) 75 set Config(LON_MIN) 41 set Config(LON_SEC) 23 set Config(LOCATION) "Ottawa" set Config(DEFAULT_PAGE) "Letter" set Config(DATESEP) "-" set Config(TIMESEP) ":" set Config(NORTHERN_HEMISPHERE) 1 set Config(WESTERN_HEMISPHERE) 1 set Config(INST_DIR) "/usr/local/bin" set Config(MAN_DIR) "/usr/local/share/man" } #*********************************************************************** # %PROCEDURE: Bail # %ARGUMENTS: # msg -- a message # %RETURNS: # Does not return # %DESCRIPTION: # Pops up an error dialog; then calls exit. #*********************************************************************** proc Bail { msg } { tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok exit 1 } #*********************************************************************** # %PROCEDURE: CheckSanity # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Checks sanity of install dir -- checks for critical files, # warns user if something looks wrong. #*********************************************************************** proc CheckSanity {} { if {![file executable ./configure]} { wm withdraw . Bail "I can't seem to execute the file ./configure -- make sure you have all required files and are running this from the top-level Remind directory" } if {![file readable ./src/custom.h.in]} { wm withdraw . Bail "I can't seem to find the file src/custom.h.in -- make sure you have all required files and are running this from the top-level Remind directory" } } #*********************************************************************** # %PROCEDURE: CreateMainDialog # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Creates and displays the main configuration dialog #*********************************************************************** proc CreateMainDialog {} { global Instdir Loc Options wm title . "Remind Configuration" wm iconname . "Remind Config" SetConfigFromRemind tabnotebook_create .tn set Instdir [tabnotebook_page .tn "Installation Directories"] CreateInstallDirDialog $Instdir set Loc [tabnotebook_page .tn "Location"] CreateLocationDialog $Loc set Options [tabnotebook_page .tn "Options"] CreateOptionsDialog $Options pack .tn -side top -expand 1 -fill both frame .buttons button .build -text "Build Remind" -command BuildRemind button .cancel -text "Cancel" -command exit pack .build .cancel -in .buttons -side left -expand 1 -fill both pack .buttons -side top -expand 0 -fill x } #*********************************************************************** # %PROCEDURE: CreateInstallDirDialog # %ARGUMENTS: # w -- frame containing widgets # %RETURNS: # Nothing # %DESCRIPTION: # Creates the "installation directories" dialog. #*********************************************************************** proc CreateInstallDirDialog { w } { global Config RemindExecutable label $w.binlabel -text "Location for programs: " entry $w.bin -width 30 $w.bin insert end $Config(INST_DIR) label $w.manlabel -text "Location for man pages: " entry $w.man -width 30 $w.man insert end $Config(MAN_DIR) text $w.blurb -width 1 -height 20 -wrap word -relief flat -takefocus 0 if { "$RemindExecutable" != "" } { $w.blurb insert end "Note: Default settings were obtained by querying the existing installed version of Remind found at: $RemindExecutable\n" } $w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)\n" grid $w.binlabel -row 0 -column 0 -sticky e grid $w.bin -row 0 -column 1 -sticky nsew grid $w.manlabel -row 1 -column 0 -sticky e grid $w.man -row 1 -column 1 -sticky nsew grid $w.blurb - -sticky nsew # Disable all text-window behaviour bindtags $w.blurb {NoSuchTag} $w.blurb configure -state disabled } #*********************************************************************** # %PROCEDURE: CreateLocationDialog # %ARGUMENTS: # w -- frame containing dialog # %RETURNS: # Nothing # %DESCRIPTION: # Creates the location dialog #*********************************************************************** proc CreateLocationDialog { w } { global Config scale $w.latdeg -label "Latitude (degrees)" -orient horizontal \ -from 0 -to 89 -length 300 -variable Config(LAT_DEG) scale $w.latmin -label "Latitude (minutes)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LAT_MIN) scale $w.latsec -label "Latitude (seconds)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LAT_SEC) scale $w.londeg -label "Longitude (degrees)" -orient horizontal \ -from 0 -to 179 -length 300 -variable Config(LON_DEG) scale $w.lonmin -label "Longtude (minutes)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LON_MIN) scale $w.lonsec -label "Longitude (seconds)" -orient horizontal \ -from 0 -to 59 -length 300 -variable Config(LON_SEC) radiobutton $w.north -text "Northern Hemisphere" \ -variable Config(NORTHERN_HEMISPHERE) -value 1 radiobutton $w.south -text "Southern Hemisphere" \ -variable Config(NORTHERN_HEMISPHERE) -value 0 radiobutton $w.west -text "Western Hemisphere" \ -variable Config(WESTERN_HEMISPHERE) -value 1 radiobutton $w.east -text "Eastern Hemisphere" \ -variable Config(WESTERN_HEMISPHERE) -value 0 label $w.loclab -text "City or Town: " entry $w.location -width 20 $w.location insert end $Config(LOCATION) grid $w.latdeg - grid $w.latmin - grid $w.latsec - grid $w.londeg - grid $w.lonmin - grid $w.lonsec - grid $w.north $w.west grid $w.south $w.east grid $w.loclab -sticky e grid $w.location -sticky nsew -row 8 -column 1 } #*********************************************************************** # %PROCEDURE: CreateOptionsDialog # %ARGUMENTS: # w -- frame containing dialog # %RETURNS: # Nothing # %DESCRIPTION: # Creates the options dialog #*********************************************************************** proc CreateOptionsDialog { w } { global Config label $w.pagelabel -text "Default page size: " menubutton $w.page -text $Config(DEFAULT_PAGE) \ -indicatoron 1 -relief raised \ -menu $w.page.menu menu $w.page.menu -tearoff 0 $w.page.menu add command -label "Letter" \ -command "$w.page configure -text Letter" $w.page.menu add command -label "A4" -command "$w.page configure -text A4" grid configure $w.pagelabel -row 0 -column 0 -sticky e grid configure $w.page -row 0 -column 1 -sticky nsew label $w.datelabel -text "Default date separator: " menubutton $w.date -text $Config(DATESEP) -indicatoron 1 -relief raised \ -menu $w.date.menu menu $w.date.menu -tearoff 0 $w.date.menu add command -label "/" -command "$w.date configure -text /" $w.date.menu add command -label "-" -command "$w.date configure -text -" grid configure $w.datelabel -row 1 -column 0 -sticky e grid configure $w.date -row 1 -column 1 -sticky nsew label $w.timelabel -text "Default time separator: " menubutton $w.time -text $Config(TIMESEP) -indicatoron 1 -relief raised \ -menu $w.time.menu menu $w.time.menu -tearoff 0 $w.time.menu add command -label ":" -command "$w.time configure -text :" $w.time.menu add command -label "." -command "$w.time configure -text ." grid configure $w.timelabel -row 2 -column 0 -sticky e grid configure $w.time -row 2 -column 1 -sticky nsew } #*********************************************************************** # %PROCEDURE: BuildRemind # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Builds Remind by: # -- creating custom.h from custom.h.in # -- running ./configure # -- running make #*********************************************************************** proc BuildRemind {} { pack forget .tn pack forget .buttons wm title . "Remind Configuration Status" text .msgs -width 130 -height 35 -wrap char -yscrollcommand ".sb set" scrollbar .sb -orient vertical -command ".msgs yview" .msgs tag configure green -foreground #005500 .msgs tag configure red -foreground #990000 pack .msgs -side left -expand 1 -fill both pack .sb -side left -expand 0 -fill y update .msgs insert end "\n>>> Creating src/custom.h...\n\n" green CreateCustomH .msgs insert end "\n>>> Calling `./configure'...\n\n" green CallConfigure .msgs insert end "\n>>> Calling `make'...\n\n" green CallMake .msgs insert end "\n----------------------------------------------\n\n" .msgs insert end "Remind" red .msgs insert end " has been built. To install it, type:\n\n" .msgs insert end "make install\n\n" green .msgs insert end "from the top-level " .msgs insert end "Remind" red .msgs insert end " directory. (You may need to be root.)\n\n" .msgs insert end "After it's installed, create an empty file called:\n" .msgs insert end " \$HOME/.reminders\n" green .msgs insert end "and type " .msgs insert end "tkremind" green .msgs insert end " for a nice easy introduction to " .msgs insert end "Remind.\n\n" red .msgs insert end "Press me to exit --> " button .msgs.ok -text "OK" -command "exit" .msgs window create end -window .msgs.ok .msgs see end } #*********************************************************************** # %PROCEDURE: RunCommand # %ARGUMENTS: # cmd -- shell command to run # %RETURNS: # Return code of command # %DESCRIPTION: # Runs a command putting output into ".msgs" #*********************************************************************** proc RunCommand { cmd } { global CmdDone set CmdDone 0 .msgs insert end "$cmd\n" red set problem [catch {set CmdFile [open "|$cmd" "r"]} err] if {$problem} { Bail "Error running command `$cmd': $err" } fconfigure $CmdFile -blocking 0 fileevent $CmdFile readable "CommandReadable $CmdFile" vwait CmdDone set problem [catch {close $CmdFile} err] if {$problem} { Bail "Error running command `$cmd': $err" } } #*********************************************************************** # %PROCEDURE: CommandReadable # %ARGUMENTS: # f -- file to read from # %RETURNS: # Nothing # %DESCRIPTION: # Reads characters from command pipeline and appends them to .msg. #*********************************************************************** proc CommandReadable { f } { global CmdDone set stuff [read $f] .msgs insert end $stuff .msgs see end if {[eof $f]} { set CmdDone 1 } } #*********************************************************************** # %PROCEDURE: CallConfigure # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Executes "./configure" with appropriate arguments # %PRECONDITIONS: # Any preconditions # %POSTCONDITIONS: # Any postconditions # %SIDE EFFECTS: # Any side effects #*********************************************************************** proc CallConfigure {} { global Instdir set bin [$Instdir.bin get] set man [$Instdir.man get] RunCommand "./configure --bindir=$bin --mandir=$man" } #*********************************************************************** # %PROCEDURE: CreateCustomH # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Creates "src/custom.h" from "src/custom.h.in" #*********************************************************************** proc CreateCustomH {} { global Loc Options Config set problem [catch {set in [open "src/custom.h.in" "r"]} err] if {$problem} { Bail "Can't read src/custom.h.in: $err" } set problem [catch {set out [open "src/custom.h" "w"]} err] if {$problem} { Bail "Can't write src/custom.h: $err" } # Retrieve values # The latitude/longitude ones are tied to the scales; we can't # modify them willy-nilly set LAT_DEG $Config(LAT_DEG) set LAT_MIN $Config(LAT_MIN) set LAT_SEC $Config(LAT_SEC) set LON_DEG $Config(LON_DEG) set LON_MIN $Config(LON_MIN) set LON_SEC $Config(LON_SEC) if {!$Config(NORTHERN_HEMISPHERE)} { set LAT_DEG "-$LAT_DEG" set LAT_MIN "-$LAT_MIN" set LAT_SEC "-$LAT_SEC" } if {!$Config(WESTERN_HEMISPHERE)} { set LON_DEG "-$LON_DEG" set LON_MIN "-$LON_MIN" set LON_SEC "-$LON_SEC" } set Config(LOCATION) [$Loc.location get] switch -- [$Options.page cget -text] { "A4" { set Config(DEFAULT_PAGE) "{\"A4\", 595, 842}" } default { set Config(DEFAULT_PAGE) "{\"Letter\", 612, 792}" } } set Config(DATESEP) [$Options.date cget -text] set Config(TIMESEP) [$Options.time cget -text] while {[gets $in line] != -1} { switch -glob -- $line { "#define DEFAULT_LATITUDE *" { set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)]; puts $out "#define DEFAULT_LATITUDE $lat" .msgs insert end "#define DEFAULT_LATITUDE $lat\n" } "#define DEFAULT_LONGITUDE *" { set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))] puts $out "#define DEFAULT_LONGITUDE $lon" .msgs insert end "#define DEFAULT_LONGITUDE $lon\n" } "#define LOCATION *" { puts $out "#define LOCATION \"$Config(LOCATION)\"" .msgs insert end "#define LOCATION \"$Config(LOCATION)\"\n" } "#define DEFAULT_PAGE *" { puts $out "#define DEFAULT_PAGE $Config(DEFAULT_PAGE)" .msgs insert end "#define DEFAULT_PAGE $Config(DEFAULT_PAGE)\n" } "#define DATESEP *" { puts $out "#define DATESEP '$Config(DATESEP)'" .msgs insert end "#define DATESEP '$Config(DATESEP)'\n" } "#define TIMESEP *" { puts $out "#define TIMESEP '$Config(TIMESEP)'" .msgs insert end "#define TIMESEP '$Config(TIMESEP)'\n" } default { puts $out $line } } } close $in close $out } #*********************************************************************** # %PROCEDURE: CallMake # %ARGUMENTS: # None # %RETURNS: # Nothing # %DESCRIPTION: # Runs "make" #*********************************************************************** proc CallMake {} { global Options set nproc 0 catch { set nproc [exec nproc] } if { $nproc != 0 } { RunCommand "make -j $nproc" } else { RunCommand "make" } } # Tabbed notebook code from "Effective Tcl/Tk Programming" # ---------------------------------------------------------------------- # EXAMPLE: tabnotebook that can dial up pages # ---------------------------------------------------------------------- # Effective Tcl/Tk Programming # Mark Harrison, DSC Communications Corp. # Michael McLennan, Bell Labs Innovations for Lucent Technologies # Addison-Wesley Professional Computing Series # ====================================================================== # Copyright (C) 1996-1997 Lucent Technologies Inc. and Mark Harrison # ====================================================================== option add *Tabnotebook.tabs.background #666666 widgetDefault option add *Tabnotebook.margin 6 widgetDefault option add *Tabnotebook.tabColor #a6a6a6 widgetDefault option add *Tabnotebook.activeTabColor #d9d9d9 widgetDefault option add *Tabnotebook.tabFont \ -*-helvetica-bold-r-normal--*-120-* widgetDefault proc tabnotebook_create {win} { global tnInfo frame $win -class Tabnotebook canvas $win.tabs -highlightthickness 0 pack $win.tabs -fill x notebook_create $win.notebook pack $win.notebook -expand yes -fill both set tnInfo($win-tabs) "" set tnInfo($win-current) "" set tnInfo($win-pending) "" return $win } proc tabnotebook_page {win name} { global tnInfo set page [notebook_page $win.notebook $name] lappend tnInfo($win-tabs) $name if {$tnInfo($win-pending) == ""} { set id [after idle [list tabnotebook_refresh $win]] set tnInfo($win-pending) $id } return $page } proc tabnotebook_refresh {win} { global tnInfo $win.tabs delete all set margin [option get $win margin Margin] set color [option get $win tabColor Color] set font [option get $win tabFont Font] set x 2 set maxh 0 foreach name $tnInfo($win-tabs) { set id [$win.tabs create text \ [expr $x+$margin+2] [expr -0.5*$margin] \ -anchor sw -text $name -font $font \ -tags [list $name]] set bbox [$win.tabs bbox $id] set wd [expr [lindex $bbox 2]-[lindex $bbox 0]] set ht [expr [lindex $bbox 3]-[lindex $bbox 1]] if {$ht > $maxh} { set maxh $ht } $win.tabs create polygon 0 0 $x 0 \ [expr $x+$margin] [expr -$ht-$margin] \ [expr $x+$margin+$wd] [expr -$ht-$margin] \ [expr $x+$wd+2*$margin] 0 \ 2000 0 2000 10 0 10 \ -outline black -fill $color \ -tags [list $name tab tab-$name] $win.tabs raise $id $win.tabs bind $name \ [list tabnotebook_display $win $name] set x [expr $x+$wd+2*$margin] } set height [expr $maxh+2*$margin] $win.tabs move all 0 $height $win.tabs configure -width $x -height [expr $height+4] if {$tnInfo($win-current) != ""} { tabnotebook_display $win $tnInfo($win-current) } else { tabnotebook_display $win [lindex $tnInfo($win-tabs) 0] } set tnInfo($win-pending) "" } proc tabnotebook_display {win name} { global tnInfo notebook_display $win.notebook $name set normal [option get $win tabColor Color] $win.tabs itemconfigure tab -fill $normal set active [option get $win activeTabColor Color] $win.tabs itemconfigure tab-$name -fill $active $win.tabs raise $name set tnInfo($win-current) $name } # ---------------------------------------------------------------------- # EXAMPLE: simple notebook that can dial up pages # ---------------------------------------------------------------------- # Effective Tcl/Tk Programming # Mark Harrison, DSC Communications Corp. # Michael McLennan, Bell Labs Innovations for Lucent Technologies # Addison-Wesley Professional Computing Series # ====================================================================== # Copyright (C) 1996-1997 Lucent Technologies Inc. and Mark Harrison # ====================================================================== option add *Notebook.borderWidth 2 widgetDefault option add *Notebook.relief sunken widgetDefault proc notebook_create {win} { global nbInfo frame $win -class Notebook pack propagate $win 0 set nbInfo($win-count) 0 set nbInfo($win-pages) "" set nbInfo($win-current) "" return $win } proc notebook_page {win name} { global nbInfo set page "$win.page[incr nbInfo($win-count)]" lappend nbInfo($win-pages) $page set nbInfo($win-page-$name) $page frame $page if {$nbInfo($win-count) == 1} { after idle [list notebook_display $win $name] } return $page } proc notebook_display {win name} { global nbInfo set page "" if {[info exists nbInfo($win-page-$name)]} { set page $nbInfo($win-page-$name) } elseif {[winfo exists $win.page$name]} { set page $win.page$name } if {$page == ""} { error "bad notebook page \"$name\"" } notebook_fix_size $win if {$nbInfo($win-current) != ""} { pack forget $nbInfo($win-current) } pack $page -expand yes -fill both set nbInfo($win-current) $page } proc notebook_fix_size {win} { global nbInfo update idletasks set maxw 0 set maxh 0 foreach page $nbInfo($win-pages) { set w [winfo reqwidth $page] if {$w > $maxw} { set maxw $w } set h [winfo reqheight $page] if {$h > $maxh} { set maxh $h } } set bd [$win cget -borderwidth] set maxw [expr $maxw+2*$bd] set maxh [expr $maxh+2*$bd] $win configure -width $maxw -height $maxh } #*********************************************************************** # %PROCEDURE: FindRemind # %ARGUMENTS: # None # %RETURNS: # Full path to an existing "remind" if one is found. Otherwise, # empty string. #*********************************************************************** proc FindRemind {} { global env set path [concat [split $env(PATH) ":"] "/usr/local/bin" "/bin" "/usr/bin" ] foreach thing $path { if [file executable [file join $thing "remind"]] { return [file join $thing "remind"] } } return {} } #*********************************************************************** # %PROCEDURE: SetConfigFromRemind # %ARGUMENTS: # None # %RETURNS: # Sets config settings based on existing remind (if one found) or else # sensible defaults. #*********************************************************************** proc SetConfigFromRemind {} { global Config RemindExecutable SetConfigDefaults set rem [FindRemind] set RemindExecutable $rem if {"$rem" == ""} { return } set dir [file dirname $rem] set Config(INST_DIR) $dir if {"$dir" == "/usr/local/bin"} { set Config(MAN_DIR) "/usr/local/share/man" } elseif {$dir == "/usr/bin"} { set Config(MAN_DIR) "/usr/share/man" } # Check for existing man page if {[file readable "/usr/share/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/share/man" } elseif {[file readable "/usr/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/man" } elseif {[file readable "/usr/local/share/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/local/share/man" } elseif {[file readable "/usr/local/man/man1/remind.1"]} { set Config(MAN_DIR) "/usr/local/man" } # Query Remind for the rest QueryRemind $rem LAT_DEG {$LatDeg} QueryRemind $rem LAT_MIN {$LatMin} QueryRemind $rem LAT_SEC {$LatSec} QueryRemind $rem LON_DEG {$LongDeg} QueryRemind $rem LON_MIN {$LongMin} QueryRemind $rem LON_SEC {$LongSec} QueryRemind $rem LOCATION {$Location} QueryRemind $rem DATESEP {$DateSep} QueryRemind $rem TIMESEP {$TimeSep} set $Config(LAT_MIN) [expr abs($Config(LAT_MIN))] set $Config(LAT_SEC) [expr abs($Config(LAT_SEC))] if {$Config(LAT_DEG) >= 0} { set Config(NORTHERN_HEMISPHERE) 1 } else { set Config(NORTHERN_HEMISPHERE) 0 set Config(LAT_DEG) [expr abs($Config(LAT_DEG))] } set $Config(LON_MIN) [expr abs($Config(LON_MIN))] set $Config(LON_SEC) [expr abs($Config(LON_SEC))] if {$Config(LON_DEG) >= 0} { set Config(WESTERN_HEMISPHERE) 1 } else { set Config(WESTERN_HEMISPHERE) 0 set Config(LON_DEG) [expr abs($Config(LON_DEG))] } # Get default page from rem2ps set rem2ps [file join $dir "rem2ps"] catch { exec $rem2ps -m help } err set errlist [split $err "\n"] set err [lindex $errlist end] if {[string match "Default media type is*" $err]} { set Config(DEFAULT_PAGE) [lindex $err end] } } proc QueryRemind { rem symbol rem_msg } { global Config catch { set fp [open "| $rem -" "r+"] puts $fp "banner %\nMSG \[$rem_msg\]%\nFLUSH\n" flush $fp gets $fp line catch { close $fp } } if {"$line" == ""} { return } set Config($symbol) $line } CheckSanity CreateMainDialog