Remembering window position and state

Here is a program that remembers its position and state. If the program is resized and positioned on the screen and then closed, it will appear in the same position when it is run again. If the program is minimized (iconified) and closed, it will appear iconified when run again, and take its proper position when restored.

Also, if the program is maximized and closed, it will appear maximized when run again, but return to its proper position when unmaximized. This apparently only works under windows. On unix or MacOS, the window will reappear maximized, but will not go back to its original position when "unmaximized". MDL

 #!/usr/bin/env tclsh
 #
 # remember.tcl --
 #
 # This file implements a simple Tk program that remembers its window position,
 # size, and state.  It saves this information to remember.ini when it closes
 # and loads it again when started.
 #
 # Copyright (c) 2005 Michael Leonhard
 #
 # This file is placed in the public domain.
 # IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 
 # SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
 # SOFTWARE, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHOR HAS BEEN ADVISED OF
 # THE POSSIBILITY OF SUCH DAMAGE.
 #
 # THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 # PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS"
 # BASIS, AND THE AUTHOR HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
 # UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 
 package require Tk
 
 namespace eval rememberini {
     namespace export sourceIni
     namespace export writeIni
     
     set INIFILE "remember.ini"
     
     # rememberini::sourceIni --
     #
     # Sets default variable values and sources the remember.ini file.
     # Informs user if sourcing fails.  Ignores missing ini file.
     #
     # Expected variables:
     #    WindowGeom = WxH+X+Y where W=width, H=height, position=(X,Y)
     #    WindowState = normal | maximized | minimized
     proc sourceIni {} {
         variable INIFILE
         # defaults
         variable WindowGeom 500x400+100+100
         variable WindowState normal
         
         # check that the file is readable
         if { ![file readable $INIFILE]} { # no inifile, use defaults
             puts "ini file not found, using defaults"
             return
         }
         
         # try to source, giving a popup if there was an error
         # (so the user knows why his settings vanished)
         if { [catch {source $INIFILE}] } {
             tk_messageBox -title "Error" -type ok -icon error -message \
                 [join [list \
                     "Settings cannot be loaded.\n" \
                     "The file \"$INIFILE\" is corrupt.\n" \
                     "Using default settings."] ""]
             return
         }
     }
     
     # rememberini::sourceIni --
     #
     # Write the variables to the remember.ini as a Tcl script.
     proc writeIni {} {
         variable INIFILE
         variable WindowGeom
         variable WindowState
         
         # open the file for writing (truncates if file exists)
         if { [catch {set fc [open $INIFILE w]}] } {
             tk_messageBox -title "Error" -type ok -icon error -message \
                 "Unable to save settings to \"$INIFILE\"."
             return
         }
         
         # write file
         puts $fc "# remember.ini - settings for Remember"
         puts $fc "# This file is generated automatically when Remember closes."
         puts $fc "# Do not edit this file! Incorrect entries will crash the program."
         puts $fc [list set WindowGeom $WindowGeom]
         puts $fc [list set WindowState $WindowState]
         
         close $fc
     }
 }
 
 namespace eval remember {
     namespace export start_program
     
     # remember::unmapEvent --
     #
     # Handles <Unmap> event which occurs when the main window is minimized.
     # Saves window state.
     proc unmapEvent w {
         # do nothing if event is not for main window
         if { $w ne "." } {
             return
         }
         # window is minimized (hidden)
         variable WindowState minimized
         puts "MainState := minimized (hidden)"
         bell
         }
     
     # remember::configureEvent --
     #
     # Handles <Configure> event which occurs when window is reposition, 
     # resized, iconified, or maximized.  Saves new position if window is not
     # minimized or maximized.  Saves window state.
     proc configureEvent { w x y height width } {
         # do nothing if event is not for main window
         if { $w ne "." } {
             return
         }
         
         # window is iconified
         if { [wm state $w] eq "iconic" } {
             variable WindowState minimized
             puts "WindowState := minimized (iconic)"
             bell
             return
         }
         # window is maximized (Windows only)
         if { [wm state $w] eq "zoomed" } {
             variable WindowState maximized
             puts "WindowState := maximized"
             bell
             return
         }
         # window has been moved/resized
         if { [wm state $w] eq "normal" } {
             variable WindowState normal
             variable WindowGeom [join [list $width x $height + $x + $y] ""]
             puts "WindowState := normal, geom:=$WindowGeom"
             return
         }
         # execution should never reach here
         puts "unknown window state [wm state .]"
     }
     
     proc makeWindow {} {
         set w .
         
         # Set window geometry and maximize/minimized state
         variable WindowState
         variable WindowGeom
         if { $WindowState eq "minimized" } { wm iconify $w}
         wm geometry $w $WindowGeom
         if { $WindowState eq "maximized" } { wm state $w zoomed}
         
         wm title $w "Remember"
         wm iconname $w "Remember"
         
         # event bindings
         bind . <Configure> "remember::configureEvent %W %x %y %h %w"
         bind . <Unmap> "remember::unmapEvent %W"
         wm protocol $w WM_DELETE_WINDOW "remember::close_program $w"
         
         # window contents (label)
         label .message -text [join [list \
             "Remember - a Tcl/Tk program that remembers\n" \
             "its window position, size, and state."] ""]
         pack .message -expand yes -fill both
     }
     
     proc start_program {} {
         # read ini file and set variables in the remember namespace
         rememberini::sourceIni
         variable WindowState $rememberini::WindowState
         variable WindowGeom $rememberini::WindowGeom
         
         makeWindow
     }
     
     proc close_program { w } {
         # set variables in rememberini namespace and write ini file
         variable WindowGeom
         variable WindowState
         set rememberini::WindowGeom $WindowGeom
         set rememberini::WindowState $WindowState
         rememberini::writeIni
         
         destroy $w
     }
 }
 
 remember::start_program