uniquename - 2014mar08

On my 'bio' page at uniquename, I mentioned that I have not done any socket programming with Tcl-Tk. But to provide ready-access to some samples of that type of Tcl/Tk programming, I provided a list of links to socket/network programming code 'snippets' on this site --- including the link Daily Dilbert.

On my 'to-do' list on the uniquename page, for several months, I have had the item 'tkGetToons - to fetch some of my favorite cartoons from Internet sites that offer them --- for example, Agnes, Baby Blues, Bizarro, Dilbert, Doonesbury, Rhymes with Orange, Speed Bump, Zits, and perhaps the sites of some editorial-page political cartoonists'.

Thanks to a code 'snippet', contributed by 's_m' (Stefano Mimmi) in 2013, on the page Daily Dilbert, that generalized comics-retrieval utility is the subject of this page.

---

THE GOALS

I tried out the Dilbert-code of 's_m' and was pleased to find that it worked.

(The Tcl 'http' package was available in the Tcl-Tk 8.5 installation on my desktop computer. However, I also have a little Acer netbook computer that I often use to test Tk scripts in its Linux environment. For some reason, unbeknownst to me at this time, the Tcl-Tk 8.5 installation on that netbook computer does not include the 'http' package. So I did all my testing,as well as development, on my desktop computer.)

The Dilbert-code of 's_m' retrieved the Dilbert 'cartoon-of-the-day'. Super! However --- I wanted to do more than that --- I wanted to be able to retrieve any of the old Dilbert cartoons from a Dilbert archive site.

And not just Dilbert. I wanted to be able to retrieve old cartoons from archive sites like those of Doonesbury, Rhymes with Orange, Speed Bump, and Zits --- and allow for easily adding more sites in the future.

In particular, my goals for the Tcl-Tk script were:

** provide a GUI for selecting a comic archive site (via a 'listbox' on the GUI)

** allow the user to specify an 'index' to a particular comic (typically year-month-day)

** put the image on a SCROLLABLE Tk canvas (to allow for large cartoons)

** provide a 'SaveAsGIF' button to allow for easily saving the image to local computer storage (similar to the 'Save' option that 's_m' had provided)

** provide a 'Help' button to explain the utility

** make the interface easy-to-use (mostly mouse-clicks rather than keyboard keying)


SCREENSHOT OF THE GUI

On the basis of those goals, I ended up with the GUI seen in the following image.

getComics_Doonesbury_2012-03-06_screenshot_838x287.jpg

Note the entry fields for year-month-day. Fortunately, all the archive sites that I have implemented so far allow for specifying a particular comic by year-month-day.

If necessary, in the future, more widgets could be added to the GUI to handle different methods of 'indexing' archived comics. And the 'indexing' widgets on the GUI could be activated/deactivated according to the archive site selected by the user.

NOTE: On either side of the Day entry field, there are buttons labeled '<' and '>'. Those buttons are to provide a way to easiliy decrement and increment the day-number --- by simply clicking on those buttons, rather than keying into the Day entry field.

Those decrement/increment buttons call on decrement/increment procs that handle transitioning into a different month or year. The procs take into account whether a month has 30 or 31 or 28 or 29 days.

Note also the scrollbars on the 'canvas' widget. They come in handy for large comic images like the following.

getComics_Zits_2012-03-06_screenshot_1020x388.jpg

And

getComics_SpeedBump_2012-03-06_screenshot_604x423.jpg

---

TYPICAL SEQUENCE OF OPERATIONS WITH THE GUI

A 'listbox' on the GUI offers a list of comic archive sites from which to choose.

Step 1:

Click on a line of the listbox to choose a web-site. The web-site name is followed by a separation character (#) after which there may be some description of the web-site.

   (A '#' in column 1 of the line makes the line a comment line
    --- for example, to allow for indicating comics for which
    retrieval has not been implemented, and reminding the user
    to keep looking for a method to retrieve those comics.)

Step 2:

Date fields (year,month,day) provide the user a way to select a specific comic to retrieve --- if a web site supports retrieval by date.

      (If some sites offer a different way, from year-month-day,
       of identifying individual comics, this code could
       activate a different widget --- or widgets --- by which
       to specify a comic --- according to the site selected
       by the user.

       In that case, 'comic selection widgets' on the GUI may have
       to be activated/deactivated according to the site selected.)

The '<' and '>' buttons on either side of the Day entry field facilitate quickly scanning through cartoons of a given site, by successive days.

You can skip to a different year or month by simply entering a new number in the Year or Month entry fields.

Step 3:

After each date is set, click on the 'Retrieve' button to display the corresponding comic.

The 'Retrieve' button causes a series of Tcl 'http' commands to be issued, commands such as

      - http::config
      - http::geturl
      - http::data
      - http::cleanup

to get the comic file (GIF) data and display it in the canvas.

---

Error/Warning Popups:

Occasionally, at a comic archive site, there may not be a comic for a specified date. In that case, you may see an error message popup in a window. Simply dismiss the window and try another date.

Also, if your network connection is not active, you will get a popup that reminds you that you need to establish your internet connection.


The code

Below, I provide the Tk script code for this 'get-comics' utility.

I follow my usual 'canonical' structure for Tk code for this Tk script:

  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts-for-widgets, widget-geometry-parms,
     text-array-for-labels-etc, win-size-control).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.
              Within each frame, define ALL the widgets.
              Then pack the widgets.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.

This Tk coding structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use).

I call your attention to step-zero. One thing that I started doing in 2013 is use of a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.

In this script, motivated by the many instances of popup messages in the code, I included message text in the text-array.


Experimenting with the GUI

As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets.

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window.

I think that I have used a pretty nice choice of the 'pack' parameters. The label and button and year-month-day entry widgets stay fixed in size and relative-location if the window is re-sized --- while the site entry widget expands/contracts horizontally whenever the window is re-sized horizontally.

Also, the WIDTH of the 'listbox' stays FIXED, while the 'canvas' can expand both horizontally (as well as vertically) --- allowing more room for the canvas (that is, large comics) if the user expands the window.

You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want.

___

Additional experimentation: You might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.

If you find the gray 'palette' of the GUI is not to your liking, you can change the value of the RGB parameter supplied to the 'tk_setPalette' command near the top of the code.


Some features in the code

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do.

The main procs are

   'get_site_selectedInListbox' - called via a button1-release binding on
                                  the listbox widget.

   'Retrieve'                   - called by the 'Retrieve' button.

The following procs are called by the 'Retrieve' proc, depending on which site(/comic-strip) was selected from the listbox widget.

         'get_babyblues_comic'
         'get_bizzaro_comic'
         'get_dilbert_comic'
         'get_doonesbury_comic'
         'get_rhymeswithorange_comic'
         'get_speedbump_comic'
         'get_zits_comic'

And there are the following 'utility' procs.

   'show_image_forPicURL'   - called by the following 'get_*_comic' procs.
                              (To avoid repeating code that is common
                               to all the procs --- after the URL of the
                               image file has been determined.)

   'save_image_toLocalFile' - called via the 'SaveAsGIF' button.

   'date_increment'         - called via the '>' button
   'date_decrement'         - called via the '<' button

   'popup_msgVarWithScroll' - called via the 'Help' button --- and
                              used for warning/error messages.

---

The 'http' package routines

   http::config
   http::geturl
   http::data
   http::cleanup

are used in the 'get_*_comic' procs and the 'show_image_forPicURL' proc.


It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of Daniel Tosh providing people who have posted videos on the internet the opportunity for 'web redemption'.


 Code for Tk script 'get_comics.tk' :
