Files
remind/tkremind
dfs e1cf4d5e70 - Added TAG and DURATION clauses for communicating more information to
back-ends and eventually converting REMIND into a full-fledged
  scheduler.

- Completely reworked the PS/PSFILE mechanism to use the more
  general SPECIAL mechanism for customizing output in REMIND back-ends.

- Modified tkremind, rem2ps and www/rem2html to use new "SPECIAL"
  communication channel.
1997-09-21 23:23:34 +00:00

1398 lines
43 KiB
Tcl
Executable File

#!/bin/sh
# -*-Mode: TCL;-*-
#--------------------------------------------------------------
# TKREMIND
#
# A cheesy graphical front/back end for Remind using Tcl/Tk
#
# This file is part of REMIND.
# Copyright (C) 1992-1997 by David F. Skoll
#
#--------------------------------------------------------------
# $Id: tkremind,v 1.13 1997-09-21 23:23:37 dfs Exp $
# the next line restarts using wish \
exec wish "$0" "$@"
#---------------------------------------------------------------------------
# GLOBAL VARIABLES
#---------------------------------------------------------------------------
# Set to 1 if you want confirmation when quitting
set ConfirmQuit 0
# Remind program to execute -- supply full path if you want
set Remind "remind"
# Rem2PS program to execute -- supply full path if you want
set Rem2PS "rem2ps"
# Reminder file to source -- default
set ReminderFile {NOSUCHFILE}
catch {set ReminderFile "$env(HOME)/.reminders"}
# Reminder file to append to -- default
set AppendFile {NOSUCHFILE}
catch {set AppendFile $ReminderFile}
#---------------- DON'T CHANGE STUFF BELOW HERE ------------------
# Month names in English
set MonthNames {January February March April May June July August September October November December}
# Day names in Remind's pre-selected language
set DayNames {}
# Day name in English
set EnglishDayNames {Sunday Monday Tuesday Wednesday Thursday Friday Saturday}
# Current month and year -- will be set by Initialize procedure
set CurMonth -1
set CurYear -1
# Absolutely today -- unlike the CurMonth and CurYear, these won't change
set TodayMonth -1
set TodayYear -1
set TodayDay -1
# Reminder option types and skip types
set OptionType 1
set SkipType 1
# Remind command line
set CommandLine {}
set PSCmd {}
# Print options -- destination file; letter-size; landscape; fill page
set PrintDest file
set PrintSize letter
set PrintOrient landscape
set PrintFill 1
#---------------------------------------------------------------------------
# Initialize -- initialize things
#---------------------------------------------------------------------------
proc Initialize {} {
global DayNames argc argv CommandLine ReminderFile AppendFile Remind PSCmd
set CommandLine "|$Remind -itkremind=1 -p"
set PSCmd "$Remind -p"
set i 0
while {$i < $argc} {
if {[regexp -- {-[bgxim].*} [lindex $argv $i]]} {
append CommandLine " [lindex $argv $i]"
append PSCmd " [lindex $argv $i]"
} else {
break
}
incr i
}
if {$i < $argc} {
set ReminderFile [lindex $argv $i]
set AppendFile $ReminderFile
incr i
if {$i < $argc} {
set AppendFile [lindex $argv $i]
incr i
}
}
# Check system sanity
if {! [file readable $ReminderFile]} {
tk_dialog .error Error "Can't read reminder file `$ReminderFile'" error 0 Ok
exit 1
}
if {! [file writable $AppendFile]} {
tk_dialog .error Error "Can't write reminder file `$AppendFile'" error 0 Ok
exit 1
}
append CommandLine " $ReminderFile"
append PSCmd " $ReminderFile"
set DayNames [GetWeekdayNames]
# puts "CommandLine: $CommandLine"
}
#---------------------------------------------------------------------------
# GetWeekdayNames - Spawn a remind process and get the names of the weekdays
# Also sets CurMonth and CurYear.
#---------------------------------------------------------------------------
proc GetWeekdayNames {} {
global CurMonth CurYear TodayYear TodayMonth TodayDay Remind
set f [open "|$Remind - 2>/dev/null" r+]
puts $f "banner %"
set i 0
while { $i < 7 } {
puts $f "msg \[wkday($i)\]%"
incr i
}
# Get current month and year as long as we're running Remind
puts $f "msg %n%"
puts $f "msg %y%"
puts $f "msg %d%"
puts $f "FLUSH"
flush $f
set ans {}
set i 0
while { $i < 7 } {
lappend ans [gets $f]
incr i
}
set CurMonth [expr [gets $f] - 1]
set CurYear [gets $f]
set TodayDay [gets $f]
set TodayMonth $CurMonth
set TodayYear $CurYear
close $f
return $ans
}
#---------------------------------------------------------------------------
# RowNumber -- get the row number of a specified day.
# Arguments:
# firstDay -- first day of month (0=Sunday, 6=Saturday)
# mondayFirst -- 1 if Monday is in first column; 0 if Sunday is
# day -- day whose row number is needed
#---------------------------------------------------------------------------
proc RowNumber {firstDay mondayFirst day} {
# If month starts on Sunday, but Monday is in first column, handle it...
set adjust 0
if {$mondayFirst && !$firstDay} {incr adjust}
set modDay [expr $day - $mondayFirst]
if { $modDay < 0 } { set modDay 6 }
return [expr ($modDay + $firstDay - 1) / 7 + 1 + $adjust]
}
#---------------------------------------------------------------------------
# ColumnNumber -- get the column number of a specified day.
# Arguments:
# firstDay -- first day of month (0=Sunday, 6=Saturday)
# mondayFirst -- 1 if Monday is in first column; 0 if Sunday is
# day -- day whose column number is needed
#---------------------------------------------------------------------------
proc ColumnNumber { firstDay mondayFirst day } {
set modDay [expr $day - $mondayFirst]
if { $modDay < 0 } { set modDay 6 }
return [expr ($modDay + $firstDay - 1) % 7]
}
#---------------------------------------------------------------------------
# CreateCalWindow -- create the calendar window.
# Arguments:
# firstDay -- first day of month (0=Sunday, 6=Saturday)
# mondayFirst -- 1 if Monday is in first column; 0 if Sunday is
# daysInMonth -- number of days in month
# month -- name of the month
# year -- year
# dayNames -- names of weekdays in current language {Sun .. Sat}
#---------------------------------------------------------------------------
proc CreateCalWindow { firstDay mondayFirst daysInMonth month year dayNames } {
global CurMonth CurYear TodayMonth TodayYear TodayDay
catch { destroy .h }
catch { destroy .b }
frame .h
label .h.title -text "$month $year" -justify center -pady 2 -relief raised
pack .h.title -side top -fill x
set numRows [RowNumber $firstDay $mondayFirst $daysInMonth]
set col 0
while { $col < 7 } {
set row 1
frame .h.col$col
set dayIndex [expr ($col + $mondayFirst) % 7]
# A bogus width is supplied for the day-name label to persuade TCL
# to make all the columns the same width.
label .h.col$col.day -text [lindex $dayNames $dayIndex] -justify center -width 10
pack .h.col$col.day -side top -fill x -expand 0
while { $row <= $numRows } {
button .h.col$col.num$row -text "" -justify center -command "" -state disabled -relief flat
text .h.col$col.t$row -width 12 -height 1 -wrap word -relief sunken -state disabled
pack .h.col$col.num$row -side top -fill x -expand 0
pack .h.col$col.t$row -side top -fill both -expand 1
incr row
}
pack .h.col$col -side left -fill both -expand 1
incr col
}
set d 1
while { $d <= $daysInMonth } {
set r [RowNumber $firstDay $mondayFirst $d]
set c [ColumnNumber $firstDay $mondayFirst $d]
.h.col$c.num$r configure -text $d -command "ModifyDay $d $firstDay" -state normal
if { $CurMonth == $TodayMonth && $CurYear == $TodayYear && $d == $TodayDay} {
.h.col$c.num$r configure -background "#00c0c0"
.h.col$c.t$r configure -background "#00c0c0"
}
incr d
}
frame .b
button .b.prev -text {Previous Month} -command {MoveMonth -1}
button .b.this -text {Today} -command {ThisMonth}
button .b.next -text {Next Month} -command {MoveMonth 1}
button .b.goto -text {Go To Date...} -command {GotoDialog}
button .b.print -text {Print...} -command {DoPrint}
button .b.quit -text {Quit} -command {Quit}
label .b.status -text "" -width 10 -relief sunken
pack .b.prev .b.this .b.next .b.goto .b.print .b.quit -side left -fill x
pack .b.status -side left -fill x -expand 1
pack .h -side top -expand 1 -fill both
pack .b -side top -fill x -expand 0
wm title . "TkRemind - $month $year"
wm iconname . "$month $year"
}
#---------------------------------------------------------------------------
# FillCalWindow -- Fill in the calendar for global CurMonth and CurYear.
#---------------------------------------------------------------------------
proc FillCalWindow {} {
global DayNames CurYear CurMonth MonthNames CommandLine
Status "Firing off Remind..."
set month [lindex $MonthNames $CurMonth]
set file [open "$CommandLine $month $CurYear 2>/dev/null" r]
# Look for # rem2ps begin line
while { [gets $file line] >= 0 } {
if { [string compare "$line" "# rem2ps begin"] == 0 } { break }
}
if { [string compare "$line" "# rem2ps begin"] != 0 } {
Status "Problem reading results from Remind!"
close $file
return 0
}
# Read month name, year, number of days in month, first weekday, Mon flag
gets $file line
regexp {^([^ ]*) ([0-9][0-9][0-9][0-9]) ([0-9][0-9]?) ([0-9]) ([0-9])} $line dummy monthName year daysInMonth firstWkday mondayFirst
CreateCalWindow $firstWkday $mondayFirst $daysInMonth $monthName $year $DayNames
Status "Filling calendar entries..."
while { [gets $file line] >= 0 } {
if { [regexp {^([0-9][0-9][0-9][0-9])/([0-9][0-9])/([0-9][0-9]) +([^ ]+) +[^ ]+ +[^ ]+ +[^ ]+(.*)} $line all year month day type stuff] == 0 } {
continue
}
if { $type != "*"} {
continue
}
set day [string trimleft $day 0]
set month [string trimleft $month 0]
set c [ColumnNumber $firstWkday $mondayFirst $day]
set r [RowNumber $firstWkday $mondayFirst $day]
.h.col$c.t$r configure -state normal
if { [string length [string trim [.h.col$c.t$r get 1.0]]] != 0} {
.h.col$c.t$r insert end " .....\n"
}
.h.col$c.t$r insert end [string trim $stuff]
.h.col$c.t$r insert end "\n"
.h.col$c.t$r configure -state disabled
}
close $file
Status "Ready."
}
#---------------------------------------------------------------------------
# MoveMonth -- move by +1 or -1 months
# Arguments:
# delta -- +1 or -1 -- months to move.
#---------------------------------------------------------------------------
proc MoveMonth {delta} {
global CurMonth CurYear
set CurMonth [expr $CurMonth + $delta]
if {$CurMonth < 0} {
set CurMonth 11
set CurYear [expr $CurYear-1]
}
if {$CurMonth > 11} {
set CurMonth 0
incr CurYear
}
FillCalWindow
}
#---------------------------------------------------------------------------
# ThisMonth -- move to current month
#---------------------------------------------------------------------------
proc ThisMonth {} {
global CurMonth CurYear TodayMonth TodayYear
# Do nothing if already there
if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } {
return 0;
}
set CurMonth $TodayMonth
set CurYear $TodayYear
FillCalWindow
}
#---------------------------------------------------------------------------
# Status -- set status string
# Arguments:
# stuff -- what to set string to.
#---------------------------------------------------------------------------
proc Status { stuff } {
catch { .b.status configure -text $stuff }
update idletasks
}
#---------------------------------------------------------------------------
# DoPrint -- print a calendar
# Arguments:
# None
#---------------------------------------------------------------------------
proc DoPrint {} {
global PrintDest PrintSize PrintOrient PrintFill PrintStatus Rem2PS PSCmd
global CurMonth CurYear MonthNames
catch {destroy .p}
toplevel .p
wm title .p "Print..."
wm iconname .p "Print..."
frame .p.f1 -relief sunken -border 2
frame .p.f11
frame .p.f12
frame .p.f2 -relief sunken -border 2
frame .p.f3 -relief sunken -border 2
frame .p.f4
radiobutton .p.tofile -text "To file: " -variable PrintDest -value file
entry .p.filename
button .p.browse -text "Browse..." -command PrintFileBrowse
radiobutton .p.tocmd -text "To command: " -variable PrintDest -value command
entry .p.command
.p.command insert end "lpr"
label .p.size -text "Paper Size:"
radiobutton .p.letter -text "Letter" -variable PrintSize -value letter
radiobutton .p.a4 -text "A4" -variable PrintSize -value a4
label .p.orient -text "Orientation:"
radiobutton .p.landscape -text "Landscape" -variable PrintOrient -value landscape
radiobutton .p.portrait -text "Portrait" -variable PrintOrient -value portrait
checkbutton .p.fill -text "Fill page" -variable PrintFill
button .p.print -text "Print" -command {set PrintStatus print}
button .p.cancel -text "Cancel" -command {set PrintStatus cancel}
pack .p.f1 .p.f2 .p.f3 -side top -fill both -expand 1 -anchor w
pack .p.fill -side top -anchor w -fill none -expand 0
pack .p.f4 -side top -fill both -expand 1 -anchor w
pack .p.f11 .p.f12 -in .p.f1 -side top -fill none -expand 0 -anchor w
pack .p.tofile .p.filename .p.browse -in .p.f11 -side left -fill none -expand 0 -anchor w
pack .p.tocmd .p.command -in .p.f12 -side left -fill none -expand 0 -anchor w
pack .p.size .p.letter .p.a4 -in .p.f2 -side top -fill none -expand 0 -anchor w
pack .p.orient .p.landscape .p.portrait -in .p.f3 -side top -fill none -expand 0 -anchor w
pack .p.print .p.cancel -in .p.f4 -side left -fill none -expand 0
bind .p <KeyPress-Escape> ".p.cancel flash; .p.cancel invoke"
bind .p <KeyPress-Return> ".p.print flash; .p.print invoke"
set PrintStatus 2
CenterWindow .p
tkwait visibility .p
set oldFocus [focus]
focus .p.filename
grab .p
tkwait variable PrintStatus
catch {focus $oldFocus}
set fname [.p.filename get]
set cmd [.p.command get]
destroy .p
if {$PrintStatus == "cancel"} {
return
}
if {$PrintDest == "file"} {
if {$fname == ""} {
tk_dialog .error Error "No filename specified" error 0 Ok
return
}
if {[file isdirectory $fname]} {
tk_dialog .error Error "$fname is a directory" error 0 Ok
return
}
if {[file readable $fname]} {
set ans [tk_dialog .error Overwrite? "Overwrite $fname?" question 0 No Yes]
if {$ans == 0} {
return
}
}
set fname "> $fname"
} else {
set fname "| $cmd"
}
# Build the command line
set cmd "$PSCmd 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PS -c3"
if {$PrintSize == "letter"} {
append cmd " -m Letter"
} else {
append cmd " -m A4"
}
if {$PrintOrient == "landscape"} {
append cmd " -l"
}
if {$PrintFill} {
append cmd " -e"
}
append cmd " $fname"
Status "Printing..."
if {[catch {eval "exec $cmd"} err]} {
tk_dialog .error Error "Error during printing: $err" error 0 Ok
}
Status "Ready."
}
#---------------------------------------------------------------------------
# PrintFileBrowse -- browse for a filename for Print dialog
# Arguments: none
#---------------------------------------------------------------------------
proc PrintFileBrowse {} {
set ans [BrowseForFile .filebrowse "Print to file..." "Ok" 0 "*.ps"]
if {$ans != ""} {
.p.filename delete 0 end
.p.filename insert end $ans
.p.filename icursor end
.p.filename xview end
}
}
#---------------------------------------------------------------------------
# GotoDialog -- Do the "Goto..." dialog
#---------------------------------------------------------------------------
proc GotoDialog {} {
global CurMonth MonthNames CurYear
catch { destroy .g }
set month [lindex $MonthNames $CurMonth]
toplevel .g
wm title .g "Go To Date"
menubutton .g.mon -text "$month" -menu .g.mon.menu -relief raised
menu .g.mon.menu -tearoff 0
foreach m $MonthNames {
.g.mon.menu add command -label $m -command ".g.mon configure -text $m"
}
frame .g.y
label .g.y.lab -text "Year: "
entry .g.y.e -width 4
.g.y.e insert end $CurYear
bind .g.y.e <Return> ".g.b.go flash; .g.b.go invoke"
frame .g.b
button .g.b.go -text "Go" -command {DoGoto}
button .g.b.cancel -text "Cancel" -command { destroy .g }
pack .g.b.go .g.b.cancel -expand 1 -fill x -side left
pack .g.mon -fill x -expand 1
pack .g.y.lab -side left
pack .g.y.e -side left -fill x -expand 1
pack .g.y -expand 1 -fill x
pack .g.b -expand 1 -fill x
bind .g <KeyPress-Escape> ".g.b.cancel flash; .g.b.cancel invoke"
CenterWindow .g
set oldFocus [focus]
grab .g
focus .g.y.e
tkwait window .g
catch {focus $oldFocus}
}
#---------------------------------------------------------------------------
# DoGoto -- go to specified date
#---------------------------------------------------------------------------
proc DoGoto {} {
global CurYear CurMonth MonthNames
set year [.g.y.e get]
if { ! [regexp {^[0-9]+$} $year] } {
tk_dialog .error Error {Illegal year specified (1990-2078)} error 0 Ok
return
}
if { $year < 1990 || $year > 2078 } {
tk_dialog .error Error {Illegal year specified (1990-2078)} error 0 Ok
return
}
set month [lsearch -exact $MonthNames [.g.mon cget -text]]
set CurMonth $month
set CurYear $year
destroy .g
FillCalWindow
}
#---------------------------------------------------------------------------
# Quit -- handle the Quit button
#---------------------------------------------------------------------------
proc Quit {} {
global ConfirmQuit
if { !$ConfirmQuit } {
destroy .
exit
}
if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } {
destroy .
exit
}
}
#---------------------------------------------------------------------------
# CreateModifyDialog -- create dialog for adding a reminder
# Arguments:
# w -- path of parent window
# day -- day number of month
# firstDay -- day number of first day of month
#---------------------------------------------------------------------------
proc CreateModifyDialog {w day firstDay} {
# Set up: Year, Month, Day, WeekdayName
global CurYear CurMonth EnglishDayNames MonthNames OptionType SkipType
global ModifyDialogResult
set OptionType 1
set SkipType 1
set year $CurYear
set month [lindex $MonthNames $CurMonth]
set wkday [lindex $EnglishDayNames [expr ($day+$firstDay-1) % 7]]
frame $w.o -border 4 -relief ridge
frame $w.o1 -border 4
frame $w.o2 -border 4
frame $w.o3 -border 4
frame $w.exp -border 4
frame $w.adv -border 4
frame $w.weekend -border 4
frame $w.time -border 4
frame $w.hol -border 4
frame $w.msg
frame $w.buttons
pack $w.o1 $w.o2 $w.o3 -side top -anchor w -in $w.o
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both
pack $w.buttons -side top -anchor w -pady 4 -expand 1 -fill x
# TYPE 1 REMINDER
radiobutton $w.type1 -variable OptionType -value 1
menubutton $w.day1 -text $day -relief raised -menu $w.day1.menu
CreateDayMenu $w.day1
menubutton $w.mon1 -text $month -relief raised -menu $w.mon1.menu
CreateMonthMenu $w.mon1
menubutton $w.year1 -text $year -relief raised -menu $w.year1.menu
CreateYearMenu $w.year1
checkbutton $w.repbut -text "and repeating every"
$w.repbut deselect
menubutton $w.repdays -text 1 -relief raised -menu $w.repdays.menu
CreateDayMenu $w.repdays 1 28 0
label $w.label1a -text "day(s) thereafter"
pack $w.type1 $w.day1 $w.mon1 $w.year1 $w.repbut $w.repbut $w.repdays $w.label1a -side left -anchor w -in $w.o1
# TYPE 2 REMINDER
radiobutton $w.type2 -variable OptionType -value 2
label $w.label2a -text First
menubutton $w.wkday2 -text $wkday -relief raised -menu $w.wkday2.menu
CreateWeekdayMenu $w.wkday2
label $w.label2b -text "on or after"
menubutton $w.day2 -text $day -relief raised -menu $w.day2.menu
CreateDayMenu $w.day2 1 31 0
menubutton $w.mon2 -text $month -relief raised -menu $w.mon2.menu
CreateMonthMenu $w.mon2
menubutton $w.year2 -text $year -relief raised -menu $w.year2.menu
CreateYearMenu $w.year2
pack $w.type2 $w.label2a $w.wkday2 $w.label2b $w.day2 $w.mon2 $w.year2 -side left -anchor w -in $w.o2
# TYPE 3 REMINDER
if { $day <= 7 } {
set which "First"
} elseif {$day <= 14} {
set which "Second"
} elseif {$day <= 21} {
set which "Third"
} elseif {$day <= 28} {
set which "Fourth"
} else {
set which "Last"
}
radiobutton $w.type3 -variable OptionType -value 3
menubutton $w.ordinal -text $which -relief raised -menu $w.ordinal.menu
menu $w.ordinal.menu -tearoff 0
$w.ordinal.menu add command -label "First" -command "$w.ordinal configure -text First"
$w.ordinal.menu add command -label "Second" -command "$w.ordinal configure -text Second"
$w.ordinal.menu add command -label "Third" -command "$w.ordinal configure -text Third"
$w.ordinal.menu add command -label "Fourth" -command "$w.ordinal configure -text Fourth"
$w.ordinal.menu add command -label "Last" -command "$w.ordinal configure -text Last"
$w.ordinal.menu add command -label "Every" -command "$w.ordinal configure -text Every"
menubutton $w.wkday3 -text $wkday -relief raised -menu $w.wkday3.menu
CreateWeekdayMenu $w.wkday3
label $w.label3 -text "in"
menubutton $w.mon3 -text $month -relief raised -menu $w.mon3.menu
CreateMonthMenu $w.mon3
menubutton $w.year3 -text $year -relief raised -menu $w.year3.menu
CreateYearMenu $w.year3
pack $w.type3 $w.ordinal $w.wkday3 $w.label3 $w.mon3 $w.year3 -side left -anchor w -in $w.o3
# EXPIRY DATE
checkbutton $w.expbut -text "Expire after"
$w.expbut deselect
menubutton $w.expday -text $day -relief raised -menu $w.expday.menu
CreateDayMenu $w.expday 1 31 0
menubutton $w.expmon -text $month -relief raised -menu $w.expmon.menu
CreateMonthMenu $w.expmon 0
menubutton $w.expyear -text $year -relief raised -menu $w.expyear.menu
CreateYearMenu $w.expyear 0
pack $w.expbut $w.expday $w.expmon $w.expyear -side left -anchor w -in $w.exp
# ADVANCE NOTICE
checkbutton $w.advbut -text "Issue"
$w.advbut deselect
menubutton $w.advdays -text 3 -menu $w.advdays.menu -relief raised
CreateDayMenu $w.advdays 1 10 0
label $w.advlab -text "day(s) in advance"
checkbutton $w.advcount -text "not counting holidays/weekend"
$w.advcount select
pack $w.advbut $w.advdays $w.advlab $w.advcount -side left -anchor w -in $w.adv
# WEEKEND
label $w.weeklab -text "Weekend is: "
pack $w.weeklab -side left -anchor w -in $w.weekend
foreach d $EnglishDayNames {
checkbutton $w.d$d -text $d
$w.d$d deselect
pack $w.d$d -side left -anchor w -in $w.weekend
}
$w.dSaturday select
$w.dSunday select
# TIMED REMINDER
checkbutton $w.timebut -text "Timed reminder at"
$w.timebut deselect
menubutton $w.timehour -text "12" -menu $w.timehour.menu -relief raised
CreateDayMenu $w.timehour 1 12 0
menubutton $w.timemin -text "00" -menu $w.timemin.menu -relief raised
menu $w.timemin.menu -tearoff 0
$w.timemin.menu add command -label "00" -command "$w.timemin configure -text {00}"
$w.timemin.menu add command -label "15" -command "$w.timemin configure -text {15}"
$w.timemin.menu add command -label "30" -command "$w.timemin configure -text {30}"
$w.timemin.menu add command -label "45" -command "$w.timemin configure -text {45}"
menubutton $w.ampm -text "PM" -menu $w.ampm.menu -relief raised
menu $w.ampm.menu -tearoff 0
$w.ampm.menu add command -label "AM" -command "$w.ampm configure -text {AM}"
$w.ampm.menu add command -label "PM" -command "$w.ampm configure -text {PM}"
checkbutton $w.timeadvbut -text "with"
$w.timeadvbut deselect
menubutton $w.timeadv -text "15" -menu $w.timeadv.menu -relief raised
menu $w.timeadv.menu -tearoff 0
foreach i {5 10 15 30 45 60} {
$w.timeadv.menu add command -label $i -command "$w.timeadv configure -text $i"
}
label $w.timelab1 -text "minutes advance notice"
checkbutton $w.timerepbut -text "repeated every"
$w.timerepbut deselect
menubutton $w.timerep -text "5" -menu $w.timerep.menu -relief raised
menu $w.timerep.menu -tearoff 0
foreach i {3 5 10 15 30} {
$w.timerep.menu add command -label $i -command "$w.timerep configure -text $i"
}
label $w.timelab2 -text "minutes"
pack $w.timebut $w.timehour $w.timemin $w.ampm $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time
# SKIP TYPE
label $w.labhol -text "On holidays or weekends:"
radiobutton $w.issue -variable SkipType -value 1 -text "Issue reminder as usual"
radiobutton $w.skip -variable SkipType -value 2 -text "Skip reminder"
radiobutton $w.before -variable SkipType -value 3 -text "Move reminder before holiday or weekend"
radiobutton $w.after -variable SkipType -value 4 -text "Move reminder after holiday or weekend"
pack $w.labhol $w.issue $w.skip $w.before $w.after -side top -anchor w -in $w.hol
# TEXT ENTRY
label $w.msglab -text "Body:"
entry $w.entry
pack $w.msglab -side left -anchor w -in $w.msg
pack $w.entry -side left -anchor w -expand 1 -fill x -in $w.msg
bind $w.entry <Return> "$w.add flash; $w.add invoke"
# BUTTONS
button $w.add -text "Add to reminder file" -command "set ModifyDialogResult 1"
button $w.preview -text "Preview reminder" -command "set ModifyDialogResult 2"
button $w.cancel -text "Cancel" -background "#e0c9c9" -command "set ModifyDialogResult 3"
pack $w.add $w.preview $w.cancel -side left -anchor w -in $w.buttons -expand 1 -fill x
bind $w <KeyPress-Escape> "$w.cancel flash; $w.cancel invoke"
set ModifyDialogResult 0
# Center the window on the root
CenterWindow $w
}
#---------------------------------------------------------------------------
# CreateMonthMenu -- create a menu with all the months of the year
# Arguments:
# w -- menu button -- becomes parent of menu
# every -- if true, include an "every month" entry
#---------------------------------------------------------------------------
proc CreateMonthMenu {w {every 1}} {
global MonthNames
menu $w.menu -tearoff 0
if {$every} {
$w.menu add command -label "every month" -command "$w configure -text {every month}"
}
foreach month $MonthNames {
$w.menu add command -label $month -command "$w configure -text $month"
}
}
#---------------------------------------------------------------------------
# CreateWeekdayMenu -- create a menu with all the weekdays
# Arguments:
# w -- menu button -- becomes parent of menu
#---------------------------------------------------------------------------
proc CreateWeekdayMenu {w} {
global EnglishDayNames
menu $w.menu -tearoff 0
foreach d $EnglishDayNames {
$w.menu add command -label $d -command "$w configure -text $d"
}
$w.menu add command -label "weekday" -command "$w configure -text weekday"
}
#---------------------------------------------------------------------------
# CreateDayMenu -- create a menu with entries 1-31 and possibly "every day"
# Arguments:
# w -- menu button -- becomes parent of menu
# min -- minimum day to start from.
# max -- maximum day to go up to
# every -- if true, include an "every day" entry
#---------------------------------------------------------------------------
proc CreateDayMenu {w {min 1} {max 31} {every 1}} {
menu $w.menu -tearoff 0
if {$every} {
$w.menu add command -label "every day" -command "$w configure -text {every day}"
}
set d $min
while { $d <= $max } {
$w.menu add command -label $d -command "$w configure -text $d"
incr d
}
}
#---------------------------------------------------------------------------
# CreateYearMenu -- create a menu with entries from this year to this year+10
# and possibly "every year"
# Arguments:
# w -- menu button -- becomes parent of menu
# every -- if true, include an "every year" entry
#---------------------------------------------------------------------------
proc CreateYearMenu {w {every 1}} {
menu $w.menu -tearoff 0
if {$every} {
$w.menu add command -label "every year" -command "$w configure -text {every year}"
}
global CurYear
set d $CurYear
while { $d < [expr $CurYear + 11] } {
$w.menu add command -label $d -command "$w configure -text $d"
incr d
}
}
#---------------------------------------------------------------------------
# ModifyDay -- bring up dialog for adding reminder.
# Arguments:
# d -- which day to modify
# firstDay -- first weekday in month (0-6)
#---------------------------------------------------------------------------
proc ModifyDay {d firstDay} {
global ModifyDialogResult AppendFile
catch {destroy .mod}
toplevel .mod
CreateModifyDialog .mod $d $firstDay
wm title .mod "Add Reminder..."
wm iconname .mod "Add Reminder"
tkwait visibility .mod
set oldFocus [focus]
while {1} {
grab .mod
focus .mod.entry
set ModifyDialogResult -1
tkwait variable ModifyDialogResult
if {$ModifyDialogResult == 3} {
catch {focus $oldFocus}
destroy .mod
return 0
}
set problem [catch {set rem [CreateReminder .mod]} err]
if {$problem} {
tk_dialog .error Error "$err" error 0 Ok
} else {
if {$ModifyDialogResult == 2} {
set err [EditReminder $err]
if {$ModifyDialogResult == 3} {
continue
}
}
catch {focus $oldFocus}
destroy .mod
Status "Writing reminder..."
set f [open $AppendFile a]
puts $f "# Next reminder was created with TkRemind"
puts $f $err
puts $f ""
close $f
FillCalWindow
return 0
}
}
}
#---------------------------------------------------------------------------
# CenterWindow -- center a window on the screen. Stolen from tk_dialog code
# Arguments:
# w -- window to center
#---------------------------------------------------------------------------
proc CenterWindow {w} {
wm withdraw $w
update idletasks
set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
- [winfo vrootx [winfo parent $w]]]
set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
- [winfo vrooty [winfo parent $w]]]
wm geom $w +$x+$y
wm deiconify $w
}
#---------------------------------------------------------------------------
# CreateReminder -- create the reminder
# Arguments:
# w -- the window containing the add reminder dialog box.
# Returns:
# The reminder as a string.
#---------------------------------------------------------------------------
proc CreateReminder {w} {
global DidOmit
set body [string trim [$w.entry get]]
if {"$body" == ""} {
error "Blank body in reminder"
}
set DidOmit 0
set needOmit 0
# Delegate the first part to CreateReminder1, CreateReminder2, or
# CreateReminder3
global OptionType SkipType repbut expbut advbut advcount
global timebut timeadvbut timerepbut
set rem [CreateReminder$OptionType $w]
# Do the "until" part
if {$expbut} {
append rem " UNTIL [$w.expday cget -text] [$w.expmon cget -text] [$w.expyear cget -text]"
}
# Advance warning
if {$advbut} {
append rem " +"
if {!$advcount} {
append rem "+"
} else {
set needOmit 1
}
append rem [$w.advdays cget -text]
}
# Timed reminder
if {$timebut} {
set hour [$w.timehour cget -text]
set min [$w.timemin cget -text]
if {[$w.ampm cget -text] == "PM"} {
if {$hour < 12} {
incr hour 12
}
} else {
if {$hour == 12} {
set hour 0
}
}
append rem " AT $hour:$min"
if {$timeadvbut} {
append rem " +[$w.timeadv cget -text]"
}
if {$timerepbut} {
append rem " *[$w.timerep cget -text]"
}
}
global SkipType
if {$SkipType == 2} {
append rem " SKIP"
set needOmit 1
} elseif {$SkipType == 3} {
append rem " BEFORE"
set needOmit 1
} elseif {$SkipType == 4} {
append rem " AFTER"
set needOmit 1
}
if {$needOmit && !$DidOmit} {
append rem " OMIT [GetWeekend $w 1]"
}
# Check it out!
global Remind
set f [open "|$Remind -arq -e -" r+]
puts $f "BANNER %"
puts $f "$rem MSG %"
puts $f "MSG %_%_%_%_"
puts $f "FLUSH"
flush $f
set err {}
catch {set err [gets $f]}
catch {close $f}
if {"$err" != ""} {
# Clean up the message a bit
regsub -- {^-stdin-\([0-9]*\): } $err {} err
error "Error from Remind: $err"
}
append rem " MSG $body"
return $rem
}
#---------------------------------------------------------------------------
# CreateReminder1 -- Create the first part of a type-1 reminder
# Arguments:
# w -- add reminder dialog window
# Returns: first part of reminder
#---------------------------------------------------------------------------
proc CreateReminder1 {w} {
global repbut
set rem "REM"
set gotDay 0
set gotMon 0
set gotYear 0
set d [$w.day1 cget -text]
if {"$d" != "every day"} {
append rem " $d"
set gotDay 1
}
set m [$w.mon1 cget -text]
if {"$m" != "every month"} {
append rem " $m"
set gotMon 1
}
set y [$w.year1 cget -text]
if {"$y" != "every year"} {
append rem " $y"
set gotYear 1
}
# Check for repetition
if {$repbut} {
if {!$gotDay || !$gotMon || !$gotYear} {
error "All components of a date must be specified if you wish to use the repeat feature."
}
append rem " *[$w.repdays cget -text]"
}
return $rem
}
#---------------------------------------------------------------------------
# CreateReminder2 -- Create the first part of a type-2 reminder
# Arguments:
# w -- add reminder dialog window
# Returns: first part of reminder
#---------------------------------------------------------------------------
proc CreateReminder2 {w} {
set wkday [$w.wkday2 cget -text]
if {"$wkday" == "weekday"} {
set wkday [GetWeekend $w 0]
}
set day [$w.day2 cget -text]
set mon [$w.mon2 cget -text]
set year [$w.year2 cget -text]
set rem "REM $wkday $day"
if {$mon != "every month"} {
append rem " $mon"
}
if {$year != "every year"} {
append rem " $year"
}
return $rem
}
#---------------------------------------------------------------------------
# CreateReminder3 -- Create the first part of a type-3 reminder
# Arguments:
# w -- add reminder dialog window
# Returns: first part of reminder
#---------------------------------------------------------------------------
proc CreateReminder3 {w} {
global MonthNames DidOmit
set which [$w.ordinal cget -text]
set day [$w.wkday3 cget -text]
set mon [$w.mon3 cget -text]
set year [$w.year3 cget -text]
set rem "REM"
if {$which != "Last"} {
if {$which == "First"} {
append rem " 1"
} elseif {$which == "Second"} {
append rem " 8"
} elseif {$which == "Third"} {
append rem " 15"
} elseif {$which == "Fourth"} {
append rem " 22"
}
if {$day != "weekday"} {
append rem " $day"
} else {
append rem " [GetWeekend $w 0]"
}
if {$mon != "every month"} {
append rem " $mon"
}
if {$year != "every year"} {
append rem " $year"
}
} else {
if {$day != "weekday"} {
append rem " $day 1 --7"
} else {
append rem " 1 -1 OMIT [GetWeekend $w 1]"
set DidOmit 1
}
if {$mon != "every month"} {
set i [lsearch -exact $MonthNames $mon]
if {$i == 11} {
set i 0
} else {
incr i
}
append rem " [lindex $MonthNames $i]"
}
if {$year != "every year"} {
if {$mon == "December"} {
incr year
}
append rem " $year"
}
}
return $rem
}
#---------------------------------------------------------------------------
# GetWeekend -- returns a list of weekdays or weekend days
# Arguments:
# w -- add reminder dialog window
# wkend -- if 1, we want weekend. If 0, we want weekdays.
# Returns:
# list of weekdays or weekend-days
#---------------------------------------------------------------------------
proc GetWeekend {w wkend} {
global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday
global EnglishDayNames
set ret {}
foreach d $EnglishDayNames {
set v [set d$d]
if {$v == $wkend} {
lappend ret $d
}
}
return $ret
}
#---------------------------------------------------------------------------
# EditReminder -- allow user to edit what gets put in reminder file
# Arguments:
# rem -- current reminder
# Returns:
# edited version of rem
#---------------------------------------------------------------------------
proc EditReminder {rem} {
catch {destroy .edit}
global ModifyDialogResult
toplevel .edit
wm title .edit "Preview reminder"
wm iconname .edit "Preview reminder"
text .edit.t -width 80 -height 5 -relief sunken
.edit.t insert end $rem
frame .edit.f
button .edit.ok -text {Add to reminder file} -command "set ModifyDialogResult 1"
button .edit.cancel -text {Cancel} -command "set ModifyDialogResult 3"
pack .edit.t -side top -fill both -expand 1
pack .edit.f -side top -fill x -expand 1
pack .edit.ok .edit.cancel -in .edit.f -side left -fill x -expand 1
bind .edit <KeyPress-Escape> ".edit.cancel flash; .edit.cancel invoke"
set ModifyDialogResult 0
CenterWindow .edit
tkwait visibility .edit
set oldFocus [focus]
focus .edit.t
grab .edit
tkwait variable ModifyDialogResult
catch {focus $oldFocus}
set rem [.edit.t get 1.0 end]
catch {destroy .edit}
return $rem
}
#---------------------------------------------------------------------------
# SetWinAttr -- sets an attribute for a window
# Arguments:
# w -- window name
# attr -- attribute name
# val -- value to set it to
# Returns:
# $val
#---------------------------------------------------------------------------
proc SetWinAttr {w attr val} {
global attrPriv
set attrPriv($w-$attr) $val
}
#---------------------------------------------------------------------------
# GetWinAttr -- gets an attribute for a window
# Arguments:
# w -- window name
# attr -- attribute name
# Returns:
# Value of attribute
#---------------------------------------------------------------------------
proc GetWinAttr {w attr} {
global attrPriv
return $attrPriv($w-$attr)
}
#---------------------------------------------------------------------------
# WaitWinAttr -- wait for a window attribute to change
# Arguments:
# w -- window name
# attr -- attribute name
# Returns:
# Value of attribute
#---------------------------------------------------------------------------
proc WaitWinAttr {w attr} {
global attrPriv
tkwait variable attrPriv($w-$attr)
return $attrPriv($w-$attr)
}
#---------------------------------------------------------------------------
# BrowseForFile -- creates and operates a file browser dialog.
# Arguments:
# w -- dialog window.
# title -- dialog title
# oktext -- text for "OK" button
# showdots -- if non-zero, shows "dot" files as well.
# Returns:
# complete path of filename chosen, or "" if Cancel pressed.
#---------------------------------------------------------------------------
proc BrowseForFile {w title {oktext "OK"} {showdots 0} {filter "*"}} {
catch {destroy $w}
toplevel $w
wm title $w $title
# Global array to hold window attributes
global a${w}
SetWinAttr $w status busy
SetWinAttr $w showdots $showdots
frame $w.fileframe
frame $w.butframe
label $w.cwd -text [pwd]
entry $w.entry
label $w.masklab -text "Match: "
listbox $w.list -yscrollcommand "$w.scroll set"
scrollbar $w.scroll -command "$w.list yview"
button $w.ok -text $oktext -command "BrowseForFileOK $w"
button $w.cancel -text "Cancel" -command "BrowseForFileCancel $w"
entry $w.filter -width 7
$w.filter insert end $filter
pack $w.cwd $w.entry -side top -expand 0 -fill x
pack $w.fileframe -side top -expand 1 -fill both
pack $w.butframe -side top -expand 0 -fill x
pack $w.list -in $w.fileframe -side left -expand 1 -fill both
pack $w.scroll -in $w.fileframe -side left -expand 0 -fill y
pack $w.ok -in $w.butframe -side left -expand 1 -fill x
pack $w.cancel -in $w.butframe -side left -expand 1 -fill x
pack $w.masklab -in $w.butframe -side left -expand 0
pack $w.filter -in $w.butframe -side left -expand 1 -fill x
# Fill in the box and wait for status to change
BrowseForFileRead $w [pwd]
bind $w <KeyPress-Escape> "$w.cancel flash; $w.cancel invoke"
bind $w.list <Button-1> "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]"
bind $w.list <Double-Button-1> "$w.ok flash; $w.ok invoke"
bind $w.list <Return> "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]; $w.ok flash; $w.ok invoke"
bind $w.entry <Return> "$w.ok flash; $w.ok invoke"
bind $w.filter <Return> "BrowseForFileRead $w"
bind $w.entry <KeyPress> "CompleteFile $w"
bind $w.entry <KeyPress-space> "ExpandFile $w"
bindtags $w.entry "Entry $w.entry $w all"
bindtags $w.list "Listbox $w.list $w all"
CenterWindow $w
set oldFocus [focus]
tkwait visibility $w
focus $w.entry
set oldGrab [grab current $w]
grab set $w
WaitWinAttr $w status
catch {focus $oldFocus}
catch {grab set $oldGrab}
set ans [GetWinAttr $w status]
destroy $w
return $ans
}
proc CompleteFile {w} {
set index [lsearch [$w.list get 0 end] [$w.entry get]* ]
if {$index > -1} {
$w.list see $index
$w.list selection clear 0 end
$w.list selection set $index
}
}
proc ExpandFile {w} {
set stuff [$w.list curselection]
if {[string compare $stuff ""]} {
$w.entry delete 0 end
$w.entry insert end [$w.list get $stuff]
}
}
proc BrowseForFileCancel {w} {
SetWinAttr $w status {}
}
proc BrowseForFileOK {w} {
set fname [$w.entry get]
if {[string compare $fname ""]} {
# If it starts with a slash, handle it specially.
if {[string match "/*" $fname]} {
if {[file isdirectory $fname]} {
BrowseForFileRead $w $fname
return
} else {
SetWinAttr $w status $fname
return
}
}
if {[string match */ $fname]} {
set fname [string trimright $fname /]
}
if {[$w.cwd cget -text] == "/"} {
set fname "/$fname"
} else {
set fname "[$w.cwd cget -text]/$fname"
}
# If it's a directory, change directories
if {[file isdirectory $fname]} {
BrowseForFileRead $w $fname
} else {
SetWinAttr $w status $fname
}
}
}
#---------------------------------------------------------------------------
# BrowseForFileRead -- read the current directory into the file browser
# Arguments:
# w -- window name
# dir -- directory
# Returns:
# nothing
#---------------------------------------------------------------------------
proc BrowseForFileRead {w {dir ""}} {
# Save working dir
set cwd [pwd]
if {$dir == ""} {
set dir [$w.cwd cget -text]
}
if {[catch "cd $dir" err]} {
tk_dialog .error Error "$err" error 0 Ok
return
}
$w.cwd configure -text [pwd]
if {[GetWinAttr $w showdots]} {
set flist [glob -nocomplain .* *]
} else {
set flist [glob -nocomplain *]
}
set flist [lsort $flist]
set filter [$w.filter get]
if {$filter == ""} {
set filter "*"
}
$w.list delete 0 end
foreach item $flist {
if {$item != "." && $item != ".."} {
if {[file isdirectory $item]} {
$w.list insert end "$item/"
} else {
if {[string match $filter $item]} {
$w.list insert end $item
}
}
}
}
if {[pwd] != "/"} {
$w.list insert 0 "../"
}
cd $cwd
$w.entry delete 0 end
}
proc main {} {
wm withdraw .
puts "\nTkRemind Copyright (c) 1996 by David F. Skoll\n"
puts "Initializing..."
Initialize
puts "Creating calendar window..."
FillCalWindow
wm deiconify .
# Choose a reasonable initial height for the calendar window.
tkwait visibility .
set x [winfo width .]
wm geometry . ${x}x600
update
}
proc t {} {
catch { destroy .foo }
toplevel .foo
CreateModifyDialog .foo 20 1
}
main