#!/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.12 1997-01-16 04:14:32 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 stuff] == 0 } { 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 ".p.cancel flash; .p.cancel invoke" bind .p ".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 ".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 ".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 "$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 "$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 ".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 "$w.cancel flash; $w.cancel invoke" bind $w.list "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]" bind $w.list "$w.ok flash; $w.ok invoke" bind $w.list "$w.entry delete 0 end; $w.entry insert 0 \[selection get\]; $w.ok flash; $w.ok invoke" bind $w.entry "$w.ok flash; $w.ok invoke" bind $w.filter "BrowseForFileRead $w" bind $w.entry "CompleteFile $w" bind $w.entry "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