#!/usr/bin/wish -f
##
## SCRIPT: get_comics.tk
##
## PURPOSE:  This Tk script offers a GUI on which the user can
##             - choose a comics site (such as dilbert.com or doonesbury.com)
##           and
##             - use buttons and other widgets on the GUI to retrieve
##               one comic at a time from the site.
##
##      A particular comic at a site is selected by a date (or some other
##      type of 'index'), where the date (or other index) is user-selected
##      via widgets on the GUI.      
##
##      A Tk 'canvas' widget on the GUI is used to display the comic
##      --- if it is a GIF (or PNG) file.
##
##           (If the site stores the comics in another format, such as
##            JPEG-JFIF, this code could run an 'external' utility such as
##            ImageMagick 'convert' to convert the retrieved file
##            to a GIF file --- and display that file.)
##
##      A 'SaveAsGIF' button on the GUI offers the user the option to
##      save the comic as a file on the user's local computer storage.
##
##           (This code automatically puts the file in the /tmp directory,
##           from which the user can transfer the file to another directory.
##           The temporary directory location can be changed by changing 
##           the setting of the 'DIRtemp' variable in this code.)
##
##+###################
## METHOD OF OPERATION:
##
##      A 'listbox' on the GUI offers a list of comic sites from
##      which to choose. 
##
##      Date fields (year,month,day) provide the user a way to
##      select a specific comic to retrieve --- if a web site
##      supports retrieval by date.
##
##           (If some sites offer a different way of identifying
##            the cartoons, this code may activate a different
##            widget --- or widgets --- by which to specify a comic
##            --- depending on the site selected by the user.)
##
##      A 'Retrieve' button causes a series of Tcl 'http' commands
##      to be issued, commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
##     A 'package require http' statement is used to determine
##     whether the Tcl 'http' package is available.
##
##+##############
## THE GUI LAYOUT:
##
## -------------------------------------------------------------------------------
## GetComics - from some comics archive web sites
## [window title]
## -------------------------------------------------------------------------------
## {Exit} {Help} Comic site: _________________________ [disabled entry widget]
##               Year: ____ Month: __ Day: __  {Retrieve} {SaveAsFile}
## _____________________ _____________________________________________________ 
## | babyblues.com     A |                                                   A
## | bizarrocomics.com | |                                                   |
## | dilbert.com       | |                                                   |
## | doonesbury.com    | |                                                   |
## | rhymeswithorange.c| |            Canvas area in which                   |
## | speedbump.com     | |                to display a                       |
## | zitscomics.com    | |              retrieved comic.                     |
## |                   | |                                                   |
## |                   | |                                                   |
## |                   | |                                                   |
## |                   V |                                                   V
## <-------------------> <--------------------------------------------------->
## 
## In the 'text-sketch' of the GUI:
##
## - Square brackets indicate a comment (not to be shown on the GUI).
## - Braces indicate a Tk 'button' widget.
## - A colon with text to the left indicates a Tk 'label' widget.
## - Underscores indicate a Tk 'entry' widget.
## - Capital-O indicates a Tk 'radiobutton' widget (if any).
## - Capital-X indicates a Tk 'checkbutton' widget (if any).
## - A line (hyphens) with an 'arrow-head' at each end indicates a Tk 'scale' widget.
##
## - A combination of vertical-bar characters and underscore characters, that
##   outline a rectangular shape, are used to indicate either a 'listbox' or
##   a 'canvas' widget. 'Arrow-heads' at the right of the box shape indicate
##   vertical scroll-bars there. 'Arrow-heads' at the bottom of the box shape
##   indicate horizontal scroll-bars there.
## 
## The scroll-bar 'arrow-heads' are drawn as follows:
## 
## - an UP  arrow-head is drawn with capital-A.
## - a DOWN arrow-head is drawn with capital-V.
## - a LEFT  arrow-head is drawn with a less-than sign.
## - a RIGHT arrow-head is drawn with a greater-than sign.
## 
## And the arrow-heads are joined by hyphens, rather than underscores.
## 
## 
## The year,month,day entry fields could be initialized with today's date.
## 
## This GUI will contain about:
## 
##    4 'button' widgets
##    2 'label'  widgets (or more)
##    2 'entry'  widgets
##    1 'listbox' widget
##    1 'canvas' widget
##    0 'scale'  widgets
##    0 'radiobutton' widgets
##    0 'checkbutton' widgets
##    0 'text' widgets
##
##+######################################################################
## 'CANONICAL' STRUCTURE OF THIS CODE:
##
##  0) Set general window parms (win-name,win-position,color-scheme,fonts,
##     widget-geometry-parms,text-array-for-labels-etc,win-size-control).
##  1a) Define ALL frames (and sub-frames, if any).
##  1b) Pack   ALL the frames and sub-frames.
##  2) Define all widgets in the frames, frame by frame.
##     When all widgets are defined for a frame, pack them.
##
##  3) Define keyboard and mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (typically, with one or two procs),
##     if needed.
##
## The code-structure detail for this particular script:
##
##  1a) Define ALL frames:
## 
##      Top-level : '.fRbuttons'  '.fRoptions'   '.fRbottom'
##
##      Sub-frames: '.fRbottom.fRsites' and '.fRbottom.fRdisplay'
##
##  1b) Pack ALL frames.
##
##  2) Define all widgets in the frames (and pack them):
##
##       - In '.fRbuttons' frame:
##            2 button widgets ('Exit','Help') and
##            a label-and-entry widget pair (to display the currently
##                                           selected comics site)
##
##       - In '.fRoptions' frame:
##            3 label-and-entry widget pairs (for year, month, day) and
##            2 button widgets ('Retrieve' and 'SaveAsGIF')
##
##       - In '.fRbottom.fRsites' frame:
##            1 listbox widget, with x-y scrollbars
##
##       - In '.fRbottom.fRdisplay' frame:
##            1 canvas widget, with x-y scrollbars
##
##                     (The widgets in the '.fRoptions' frame could be
##                      packed/activated depending on the web site chosen
##                      by the user.)
##
##  3) Define BINDINGS: 
##       - a button1-release binding on the listbox
##          (See the BINDINGS section to see if
##           any other bindings were eventually defined.)
##
##  4) Define PROCS:
##
##     'get_site_selectedInListbox' - called via a button1-release binding on
##                                    the listbox widget.
##
##     'Retrieve'                  - called by the 'Retrieve' button.
##
##     The following procs are called by the 'Retrieve' proc, depending on
##     which site(/comic-strip) was selected from the listbox widget.
##
##         'get_babyblues_comic'
##         'get_bizzaro_comic'
##         'get_dilbert_comic'
##         'get_doonesbury_comic'
##         'get_rhymeswithorange_comic'
##         'get_speedbump_comic'
##         'get_zits_comic'
##
##        Of course, other comic sites could be added to the listbox,
##        along with adding a corresponding 'get' proc.
##
##        NOTE: These procs will need to be changed as the sites change
##              the way they store their comics.
##
##     'save_image_toLocalFile' - called via the 'SaveAsGIF' button.
##
##     'popup_msgVarWithScroll' - for a Help button --- and for warning/error
##                                messages.
##
##     See the (top of the) PROCS section of the code for a more detailed list
##     and description of the procs.
##
##  5) Additional GUI initialization:
##
##         The 'DIRtemp' variable is set here. Change this 'set'
##         statement to change where the image files will be created.
##
##+########################################################################
## DEVELOPED WITH:
##   Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala).
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##                                  showed   8.5 8.5   on Ubuntu 9.10.
##+#######################################################################
## MAINTENANCE HISTORY:
## Created by: Blaise Montandon 2014feb06 Started coding based on a 2013
##                                        Tcl-Tk script by 's_m' at
##                                        http://wiki.tcl.tk/8899.
##                                        Got the GUI up, with dummied
##                                        out procs.
## Changed by: Blaise Montandon 2014mar07 Added scrollbars to canvas.
##                                        Started developing and testing
##                                        the procs.
##                                        Added '>' & '<' buttons.
## Changed by: Blaise Montandon 2014mar08 Added 'popupMSG' text to the
##                                        $aRtext array, for use in the
##                                        'get_*_comic' procs.
##                                        Added var 'fileMIDNAME' for use
##                                        in the proc for 'SaveAsGIF'. 
##+#######################################################################

##+#######################################################################
## Set window parms --- WIN-TITLE and WIN-POSITION.
##+#######################################################################

wm title    . "GetComics - from some comics archive web sites"
wm iconname . "tkGetComics"

wm geometry . +15+30


##+#########################################################
## Set the COLOR SCHEME (palette) for the window ---
## and some colors for its widgets --- such as scale widgets.
##+#########################################################

## gray
set R255pal 210
set G255pal 210
set B255pal 210

## sandy brown
# set R255pal 244
# set G255pal 164
# set B255pal 96

set hexCOLORpal [format "#%02X%02X%02X" $R255pal $G255pal $B255pal]
tk_setPalette "$hexCOLORpal"

set BKGD_listbox "#f0f0f0"
set BKGD_entry   "#f0f0f0"
# set BKGD_radbutt "#f0f0f0"


##+########################################################
## SET 'FONT-NAMES'.
##
## We use a VARIABLE-WIDTH FONT for LABEL and BUTTON widgets
## --- and the numeric values shown by SCALE widgets.
##
## We generally use a FIXED-WIDTH FONT for the text in
## ENTRY and LISTBOX widgets.
##
## We MAY use a FIXED-WIDTH FONT for help text in a TEXT
## widget (so that any columns in the text stay lined up).
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -14 \
   -weight bold \
   -slant roman

font create fontTEMP_SMALL_varwidth \
   -family {comic sans ms} \
   -size -10 \
   -weight bold \
   -slant roman


## Some other possible (similar) variable width fonts:
##  Arial
##  Bitstream Vera Sans
##  DejaVu Sans
##  Droid Sans
##  FreeSans
##  Liberation Sans
##  Nimbus Sans L
##  Trebuchet MS
##  Verdana

font create fontTEMP_fixedwidth  \
   -family {liberation mono} \
   -size -14 \
   -weight bold \
   -slant roman

## Some other possible fixed width fonts (esp. on Linux):
##  Andale Mono
##  Bitstream Vera Sans Mono
##  Courier 10 Pitch
##  DejaVu Sans Mono
##  Droid Sans Mono
##  FreeMono
##  Nimbus Mono L
##  TlwgMono


##+###########################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. padding and borderwidths for Buttons and Labels)
##+###########################################################

## For LABEL widgets:

set PADYpx_label 0
set PADXpx_label 0

# set BDwidthPx_label 0
  set BDwidthPx_label 2


## For BUTTON widgets:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2


## For the LISTBOX widget:

set initListboxWidthChars 25
set initListboxHeightChars 7
set BDwidthPx_listbox 2


## For ENTRY widgets:

set BDwidthPx_entry 2


## For TEXT widgets:

set BDwidthPx_text 2


## CANVAS widget geom settings:

set initCanWidthPx  300
set initCanHeightPx 100

# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0


##+####################################################################
## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI.
##     NOTE: This can aid INTERNATIONALIZATION. This array can
##           be set according to a nation/region parameter.
##+####################################################################

## if { "$VARlocale" == "en"}

## For 'fRbuttons' frame:

set aRtext(buttonEXIT) "Exit"
set aRtext(buttonHELP) "Help"

## We use leading spaces to provide left-padding,
## separating the SITE label from the HELP button.
set aRtext(labelSITE)  "    Comic Site:"

## For 'fRoptions' frame:

## We use leading spaces to provide left-padding,
## separating the YEAR label from the left side of the window.
set aRtext(labelYEAR)   "                Comic Year:"
set aRtext(labelMONTH)  "Month:"
set aRtext(labelDAY)    "Day:"

set aRtext(buttDATEINCR) ">"
set aRtext(buttDATEDECR) "<"

set aRtext(buttRETRIEVE) "Retrieve"
set aRtext(buttSAVE)     "SaveAsGIF"

## For popup-msg in the 'show_image_forPicURL' proc:

set aRtext(popupMSGhttpData1) \
   "The 'http::data' did not return GIF data. First 1000 chars are:"
set aRtext(popupMSGhttpData2)  \
   "*** NOT PUTTING A NEW PICTURE ON THE CANVAS! ***"

## For popup-msgs in the 'get_*_comic' procs:

set aRtext(popupMSGhttpGeturl1) \
   "The 'http::geturl' command failed on URL"
set aRtext(popupMSGhttpGeturl2) \
   "*** IS YOUR NETWORK CONNECTION UP?? ***"

## For popup-msg in the 'save_image_toLocalFile' proc:

set aRtext(popupMSGsavedGIF1) \
   "The GIF file was saved with filename:"

## For popup-msg in the 'save_image_toLocalFile' proc:

set aRtext(popupMSGhttpREQUIRE) \
   "The 'http' package is not available."

## For popup-err-msgs:

set aRtext(popupMSGerrMsg) \
   "Error message: "

## END OF  if { "$VARlocale" == "en"}


##+########################################################
## Set a MINSIZE of the window (roughly) -- according to the
## approx max WIDTH of the buttons in the 'fRbuttons' frame
## --- and according to the approx HEIGHT of the 4 frames.
##+########################################################

set minWinWidthPx [font measure fontTEMP_varwidth \
   " $aRtext(buttonEXIT) $aRtext(buttonHELP) $aRtext(labelSITE) \
___________________________________________"]

## Add some pixels to account for right-left-side window decoration
## (about 8 pixels), about 4 widgets x 4 pixels/widget for borders/padding.

set minWinWidthPx [expr {24 + $minWinWidthPx}]


## MIN HEIGHT --- allow at least
##     1 char   high for the 'fRbuttons' frame
##     1 char   high for the 'fRoptions' frame
##     7 chars  high for the listbox in the 'fRbottom' frame

set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {9 * $charHeightPx}]

## Add some pixels to account for top-bottom window decoration
## (about 28 pixels) and frame/widget padding vertically
## (about 4 pixels/frame x 3 frames).

set minWinHeightPx [expr {40 + $minWinHeightPx}]

## FOR TESTING:
#   puts "minWinWidthPx = $minWinWidthPx"
#   puts "minWinHeightPx = $minWinHeightPx"

wm minsize . $minWinWidthPx $minWinHeightPx

## We allow the window to be resizable and we pack the canvas
## (and listbox?) with '-fill both' so that the canvas (and listbox?)
## can be enlarged by enlarging the window.

## If you want to make the window un-resizable, 
## you can use the following statement.

# wm resizable . 0 0


##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : '.fRbuttons'  '.fRoptions'   '.fRbottom' 
##
##   Sub-frames: '.fRbottom.fRsites' and '.fRbottom.fRdisplay'
##+################################################################

## FOR TESTING of expansion of frames (esp. during window expansion):
#  set RELIEF_frame raised
#  set BDwidth_frame 2

set RELIEF_frame flat
set BDwidth_frame 0

frame .fRbuttons   -relief $RELIEF_frame  -borderwidth $BDwidth_frame
frame .fRoptions   -relief $RELIEF_frame  -borderwidth $BDwidth_frame
frame .fRbottom    -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRbottom.fRsites   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

# frame .fRbottom.fRdisplay -relief $RELIEF_frame  -borderwidth $BDwidth_frame
  frame .fRbottom.fRdisplay -relief raised         -borderwidth 2

##+########################################
## PACK the top-level FRAMES, top to bottom. 
## Allow the '.fRbottom' frame to expand.
##+########################################

pack .fRbuttons \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRoptions \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRbottom \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+####################################
## PACK the SUB-FRAMES, in '.fRbottom'
## --- left to right. 
##+####################################

pack .fRbottom.fRsites \
   -side left \
   -anchor nw \
   -fill y \
   -expand 0

pack .fRbottom.fRdisplay \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

## NOTE: We want to make sure the canvas has
## plenty of room, esp. if the user expands the
## window horizontally.
##
## Hence we want the canvas to expand horizontally,
## but not the listbox. So we use '-fill y' for the
## pack parameter of the listbox, rather than '-fill both'.

## OK. ALL frames are defined and packed.
##
## Ready to define widgets.


##+################################################################
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES,
## frame-by-frame. When all widgets for a frame are defined,
## pack them in the frame.
##+################################################################
##+################################################################


##+################################################################
## IN THE '.fRbuttons' frame -
## DEFINE some BUTTONS (Exit,Help) and a LABEL-and-ENTRY
## widget pair.
##+################################################################

button .fRbuttons.buttEXIT \
   -text "$aRtext(buttonEXIT)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {exit}

button .fRbuttons.buttHELP \
   -text "$aRtext(buttonHELP)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {popup_msgVarWithScroll .topHelp "$HELPtext"}

label .fRbuttons.labelSITE \
   -text "$aRtext(labelSITE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_label

# set ENTRYsite ""
  set ENTRYsite "dilbert.com"

entry .fRbuttons.entrySITE \
   -textvariable ENTRYsite \
   -font fontTEMP_fixedwidth \
   -bg "$BKGD_entry" \
   -relief sunken \
   -bd $BDwidthPx_entry

.fRbuttons.entrySITE configure -state disabled

#   -width 20 \

## Pack ALL the widgets in frame 'fRbuttons'.

pack .fRbuttons.buttEXIT \
     .fRbuttons.buttHELP \
     .fRbuttons.labelSITE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

pack .fRbuttons.entrySITE \
   -side left \
   -anchor w \
   -fill x \
   -expand 1


##+################################################################
## IN THE '.fRoptions' frame -
## DEFINE some LABEL-and-ENTRY widget pairs (for year,month,day)
## and some BUTTONS (Retrieve, SaveAsGIF).
##+################################################################

## YEAR:

label .fRoptions.labelYEAR \
   -text "$aRtext(labelYEAR)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_label

## Variable VARyear is initialized in the 'Additional GUI Initialization'
## section at the bottom of this script.

entry .fRoptions.entryYEAR \
   -textvariable VARyear \
   -font fontTEMP_fixedwidth \
   -width 4 \
   -bg "$BKGD_entry" \
   -relief sunken \
   -bd $BDwidthPx_entry


## MONTH:

label .fRoptions.labelMONTH \
   -text "$aRtext(labelMONTH)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_label

## Variable VARmonth is initialized in the 'Additional GUI Initialization'
## section at the bottom of this script.

entry .fRoptions.entryMONTH \
   -textvariable VARmonth \
   -font fontTEMP_fixedwidth \
   -width 2 \
   -bg "$BKGD_entry" \
   -relief sunken \
   -bd $BDwidthPx_entry


## DAY:

label .fRoptions.labelDAY \
   -text "$aRtext(labelDAY)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief flat \
   -bd $BDwidthPx_label

## Variable VARday is initialized in the 'Additional GUI Initialization'
## section at the bottom of this script.

entry .fRoptions.entryDAY \
   -textvariable VARday \
   -font fontTEMP_fixedwidth \
   -width 2 \
   -bg "$BKGD_entry" \
   -relief sunken \
   -bd $BDwidthPx_entry

## '>' & '<' BUTTONS:

button .fRoptions.buttDATEINCR \
   -text "$aRtext(buttDATEINCR)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {date_increment}

button .fRoptions.buttDATEDECR \
   -text "$aRtext(buttDATEDECR)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {date_decrement}


## RETRIEVE & SAVE BUTTONS:

button .fRoptions.buttRETRIEVE \
   -text "$aRtext(buttRETRIEVE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {Retrieve}

button .fRoptions.buttSAVE \
   -text "$aRtext(buttSAVE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {save_image_toLocalFile}

## Pack ALL the widgets in frame 'fRoptions'.

pack .fRoptions.labelYEAR \
     .fRoptions.entryYEAR \
     .fRoptions.labelMONTH \
     .fRoptions.entryMONTH \
     .fRoptions.labelDAY \
     .fRoptions.buttDATEDECR \
     .fRoptions.entryDAY \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

## Pack the day increment button with some right-padding
## --- to separate from the RETRIEVE & SAVE buttons.

pack .fRoptions.buttDATEINCR \
   -side left \
   -anchor w \
   -fill none \
   -expand 0 \
   -padx {0 40}


pack .fRoptions.buttRETRIEVE \
     .fRoptions.buttSAVE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+######################################################
## In FRAME '.fRbottom.fRsites' -
## DEFINE-and-PACK a LISTBOX widget,
## with scrollbars --- for a list of web sites.
##+######################################################

listbox .fRbottom.fRsites.listbox \
   -width $initListboxWidthChars \
   -height $initListboxHeightChars \
   -font fontTEMP_fixedwidth \
   -relief raised \
   -borderwidth $BDwidthPx_listbox \
   -state normal \
   -yscrollcommand ".fRbottom.fRsites.scrbary set" \
   -xscrollcommand ".fRbottom.fRsites.scrbarx set"

scrollbar .fRbottom.fRsites.scrbary \
   -orient vertical \
   -command ".fRbottom.fRsites.listbox yview"

scrollbar .fRbottom.fRsites.scrbarx \
   -orient horizontal \
   -command ".fRbottom.fRsites.listbox xview"


##+##########################################################
## INSERT WEB-SITE LISTBOX ENTRIES that hold web-site URLs
## --- each followed by a separator character (#) ---
## followed by an arbitrary amount of descriptive info.
##
## By adding a '#' at the beginning of each line, we signal
## that that web-site is not implemented yet --- but it
## is being considered for addition at some future date.
##+##########################################################

## Make sure the listbox is empty.

.fRbottom.fRsites.listbox delete 0 end

## New WEB-SITES are to be added/activated here:

# .fRbottom.fRsites.listbox insert end "## babyblues.com # the Baby Blues comics"
.fRbottom.fRsites.listbox insert end "## For Baby Blues comics, a suitable archive site and retrieval method has not been found yet."

# .fRbottom.fRsites.listbox insert end "## bizarrocomics.com #  the Bizarro comics"
.fRbottom.fRsites.listbox insert end "## For Bizarro comics, a suitable archive site and retrieval method has not been found yet."

.fRbottom.fRsites.listbox insert end "dilbert.com #  the Dilbert comics"
.fRbottom.fRsites.listbox insert end "gocomics.com/doonesbury/ #  the Doonesbury comics"
.fRbottom.fRsites.listbox insert end "rhymeswithorange.com # the Rhymes With Orange comics"
.fRbottom.fRsites.listbox insert end "gocomics.com/speedbump/ # the Speed Bump comics"
.fRbottom.fRsites.listbox insert end "zitscomics.com # the Zits comics"



##+###############################################################
## Get the number of web-sites loaded into the listbox. We may use
## this variable to show users how many are in the listbox, some
## of which may be out of sight.
##+###############################################################

set numSites [.fRbottom.fRsites.listbox index end]


##+####################################
## Pack the listbox and its scrollbars.
##+####################################

pack .fRbottom.fRsites.scrbary \
   -side right \
   -anchor e \
   -fill y \
   -expand 0

pack .fRbottom.fRsites.scrbarx \
   -side bottom \
   -anchor s \
   -fill x \
   -expand 0

## We need to pack the listbox AFTER
## the scrollbars, to get the scrollbars
## positioned properly --- BEFORE
## the listbox FILLS the pack area.

pack .fRbottom.fRsites.listbox \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+######################################################
## In FRAME '.fRbottom.fRdisplay' -
## DEFINE-and-PACK a CANVAS WIDGET, on which to display
## images of comic files.
##
## We highlightthickness & borderwidth of the canvas to
## zero, as suggested on page 558, Chapter 37, 'The Canvas
## Widget', in the 4th edition of the book 'Practical
## Programming in Tcl and Tk'.
##
## We provide x-y scrollbars on the canvas in case some
## of the comics are so large (horizontally or vertically)
## that they exceed the size of the monitor screen.
##+#######################################################

canvas .fRbottom.fRdisplay.can \
   -width 300 \
   -height 100 \
   -relief flat \
   -borderwidth 0 \
   -highlightthickness 0 \
   -yscrollcommand ".fRbottom.fRdisplay.scrolly set" \
   -xscrollcommand ".fRbottom.fRdisplay.scrollx set"

## FOR TESTING:
#   -relief raised \
#   -borderwidth 2 \
## OTHERWISE:
#   -relief flat \
#   -borderwidth 0 \

## NOT NEEDED?
#   -width  $initCanWidthPx \
#   -height $initCanHeightPx \

scrollbar .fRbottom.fRdisplay.scrolly \
   -orient vertical \
   -command ".fRbottom.fRdisplay.can yview"

scrollbar .fRbottom.fRdisplay.scrollx \
   -orient horizontal \
   -command ".fRbottom.fRdisplay.can xview"

##########################################################
## PACK the widgets in frame '.fRbottom.fRdisplay'.
##
## NOTE:
## NEED TO PACK THE SCROLLBARS BEFORE THE CANVAS WIDGET.
## OTHERWISE THE CANVAS WIDGET TAKES ALL THE FRAME SPACE.
##########################################################

pack .fRbottom.fRdisplay.scrolly \
   -side right \
   -anchor e \
   -fill y \
   -expand 0

pack .fRbottom.fRdisplay.scrollx \
   -side bottom \
   -anchor s \
   -fill x \
   -expand 0

## !!!NEED TO USE '-expand 0' FOR THE X AND Y SCROLLBARS, so that
## the canvas is allowed to fill the remaining frame-space nicely
## --- without a gap between the canvas and its scrollbars.

pack .fRbottom.fRdisplay.can \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

## Alternatives for packing the canvas:
#  -side top \
#        -anchor center \
##
#  -side left \
#        -anchor nw \
## Either one seems to work OK.

##+#####################################################################
## END OF SECTIONS TO SETUP THE GUI.
## ALL FRAMES and WIDGETS are DEFINED-and-PACKED.
##
## We are now ready to define BINDINGS and PROCS, and do some
## additional initialization of the GUI.
##+#####################################################################


##+#######################################################################
## DEFINE BINDINGS SECTION, including:
##    - button1-release on the listbox
##+#######################################################################

bind .fRbottom.fRsites.listbox <ButtonRelease-1>  {get_site_selectedInListbox}



##+######################################################################
## DEFINE PROCS SECTION.
##
##  Procs here include:
##
##     'get_site_selectedInListbox' - called via a button1-release binding on
##                                    the listbox widget.
##                                    Puts the selected site-ID into a
##                                    disabled entry widget on the GUI, via
##                                    the ENTRYsite variable.
##
##     'Retrieve'                  - called by the 'Retrieve' button.
##                                   To execute one of the following 'get'
##                                   procs, chosen according to the site-ID
##                                   in the ENTRYsite variable.
##
##  'show_image_forPicURL'         - called by the following 'get_*_comic' procs.
##                                   For a given fully-qualified URL for a picture
##                                   file (GIF or whatever), this proc loads the
##                                   picture data into a Tk 'photo' image structure
##                                   and places the image structure on the canvas.
##
##     The following procs are called by the 'Retrieve' proc, depending on
##     which site(/comic-strip) was selected from the listbox widget.
##
##         'get_babyblues_comic'
##         'get_bizzaro_comic'
##         'get_dilbert_comic'
##         'get_doonesbury_comic'
##         'get_rhymeswithorange_comic'
##         'get_speedbump_comic'
##         'get_zits_comic'
##
##        Of course, other comic sites could be added to the listbox,
##        along with adding a corresponding 'get_*_comic' proc.
##
##        These 'get' procs are used to find the appropriate picture file
##        URL in order to fetch the image data from the site and load it
##        into a Tk 'photo' image structure and put that photo-image
##        on the Tk 'canvas' widget. Each site may require a unique
##        way to determine the URL of the picture file, hence a separate
##        'get' proc for each web site.
##
##     'save_image_toLocalFile' - called via the 'SaveAsGIF' button.
##                                Writes the image data from the
##                                Tk 'photo' image structure to
##                                a file on the user's local storage.
##
##     'date_increment'         - called via the '>' button
##     'date_decrement'         - called via the '<' button
##
##     'popup_msgVarWithScroll' - called via the 'Help' button --- and
##                                used for warning/error messages.
##
##  'popup_msgVarWithScroll_wait' - for showing error messages, and not
##                                  allowing further actions with the GUI.
##
##+########################################################################


##+#########################################################################
## proc  'get_site_selectedInListbox'
##+#########################################################################
## PURPOSE: Puts a 'site-ID' from a selected line of the sites listbox
##          into the entry widget text variable 'ENTRYsite'.
##
## CALLED BY: button1-release on the sites listbox
##
## See the listbox insert statements above (where the listbox widget was
## defined) for the web-site strings that were loaded in the listbox.
##+#########################################################################

proc get_site_selectedInListbox {} {

   global ENTRYsite

   ## FOR TESTING: (to dummy out this proc)
   #  return

   #################################################
   ## Get the (vertical) index of the line selected.
   #################################################

   set sel_index [ .fRbottom.fRsites.listbox curselection ]

   #######################################################
   ## Put the first part of the line (before a # sign)
   ## into the ENTRYsite variable --- if the line does not
   ## start with a # sign in column 1 (a comment line).
   #######################################################

   if { $sel_index != "" } {
      set SITEline  [ .fRbottom.fRsites.listbox get $sel_index ]
      set FIRSTchar [string index "$SITEline" 0]
      if { "$FIRSTchar" == "#" } {return}
      ## Separate any comments after a '#' sign from the 'site-ID'.
      set TEMPlist [split $SITEline #]
      set ENTRYsite [lindex $TEMPlist 0]
      set ENTRYsite [string trim $ENTRYsite]
   } else {return}

   ## FOR TESTING:
   #   puts "proc 'get_site_selectedInListbox' >  ENTRYsite: $ENTRYsite"

}
## END OF PROC 'get_site_selectedInListbox'


##+#########################################################################
## proc  'Retrieve'
##+#########################################################################
## PURPOSE: For a selected web-site in variable 'ENTRYsite' --- and for
##          other a comic 'index' specification such as VARyear,VARmonth,VARday,
##          one of the 'get_*_comic' procs is executed --- to retrieve
##          a specified comic from the selected site and display it
##          on the Tk 'canvas' widget.
##
## CALLED BY: the 'Retrieve' button
##+#########################################################################

proc Retrieve {} {

   global ENTRYsite VARyear VARmonth VARday fileMIDNAME

   ## FOR TESTING: (to dummy out this proc)
   #  return

   if {"$ENTRYsite" == "babyblues.com"} {
      set fileMIDNAME "BabyBlues"
      get_babyblues_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "bizarrocomics.com"} {
      set fileMIDNAME "Bizarro"
      get_bizarro_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "dilbert.com"} {
      set fileMIDNAME "Dilbert"
      get_dilbert_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "gocomics.com/doonesbury/"} {
      set fileMIDNAME "Doonesbury"
      get_doonesbury_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "rhymeswithorange.com"} {
      set fileMIDNAME "RhymesWithOrange"
      get_rhymeswithorange_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "gocomics.com/speedbump/"} {
      set fileMIDNAME "SpeedBump"
      get_speedbump_comic $VARyear $VARmonth $VARday
   }

   if {"$ENTRYsite" == "zitscomics.com"} {
      set fileMIDNAME "Zits"
      get_zits_comic $VARyear $VARmonth $VARday
   }

   ## More 'if' lines can be added here, with appropriate 'get_*_comic' procs added below.

}
## END OF proc 'Retrieve'


##+#############################################################################
## proc  'show_image_forPicURL'
##+#############################################################################
## PURPOSE: For a given fully-qualified URL for a picture file (GIF or whatever)
##          at a (comics archive) web-site, this proc loads the picture data into
##          a Tk 'photo' image structure and places the image structure
##          on the canvas widget of this GUI.
##
## CALLED BY: each of the following 'get_*_comic' procs
##+#############################################################################

proc show_image_forPicURL {picURL} {

   ## Make the current picture data available to the writeGIF proc.

   global imageID aRtext
   # global picDATA
 
   ############################################################
   ## Get the image data of the specified day's comic strip ---
   ## via a 'http:geturl' command and a 'http:data' command.
   ############################################################

   set urlID [http::geturl "$picURL"]

   ## FOR TESTING:
   #  puts "urlID: $urlID"

   set picDATA [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of 'binary characters'.)
   #    puts "picDATA: $picDATA"

   set First3 [string range "$picDATA" 0 2]
   set First1000 [string range "$picDATA" 0 999]

   #################################################
   ## Check for GIF file data, to make sure we can
   ## use the 'image create photo' command below.
   #################################################

   if {"$First3" != "GIF"} {
      set ERRmsg "$aRtext(popupMSGhttpData1)

$First1000

$aRtext(popupMSGhttpData2)
"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }
   

   http::cleanup $urlID

   ###############################################################
   ## Create the Tk 'photo' image 'structure' from the image data.
   ###############################################################
   ## We might want to change this to a technique that keeps
   ## reusing the same image ID. Example:
   ##   image create photo imageID1 -data "$picDATA"
   ###############################################################

   set imageID [image create photo -data "$picDATA"]

   ## FOR TESTING:
   #   puts "imageID: $imageID"

   #################################################
   ## Put the image on the Tk 'canvas' widget ---
   ## via its 'handle' --- after clearing the canvas.
   #################################################

   .fRbottom.fRdisplay.can delete all

   .fRbottom.fRdisplay.can create image 0 0 -anchor nw -image $imageID

   #################################################
   ## Resize the canvas to show as much of the image
   ## as possible.
   #################################################

   set IMGwidthPx  [image width $imageID]  
   set IMGheightPx [image height $imageID] 
   .fRbottom.fRdisplay.can configure -width $IMGwidthPx -height $IMGheightPx \
     -scrollregion "0 0 $IMGwidthPx $IMGheightPx"

}
##END OF proc 'show_image_forPicURL'


##+#############################################################################
## proc  'get_month_name'
##+#############################################################################
## PURPOSE: For a given month number, return its name.
##          Example: for '03', return 'march'.
##
##          We provide another parameter to allow for returning names
##          like 'March' or 'Mar' or 'mar'.
##
## CALLED BY: some of the following 'get_*_comic' procs ---
##            for example, 'get_rhymeswithorange_comic'
##+#############################################################################

proc get_month_name {NUMmonth} {

   if {"$NUMmonth" == "01"} {set NAMEmonth "january"}
   if {"$NUMmonth" == "02"} {set NAMEmonth "february"}
   if {"$NUMmonth" == "03"} {set NAMEmonth "march"}
   if {"$NUMmonth" == "04"} {set NAMEmonth "april"}
   if {"$NUMmonth" == "05"} {set NAMEmonth "may"}
   if {"$NUMmonth" == "06"} {set NAMEmonth "june"}
   if {"$NUMmonth" == "07"} {set NAMEmonth "july"}
   if {"$NUMmonth" == "08"} {set NAMEmonth "august"}
   if {"$NUMmonth" == "09"} {set NAMEmonth "september"}
   if {"$NUMmonth" == "10"} {set NAMEmonth "october"}
   if {"$NUMmonth" == "11"} {set NAMEmonth "november"}
   if {"$NUMmonth" == "12"} {set NAMEmonth "december"}

   return $NAMEmonth
}
##END OF proc 'get_month_name'


##+#########################################################################
## proc  'get_babyblues_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from the babyblues.com web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of the image file):
## http://www.babyblues.com/archive/index.php?formname=getstrip&GoToDay=01/02/2013
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################

proc get_babyblues_comic {yyyy mm dd} {

   global aRtext

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ###########################################################################
   ## Read from the site babyblues.com --- a mm/dd/yyyy page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## Example page URL:
   ## http://www.babyblues.com/archive/index.php?formname=getstrip&GoToDay=01/02/2013
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ###########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set tempURL "http://www.babyblues.com/archive/index.php?formname=getstrip&GoToDay=${mm}/${dd}/$yyyy"

   ## FOR TESTING:
      puts "yyyy: $yyyy     mm: $mm    dd: $dd"
      puts "tempURL: $tempURL"

   #################################################
   ## Check for error from the http::geturl command.
   #################################################

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl2)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
      puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   ###############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###############################################################################

   regexp -nocase {class="comic archive\-.*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
      puts "picURL: $picURL"

   ## Typical value of picURL for babyblues.com:
   ## http://safr.kingfeatures.com/idn/babyblues/zone/js/index.php?cn=72&zn=132&fn=22&fd=Tuesday, March 5, 2013&wt=2&fs=0&null=0

   http::cleanup $urlID

   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################
   ## First, replace whitespace in the URL with %20.
   ## Reference: http://wiki.tcl.tk/1039 on 'exec'
   ## --- to avoid getting 'Illegal characters in URL path'
   ## error from the ' http::geturl "$picURL" ' statement
   ## in the 'show_image_...' proc.
   ##########################################################

   set picURL [string map {{ } %20} $picURL]

   ## FOR TESTING:
      puts "picURL after replacing each space by '%20':"
      puts "$picURL"

   ## I get the error 'Unauthorized Request! Invalid Domain ().'
   ## when I paste this URL manually into the URL entry field
   ## of a web browser (Seamonkey).

   show_image_forPicURL "$picURL"

}
## END OF proc 'get_babyblues_comic"


#######################################################################
##
## Unfortunately, the main Bizarro web site provides a URL to
## a JPEG file on a page with a name like:
## http://bizarrocomics.com/2011/07/30/see-um-syrup-wedding/
## which contains an extra qualifier besides yyyy/mm/dd.
##
## I do not have a way (yet) to determine/find that extra qualifier.
## So I do not provide a 'get_bizarro_comic' proc --- yet.
## 
## By the way, the typical URL of the JPEG files looks like:
## http://bizarrocomics.com/files/uploads/2011/07/bz-panel-07-29-11.jpg
#######################################################################


##+#########################################################################
## proc  'get_dilbert_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from the dilbert.com web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of an image file):
## http://www.dilbert.com/strips/comic/2014-03-07/
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################
## This dilbert-code was 'inspired by' the Tcl-Tk code for retrieval of a Dilbert 
## 'comic-of-the-day' --- at http://wiki.tcl.tk/8899 (created 2005) ---
## the code that was contributed by 's_m' on 2013apr10.
##
## In a test run of that code in early 2014, the values assigned to the
## variables 'um', 'picurl', 'um', 'ext', and 'filename'  were:
##
## um = ::http::1
## picurl = /dyn/str_strip/000000000/00000000/0000000/200000/10000/1000/500/211518/211518.strip.gif
## um = ::http::2
## ext = .gif
## filename = dil_img/dilbert20140206.gif
############################################################################

proc get_dilbert_comic {yyyy mm dd} {

   global aRtext

   ## Make the current picture data available to the writeGIF proc.

   global imageID
   # global picDATA

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ########################################################################
   ## Read from the site dilbert.com --- a yyyy-mm-dd page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## (This may need to be changed if the site comic archive changes.)
   ########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set tempURL "http://www.dilbert.com/strips/comic/${yyyy}-${mm}-$dd/"

   ## FOR TESTING:
   #  puts "yyyy: $yyyy     mm: $mm    dd: $dd"
   #  puts "tempURL: $tempURL"

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl2)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
   #   puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   #############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ############################################################################

   regexp -nocase {class="STR_Content".*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
   #   puts "picURL: $picURL"

   ## Typical 'relative' value in picURL for dilbert.com:
   ## dyn/str_strip/000000000/00000000/0000000/200000/10000/4000/200/214290/214290.strip.gif

   http::cleanup $urlID


   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################

   if {[info exists picURL] != 0} {
      show_image_forPicURL "http://www.dilbert.com$picURL"
   } else {
      set ERRmsg "The URL of a picture file was not found for a web page at

$tempURL

IT MAY BE THAT DATE ${yyyy}-${mm}-$dd IS INVALID (the page may not exist).
"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

}
## END OF PROC 'get_dilbert_comic'


##+#########################################################################
## proc  'get_doonesbury_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from a doonesbury archive web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of the image file):
## http://www.gocomics.com/doonesbury/2014/03/01
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################

proc get_doonesbury_comic {yyyy mm dd} {

   global aRtext

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ###########################################################################
   ## Read from a doonesbury archive site --- yyyy/mm/dd page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## Example page URL:
   ## http://www.gocomics.com/doonesbury/2014/03/01
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ###########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set tempURL "http://www.gocomics.com/doonesbury/${yyyy}/${mm}/${dd}/"

   ## FOR TESTING:
   #   puts "yyyy: $yyyy     mm: $mm    dd: $dd"
   #   puts "tempURL: $tempURL"

   #################################################
   ## Check for error from the http::geturl command.
   #################################################

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl2)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
   #   puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   ###############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###############################################################################

   regexp -nocase {class="feature_item".*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
   #   puts "picURL: $picURL"

   ## Typical picURL value for www.gocomics.com/doonesbury/:
   ## http://assets.amuniversal.com/fee42e407aff0131fb33005056a9545d

   http::cleanup $urlID

   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################

   show_image_forPicURL "$picURL"

}
## END OF proc 'get_doonesbury_comic"


##+#########################################################################
## proc  'get_rhymeswithorange_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from the rhymeswithorange.com web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of the image file):
## http://rhymeswithorange.com/comics/march-5-2014/
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################

proc get_rhymeswithorange_comic {yyyy mm dd} {

   global aRtext

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ###########################################################################
   ## Read from the site rhymeswithorange.com --- a month-d-yyyy page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## Example page URL:
   ## http://rhymeswithorange.com/comics/march-5-2014/
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ###########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set NAMEmonth [get_month_name $mm]
   set NUMday    [scan $dd %d]
   set tempURL "http://rhymeswithorange.com/comics/${NAMEmonth}-${NUMday}-$yyyy/"

   ## FOR TESTING:
   #   puts "yyyy: $yyyy     mm: $mm    dd: $dd"
   #   puts "tempURL: $tempURL"

   #################################################
   ## Check for error from the http::geturl command.
   #################################################

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl2)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
   #   puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   ###############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###############################################################################

   regexp -nocase {class="entry-content".*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
   #   puts "picURL: $picURL"

   ## Typical picURL value for rhymeswithorange.com:
   ## http://safr.kingfeatures.com/idn/etv/zone/xml/content.php?file=aHR0cDovL3NhZnIua2luZ2ZlYXR1cmVzLmNvbS9SaHltZXNXaXRoT3JhbmdlLzIwMTQvMDMvUmh5bWVzX3dpdGhfT3JhbmdlLjIwMTQwMzA1XzkwMC5naWY=

   http::cleanup $urlID

   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################

   show_image_forPicURL "$picURL"

}
## END OF proc 'get_rhymeswithorange_comic"



##+#########################################################################
## proc  'get_speedbump_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from a speedbump archive web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of the image file):
## http://www.gocomics.com/speedbump/2014/03/01
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################

proc get_speedbump_comic {yyyy mm dd} {

   global aRtext

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ###########################################################################
   ## Read from a speedbump archive site --- yyyy/mm/dd page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## Example page URL:
   ## http://www.gocomics.com/speedbump/2014/03/01
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ###########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set tempURL "http://www.gocomics.com/speedbump/${yyyy}/${mm}/${dd}/"

   ## FOR TESTING:
   #   puts "yyyy: $yyyy     mm: $mm    dd: $dd"
   #   puts "tempURL: $tempURL"

   #################################################
   ## Check for error from the http::geturl command.
   #################################################

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl2)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
   #   puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   ###############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###############################################################################

   regexp -nocase {class="feature_item".*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
   #   puts "picURL: $picURL"

   ## Typical picURL value for www.gocomics.com/speedbump/:
   ## http://assets.amuniversal.com/335222d079470131f9e9005056a9545d

   http::cleanup $urlID

   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################

   show_image_forPicURL "$picURL"

}
## END OF proc 'get_speedbump_comic"


##+#########################################################################
## proc  'get_zits_comic'
##+#########################################################################
## PURPOSE: For a comic 'index' specification (VARyear,VARmonth,VARday),
##          retrieve the indicated comic from the zitscomics.com web site
##          --- and display it on the Tk 'canvas' widget.
##
## Example archive-page URL (whose text contains the URL of the image file):
## http://zitscomics.com/comics/march-5-2014/
##
## METHOD:  The Tcl 'http' package commands are used to retrieve
##          a specific comic from the web site and display it in the
##          canvas widget.
##
##      A  series of Tcl 'http' commands are issued --- commands such as
##         - http::config
##         - http::geturl
##         - http::data
##         - http::cleanup
##
## CALLED BY: proc 'Retrieve'
############################################################################

proc get_zits_comic {yyyy mm dd} {

   global aRtext

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ###########################################################################
   ## Read from the site zitscomics.com --- a month-d-yyyy page ---
   ## and get the HTML text that contains a link to that day's comic image file.
   ##
   ## Example page URL:
   ## http://zitscomics.com/comics/march-5-2014/
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###########################################################################
   ## To help catch cases where the user has not established a
   ## network connection yet, we use a catch' statement like:
   ##
   ## if {[catch {$command} CatchMsg] != 0} {..show an err msg.. ; return}
   ###########################################################################

   http::config -useragent "Mozilla/1.0" -accept "image/gif,image/jpeg,text/*"

   set NAMEmonth [get_month_name $mm]
   set NUMday    [scan $dd %d]
   set tempURL "http://zitscomics.com/comics/${NAMEmonth}-${NUMday}-$yyyy/"

   ## FOR TESTING:
   #   puts "yyyy: $yyyy     mm: $mm    dd: $dd"
   #   puts "tempURL: $tempURL"

   #################################################
   ## Check for error from the http::geturl command.
   #################################################

   if {[catch { set urlID [eval http::geturl "$tempURL"] } CatchMsg] != 0} {
      set CatchMsg [string trim "$CatchMsg"]
      set ERRmsg "$aRtext(popupMSGhttpGeturl1)

$tempURL

$aRtext(popupMSGhttpGeturl1)

$aRtext(popupMSGerrMsg)$CatchMsg"
      popup_msgVarWithScroll .topErr "$ERRmsg"
      return
   }

   ## FOR TESTING:
   #   puts "urlID: $urlID"

   #################################################
   ## Get the text of the HTML file.
   #################################################

   set HTMLtext [http::data $urlID]

   ## FOR TESTING:
   ##     (WARNING: This can be many screen-fulls of text lines.)
   # puts "HTMLtext: $HTMLtext"

   ###############################################################################
   ## Use 'regexp' to get the URL of the yyyy-mm-dd image file
   ## from the HTML text.
   ##    Reference: pages 158-162 of the 4th edition of
   ##               'Practical Programming in Tcl & Tk'.
   ## Syntax:
   ##   regexp ?flags? pattern string ?match sub1 sub2 ...?
   ##
   ## (This technique may need to be changed if the site comic archive changes.)
   ###############################################################################

   regexp -nocase {class="entry-content".*?src="([^"]+)"} "$HTMLtext" to picURL

   ## FOR TESTING:
   #    puts "picURL: $picURL"

   ## Typical picURL value for zitscomics.com:
   ## http://safr.kingfeatures.com/idn/etv/zone/xml/content.php?file=aHR0cDovL3NhZnIua2luZ2ZlYXR1cmVzLmNvbS9aaXRzLzIwMTQvMDMvWml0cy4yMDE0MDMwNV85MDAuZ2lm

   http::cleanup $urlID

   ##########################################################
   ## Get the image data of the specified day's comic strip
   ## and display it on the 'canvas' widget.
   ##########################################################

   show_image_forPicURL "$picURL"

}
## END OF proc 'get_zits_comic"


##+#########################################################################
## proc  'save_image_toLocalFile'
##+#########################################################################
## PURPOSE: Writes an image file to directory DIRtemp using
##          image data from the Tk 'photo' image 'structure'
##          with the 'handle' --- imageID.
##
## CALLED BY: the 'SaveAsGIF' button
##+#########################################################################

proc save_image_toLocalFile {} {

   global DIRtemp imageID VIEWER_images \
      ENTRYsite VARyear VARmonth VARday fileMIDNAME aRtext
   # global picDATA

   ## FOR TESTING: (to dummy out this proc)
   # return
 
   ########################################
   ## Make the filename for the GIF file.
   ########################################

   ## FROM http://wiki.tcl.tk/8899 :
   # set filename [clock format [clock seconds] -format "dil_img/dilbert%Y%m%d$ext"]

   # set comicNUM [clock seconds]
   # set TEMPfilename "$DIRtemp/comic_${comicNUM}.gif"

   set TEMPfilename "$DIRtemp/comic_${fileMIDNAME}_${VARyear}_${VARmonth}_${VARday}.gif"

   ## FOR TESTING:
   #  puts "TEMPfilename: $TEMPfilename"

   ########################################
   ## WRITE the GIF file.
   ########################################

   ## The technique used by 's_m' at http://wiki.tcl.tk/8899 :
   ##
   ## set fileID [open "$TEMPfilename" w]
   ## fconfigure $fileID -translation binary -encoding binary
   ## puts $fileID $picDATA
   ## close $fileID

   ## A technique to assure that a GIF file is written.
   ##   Reference: pages 629-631 of the 4th edition of
   ##              'Practical Programming in Tcl & Tk'.

   $imageID write "$TEMPfilename" -format gif


   #######################################################
   ## Tell the user the location and name of the GIF file.
   #######################################################

   set LOCmsg "$aRtext(popupMSGsavedGIF1)

$TEMPfilename" 

   popup_msgVarWithScroll .topLoc "$LOCmsg"


   #######################################################
   ## OPTIONAL.
   ## Confirm that the GIF file was written, by
   ## showing the file in an 'external' image viewer.
   #######################################################
   ## We us a 'background' run --- per page 107  of the
   ## 4th edition of 'Practical Programming in Tcl & Tk', where
   ## there is the following quote on the Tcl 'exec' command and
   ## 'background' mode:
   ##
   ## "A trailing '&' causes the program to run in the background.
   ##  In this case, the process identifier is returned by the 'exec'
   ##  command. Otherwise, the 'exec' command blocks during execution
   ##  of the program, and the standard output of the program is the
   ##  return code of 'exec'."
   ##
   ## We use 'eval' to avoid a 'not found' error when we append
   ## some parms (or a space) to the viewer command.
   ###############################################################

   if {0} {

      catch {eval exec $VIEWER_images "$TEMPfilename" &} ViewerPID

      ## FOR TESTING:
      #    puts "VARcommand: $VARcommand"
      #    puts "ViewerPID: $ViewerPID"

   }
   ## END OF  if {0/1} section

}
## END OF proc 'save_image_toLocalFile'



##+#########################################################################
## proc  'date_increment'
##+#########################################################################
## PURPOSE: Increases the date by one day --- by updating the
##          VARyear, VARmonth, VARday entry ariables.
##
## CALLED BY: the '>' button
##+#########################################################################

proc date_increment {} {

   global VARyear VARmonth VARday

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ## Remove leading zeros (if any) from VARmonth and
   ## VARday --- to avoid 'invalid octal number' errors
   ## from '08' and above. Ref: http://wiki.tcl.tk/15158

   # set VARmonth [string trimleft $VARmonth 0]
   # if { "$VARmonth" == "" } {set VARmonth 0}
   # set VARday   [string trimleft $VARday 0]
   # if { "$VARday" == "" } {set VARday 0}

   ## Convert text VAR strings to numeric variables.

   # set NUMyear  [expr {int($VARyear)}]
   # set NUMmonth [expr {int($VARmonth)}]
   # set NUMday   [expr {int($VARday)}]

   ## Alternatively, use 'scan'.  Ref: http://wiki.tcl.tk/948

   set NUMyear  [ scan  $VARyear  %d ]
   set NUMmonth [ scan  $VARmonth %d ]
   set NUMday   [ scan  $VARday   %d ]

   ## Increment num-days.

   incr NUMday

   ## Handle day-overflow for months 9,4,6,11.

   if {$NUMmonth == 9 || $NUMmonth == 4 || \
       $NUMmonth == 6 || $NUMmonth == 11} {

      if {$NUMday > 30} {
         set NUMday 1
         incr NUMmonth
         if {$NUMmonth > 12} {
            set NUMmonth 1
            incr NUMyear
         }
      }
      ## END OF if {$NUMday > 30}

      ## Reset VARyear VARmonth VARday before 'return'.

      set VARyear  [format "%04d" $NUMyear]
      set VARmonth [format "%02d" $NUMmonth]
      set VARday   [format "%02d" $NUMday]

      return
   }
   ## END OF  if $NUMmonth == 9,4,6,11


   ## Handle day-overflow for the other months, except 2
   ## --- months 1,3,5,7,8,10,12.

   if {$NUMmonth == 1 || $NUMmonth == 3 || $NUMmonth == 5 || \
       $NUMmonth == 7 || $NUMmonth == 8 || $NUMmonth == 10 || \
       $NUMmonth == 12 } {

      if {$NUMday > 31} {
         set NUMday 1
         incr NUMmonth
         if {$NUMmonth > 12} {
            set NUMmonth 1
            incr NUMyear
         }
      }
      ## END OF if {$NUMday > 30}

      ## Reset VARyear VARmonth VARday before 'return'.

      set VARyear  [format "%04d" $NUMyear]
      set VARmonth [format "%02d" $NUMmonth]
      set VARday   [format "%02d" $NUMday]

      return
   }
   ## END OF  if $NUMmonth == 1,3,5,7,8,10,12.


   ## Handle day-overflow for month 2.

   if {[expr {$NUMyear % 4}] == 0} { 
      ## For leap year:
      if {$NUMday > 29} {
         set NUMday 1
         incr NUMmonth
         if {$NUMmonth > 12} {
            set NUMmonth 1
            incr NUMyear
         }
      }
      ## END OF if {$NUMday > 29}
   } else {
      ## For non-leap year:
      if {$NUMday > 28} {
         set NUMday 1
         incr NUMmonth
         if {$NUMmonth > 12} {
            set NUMmonth 1
            incr NUMyear
         }
      }
      ## END OF if {$NUMday > 28}
   }
   ## END OF  if {[expr {$NUMyear % 4}] == 0}

   ## Reset VARyear VARmonth VARday before 'return'.

   set VARyear  [format "%04d" $NUMyear]
   set VARmonth [format "%02d" $NUMmonth]
   set VARday   [format "%02d" $NUMday]

}
## END OF proc 'date_increment'


##+#########################################################################
## proc  'date_decrement'
##+#########################################################################
## PURPOSE: Decreases the date by one day --- by updating the
##          VARyear, VARmonth, VARday entry ariables.
##
## CALLED BY: the '<' button
##+#########################################################################

proc date_decrement {} {

   global VARyear VARmonth VARday

   ## FOR TESTING: (to dummy out this proc)
   #  return

   ## Remove leading zeros (if any) from VARmonth and
   ## VARday --- to avoid 'invalid octal number' errors
   ## from '08' and above. Ref: http://wiki.tcl.tk/15158

   # set VARmonth [string trimleft $VARmonth 0]
   # if { "$VARmonth" == "" } {set VARmonth "0"}
   # set VARday   [string trimleft $VARday 0]
   # if { "$VARday" == "" } {set VARday "0"}

   ## Convert text VAR strings to numeric variables.

   # set NUMyear  [expr {int($VARyear)}]
   # set NUMmonth [expr {int($VARmonth)}]
   # set NUMday   [expr {int($VARday)}]

   ## Alternatively, use 'scan'.  Ref: http://wiki.tcl.tk/948

   set NUMyear  [ scan  $VARyear  %d ]
   set NUMmonth [ scan  $VARmonth %d ]
   set NUMday   [ scan  $VARday   %d ]


   ## DEcrement num-days.

   incr NUMday -1

   ## Handle day-UNDERflow for months 9+1,4+1,6+1,11+1.

   if {$NUMmonth == 10 || $NUMmonth == 5 || \
       $NUMmonth == 7  || $NUMmonth == 12} {

      if {$NUMday < 1} {
         set NUMday 30
         incr NUMmonth -1
         if {$NUMmonth < 1} {
            set NUMmonth 12
            incr NUMyear -1
         }
      }
      ## END OF if {$NUMday < 1}

      ## Reset VARyear VARmonth VARday before 'return'.

      set VARyear  [format "%04d" $NUMyear]
      set VARmonth [format "%02d" $NUMmonth]
      set VARday   [format "%02d" $NUMday]

      return
   }
   ## END OF  if $NUMmonth == 9+1,4+1,6+1,11+1.


   ## Handle day-UNDERflow for the other months, except 2+1
   ## --- months 1+1,3+1,5+1,7+1,8+1,10+1,12+1(=1).

   if {$NUMmonth == 2 || $NUMmonth == 4 || $NUMmonth == 6 || \
       $NUMmonth == 8 || $NUMmonth == 9 || $NUMmonth == 11 || \
       $NUMmonth == 1 } {

      if {$NUMday < 1} {
         set NUMday 31
         incr NUMmonth -1
         if {$NUMmonth < 1} {
            set NUMmonth 12
            incr NUMyear -1
         }
      }
      ## END OF if {$NUMday < 1}

      ## Reset VARyear VARmonth VARday before 'return'.

      set VARyear  [format "%04d" $NUMyear]
      set VARmonth [format "%02d" $NUMmonth]
      set VARday   [format "%02d" $NUMday]

      return
   }
   ## END OF  if $NUMmonth == 1,3,5,7,8,10,12.


   ## Handle day-UNDERflow for month 2+1(=3).

   if {[expr {$NUMyear % 4}] == 0} { 
      ## For leap year:
      if {$NUMday < 1} {
         set NUMday 29
         incr NUMmonth -1
         if {$NUMmonth < 1} {
            set NUMmonth 12
            incr NUMyear -1
         }
      }
      ## END OF if {$NUMday < 1}
   } else {
      ## For non-leap year:
      if {$NUMday < 1} {
         set NUMday 28
         incr NUMmonth -1
         if {$NUMmonth < 1} {
            set NUMmonth 12
            incr NUMyear -1
         }
      }
      ## END OF if {$NUMday < 1}
   }
   ## END OF  if {[expr {$NUMyear % 4}] == 0}

   ## Reset VARyear VARmonth VARday before 'return'.

   set VARyear  [format "%04d" $NUMyear]
   set VARmonth [format "%02d" $NUMmonth]
   set VARday   [format "%02d" $NUMday]

}
## END OF proc 'date_decrement'


##+########################################################################
## PROC 'popup_msgVarWithScroll'
##+########################################################################
## PURPOSE: Report help or error conditions to the user.
##
##       We do not use focus,grab,tkwait in this proc,
##       because we use it to show help when the GUI is idle,
##       and we may want the user to be able to keep the Help
##       window open while doing some other things with the GUI
##       such as putting a filename in the filename entry field
##       or clicking on a radiobutton.
##
##       For a similar proc with focus-grab-tkwait added,
##       see the proc 'popup_msgVarWithScroll_wait' in a
##       3DterrainGeneratorExaminer Tk script.
##
## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk',
##            4th edition, by Welch, Jones, Hobbs.
##
## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg)
##            and a variable holding text (many lines, if needed).
##
## CALLED BY: 'help' button
##+########################################################################
## To have more control over the formatting of the message (esp.
## words per line), we use this 'toplevel-text' method, 
## rather than the 'tk_dialog' method -- like on page 574 of the book 
## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications
## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor".
##+########################################################################

proc popup_msgVarWithScroll { toplevName VARtext } {

   ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global.
   ## global env

   # bell
   # bell
  
   #################################################
   ## Set VARwidth & VARheight from $VARtext.
   #################################################
   ## To get VARheight,
   ##    split at '\n' (newlines) and count 'lines'.
   #################################################
 
   set VARlist [ split $VARtext "\n" ]

   ## For testing:
   #  puts "VARlist: $VARlist"

   set VARheight [ llength $VARlist ]

   ## For testing:
   #  puts "VARheight: $VARheight"


   #################################################
   ## To get VARwidth,
   ##    loop through the 'lines' getting length
   ##     of each; save max.
   #################################################

   set VARwidth 0

   #############################################
   ## LOOK AT EACH LINE IN THE LIST.
   #############################################
   foreach line $VARlist {

      #############################################
      ## Get the length of the line.
      #############################################
      set LINEwidth [ string length $line ]

      if { $LINEwidth > $VARwidth } {
         set VARwidth $LINEwidth 
      }

   }
   ## END OF foreach line $VARlist

   ## For testing:
   #    puts "VARwidth: $VARwidth"


   ###############################################################
   ## NOTE: VARwidth works for a fixed-width font used for the
   ##       text widget ... BUT the programmer may need to be
   ##       careful that the contents of VARtext are all
   ##       countable characters by the 'string length' command.
   ###############################################################


   #####################################
   ## SETUP 'TOP LEVEL' HELP WINDOW.
   #####################################

   catch {destroy $toplevName}
   toplevel  $toplevName

   # wm geometry $toplevName 600x400+100+50

   wm geometry $toplevName +100+50

   wm title     $toplevName "Note"
   # wm title   $toplevName "Note to $env(USER)"

   wm iconname  $toplevName "Note"


   #####################################
   ## In the frame '$toplevName' -
   ## DEFINE THE TEXT WIDGET and
   ## its two scrollbars --- and
   ## DEFINE an OK BUTTON widget.
   #####################################

   if {$VARheight > 10 || $VARwidth > 80} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_fixedwidth \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      scrollbar $toplevName.scrolly \
         -orient vertical \
         -command "$toplevName.text yview"

      scrollbar $toplevName.scrollx \
         -orient horizontal \
         -command "$toplevName.text xview"
   } else {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_varwidth \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 
   }

   button $toplevName.butt \
      -text "OK" \
      -font fontTEMP_varwidth  \
      -command  "destroy $toplevName"

   ###############################################
   ## PACK *ALL* the widgets in frame '$toplevName'.
   ###############################################

   ## Pack the bottom button BEFORE the
   ## bottom x-scrollbar widget,

   pack  $toplevName.butt \
      -side bottom \
      -anchor center \
      -fill none \
      -expand 0


   if {$VARheight > 10 || $VARwidth > 80} {
      ## Pack the scrollbars BEFORE the text widget,
      ## so that the text does not monopolize the space.

      pack $toplevName.scrolly \
         -side right \
         -anchor center \
         -fill y \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the Y-scrollbar.
      ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS
      ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA.
                
      pack $toplevName.scrollx \
         -side bottom \
         -anchor center \
         -fill x  \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the X-scrollbar.
      ## THAT KEEPS THE TEXT AREA FROM EXPANDING.

      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   } else {
      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   }


   #####################################
   ## LOAD MSG INTO TEXT WIDGET.
   #####################################

   ##  $toplevName.text delete 1.0 end
 
   $toplevName.text insert end $VARtext
   
   $toplevName.text configure -state disabled
  
}
## END OF PROC 'popup_msgVarWithScroll'


##+########################################################################
## PROC 'popup_msgVarWithScroll_wait'
##+########################################################################
## PURPOSE: Report error conditions to the user.
##          Sets 'focus' on this toplevel window, does a 'grab',
##          and does 'tkwait' so that execution stops until the
##          user responds to this window.
##
## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg)
##            and a variable holding text (many lines, if needed).
##
## CALLED BY: various procs that need to popup an error message.
##+########################################################################

proc popup_msgVarWithScroll_wait { toplevName VARtext } {

   popup_msgVarWithScroll $toplevName $VARtext
   focus $toplevName
   grab $toplevName
   tkwait window $toplevName

}
## END OF PROC 'popup_msgVarWithScroll_wait'


######################################################
## Set the HELP text variable for the 'Help' button.
######################################################

set HELPtext "\
\ \ \ \ \ ** HELP for this Get-Comics Utility **

This utility offers a GUI with which the user can

   - choose a comics archive site (such as dilbert.com or
     gocomics.com/doonesbury/)

and

   - use a 'Retrieve' button on the GUI to retrieve one comic at
     a time from the site.

A particular comic at a site is selected by a date (or some other
type of 'index'), where the date (or other index) is user-selected
via widgets on the GUI.

      (Since each site will generally have a different way of
       archiving comics on the site, the way to specify a
       particular comic will, in general, depend on the storage
       and access methods of the site. Fortunately, year-month-day is
       a sufficient index to the individual comics for many sites.)

A 'canvas' widget on the GUI is used to display the comic
--- if it is a GIF file.

      (If the web-site stores the comics in another file format,
       such as JPEG-JFIF, this code could run a utility such as
       ImageMagick 'convert' to convert the retrieved file
       to a GIF file --- and display that file.)

There are horizontal and vertical scroll bars on the canvas, to
allow for scrolling the comic if it is bigger (horizontally or
vertically) than the maximum size of the canvas --- which is
determined by the size of the user's computer monitor.

A 'SaveAsGIF' button on the GUI offers the user the option to save
each comic as a file on the user's local computer storage devices.

   (The Tcl-Tk code that implements this utility automatically
    puts the file in the /tmp directory --- from which the user
    can transfer the file to another directory.

    The temporary-directory location can be changed by changing the
    setting of the 'DIRtemp' variable at the bottom of this code.)

*******************
METHOD OF OPERATION:
*******************

A 'listbox' on the GUI offers a list of comic archive sites from
which to choose.

Click on a line of the listbox to choose a web-site. The
web-site name is followed by a separation character (#)
after which there may be some description of the web-site.

   (A '#' in column 1 of the line makes the line a comment line
    --- for example, to allow for indicating comics for which
    retrieval has not been implemented, and reminding the user
    to keep looking for a method to retrieve those comics.)

Date fields (year,month,day) provide the user a way to
select a specific comic to retrieve --- if a web site
supports retrieval by date.

      (If some sites offer a different way, from year-month-day,
       of identifying individual comics, this code could
       activate a different widget --- or widgets --- by which
       to specify a comic --- according to the site selected
       by the user.

       In that case, 'comic selection widgets' on the GUI may have
       to be activated/deactivated according to the site selected.)

The '<' and '>' buttons on either side of the Day entry field
facilitate quickly scanning through cartoons of a given site,
by successive days.

You can skip to a different year or month by simply entering
a new number in the Year or Month entry fields.

After each date is set, click on the 'Retrieve' button to
display the corresponding comic.

The 'Retrieve' button causes a series of Tcl 'http' commands
to be issued, commands such as
      - http::config
      - http::geturl
      - http::data
      - http::cleanup
to get the comic file (GIF) data and display it in the canvas.

Occasionally, at a comic archive site, there may not be
a comic for a specified date. In that case, you may see
an error message popup in a window. Simply dismiss the
window and try another date.

A 'package require http' statement is used (in the code) to
determine whether the Tcl 'http' package is available. A
popup message window will inform you if you need to install
the Tcl 'http' package.
"

## END of setting var 'HELPtext'.


##+#####################################################
## The Additional-GUI-Initialization SECTION:
##+#####################################################

##+######################################################
## Set a default directory for the saved image files.
##+######################################################

set DIRtemp "/tmp"


##+######################################################
## Set an 'external' image viewer to be used to show
## image files.  (May be used in the SaveAsGIF proc.)
##+######################################################

# set VIEWER_images "/usr/bin/ffplay"
# set VIEWER_images "/usr/bin/display"
  set VIEWER_images "/usr/bin/eog"


##+###########################################################
## Initialize the year, month, day entry field variables.
##+###########################################################

# set YMDstring [clock format [clock seconds] -format "%Y %b %m"]

set VARyear  [clock format [clock seconds] -format "%Y"]
set VARmonth [clock format [clock seconds] -format "%m"]
set VARday   [clock format [clock seconds] -format "%d"]


##+###########################################################
## AFTER the initialized GUI is displayed, check that the
## Tcl 'http' package is available.
##+###########################################################

if {[catch {package require http} CatchMsg]} {
   set ERRmsg "$aRtext(popupMSGhttpREQUIRE)

$aRtext(popupMSGerrMsg)$CatchMsg"
   popup_msgVarWithScroll .topErr "$ERRmsg"
}


INSTALLING THE SCRIPT:

For this 'get-comics' utility, a SINGLE Tkscript can be put in a sub-directory of the user's home directory, such as $HOME/apps/tkGetComics.

Then the user can use their desktop system (such as Gnome or KDE) to set up the Tk script as an icon on the desktop. Then the user can click on the icon to startup the GUI.


SOME POSSIBLE ENHANCEMENTS

This Tk script is the first utility that I have added to the bottom of my 'bio' page at uniquename --- in the 'CNP' (Code using Network Protocols) group.

I have many more scripts in various states of development --- in the other categories of 'done and to-do' projects on my 'bio' page.

But I may return to this 'get-comics' Tk script to provide some enhancements, such as:

More Warning/Error Popups

In using this utility, I may discover error conditions that I did not encounter in testing. I may add ways to 'trap' the errors and provide some pop-up messages in addition to the ones I already implemented during the testing phase.

Additonal Comics

Some additional comics may be added. This involves adding 'listbox' entries and adding 'get_*_comic' proc for each new comic archive site.

JPEG Support

Some sites provide comics in JPEG-JFIF format rather than GIF format. In those cases, I could add some code, using the ImageMagick 'convert' command, to convert the JPEG file to a GIF file ('under the covers') --- and then place the GIF image on the canvas.


IN CONCLUSION

I want to thank those who authored the 'http' package ('beedub' and others?) that allowed me to avoid having to go a level lower --- into Tcl 'socket' programming.

Furthermore, as I have said on several other code-donation pages on this wiki ...

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.