tkScriptApplicator - a mini-system to select files and select-and-apply a shell script

uniquename - 2014jul29

I have a set of about 450 'Nautilus' scripts that I find quite handy to use for computing activities in many categories. In fact, I have the scripts stored in directories whose names indicate the areas of functionality:

  • AUDIOtools
  • EXAMINEfiles
  • FILESlists
  • FINDlists
  • IMAGEtools
  • LinuxHELPS
  • PROC-MEMlists
  • SPACElists
  • TERMtools
  • VIDEOtools
  • and more

I use those Nautilus scripts via the Nautilus file manager that was available in the Gnome 2 desktop environment.

The general idea of 'Nautilus scripts':

You use the Nautilus file manager to navigate to a directory (for example, a directory of image files). Then you 'select-file(s)-and-apply-a-script-to-them'. Here is a little more detail on how it works.

You select the file (or files) in the usual way (using Ctl or Shift keys to select multiple files). Then, to select-and-apply a script, you right-click on the file-manager window. A menu including a 'Scripts' option pops up. When you move the mouse cursor over the 'Scripts' option, a hierarchical structure of scripts may be displayed --- scripts in sub-directories where the sub-directories can have names like those in the list above.

When you click on a script that appears in the menu hierarchy, the script is executed, with the file (or files) you selected passed as arguments to the script.

Here is an example that shows what the 'Nautilus scripts' hierarchy of menus can look like.

nautilusScriptsMenu_atTERMtools_1001x639_CompressType00.png

In this case, I right-clicked on a file in the Nautilus window and the Nautilus menu popped up with the 'Scripts >' option showing in the menu. By simply moving the mouse cursor over that 'Scripts' line, another menu in the cascade opens up showing names of Nautilus scripts and/or names of sub-directories containing Nautilus scripts.

(Nautilus starts the hierarchy of scripts at the directory $HOME/.gnome2/nautilus-scripts.)

In this case, subdirectory names like the ones listed above showed up. I moved the mouse cursor over the 'TERMtools' category (sub-directory name) and another menu in the cascade opened up --- showing the names of the several scripts in the 'TERMtools' category.

At that point, I could have clicked on the name of one of the scripts. The script would have started up and the menu cascade would disappear.

For example, I could have clicked on the script named

    00_anyfile4Dir_START-Gterminal-here_gnome-terminal.sh

That script, as the name suggests, does not use the filenames passed to it, other than to determine the directory name that contains the selected file. That directory name is used to start up a terminal window with a shell prompt that is positioned at that directory as the 'current working directory'.

As the script-name suggests, the script starts up a 'Gterminal', using the 'gnome-terminal' command.

---

THE RUB

Here's the rub. Around 2010, the developers of the Gnome desktop environment came out with Gnome 3. They took a view of the desktop environment that essentially threw out what they considered a lot of bathwater --- but that bathwater included many babies.

The Nautilus 2.28 file-manager that I was using suffered some 'regressions'. Some of the handy features of the Nautilus right-click menu were deemed no longer worth supporting. For example, the 'Open With > Other Application ...' option of the menu no longer worked in releases of Nautilus soon after the 2.28.1 version.

Not only 3.x versions lacked formerly provided handy capabilities, but even some 2.x versions after 2.28.1 suffered 'regressions'.

I became concerned about the loss of capabilities, especially loss of the 'Nautilus scripts' capability --- which was never even mentioned in press-releases, articles, reviews, etc. about Gnome 3.

I was not the only one concerned. Some Gnome 2 users in Argentina were so concerned that they forked the Gnome 2 desktop environment (with the Nautilus file manager that is central to that environment) to a new MATE (MAH-tay) desktop environment. They forked the Nautilus file manager to a file manager called 'Caja' (CAH-hah, Spanish for box).

(The 'Caja' file manager looks for 'Nautilus scripts' starting at the directory $HOME/.config/caja/scripts.)

Unfortunately, they forked Caja from a 2.x release that already included some regressions.

At that point, I realized that I could no longer depend on either Gnome 3 developers or MATE developers to preserve the 'Nautilus scripts' capability.

I realized that I could develop my own file(s)-selector --- along with a script-selector --- using Tcl-Tk to make the GUI's. In fact, around 2011, I created a system that I called 'feHappyScripts', as one of the subsystems of my Freedom Environment systems, available (for free) at www.freedomenv.com.

I rushed that system out, because I wanted the comfort of knowing that I could devise a Tcl-Tk system that provides the 'Nautilus scripts' capability.

That 'feHappyScripts' system is quite rough around the edges. It was basically a 'proof of concept' with a lot of unpolished code.

Even though it was unpolished, I was pleased to see that the Tk GUI listboxes could be filled with filenames even faster than the Nautilus file manager could present the names. For example, for directories containing more than 1,500 files, the names came up in a fraction of a second, whereas Nautilus took more than 5 seconds to start showing the names.

This page is meant to provide a more polished implementation of a 'Nautilus scripts' capability --- using several Tk scripts.

For one thing, in my first pass at this, I used the 'ls -Ap' command to get the file lists with which to put directory names and 'regular' file names in the GUI's.

This time around I eventually decided to use the Tcl 'glob' command.

Furthermore, my first pass did not handle 'symbolic link' files --- for directories or for 'regular' files.

This time around, I wanted to at least make an attempt to handle 'symbolic links' --- even 'broken' links.

So here is a 'tkScriptApplicator' mini-system.

---

THE GOALS

My two main goals for the 'tkScriptApplicator' mini-system were to provide two Tk GUI scripts:

  • a main 'file(s)-selector' Tk script
  • a 'script-selector' script, to be called by the 'file(s)-selector' GUI

In the process of developing the 'file(s)-selector' Tk script, I found that I needed one more Tk script:

  • a 'directory-selector' script, for a 'Jump2Dir' button on the 'file(s)-selector' GUI

This 'directory-selector' script was to also provide the option of editing a list of 'favorite directories'.

---

The two main Tk GUI's of this utility (the 'file(s)-selector' and the 'script-selector') could be implemented in many different ways.

For example, a SINGLE listbox could be used to hold both sub-directory names and filenames of a 'current directory'.

But I decided to use a TWO-listbox layout --- ONE listbox for sub-directory names and ONE listbox for 'regular' filenames.

I was influenced in this by a file-selector GUI that was used in a CAD/CAM/CAE system called 'I-DEAS' that was developed by SDRC (Structural Dynamics Research Corporation) of Milford, Ohio --- around 1990 to 2003.

(SDRC and the I-DEAS software was eventually acquired by the company that developed the Unigraphics CAD/CAM/CAE software, around 2003 --- and Unigraphics was eventually acquired by Siemens, around 2008.)

In both the 'file(s)-selector' GUI and the 'script-selector' GUI, the TWO listboxes are side-by-side:

*** the LEFT listbox for the SUB-DIRECTORIES of the 'current directory' (specified at the top of the GUI)

and

*** the RIGHT listbox for the 'regular' (non-directory) files in the 'current directory'.

---

I should point out here that I was not especially interested in coming up with a 'beautiful utility'. I just wanted a utility that would make sure that I had a 'Nautilus scripts' capability --- a capability that is not subject to the whims and regressions of the Gnome 3 developers.

I am certainly interested in making pretty GUI's --- as I have expressed on my 'bio' page at uniquename, and as my pages at Experiments in making embellished GUI's and Version 2 of a demo of THEMES for Tk GUI's, using images and colors have indicated.

But at this time, I am satisfied to implement the 'functionality', and let the 'beauty' go for a later date (when I have more beauty tools/code at hand).


SCREENSHOTS OF THE GUI's

On the basis of considerations mentioned above, I ended up with the several GUI's seen in the following several images.

The 'file(s)-selector' GUI:

tkScriptApplicator_mainGUI_at-home-dir_562x506_CompressType00.png

The 'script-selector' GUI:

tkScriptApplicator_scriptSelectorGUI_initialView_812x495_CompressType00.png

The 'directory-selector' GUI:

tkScriptApplicator_dirSelector_forJump2Dir_396x609_CompressType00.png

---

The 'script-selector' GUI image above shows the main 'category' directories of my 'feNautilusScripts' --- which are available as a (free) package at www.freedomenv.com. However, the files-listbox is essentially empty, because my Nautilus scripts are in sub-directories of $HOME/.gnome2/nautilus-scripts.

Here is an image captured after I clicked on the 'IMAGEtools' subdirectory.

tkScriptApplicator_scriptSelectorGUI_inIMAGEtoolsDir_812x495_CompressType00.png

I use four-part script names that are described at the www.freedomenv.com site.

---

One very pleasing aspect of the Tcl-Tk implementation is its speed. When I navigated to the directory '/usr/lib' with the 'file(s)-selector' GUI, the list of over 2,500 files was displayed in a fraction of a second. ("Blink and you miss it.")

tkScriptApplicator_mainGUI_at-usr-lib_663x563_CompressType00.png

It takes about 5 seconds for these files to be displayed in Nautilus --- at least the first time the directory is accessed. (It is faster in subsequent navigation to the directory in the same session.)

I suppose Nautilus is slower (in spite of it being a compiled program and the Tcl-Tk scripts being 'interpreted') because of overhead, such as showing, or trying to show, little image icons representing various types of directories and files.


BASIC OPERATING SEQUENCE

Here is a quick overview of the operation of the 3 GUI's above.

1) In the 'file(s)-selector' GUI, select one or more files to be provided to a script.

If necessary, use the 'Jump2Dir' button to quickly navigate to a directory chosen from a list of 'favorites'.

Further navigation is possible by single-clicks on the directories in the left listbox --- either click on '..' (to go UP to a parent directory) or click on a sub-directory name to go DOWN into that directory.

2) Click on the 'Apply-a-Script' button of the 'file(s)-selector' GUI to bring up the 'script-selector' GUI --- by which to navigate to a suitable scripts subdirectory and choose a script.

3) Click on the 'UseIt' button of the 'script-selector' GUI to execute the selected script according to the selected file(s).

Clicking on the 'UseIt' button actually returns the script filename to the 'file(s)-selector' Tk script and exits the 'script-selector' GUI. It is the 'file(s)-selector' script that actually executes the selected script, providing the selected file(s) to that script.

4) If the script was to make a new file in the current directory of the 'file(s)-selector' GUI, the user can click on the 'Refresh' button to re-fill the listboxes and make the new file visible.

---

Other options (such as Rename, Delete, Copy, Move, Properties, etc.) could be added (eventually) to the 'file(s)-selector' GUI to make it a more full-featured file manager.

But note that many functions that a user may want to perform can be implemented in Nautilus scripts. For example, a file-manager may be set up to play a '.mp4' file with the 'Totem' movie player when a user clicks on the '.mp4' file.

Alternatively, a user could have a Totem-player Nautilus script, say in a 'VIDEOtools' subdirectory. The user could click on the '.mp4' file in the 'file(s)-selector' GUI of this 'tkScriptApplicator' mini-system and choose to run the Totem-player script.


DESCRIPTION OF THE CODE

Below, I provide the Tk script code for the three Tk GUI's of this 'tkScriptApplicator' mini-system --- along with a 'fave_dirs.lis' text file to be used by the 'directory-selector' Tk script.

I follow my usual 'canonical' structure for Tk code for these 3 Tk scripts:

  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.

(I have put some of the info/warning/error messages in the text-array --- but I have left some messages hard-coded in the procs. I may someday put all the messages in the text-array assignment section of the Tk scripts.)


Experimenting with the GUI's

As in all my Tk scripts that use the 'pack' geometry manager (which is all of my 100-plus Tk 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 for the 'file(s)-selector', 'script-selector', and 'directory-selector' Tk scripts.

The label and button widgets stay fixed in size and relative-location if the window is re-sized --- while the entry widgets expand/contract horizontally whenever the window is re-sized horizontally. And the listbox widgets expand/contract both vertically and horizontally when the window is resized in either direction.

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 'normal' to 'bold' (if you have a screen resolution bigger than 1024x768) --- or change '-slant' from 'roman' to 'italic' --- or change the font sizes. 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 three GUI's is not to your liking, you can change the value of the RGB parameter supplied to the 'tk_setPalette' command near the top of each of the 3 Tk scripts.


Some features in the code

There are plenty of comments in the code, to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code of any of the 3 Tk scripts to see a list of the procs used in each script, along with brief descriptions of how they are called and/or what they do.

The main procs of the 'file(s)-selector' GUI are

  'fill_lists'  - fills the 'dirs' and 'fils' listboxes based on var DIRFILmask

  'get_subdirs_inCURdir_inclLinks' - called by 'fill_lists' proc
  'get_files_inCURdir_inclLinks'   - called by 'fill_lists' proc

  'get_selected_files' - uses selected relative-filename[s] to set 'FULFILnames'

  'navigate'    - changes dir in 'DIRFILmask' entry field & runs 'fill_list'

  'jump2dir'    - gets a new dir for dirfilmask; uses Tk script
                  'dirSelector_orEditListOfFavorites_1listbox.tk'

  'selectANDapply_script'  - gets a script and applies it to 'FULFILnames' ;
                             uses Tk script 'scriptSelector_2listboxes.tk'

  'set_status_msg'    - puts supplied msg into the Status msg frame

  The following 6 procs for for the window/widget resizing buttons:
    'dir_listbox_width_increase'
    'dir_listbox_width_reduce'
    'fil_listbox_width_increase'
    'fil_listbox_width_reduce'
    'tall_win'
    'short_win'

    'toggle_fontsize'       - called by the 'TogFontSize' button
    'toggle_ybars'          - called by the '<-Ybars->' button

  'popup_msgVarWithScroll' - used to show the $HELPtext var ; also used
                              to show user various popup messages for
                              info, warnings, errors, etc.

One thing worth noting is the use of the -selectmode parameter on the various listbox definitions in the 3 Tk scripts.

In the 'file(s)-selector' script:

We use '-selectmode single' on the 'dirs' listbox, but we use '-selectmode extended' in the 'fils' listbox --- because we want to allow the user to select multiple 'regular' files, but only one 'directory' file at a time.

In the 'script-selector' script:

We use '-selectmode single' on BOTH the 'dirs' listbox and the 'fils' listbox --- because we want to allow the user to select only one script at at time, AND only one 'directory' file at a time.

In the 'directory-selector' script:

There is only one listbox and we use '-selectmode single' because we want to allow the user to 'jump' to only one directory at a time.


Another thing worth noting is the implementation of the 'naroDir', 'wideDir', 'naroFil', 'wideFil', 'tallWin' and 'shortWin' buttons on the 'file(s)-selector' and 'script-selector' GUI's.

I find it frustrating to grab the edge of the window to resize it. It seems that the 'sensitive edge' is only about 1 pixel wide --- perhaps a hold-over from the days when a 640x480 monitor was big.

By simply clicking on the 'naroDir', ... , 'shortWin' buttons, I can resize the listboxes (either width or height) with a minimum of frustration.


If you prefer the vertical-scrollbar of the 'dirs' and 'fils' listboxes --- of the 'file(s)-selector' and 'script-selector' GUI's --- to be on the right rather than the left, you can click on the '<-Ybars->' button --- to toggle the 'y-bars' right and left.


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 hours and hours of 'fractal animation' videos --- until their minds turn to mush.


 Code for Tk script 'tkScriptApplicator_multifilesSelector_2listboxes.tk' :
#!/usr/bin/wish -f
##
## If 'wish' is at a location like /usr/local/bin/wish, not /usr/bin/wish,
## you can do 'sudo ln -s /usr/local/bin/wish /usr/bin/wish' on the
## appropriate host(s).
##
##+########################################################################
## Tk SCRIPT NAME:  tkScriptApplicator_multifilesSelector_2listboxes.tk
##
## WHERE:           typically installed in an 'apps' directory
##                  such as $HOME/apps/tkScriptApplicator
##
##+########
## PURPOSE: This Tk script provides a Graphical User Interface (GUI) for
##          SELECTING A FILE OR FILES --- and this GUI is then used to call
##          an auxiliary Tk GUI script for SELECTING AND APPLYING A SHELL
##          SCRIPT TO THE SELECTED FILE(S).
##
##          An 'Apply-a-Script' button on this GUI is used by the user to invoke
##          the auxiliary script-selector GUI ('scriptSelector_2listboxes.tk')
##          with which to select the script to be applied to the
##          selected file(s).
##
##    ***** This GUI utility is intended to offer an alternative to
##    ***** the Nautilus file-manager of the Gnome 2 desktop environment
##    ***** (or the Caja file-manager of the MATE desktop environment).
##    *****
##    ***** In particular, this utility is an alternative to the 'Nautilus Scripts'
##    ***** 'select-file(s)-and-apply-a-script-to-them' capability
##    ***** that was rather unique to the Nautilus 2.28 file-manager
##    ***** that was available circa 2009 (but has since suffered 'regressions').
##
##          This set of Tk GUI's does not provide the shell scripts
##          that are to be applied to the selected file(s), but
##          such scripts can be found in the typical locations for those
##          scripts in the Gnome 2 and MATE systems:
##                 $HOME/.gnome2/nautilus-scripts
##          or
##                 $HOME/.config/caja/scripts
##
##          Either of those two directory names can be set in a
##          'set DIRshellScripts' statement at the bottom of the
##          script-selector Tk script ('scriptSelector_2listboxes.tk').
##
##          A package of more than 450 'Nautilus scripts' is available at
##          www.freedomenv.com, for installation in either of those
##          shell-scripts directories.
##
##         A third Tk script ('dirSelector_orEditListOfFavorites_1listbox.tk')
##         is also a part of this utility. That Tk script allows the user to
##         quickly 'jump' to another directory of file(s)-to-be-selected.
##         That Tk script allows the user to easily edit a list of
##         'directory favorites' to which the user may quickly jump. This
##         'favorites' feature is similar to the 'directory-Bookmarks' feature
##         in the Nautilus file manager (and in the Gnome 2 desktop top panel).
##
##+###################################
## The GUI DESIGN and OPERATION BASICS:
##
##          The two main Tk GUI's of this utility (the file(s)-selector and the
##          script-selector) could be implemented in many different ways.
##
##          For example, a SINGLE listbox could be used to hold both
##          sub-directory names and filenames of a 'current directory'.
##          But this GUI uses a TWO-listbox layout --- ONE listbox for
##          sub-directory names and ONE listbox for 'regular' filenames.
##
##          The TWO listboxes are side-by-side in this file(s)-selector GUI:
##             - the LEFT listbox for the SUB-DIRECTORIES of the
##               'current directory' (specified at the top of the GUI)
##          and 
##             - the RIGHT listbox for the 'regular' (non-directory) files
##               in the 'current directory'.
##
##          'Hidden' (dot prefixed) directories and files are shown in
##          the two listboxes --- we do not hide them.
##
##          File(s) are selected by clicking on filename(s) in the right-hand,
##          files-listbox --- using the Ctrl and Shift keys as needed, if
##          the user wants to select more than one file.
##
##          This file(s)-selector GUI supports a mask for 'filtering' filenames.
##
##          A 'Jump2dir' button on this GUI calls on the Tk script
##                      'dirSelector_orEditListOfFavorites_1listbox.tk'
##          to allow the user to choose (jump to) a directory from a list
##          of 'favorites'. Once a 'favorite' is chosen, the user is returned
##          to the main GUI which shows the files in the newly selected directory.
##
##          There is a file-mask in a CurrentDirectory-and-FileMask entry field,
##          defaulted to '*'. The user can set that file-mask to filter
##          the filenames being shown. Example masks: *.htm*  OR
##          *.jpg  OR  *.sh  OR  [a-m]*
##
##   A message in the GUI window indicates to the user the main options:
##     - Navigate-dirs                     (click in the left listbox)
##     - QuickChange the current-directory (click on the 'Jump2dir' button)
##     - Select-file(s)                    (click in the right listbox)
##     - Select-and-Apply a shell script   (click on the 'Apply-a-Script' button)
##   In addition, the user can
##     - show Help                         (click on the 'Help' button)
##     - Exit                              (click on the 'Exit' button)
##
##   Other options (such as Rename, Delete, Copy, Move, Properties, etc.)
##   could be added (eventually) to this file(s)-selector GUI to make it
##   a more full-featured file manager.
##
##           The options available to the user in this implmentation
##           are indicated by the following 'sketch' of the GUI:
##
## ---------------------------------------------------------------------
##
## THE SKETCH CONVENTIONS for GUI sketch below:
##
## SQUARE-BRACKETS indicate a comment not to be included on the GUI.
## BRACES          indicate a Tk 'button' widget.
## UNDERSCORES     indicate a Tk 'entry' widget.
## A COLON         indicates that the text before the colon is on
##                 a 'label' widget.
## CAPITAL-O       indicates a Tk 'radiobutton' widget.
## CAPITAL-X       indicates a Tk 'checkbutton' widget.
##
##  ----------------------------------------------------------------------------------------
##  tkScriptApplicator - DirectoryNavigator-FileSelector for the Script-Selector - verYYYMMMDD
##  [window title]
##  ----------------------------------------------------------------------------------------
##
##   TopLevel Frame
##   and SubFrame
##    names
##     |
##     V
## .fRtitle       Purpose: Run a 'Nautilus script' on selected file(s).
##                [This text string is in a label widget in this frame.]
##
## .fRdirmaskHead CURRENT DIRECTORY & FILE MASK:
##
## .fRdirfilmask  {Jump2Dir} $env(HOME)/*_________________________________________________
##
## .fRlists       [This 'fRlists' frame is to contain two subframes --- 'fRdir' and 'fRfil' ---
##                 with 'fRdir' on the left of 'fRfil'.]
##
## .fRlists.fRdir   This frame is to contain a listbox with x-and-y scrollbars.
##                  This frame is also to contain a heading (in a label) above the listbox.
##
## .fRlists.fRfil   This frame is to contain a listbox with x-and-y scrollbars.
##                  This frame is also to contain a heading (in a label) above the listbox.
##
## .fRcntlbutts1   {Exit} {Help} {Apply-a-Script} {Refresh}
##
## .fRcntlbutts2   {naroDir} {wideDir} {naroFil} {wideFil} {tallWin} {shortWin}
##
## .fRstatusmsg    Select-File(s)-and-Apply-a-Script  -OR-  Select-Subdir-to-Navigate  -OR-
##                 Jmp2dir  -OR-  Exit.  OR change the file mask.  Example file masks:  * (all)
##                 OR   *.txt   OR   *.htm*   OR   a*   OR   .*   OR  [a-z]*  OR  *.[gjp][ipn][fg]
##                 Click 'Refresh' button or press Enter after entering a new mask.
##
##                 [This initial message in a label widget may be changed in response to various
##                  user actions on the GUI --- directory selection, etc.]
##
##
##+##############
## GUI components:
##
## From the GUI 'sketch' above, it is seen that the GUI consists of ABOUT:
##
##  11 'button' widgets
##   5 'label'  widgets (mostly for headings)
##   1 'entry'  widget (for the current-directory-and-file-mask)
##   2 'listbox' widgets (with scrollbars)
##   0 'text' widgets
##   0 'checkbutton' widgets
##   0 'radiobutton' widgets
##   0 'scale'  widgets
##   0 'canvas' widgets
##
##+################################
## NOTES on the 'CURRENT DIRECTORY':
##
##    This Tk script accepts the script filename from 'shell-script-selector'
##    Tk script and passes the fully-qualified filenames selected from
##    this 'file(s)-selector' GUI to the user-selected shell-script.
##    In other words, this Tk script does the 'exec' of the selected shell script.
##
##    To mimic the performance of the Nautilus file-manager, just before
##    performing the 'exec', this script changes the 'current-working-directory'
##    to the directory from which the file(s) was/were selected.
##    For details, see the proc 'selectANDapply_script' below.
##
##+####################
## NOTES on 'PLATFORMS' (operating systems):
##
##    The Tcl 'glob' command is used to get the filenames --- both
##    directory names and 'regular' file names --- to fill the 2 listboxes
##    of this 'file(s)-selector' GUI.
##
##    Furthermore, Tcl 'file' commands, such as
##
##     file type $file      (Returns: file, directory, characterSpecial,
##                                    blockSpecial, fifo, link, OR socket)
##     file readlink $file  (Good for finding the target of a link.)
##     file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##     file exists $file
##
##    are used to get the directory names and 'regular' file names for
##    the current directory specified at the top of the GUI. AND,
##    the intent is to handle 'link' files as well as 'directory' and
##    'file' files --- even if a link is 'broken' (pointing to a file
##    that no longer exists).
##
##    With these factors in mind, this 'tkScriptApplicator' mini-system
##    utility will probably support a variety of Linux and Unix hosts
##    (most Linux distros, BSD flavors, HP-UX, IBM-AIX, Sun-Solaris,
##     etc. --- and probably the Apple Mac OS, since it is BSD-based).
##
##    Note that a main requirement is that the 'Nautilus' scripts (that
##    are to be applied to the selected files) are available and run on
##    the user's operating system. ALSO, any utility programs called by
##    those shell scripts need to be available on the the user's
##    operating system and need to be runnable on that operating system.
##
##    So the nature of the 'Nautilus' scripts is a key factor in
##    determining on which operating systems this 'tkScriptApplicator'
##    mini-system can be run.
##
##+########################
## NOTE on 'SYMBOLIC LINKS':
##
##    With respect to the Tcl 'file' statements used in this
##    'file(s)-selector' Tk script, an example of how the listbox-filling
##    code could be constructed can be seen in the 'dirview' script on
##    pages 229-234 of the first edition of the book
##    'Graphical Applications with Tcl & Tk' (1996) by Eric F. Johnson.
##
##    However, that code needs quite a bit of enhancement to handle
##    'symbolic-links' robustly (whether they are broken or intact).
##
##    This Tk script-system is being released in 2014jul in a preliminary
##    form that is intended to handle 'symbolic links' in a robust fashion.
##
##    Some testing has been done in an attempt to implement 'robust navigation'
##    of symbolic-links to directories --- even when the links are 'broken' (not
##    pointing to an existing directory anymore).
##
##       (The 'file(s)-selector' and 'script-selector' GUI's could notify the user
##        of 'broken links' that are encountered. But that has not been done in
##        the 2014jul release. Broken links are simply not shown in the listboxes.)
##
##    Navigating directories that are symbolic-links will typically not be
##    an issue, because the 'file(s)-selector' Tk script is usually selecting
##    files from a user's home directory, and the user is not typically making
##    'symbolic links' in the various sub-directories of his/her home
##    directory.
##
##    Furthermore, navigating symbolic-links will typically not be an issue for
##    the 'script-selector' Tk script, because the sub-directories containing
##    the 'Nautilus' shell scripts are typically not symbolic-links.
##
##    HOWEVER, there are times when it may be nice to make a symbolic link for
##    a directory --- for example, to point to a directory with a longer name,
##    that is, with many more directory levels.
##
##    If it turns out that there is a type of symbolic-link (for example,
##    a symbolic-link that points to a series of symbolic-links, relative
##    and/or absolute paths) that is not handled successfully/properly,
##    note that the user may work around this issue by navigating to the
##    actual target of a 'symbolic link'.
##
##+########################################################################
## STRUCTURE OF THIS CODE:
##
##  0) Set general window parms (win-name, win-position, win-color-scheme,
##     fonts, widget-geom-parms, text-array-for-labels-etc, win-size-control).
##
##  1a) Define ALL frames (and sub-frames, if any).
##  1b) Pack the frames.
##
##  2) Define & pack all widgets in the frames, frame by frame.
##     After all the widgets for a frame are defined, pack them in the frame.
##
##  3) Define keyboard and/or mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (typically with one or more of
##     the procs), if needed.
##
## In more detail, for this particular Tk script:
##
##  1a) Define ALL frames -- and sub-frames:
##
##  '.fRtitle'       to hold a label widget.
##
##  '.fRdirmaskHead' to hold a label widget.
##
##  '.fRdirfilmask'  to hold a button and an entry widget.
##
##  '.fRlists'       to contain the followingtwo subframes --- 'fRdir' and 'fRfil' ---
##                   with 'fRdir' on the left of 'fRfil'.
##
##  '.fRlists.fRdir'  to contain a listbox with x-and-y scrollbars ---
##                    with a label widget above the listbox.
##
##  '.fRlists.fRfil'  to contain a listbox with x-and-y scrollbars ---
##                    with a label widget above the listbox.
##
##  '.fRcntlbutts1'   to contain about 4 button widgets.
##
##  '.fRcntlbutts2'   to contain about 6 button widgets.
##
##  '.fRstatusmsg'    to contain a text widget.
##
##       This is about 7 top-level frames.
##       Most of these toplevel-frames are 1 character high.
##       Exceptions:  '.fRlists' and '.fRstatusmsg'
##
##  1b) Pack ALL these frames and sub-frames.
##
##  2) Define & pack all widgets in these frames -- basically going through
##     frames & their interiors in top-to-bottom and/or left-to-right, order.
##
##  3) Define bindings:  See BINDINGS section below.
##
##     SOME key-bindings are on the directories and files listboxes.
##
##  4) Define procs:   See the PROCS section below.
##
##     SOME of the procs are:
##
##    'fill_lists'           - to fill the 2 listboxes with names
##
##  'get_subdirs_inCURdir_inclLinks' - called by 'fill_lists' proc
##  'get_files_inCURdir_inclLinks'   - called by 'fill_lists' proc
##
##    'get_selected_files'   - to get the user-selected filename(s)
##    'navigate'             - to respond to requests on the dir-listbox
##    'jump2dir'             - called by a click on the 'Jump2dir' button
##   'selectANDapply_script' - called by the 'Apply-a-Script' button
##    'set_status_msg'       - used to set the message at the bottom of the GUI
##
##  The following 6 procs for for the window/widget resizing buttons:
##    'dir_listbox_width_increase'
##    'dir_listbox_width_reduce'
##    'fil_listbox_width_increase'
##    'fil_listbox_width_reduce'
##    'tall_win'
##    'short_win'
##
##    'toggle_fontsize'       - called by the 'TogFontSize' button
##    'toggle_ybars'          - called by the '<-Ybars->' button
##
##  'popup_msgVarWithScroll'  - called by the 'Help' button to show the
##                              $HELPtext var ; also used to show the
##                              user various popup messages for
##                              info, warnings, errors, etc.
##
##  5) ADDITIONAL GUI INITIALIZATION section:
##     See this section at the bottom of this script.
##+########################################################################
## INPUTS: There are no inputs to this Tk script. The user is prompted
##         to select a directory and to select one or more filenames
##         in that directory.
##
##         This Tk-GUI script sets a default for the initial
##         'current directory' --- along with an initial file-mask.
##
##         That initial directory-and-mask is set near the bottom
##         of this Tk script by the statement:
##
##             set DIRFILmask "$env(HOME)/*"
##
##         You can edit the Tk script to change that (and other)
##         initial and parameter settings.
##
##+#####################################################################
## OUTPUTS:
##     None from this GUI, but there may be output via a user-selected
##     shell script that is applied to the user-selected filenames
##     via the 'Apply-a-Script' button of this GUI.
##
##     Outputs from the shell scripts are typically
##        1) new media file(s) --- image, audio, video, 3D --- created from
##           user-selected media file(s)
##     OR
##        2) text output --- such as computer system conditions or
##           file properties --- shown in a popup window with a
##           scrollable text widget
##     OR
##        3) renamed files
##     OR
##        4) 'player' or 'reader' windows, showing selected file(s).
##
##+########################################################################
## INSTALLATION:
##
## The set of several files for this utility consists of several Tk scripts:
##
## - this main Tk script 'tkScriptApplicator_multifilesSelector_2listboxes.tk'
##
## - the Tk script 'scriptSelector_2listboxes.tk'
##
## - the Tk script 'dirSelector_orEditListOfFavorites_1listbox.tk'
##
## These Tk scripts could be put in a sub-directory of the
## user's home directory --- such as $HOME/apps/tkScriptApplicator.
##
## Then the user can use his/her desktop system (such as Gnome or KDE)
## to set up the main Tk script as an icon on the desktop (or in a
## desktop 'panel').
##
## Then, whenever the user wants to apply a 'Nautilus script' to one or
## more files, the user can click on the icon to startup the main Tk script.
##
## The user can change the 'set' statement at the bottom of the
## 'scriptSelector_2listboxes.tk' Tk script to point to the desired
## directory that holds the 'Nautilus scripts'.
##
## Typically, the 'set' statements look like
##
##        set DIRshellScripts "$env(HOME)/.gnome2/nautilus-scripts"
##  OR
##        set DIRshellScripts "$env(HOME)/.config/caja/scripts"
##
## but the 'Nautilus scripts' could be placed in any directory.
## Example: $env(HOME)/ScriptsThatMakeMeHappy
##
##+#####################################################################
## MAIN INTERNAL (GLOBAL & LOCAL) VARS:
##
##   DIRFILmask  Set at script initialization --- and built in a Binding
##               for single-click on a subdir-item in the dir-listbox
##               --- and in a Dir-Fil-Mask entry-field Binding.
##
##   CURdir      Extracted when needed from DIRFILmask.
##
##   FILmask     Extracted when needed from DIRFILmask.
##
##   RELDIRsel   Extracted from 'dir' listbox, in the
##               'navigate' proc.    (Should NOT contain an
##               ending '/' (slash), nor a preceding slash,
##               nor any slashes.)
##
##   FULFILnames  Set from file-listbox, by the 'get_selected_files' proc.
##                The selected script, from 'scriptSelector_2listboxes.tk',
##                is applied to the filename(s) in this var.
##
##  NOTE:  In various parts of this script, at places indicated by a
##         '## FOR TESTING:' line, there are commented 'puts' statements
##         to help in development and testing. In particular,
##         there are commented 'puts' statements in the 'fill_lists' proc
##         (and procs that it calls) that can be uncommented to test/verify
##         the settings of the various 'dir' and 'fil' (and 'mask') vars
##         in various navigation scenarios.
##+########################################################################
## WIDGET NAMING CONVENTION:
##
##   In this script, in a widget hierarchy, the names of
##
##           frames      are prefixed by 'fR'
##           buttons     are prefixed by 'butt' or 'but'
##           labels      are prefixed by 'lab' or 'label'
##           entry-boxes are prefixed by 'ent' or 'entry'
##           listboxes   are prefixed by 'listbox' or 'lbox'
##           scrollbars  are prefixed by 'scrbar'
##           text        are prefixed by 'text'
##
##    Although not used in this script, conventions for other widgets:
##
##        radio-buttons  are prefixed by 'radbutt' or 'radbut'
##        check-buttons  are prefixed by 'chkbutt' or 'chkbut'
##
##    A generic example for an entry widget and its parent frames:
##              .fRtop.fRdir.entDir
##+########################################################################
## NOTE ON SEPARATING CODE FROM COMMENTS:
##
##       You can see only the non-commented, executable lines of this
##       script by using
##              egrep -v '^ *##|^ *# ' <script-name>
##       or
##              grep -v '^ *##' <script-name> | grep -v '^ *# '
##
##       OR, extract most of the comments with
##              egrep '^ *##|^ *# ' <script-name>
##
##+#######################################################################
## 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
## but this script should work in most previous 8.x versions, and probably
## even in some 7.x versions (if font handling is made 'old-style').
##+########################################################################
## MAINTENANCE HISTORY:
## Started by: Blaise Montandon 2014jun28 Started developing this script
##                                        and auxiliary Tk scripts
##                                        based on similar FE scripts from
##                                        the 'feHappyScripts' system.
## Changed by: Blaise Montandon 2014jul18 Removed a lot of unused code from
##                                        the FE 'HappyScript'.
## Changed by: Blaise Montandon 2014jul25 Started putting code in the
##                                        'fill_lists' proc.
## Changed by: Blaise Montandon 2014jul27 Replaced an 'ls -Ap' technique
##                                        by a 'glob' technique. 
##+########################################################################
## HEY, ME!
## IF YOU UPDATE THIS SCRIPT, REMEMBER TO CHANGE THE FOLLOWING VERSION ID!!!
##+########################################################################
## For window title:
set VERSIONfilesel "ver2014jul29"

##+######################################################
## Set WINDOW TITLE and POSITION.
##+######################################################

wm title . \
"tkScriptApplicator - This is the DirectoryNavigator-and-FileSelector for the Script-Selector - $VERSIONfilesel"

wm iconname . "tkScriptApplicator"

wm geometry . +15+30


##+######################################################
## Set the COLOR SCHEME for the window and its widgets ---
## such as listbox and entry field background color.
##+######################################################

tk_setPalette "#e0e0e0"

# set titleBKGD  "#b0b0b0"
set titleBKGD  "#99ff99"

set entryBKGD   "#ffffff"
set listboxBKGD "#ffffff"

# set STATUSmsgBKGD "#f0f0f0"
set STATUSmsgBKGD "#ff9999"

# set radbuttBKGD "#c0c0c0"
# set chkbuttBKGD "#c0c0c0"
# set scaleBKGD   "#f0f0f0"


##+########################################################
## DEFINE (temporary) FONT NAMES.
##
## We use a VARIABLE-WIDTH font for text on LABEL and
## BUTTON widgets.
##
## We use a FIXED-WIDTH font for LISTBOX lists,
## for text in ENTRY fields --- and often for text in
## TEXT widgets.
##
## 'weight' can be 'bold' or 'normal'.
## 'slant'  can be 'roman' or 'italic'.
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -12 \
   -weight normal \
   -slant roman

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

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


font create fontTEMP_fixedwidth  \
   -family {droid sans mono} \
   -size -12 \
   -weight normal \
   -slant roman

font create fontTEMP_SMALL_fixedwidth  \
   -family {droid sans mono} \
   -size -10 \
   -weight normal \
   -slant roman

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


##+###########################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. width and height of canvas, and padding for Buttons)
##+###########################################################

## Some WINDOW-MANAGER BORDER width settings:
## (We could try to set these parameters via a re-usable proc
##  that uses the 'wm' and 'winfo' commands. But that may not
##  work for all window managers and operating systems.
##  So we simply set the values here.)

set wmPIXELS_left 3
set wmPIXELS_top 23


## LABEL widget geom settings:

set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2
set RELIEF_label_lo "flat"


## BUTTON widget geom settings:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2
## We use '-relief raised' for all 'button' widgets.


## ENTRY widget geom settings:

set BDwidthPx_entry 2
## We use '-relief sunken' for all 'entry' widgets.


## LISTBOX widget geom settings:

set initWIDTHchars_dir_listbox 12
set initWIDTHchars_fil_listbox 25
set initHEIGHTchars_fil_listbox 15
set minHEIGHTchars_fil_listbox 1

set BDwidthPx_listbox 2
## We use '-relief sunken' for the 'listbox' widget.


## TEXT widget geom settings:

set BDwidthPx_text 2
# set RELIEF_numtext "ridge"


## We nullify the following settings. NOT USED, yet.
if {0} {

## RADIOBUTTON widget geom settings:

set PADXpx_radbutton 0
set PADYpx_radbutton 0
set BDwidthPx_radbutt 2
set RELIEF_radbutt_hi "raised"


## CHECKBUTTON widget geom settings:

set PADXpx_chkbutton 0
set PADYpx_chkbutton 0
set BDwidthPx_chkbutt 2
set RELIEF_chkbutt_hi "raised"


## SCALE widget geom parameters:

# set BDwidthPx_scale 2
# set scaleThicknessPx 10

}
## END OF if {0}  (to nullify these settings)


##+####################################################################
## 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 the 'fRtitle' frame:

set aRtext(titleMessage) \
"Purpose: Run a 'Nautilus script' on selected file(s)."

## For the 'fRdirfilmask' frame:

set aRtext(buttJump2Dir)  "Jump2Dir"
set aRtext(labCurDirMask) "CURRENT DIRECTORY & FILE MASK:"


## For the 'fRlists.fRdir' frame:

set aRtext(labListSubdirs) "Parent Dir \[..\]  &  Sub-Dirs
\[ Navigate = Single-Click \]"


## For the 'fRlists.fRfil' frame:

set aRtext(labListFiles) "Files
\[ Select = Single-Click \]"


## For the 'fRcntlbutts1' frame:

set aRtext(buttExit)        "Exit"
set aRtext(buttHelp)        "Help"
set aRtext(buttApplyScript) "Apply-a-Script"
set aRtext(buttRefresh)     "Refresh"
set aRtext(buttFontSize)    "TogFontSize"
set aRtext(YbarsSIDE)       "<-Ybars->"

## For the 'fRcntlbutts2' frame:

set aRtext(buttDirListLeft)  "naroDir"
set aRtext(buttDirListRight) "wideDir"
set aRtext(buttFilListLeft)  "naroFil"
set aRtext(buttFilListRight) "wideFil"
set aRtext(buttTallWin)      "tallWin"
set aRtext(buttShortWin)     "shortWin"


## For the 'fRstatusmsg' frame:

set aRtext(filledSTATUSmsg) \
"Select-File(s)-and-Apply-a-Script  -OR-  Select-Subdir-to-Navigate  -OR-
Jmp2dir  -OR-  Exit.  OR change the file mask.  Example file masks:  * (all)
OR   *.txt   OR   *.htm*   OR   a*   OR   .*   OR  \[a-z\]*  OR  *.\[gjp\]\[ipn\]\[fg\]
Click 'Refresh' button or press Enter after entering a new mask."

set aRtext(twolineSTATUSmsg) \
"Select-File(s)-and-Apply-a-Script  -OR-  Select-Subdir-to-Navigate  -OR-
Jmp2dir  -OR-  Exit."

set aRtext(unfilledSTATUSmsg) \
"If 'Current-Directory-and-FileMask' is OK, click the 'Refresh' button
to fill the two listboxes.  Then  Select-File(s)-and-Apply-a-Script  -OR-
Select-Subdir-to-Navigate  -OR-  Jmp2dir  -OR-  Exit."

set aRtext(maskExamplesSTATUSmsg) \
"If the Directory-and-File-Mask is OK,  **CLICK 'Refresh' TO START**.

Examples of valid file masks:
  * (all files)   OR   *.txt   OR   *.htm*   OR   a*   OR   .*   OR  \[a-c\]*"

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


##+######################################################################
## Set a MIN-SIZE of the window (roughly).
##
## For WIDTH, allow for a min-width for the side-by-side sub-directories
## and files listboxes.
##
## For HEIGHT, allow for the stacked frames:
##      1 char   high for the '.fRtitle'        frame
##      1 char   high for the '.fRdirmaskHead'  frame
##      1 char   high for the '.fRdirfilmask'   frame
##      N chars  high for the '.fRlists'        frame
##      1 char   high for the '.fRcntlbutts1'   frame
##      1 char   high for the '.fRcntlbutts2'   frame
##      1 char   high for the '.fRstatusmsg'    frame
##+#####################################################################

## FOR WIDTH:
## We set the min-width according to character units.
## We allow for about 10 characters minimum for the directories
## listbox and about 20 chars minimum for the files listbox.

set charWidthPx [font measure fontTEMP_varwidth "W"]
set DIRminwidthPX [expr {10 * $charWidthPx}]
set FILminwidthPX [expr {20 * $charWidthPx}]
set minWinWidthPx [expr {$DIRminwidthPX + $FILminwidthPX}]

## We will use the DIRminwidthPX,FILminwidthPX vars in
## some procs below for the 'naroDir' and 'naroFil' buttons.

##+#################################################
## For HEIGHT --- for
##      1 char   high for the '.fRtitle'        frame
##      1 char   high for the '.fRdirmaskHead'  frame
##      1 char   high for the '.fRdirfilmask'   frame
##      N chars  high for the '.fRlists'        frame
##      1 char   high for the '.fRcntlbutts1'   frame
##      1 char   high for the '.fRcntlbutts2'   frame
##      4 chars  high for the '.fRstatusmsg'    frame
##    --------
##    9+N chars  high for the 7 frames
## where N =  $minHEIGHTchars_fil_listbox + 3 chars, where
## 3 chars allows for a 2-line label above the listboxes and
## allows (roughly) for the horizontal scrollbar of the 2 listboxes.
##+#################################################

set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {(12 + $minHEIGHTchars_fil_listbox) * $charHeightPx}]

## Add about 25 pixels for top-bottom window decoration --
## and some pixels for top-and-bottom of frame/widget borders
## (~7 widgets x 4 pixels/widget = 28 pixels).

set minWinHeightPx [expr {53 + $minWinHeightPx}]


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

wm minsize . $minWinWidthPx $minWinHeightPx

## We need to allow the window to be resizable ---
## so that the 'fRdir' and 'fRfil' frames can expand,
## both horizontally and vertically.

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

## If you want the window to resize in x-direction, but not y.
#   wm resizable . 1 0


##+#######################################################################
##+#######################################################################
##
##  DEFINE *ALL* TOP-LEVEL FRAMES -- (top to bottom):
##
##     - 'fRtitle'        to contain a label widget
##     - 'fRdirmaskHead'  to contain a label widget
##     - 'fRdirfilmask'   to contain a button and an entry widget
##     - 'fRlists'        to contain 'dir' & 'fil' frames, side-by-side
##     - 'fRcntlbutts1'   to contain bottom buttons - Exit,Help,Apply-Script,...
##     - 'fRcntlbutts2'   to contain more bottom buttons
##     - 'fRstatusmsg'    to contain a label widget
##
##  Also define ALL the SUB-FRAMES --- the two side-by-side SUB-FRAMES
##  within 'fRlists' -- 'fRlists.fRdir'  &  'fRlists.fRfil'.
##+#######################################################################
##+#######################################################################

set RELIEF_frame flat
set BDwidthPx_frame 0

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

frame .fRtitle          -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRdirmaskHead    -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRdirfilmask     -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRlists          -relief $RELIEF_frame   -bd $BDwidthPx_frame

frame .fRcntlbutts1     -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRcntlbutts2     -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRstatusmsg      -relief $RELIEF_frame   -bd $BDwidthPx_frame

## The SUB-FRAMES:

frame .fRlists.fRdir    -relief $RELIEF_frame   -bd $BDwidthPx_frame
frame .fRlists.fRfil    -relief $RELIEF_frame   -bd $BDwidthPx_frame


##+########################################################
## PACK *ALL* the top-level FRAMES.
##+########################################################
##  The following direct, top-to-bottom approach
##  would be nice, but it does not have
##  good control of various frames.
##
##  We want 'fRlists' to 'fill y' and be able to y-expand,
##  as well as 'fill x' and be able to x-expand  --- but
##  we do not want other frames ('fRtitle', 'fRdirfilmask',
##  'fRstatusmsg', 'fRcntlbutts1') to y-expand.
##
##+########################################################
## WE WILL HAVE TO PACK THEM IN SUB-GROUPS, *NOT* like this:
##
## pack  .fRtitle \
##       .fRdirmaskHead \
##       .fRdirfilmask \
##       .fRlists \
##       .fRcntlbutts1 \
##       .fRcntlbutts2 \
##       .fRstatusmsg \
##    -side top \
##    -anchor nw \
##    -fill both \
##    -expand 1  (1 = true)
##
##+########################################################

##+########################################################
## We want the following for the 'fRlists.fRdir' &
## 'fRlists.fRfil' SUB-FRAMES:
##             -fill both  -expand 1
## so that the listboxes within these frames can expand.
##+########################################################
## GENERAL 'pack' RULE FOR A LISTBOX & ITS SCROLLBARS:
##
##    To allow a listbox  to expand-xy
##    and its y-scrollbar to expand-y
##    and its x-scrollbar to expand-x,
##
##    any 'parent'-frames containing these widgets
##    should use parms:
##           -fill both   -expand 1
##
##    For packing the widgets themselves, we use:
##       y-scrollbar:  -fill y      -expand 0
##       x-scrollbar:  -fill x      -expand 0
##       listbox:      -fill both   -expand 1
##    The order of packing these widgets is important.
##    See comments below, where these widgets are packed.
##+########################################################


##+##########################################################
## PACK 'fRtitle' & 'fRdirmaskHead' & 'fRdirfilmask' FRAMEs
## --- at TOP of window, top to bottom.
##+##########################################################
## DO NOT USE '-expand 1' HERE.
## THAT WOULD ALLOW these FRAMES TO Y-EXPAND.
##+##########################################################

pack  .fRtitle \
      .fRdirmaskHead \
      .fRdirfilmask \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

##+############################################################
## PACK 'fRcntlbutts1' & 'fRcntlbutts2' & 'fRstatusmsg' FRAMEs
## --- at BOTTOM of window.
##+############################################################
## DO NOT USE '-expand 1' HERE; THAT WOULD KEEP the
## 'lists' LISTBOX FRAME FROM Y-EXPANDING.
##+############################################################

pack .fRstatusmsg \
     .fRcntlbutts2 \
     .fRcntlbutts1 \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0


##+##############################################################
## PACK 'fRlists' FRAME  --
## *UNDER* 'fRtitle' & 'fRdirmaskHead' & 'fRdirfilmask' FRAMEs
## AND
## *OVER* 'fRstatusmsg' & 'fRcntlbutts1' & 'fRcntlbutts2' FRAMEs.
## where
##   'fRlists'  will contain two side-by-side frames --
##   'fRdir', 'fRfils' -- containing a listbox and
##   xy-scrollbars each.
##+############################################################
## NOTE: We pack the 'fRlists' frame AFTER the bottom 3 frames,
## to keep the bottom 3 frames from 'scrunching up'
## when the user grabs the bottom edge of the window
## and moves it up. I.e. we want to make the 'fRlist' frame
## scrunch up before the bottom 3 frames scrunch up.
##+############################################################

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


##+##############################################################
## PACK THE two SUB-FRAMES of the 'fRlists' FRAME --
## 'fRdir' and 'fRfil'.
##+##############################################################
## '-fill both'  AND  '-expand 1'
## SHOULD BE USED HERE, TO ALLOW THE LISTBOX IN '.fRlists.fRdir'
## and THE LISTBOX IN '.fRlists.fRfil' TO FILL AND XY-EXPAND.
##+##############################################################

pack .fRlists.fRdir \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

pack .fRlists.fRfil \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1


##+################################################################
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES.
##+################################################################


##+##################################################
## IN THE '.fRtitle' FRAME --
## DEFINE A LABEL WIDGET.   THEN PACK IT.
##+##################################################

label .fRtitle.lab \
   -text "$aRtext(titleMessage)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -bd $BDwidthPx_label \
   -relief raised \
   -bg "$titleBKGD"

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


##+##################################################
## IN THE 'fRdirmaskHead' FRAME --
## DEFINE a LABEL WIDGET. THEN PACK IT.
##+##################################################

label .fRdirmaskHead.lab \
   -text "$aRtext(labCurDirMask)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -bd 0 \
   -relief flat

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


##+##################################################
##  IN THE 'fRdirfilmask' FRAME --
##  DEFINE 'Jump2dir'-BUTTON and an ENTRY WIDGET.
##  THEN PACK THEM.
##+##################################################
## We do not specify a width of the 'entry' widget.
## The '-fill x -expand 1' parameters that we use
## to pack it allows it to expand into this frame.
## The parent frame of the entry widget was packed
## with '-fill x -expand 0'. This '-expand 0' keeps
## the entry widget from expanding in the y-direction.
##+##################################################

button .fRdirfilmask.buttJump2Dir \
   -text "$aRtext(buttJump2Dir)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -command {jump2dir}

entry .fRdirfilmask.entryDIRFILmask \
   -textvariable DIRFILmask \
   -font fontTEMP_fixedwidth \
   -relief sunken \
   -bd $BDwidthPx_entry \
   -bg $entryBKGD


## PACK all the widgets in frame '.fRdirfilmask'.

pack .fRdirfilmask.buttJump2Dir \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

pack .fRdirfilmask.entryDIRFILmask \
   -side left \
   -anchor w \
   -fill x \
   -expand 1


##+###############################################################
## In the 'fRlists.fRdir' FRAME -- DEFINE WIDGETS:
##     - LABEL (to be at top)
##     - SCROLLBAR-Y and LISTBOX  (to be left-to-right)
##     - SCROLLBAR-X (to be at bottom).
## THEN PACK THEM.
##+###############################################################
## NOTE: Set 'selectmode' of dir-listbox  to 'single'.
##+###############################################################

label .fRlists.fRdir.lab \
   -text "$aRtext(labListSubdirs)" \
   -width $initWIDTHchars_dir_listbox \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -bd 0 \
   -relief flat

listbox .fRlists.fRdir.listbox \
   -width $initWIDTHchars_dir_listbox \
   -font fontTEMP_fixedwidth \
   -bd $BDwidthPx_listbox \
   -relief sunken \
   -bg $listboxBKGD \
   -selectmode single \
   -yscrollcommand ".fRlists.fRdir.scrbary set" \
   -xscrollcommand ".fRlists.fRdir.scrbarx set"

scrollbar .fRlists.fRdir.scrbary \
   -orient vertical \
   -command ".fRlists.fRdir.listbox yview"

scrollbar .fRlists.fRdir.scrbarx \
   -orient horizontal \
   -command ".fRlists.fRdir.listbox xview"

##+###################################################
## PACK all the widgets in frame 'fRlists.fRdir' ---
## lab,scrbarx,scrbary,listbox -- in that order.
##+###################################################
## DO NOT USE '-expand 1' for the X-SCROLLBAR.
## THAT WOULD PREVENT ITS LISTBOX FROM Y-EXPANDING.
##+###################################################
## DO NOT USE '-expand 1' for the Y-SCROLLBAR.
## THAT WOULD ALLOW the Y-SCROLLBAR TO X-EXPAND, which
## PUTS BLANK SPACE BETWEEN Y-SCROLLBAR & ITS LISTBOX.
##+###################################################

pack .fRlists.fRdir.lab \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRlists.fRdir.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRlists.fRdir.scrbary \
   -side left \
   -anchor nw \
   -fill y \
   -expand 0

##+###################################################
## NOTE: WE PACK THE SCROLL BARS BEFORE THE LISTBOX --
## OTHERWISE THE LISTBOX WOULD HOG THE FRAME-SPACE.
##+###################################################

pack .fRlists.fRdir.listbox \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1


##+################################################################
## In the 'fRlists.fRfil' FRAME -- DEFINE WIDGETS:
##     - LABEL (to be at top)
##     - LISTBOX and SCROLLBAR-Y (to be left-to-right)
##     - SCROLLBAR-X (to be at bottom).
## THEN PACK THEM.
##+################################################################
## NOTE: Set 'selectmode' of fil-listbox  to 'extended', to
## allow for selection of mutliple files, with Ctrl & Shift keys.
##+################################################################

label .fRlists.fRfil.lab \
   -text "$aRtext(labListFiles)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -bd 0 \
   -relief flat

listbox .fRlists.fRfil.listbox \
   -width $initWIDTHchars_fil_listbox \
   -height $initHEIGHTchars_fil_listbox \
   -font fontTEMP_fixedwidth \
   -bd $BDwidthPx_listbox \
   -relief sunken \
   -bg $listboxBKGD \
   -selectmode extended \
   -yscrollcommand ".fRlists.fRfil.scrbary set" \
   -xscrollcommand ".fRlists.fRfil.scrbarx set"

scrollbar .fRlists.fRfil.scrbary \
     -orient vertical \
     -command ".fRlists.fRfil.listbox yview"

scrollbar .fRlists.fRfil.scrbarx \
     -orient horizontal \
     -command ".fRlists.fRfil.listbox xview"

##+##################################################
## PACK all the widgets in frame 'fRlists.fil' ---
## lab,scrbarx,scrbary,listbox -- in that order.
##+##################################################
## DO NOT USE '-expand 1' for the X-SCROLLBAR.
## THAT WOULD PREVENT ITS LISTBOX FROM Y-EXPANDING.
##+##################################################
## DO NOT USE '-expand 1' for the Y-SCROLLBAR.
## THAT WOULD ALLOW the Y-SCROLLBAR TO X-EXPAND, which
## PUTS BLANK SPACE BETWEEN Y-SCROLLBAR & ITS LISTBOX.
##+##################################################

pack .fRlists.fRfil.lab \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRlists.fRfil.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRlists.fRfil.scrbary \
   -side left \
   -anchor nw \
   -fill y \
   -expand 0


##+###################################################
## NOTE: WE PACK THE SCROLL BARS BEFORE THE LISTBOX --
## OTHERWISE THE LISTBOX WOULD HOG THE FRAME-SPACE.
##+###################################################

pack .fRlists.fRfil.listbox \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1


##+#############################################
## In the 'fRcntlbutts1' FRAME ---
## DEFINE many BUTTON WIDGETS: 'Exit','Help',
## 'Apply-a-Script', 'Refresh',...
## THEN PACK THEM.
##+#############################################

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

button .fRcntlbutts1.buttHelp \
   -text "$aRtext(buttHelp)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {popup_msgVarWithScroll .topHELP "$HELPtext"}

button .fRcntlbutts1.buttApplyScript \
   -text "$aRtext(buttApplyScript)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {selectANDapply_script}

button .fRcntlbutts1.buttRefresh \
   -text "$aRtext(buttRefresh)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {fill_lists}

button .fRcntlbutts1.buttFontSize \
   -text "$aRtext(buttFontSize)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {toggle_fontsize}

button .fRcntlbutts1.buttYbarsSIDE \
   -text "$aRtext(YbarsSIDE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {toggle_ybars}

##+##############################################
## PACK all the widgets in frame '.fRcntlbutts1'.
##+##############################################
## NOTE: You can change the left-to-right order of
##       the buttons by changing this packing order.
##+#################################################

pack .fRcntlbutts1.buttExit \
     .fRcntlbutts1.buttApplyScript \
     .fRcntlbutts1.buttHelp \
     .fRcntlbutts1.buttRefresh \
     .fRcntlbutts1.buttFontSize \
     .fRcntlbutts1.buttYbarsSIDE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+#################################################
## FOLLOWING ARE SOME BUTTONS THAT COULD ALLOW
## FOR THIS FILE-SELECTOR GUI TO BECOME MORE LIKE
## A FILE-MANAGER GUI.
## (But note that a lot of these functions can
##  be performed by Nautilus scripts.)
##+#################################################

## COMMENT THESE DEFINITIONS, for now.
if {0} {

set EditPID ""

button .fRcntlbutts1.butEditFile \
   -text "$aRtext(EDITtxtfil)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {edit_txtfil}

set ViewerPID ""

button .fRcntlbutts1.butViewFile \
   -text "$aRtext(VIEWtxtfil)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {view_txtfil}

button .fRcntlbutts1.butFileInfo \
   -text "$aRtext(FileSelINFO)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {file_info}

button .fRcntlbutts1.butAllFilesInfo \
   -text "$aRtext(AllFilesINFO)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {all_files_info}

button .fRcntlbutts1.butRmFile \
   -text "$aRtext(REMOVEfile)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {rm_file}


##+########################################################
## PACK the additional file-management BUTTONS - side-right.
##+########################################################

pack .fRcntlbutts1.butRmFile \
     .fRcntlbutts1.butAllFilesInfo \
     .fRcntlbutts1.butFileInfo \
     .fRcntlbutts1.butViewFile \
     .fRcntlbutts1.butEditFile \
  -side right \
  -anchor e \
  -fill none \
  -expand 0

}
## END OF if {0}
## (to comment these 'additional file-management' buttons, for now)


##+#############################################
## In the 'fRcntlbutts2' FRAME ---
## DEFINE  6 BUTTON WIDGETS --- for
## resizing the 'dir' and 'fil' listboxes.
## THEN PACK THEM.
##+#############################################

button .fRcntlbutts2.butDirListLeft \
   -text "$aRtext(buttDirListLeft)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {dir_listbox_width_reduce}

button .fRcntlbutts2.butDirListRight \
   -text "$aRtext(buttDirListRight)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {dir_listbox_width_increase}

button .fRcntlbutts2.butFilListLeft \
   -text "$aRtext(buttFilListLeft)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {fil_listbox_width_reduce}

button .fRcntlbutts2.butFilListRight \
   -text "$aRtext(buttFilListRight)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {fil_listbox_width_increase}

button .fRcntlbutts2.butTallWin \
   -text "$aRtext(buttTallWin)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {tall_win}

button .fRcntlbutts2.butShortWin \
   -text "$aRtext(buttShortWin)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {short_win}


##+#################################################
## Pack all the widgets in frame '.fRcntlbutts2'.
##+#################################################
## NOTE: You can change the left-to-right order of
##       the buttons by changing this packing order.
##+#################################################

pack .fRcntlbutts2.butDirListLeft \
     .fRcntlbutts2.butDirListRight \
     .fRcntlbutts2.butFilListLeft \
     .fRcntlbutts2.butFilListRight \
     .fRcntlbutts2.butTallWin \
     .fRcntlbutts2.butShortWin \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+#########################################
## In 'fRstatusmsg' FRAME --
## DEFINE A LABEL WIDGET. THEN PACK IT.
##+#########################################

label .fRstatusmsg.lab \
   -text "$aRtext(filledSTATUSmsg)" \
   -font fontTEMP_fixedwidth \
   -justify left \
   -anchor w \
   -bd $BDwidthPx_label \
   -relief raised \
   -bg "$STATUSmsgBKGD"

pack .fRstatusmsg.lab \
   -side top \
   -anchor w \
   -fill x \
   -expand 0

##+#####################################################################
##+#####################################################################
## END OF MAIN SECTION TO SETUP THE GUI.
##+#####################################################################
##+#####################################################################


##+#####################################################################
##+#####################################################################
## DEFINE BINDINGS (to procs):
##
##  Fill-listboxes/Navigate(chgdir)/Select-file(s) bindings:
##
##   - .fRdirfilmask.entryDIRFILmask   <Key-Return>    (fill_lists)
##
##   - .fRlists.fRdir.listbox <ButtonRelease-1>        (navigate)
##   - .fRlists.fRfil.listbox <ButtonRelease-1>        (select)
##
##  Scroll-thru-listboxes *keyboard*-bindings:
##
##   - .fRlists.fRdir.listbox    <Enter>      (focus on mouse-entry)
##   - .fRlists.fRfil.listbox    <Enter>      (focus on mouse-entry)
##
##   - .fRlists.fRdir.listbox    <Home>        (top of box)
##   - .fRlists.fRfil.listbox    <Home>        (top of box)
##   - .fRlists.fRdir.listbox    <End>         (bottom of box)
##   - .fRlists.fRfil.listbox    <End>         (bottom of box)
##
##   - Listbox    <Control-Home>              (override default behavior)
##   - Listbox    <Control-End>               (override default behavior)
##
##   - .fRlists.fRdir.listbox    <Prior>       (page up)
##   - .fRlists.fRfil.listbox    <Prior>       (page up)
##   - .fRlists.fRdir.listbox    <Next>        (page down)
##   - .fRlists.fRfil.listbox    <Next>        (page down)
##
##   - .fRlists.fRdir.listbox    <Key-Up>      (1 line up)
##   - .fRlists.fRfil.listbox    <Key-Up>      (1 line up)
##   - .fRlists.fRdir.listbox    <Key-Down>    (1 line down)
##   - .fRlists.fRfil.listbox    <Key-Down>    (1 line down)
##
##+#####################################################################
##+#####################################################################

##+#########################################################
## BINDING  .fRdirfilmask.entryDIRFILmask <Key-Return>
##+#########################################################
## PURPOSE: Fills the subdirs & fils listboxes.
##          Simply calls the 'fill_lists' proc.
##+#########################################################
## CALLED BY:
##   THE USER HITTING RETURN AFTER EDITING THE DIR&FILE-MASK
##   ENTRY FIELD in the '.fRdirfilmask.entry' widget.
##+#########################################################

bind .fRdirfilmask.entryDIRFILmask <Key-Return> {fill_lists}


##+##########################################################
## BINDING  .fRlists.fRdir.listbox <ButtonRelease-1>
##+##########################################################
## PURPOSE: Runs the 'navigate' proc, which uses a selected
##          subdir and the 'current directory' to
##          change the 'current directory' and exec the
##          'fill_lists' proc to refresh the listboxes.
##+#########################################################
## CALLED BY:
##      A MOUSE-BUTTON-1 SINGLE-CLICK (& release)
##      ON A (SUB)DIRECTORY in the
##      '.fRlists.fRdir.listbox' widget.
##+##########################################################
##  This binding implements navigating the directory
##  structure via the 'navigate' proc.  The 'navigate' proc
##   1) resets the DIRFILmask entry
##   2) runs the 'fill_lists' procedure.
##+##########################################################

bind .fRlists.fRdir.listbox <ButtonRelease-1> {navigate}


##+################################################################
##  KEYBOARD BINDINGS FOLLOW:
##+################################################################


##+################################################################
## BINDING  all <Escape>          (not implemented currently)
##+################################################################
##  Executes if
##      THE ESCAPE KEY IS HIT (while cursor is in the window).
##+################################################################

# bind all <Escape> {
#   exit
# }


##+#################################################
## FOR TESTING KEY-BINDINGS:
##     TO SHOW THE 'KEYSYM' FOR A PARTICULAR KEY
##     on this system.   Uses a binding to the widget
##     '.' and event  <Any-Key>  .
##+#################################################
## Reference: the Brent Welch book, 'Practical
## Programming in Tcl and Tk', Chapter 23, 'Binding
## Commands to Events, page 291.
##+#################################################
#
#  bind . <Any-Key>  {
#
#     ## FOR TESTING:
#     # puts "In widget '.' : Keysym for key pressed: %K"
#
#     puts "In widget '.' , for a key press :"
#     puts "Keysym:  %K    Print-char-and-modifiers: %A"
#     puts ""
#
#  }
#  ## END OF BINDING for . <Any-Key>



##+########################################################
## TEST: Show default bindings of  .fRlists.fRdir.listbox
##       with 'bindtags':
##+#################################################
## Reference: the Brent Welch book, 'Practical
## Programming in Tcl and Tk', Chapter 23, 'Binding
## Commands to Events, page 287.
##+########################################################
## SHOWS:   BINDtags: .fRlists.fRdir.listbox Listbox . all
##+########################################################
#
#  set BINDtags [ bindtags .fRlists.fRdir.listbox ]
#  puts ".fRlists.fRdir.listbox BINDtags: $BINDtags"


##+#######################################################
## If it were needed, we could
##       OVER-RIDE THE Class 'Listbox' BINDINGS,
## WITH THE BINDINGS WE SET FOR '.fRlists.fRdir.listbox'.
##+#######################################################
#   bindtags .fRlists.fRdir.listbox [ list .fRlists.fRdir.listbox ]
##  OR:
#   bindtags .fRlists.fRdir.listbox \
#                             [ list Listbox . .fRlists.fRdir.listbox all ]

##+##################################################
## TEST: Show new bindings with 'bindtags':
##+##################################################
#
#    set BINDtags [ bindtags .fRlists.fRdir.listbox ]
#    puts ".fRlists.fRdir.listbox BINDtags: $BINDtags"


##+#########################################################
## <Enter> BINDINGs:
##
##         - .fRlists.fRdir.listbox   <Enter>
##         - .fRlists.fRfil.listbox   <Enter>
##
## PURPOSE:
##     To put keyboard focus into one of these two
##     listboxes when the mouse cursor enters it
##     --- SO THAT THE FOLLOWING KEYBOARD BINDINGS
##     CAN TAKE EFFECT --- for keys:
##     Home, End, Prior, Next, up-arrow, down-arrow.
##+#########################################################

bind .fRlists.fRdir.listbox <Enter>  {focus .fRlists.fRdir.listbox}

bind .fRlists.fRfil.listbox <Enter>  {focus .fRlists.fRfil.listbox}


##+####################################################
##  <Home> key BINDINGs:
##         - .fRlists.fRdir.listbox   <Home>
##         - .fRlists.fRfil.listbox   <Home>
##+####################################################

bind .fRlists.fRdir.listbox <Home>  {
   .fRlists.fRdir.listbox see 1
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Home>

bind .fRlists.fRfil.listbox <Home>  {
   .fRlists.fRfil.listbox see 1
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Home>


##+####################################################
##  <End> key BINDINGs:
##         - .fRlists.fRdir.listbox   <End>
##         - .fRlists.fRfil.listbox   <End>
##+####################################################

bind .fRlists.fRdir.listbox <End>  {
   .fRlists.fRdir.listbox see end
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <End>


bind .fRlists.fRfil.listbox <End>  {
   .fRlists.fRfil.listbox see end
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <End>


##+####################################################
## De-activate the default <Control-Home> binding for
## Listboxes -- to avoid selecting top entry when
## <Home> key is used.
##+####################################################

bind Listbox <Control-Home>  {}

##+####################################################
## De-activate the default <Control-End> binding for
## Listboxes -- to avoid selecting bottom entry when
## <End> key is used.
##+####################################################

bind Listbox <Control-End>  {}


##+####################################################
## <Prior> BINDINGs:      (Page-Up key)
##         - .fRlists.fRdir.listbox   <Prior>
##         - .fRlists.fRfil.listbox   <Prior>
##+####################################################

bind .fRlists.fRdir.listbox <Prior>  {
   .fRlists.fRdir.listbox  yview scroll -1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Prior>

bind .fRlists.fRfil.listbox <Prior>  {
   .fRlists.fRfil.listbox  yview scroll -1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Prior>


##+####################################################
##  <Next> BINDINGs:      (Page-Down key)
##         - .fRlists.fRdir.listbox   <Next>
##         - .fRlists.fRfil.listbox   <Next>
##+####################################################

bind .fRlists.fRdir.listbox <Next>  {
   .fRlists.fRdir.listbox yview scroll +1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Next>

bind .fRlists.fRfil.listbox <Next>  {
   .fRlists.fRfil.listbox yview scroll +1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Next>


##+####################################################
##  <Key-Up> BINDINGs:      (Up-arrow key)
##         - .fRlists.fRdir.listbox   <Key-Up>
##         - .fRlists.fRfil.listbox   <Key-Up>
##+####################################################

bind .fRlists.fRdir.listbox <Key-Up>  {
   .fRlists.fRdir.listbox yview scroll -1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Key-Up>

bind .fRlists.fRfil.listbox <Key-Up>  {
   .fRlists.fRfil.listbox yview scroll -1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Key-Up>


##+####################################################
##  <Key-Down> BINDINGs:      (Down-arrow key)
##         - .fRlists.fRdir.listbox   <Key-Down>
##         - .fRlists.fRfil.listbox   <Key-Down>
##+####################################################

bind .fRlists.fRdir.listbox <Key-Down>  {
   .fRlists.fRdir.listbox  yview scroll +1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Key-Down>

bind .fRlists.fRfil.listbox <Key-Down>  {
   .fRlists.fRfil.listbox  yview scroll +1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Key-Down>


##+######################################################
## FOR TESTING: (show current bindings ---
## to bindtags '.fRlists.fRdir' & 'Frame' & '.' & 'all').
##+######################################################
#
#   set BINDings [ bind .fRlists.fRdir ]
#   puts ".fRlists.fRdir BINDings: $BINDings"
#
#   set BINDings [ bind .fRlists.fRdir <End> ]
#   puts ".fRlists.fRdir <End> BINDings: $BINDings"
#
#   set BINDings [ bind Frame ]
#   puts "Frame BINDings: $BINDings"
#
#   set BINDings [ bind . ]
#   puts ". BINDings: $BINDings"
#
#   set BINDings [ bind all ]
#   puts "all BINDings: $BINDings"


##+#################################################################
##+#################################################################
##  END OF BINDINGS SECTION
##+#################################################################
##+#################################################################



##+#################################################################
##+#################################################################
## DEFINE PROCS:
##
##  'fill_lists'  - fills the dirs and fils listboxes based on var DIRFILmask
##
##  'get_subdirs_inCURdir_inclLinks' - called by 'fill_lists' proc
##  'get_files_inCURdir_inclLinks'   - called by 'fill_lists' proc
##
##  'get_selected_files' - uses selected rel-filename[s] to set 'FULFILnames'
##
##  'navigate'    - changes dir in 'DIRFILmask' entry field & runs 'fill_list'
##
##  'jump2dir'    - gets a new dir for dirfilmask; uses Tk script
##                  'dirSelector_orEditListOfFavorites_1listbox.tk'
##
##  'selectANDapply_script'  - gets a script and applies it to 'FULFILnames' ;
##                             uses Tk script 'scriptSelector_2listboxes.tk'
##
##  'set_status_msg'    - puts supplied msg into the Status msg frame
##
##  The following 6 procs for for the window/widget resizing buttons:
##    'dir_listbox_width_increase'
##    'dir_listbox_width_reduce'
##    'fil_listbox_width_increase'
##    'fil_listbox_width_reduce'
##    'tall_win'
##    'short_win'
##
##    'toggle_fontsize'       - called by the 'TogFontSize' button
##    'toggle_ybars'          - called by the '<-Ybars->' button
##
##  'popup_msgVarWithScroll' - used to show the $HELPtext var ; also used
##                              to show user various popup messages for
##                              info, warnings, errors, etc.
##
## SOME PROCS NOT IMPLEMENTED YET:
## (Could be implemented to make this file-selector
##  more like a file manager.)
##
##   'edit_txtfil' - to edit a selected text file
##   'view_txtfil' - to show the contents of a selected text file
##   'file_info'   - to show info for a selected file
##   'all_files_info' - to show info for all the files in the current directory
##   'rm_file'        - to remove a selected file(s)
##
##+#################################################################
##+#################################################################


##+###################################################################
## PROC  'fill_lists'
##+###################################################################
## PURPOSE:
##   Fills the two listboxes (dirs & fils), according to the contents
##   of var DIRFILmask.
##
## CALLED BY: the 'navigate' proc  -OR- the 'Refresh' button  -OR-
##            the  fRdirfilmask.entry <Key-Return> BINDING
##+###################################################################
## LOGIC NOTES:
##  Basically, 'fill_lists' should accept DIRFILmask (a global var) and
##   1) Set CURdir  from DIRFILmask (chars before last "/")
##   2) Set FILmask from DIRFILmask (chars after  last "/")
##   3) Fill the 'dir' listbox with directory names under/in $CURdir
##   4) Fill the 'fil' listbox with relative-filenames matching
##      $DIRFILmask = $CURdir/$FILmask
## Ideally, steps 3 and 4 will support 'symbolic links' (broken or not).
##+####################################################################

proc fill_lists {  } {

   global DIRFILmask FULFILnames CURdir FILmask aRtext env

   ####################################################
   ## TRIM DIRFILmask (of leading or trailing blanks).
   ####################################################

   set DIRFILmask [ string trim "$DIRFILmask" ]

   #############################################
   ## EXTRACT CURdir & FILmask from $DIRFILmask.
   #############################################

   set CURdir [ file dirname "$DIRFILmask" ]

   set FILmask [ file tail "$DIRFILmask" ]

   if { "$FILmask" == "" } { set FILmask {*} }

   #############################################
   ## FOR TESTING:  (show CURdir & FILmask vars)
   #############################################
   if {0} {
      puts "proc 'fill_lists' : (just entered the proc)"
      puts "   DIRFILmask: $DIRFILmask"
      puts "   CURdir: $CURdir"
      puts "   FILmask: $FILmask"
   }

   ##################################################
   ## CHECK IF THE DIRECTORY in DIRFILmask EXISTS.
   ## Pop an error msg if it does not -- and get out.
   ## ( "$CURdir" == "" is the case when the parent directory is root.)
   ##################################################

   if { ! [ file isdirectory $CURdir ] } {

      popup_msgVarWithScroll .topErr \
"The DIRECTORY in the DirFileMask entry field
    $CURdir
WAS NOT FOUND.
Change it and use the 'Refresh' button or Enter key.  Or 'Exit'."

      set_status_msg \
"The DIRECTORY in the DirFileMask entry field
    $CURdir
was NOT FOUND.
Change it and use the 'Refresh' button or Enter key.  Or 'Exit'."

      return

   }
   ## END OF  if { ! [ file isdirectory $CURdir ] }


   ####################################################
   ## TURN THE CURSOR INTO A WATCH.
   ## (Not needed? The listboxes fill rapidly,
   ## even for directories with more than 1500 files.)
   ###################################################
   # . configure -cursor watch
   ## Needed only to change the cursor?
   # update

   ##################################################
   ## RESET THE FULFILnames (selected files) TO NULL.
   ##  (no; the user may want the selection even if it
   ##   goes with a previous DIRFILmask.)
   #################################################
   ##  If necessary, do this at appropriate place
   ##  in 'navigate' proc.
   #################################################
   ##  set FULFILnames ""

   #############################################
   ## RESET THE STATUS MESSAGE TO NULL.
   ## (or indicate that filling is starting)
   #############################################
   # set_status_msg ""
   set_status_msg "Filling listbox ..."

   #######################################################################
   ## (RE)FILL THE DIRECTORY LISTBOX.
   ## (See the code in proc 'get_subdirs_inCURdir_inclLinks'.)
   ########################################################################

   # if { "$CURdir" == "" } { set CURdir "/" }

   set SubDirsLIST [get_subdirs_inCURdir_inclLinks]


   ##############################################################
   ## APPEND $SubDirsLIST TO THE LISTBOX. (without using a loop)
   ##############################################################
   ## Ref: Brent Welch, Practical Programming in Tcl & Tk,
   ##      Chapter 29, The Listbox Widget, page 359
   ##############################################################
   ## To get this to work WITHOUT USING A listbox-insert LOOP,
   ## we use a Tcl list of dirs.
   ##############################################################

   .fRlists.fRdir.listbox  delete 0 end

   eval .fRlists.fRdir.listbox  insert end $SubDirsLIST


   #############################################
   ## GET THE NUMBER OF SUBDIRS IN THE LISTBOX.
   #############################################

   set num_dirs [ .fRlists.fRdir.listbox size ]

   ######################################################
   ## RESET THE HEADING OF THE DIRECTORY ('dir') LISTBOX.
   ######################################################

   .fRlists.fRdir.lab  configure -text \
"$num_dirs Dirs - Parent Dir \[..\] & SubDirs
\[ Navigate = Single-Click \]"


   #######################################################################
   ## (RE)FILL THE non-directory-FILES ('fil') LISTBOX.
   ## (See the code in proc 'get_files_inCURdir_inclLinks'.)
   #######################################################################

   set FilesLIST [get_files_inCURdir_inclLinks]


   ######################################################################
   ## APPEND FILENAMES TO THE 'fil' LISTBOX (avoiding a 'foreach' loop).
   ######################################################################
   ## Ref: Brent Welch, Practical Programming in Tcl & Tk,
   ##      Chapter 29, The Listbox Widget, page 359
   ##############################################################
   ## To get this to work WITHOUT USING A listbox-insert LOOP,
   ## we we use a Tcl list of filenames.
   ##############################################################

   .fRlists.fRfil.listbox delete 0 end

   eval  .fRlists.fRfil.listbox  insert end $FilesLIST

   #############################################
   ## GET THE NUMBER OF FILES IN THE LISTBOX.
   #############################################

   set num_files [ .fRlists.fRfil.listbox size ]

   ##################################################
   ## RESET THE HEADING OF THE FILES ('fil') LISTBOX.
   ##################################################

   .fRlists.fRfil.lab configure -text \
"     $num_files Files match the mask
\[ Select = Single-Click \]"


   ##################################################################
   ## PUT A MSG IN THE STATUS MSG AREA?
   ## (Yes, for when the 'Refresh' button is clicked.  For other cases,
   ##  where other procs call this 'fill_lists' proc -- like
   ##  'navigate' -- the other proc can overwrite the status msg area.)
   ##################################################################

   set_status_msg "$aRtext(filledSTATUSmsg)"

   #############################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   #############################################
   # . configure -cursor { left_ptr red white }

}
## END OF PROC 'fill_lists'


##+##############################################################
## PROC 'get_subdirs_inCURdir_inclLinks'
##+##############################################################
## PURPOSE:
##  For given CURdir, this routine returns a Tcl list of the
##  directory files in CURdir --- including 'symbolic links'
##  to directories (handling broken links 'appropriately').
#################################################################
## METHOD:
## To find the sub-directories of CURdir, for the 'dir' listbox,
## we use the Tcl 'glob' command.
##
## For example, to find the sub-directories to put in the 'dir'
## listbox, the following seems to work to show get both non-hidden
## and hidden files:
##
##         glob -nocomplain  $CURdir/*  $CURdir/.*
##
## (The '-nocomplain' allows for returning an empty list without
##  raising an error.)
##
## We show 'hidden' sub-directories (dot-prefix on the filenames)
## as well as 'non-hidden' sub-directories. (The showing of 'hidden'
## files could be made an option via a checkbutton on the GUI.)
##
## From this list of filenames, in a 'foreach' loop, we can select
## the 'directory' files.
##
## Among the files returned by the 'glob' above, there could be
## 'symbolic links' to directories. In the same 'foreach' loop
## that is used to select out the directory files, we find the
## 'link' files and figure out which links point to directories.
##
## If there is a 'directory-link', we append the link to the list
## of directories, for display in the 'dir' listbox. (We can
## check if the link is 'broken', and we can choose to NOT
## append it --- or popup a warning.)
##
## ---
##
## In the 'foreach' loop, we can make use of Tcl commands like
##
##  file type $file      (Returns: file, directory, characterSpecial,
##                                 blockSpecial, fifo, link, OR socket)
##  file readlink $file  (Good for finding the target of a link.)
##  file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##
##  file exists $file
##
## If necessary, we could also make use of Tcl 'file' commands like
## 
##  file isdirectory $file
##  file isfile $file
##
##  file link $file
##  file stat $file aRfileStat
##  file lstat $file aRlinkStat
##
## ...................................................................
## Ref: the Eric Johnson book " Graphical Applications with Tcl & Tk",
## page 225-226, Chapter 6 Lists, Files, and Directories.
## ---
## The 'dirview.tcl' script in chapter 6 gives an example of using
## a 'foreach' loop to extract directories and non-directories
## from 'glob' output. But that code would have to be made more
## robust to handle 'symbolic links' properly --- and exclude
## special files like fifo, socket, and device files.
##+##########################################################
## CALLED BY: 'fill_lists' proc
##+##########################################################

proc get_subdirs_inCURdir_inclLinks {} {

   global CURdir DEBUGlink0or1

   #############################################################
   ## Get the list of files to check for 'directory' and 'link'
   ## file types.
   ############################################################

   if {"$CURdir" == "/"} {
      set tempFilesLIST [glob -nocomplain  /*  /.*]
   } else {
      set tempFilesLIST [glob -nocomplain  $CURdir/*  $CURdir/.*]
   }

   ## Alternative (using 'catch' to get return-code):
   #   set tempFilesLIST ""
   #   set RETcode [ catch { set tempFilesLIST  [ .... ] } ]

   ## FOR TESTING:
   #    puts "tempFilesLIST: $tempFilesLIST"
   #    puts "RETcode: $RETcode"


   ################################################################
   ## Make sure the variable 'tempDirsLIST' does not exist.
   ##
   ## "You can call 'lappend' with the name of an undefined
   ## variable and the variable will be created." - page 66 of the
   ## 4th edition of 'Practical Programming in Tcl and Tk' by
   ## Welch, Jones, Hobbs.
   ###############################################################

   catch {unset tempDirsLIST}


   ###################################################################
   ## Peform the 'foreach' loop to append the files of type
   ## 'directory' to the Tcl list 'tempDirsLIST'.
   ###################################################################
   ## If a file is of type 'link', we determine if the link is of type
   ## 'directory'. If so, we append the link-name to 'tempDirsLIST'.
   ###################################################################
   ## If 'file readlink' is not sufficient,
   ## we may eventually have to handle the case where the
   ## a symbolic link points to another symbolic link and
   ## on and on until the eventual target is determined.
   ## We could put that logic in a proc with a name like
   ## 'get_link_target'.
   ###################################################################

   foreach tempFilename $tempFilesLIST {

      ############################################################
      ## If 'tempFilename' is of type 'directory', append the
      ## filename to tempDirsLIST and 'continue' to the next file.
      ############################################################
      ## Exceptions are the sub-dirs "." and "..". We do not
      ## show "." and we show ".." for all directories except root.
      ############################################################

      if {[file type $tempFilename] == "directory"} {
         set tempTail [file tail "$tempFilename"]
         if {"$tempTail" == "."} {continue}
         if {"$tempTail" == ".." && "$CURdir" == "/"} {continue}
         lappend tempDirsLIST "$tempTail"
         continue
      }

      ########################################################
      ## If 'tempFilename' is NOT of type 'link',
      ## 'continue' to the next file in the loop.
      ########################################################

      if {[file type $tempFilename] != "link"} {continue}

      ########################################################
      ## The remainder of this loop is to handle 'link' files.
      ########################################################
      ## Get the filename of the target of the link.
      ## (If we need to do a more complex check for the *ultimate*
      ## target of the link, we could put that logic in a proc
      ## with a name like 'get_link_target'.)
      ########################################################

      set targetName [file readlink $tempFilename]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts ""
         puts "proc 'get_subdirs_inCURdir_inclLinks' :"
         puts "tempFilename: $tempFilename"
         puts "targetName:   $targetName"
      }

      if {[file pathtype $targetName] == "relative"} {
         set targetName "$CURdir/$targetName"
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
           puts "targetName:   $targetName  RESET"
         }
      }

      #######################################################
      ## If the target does not exist (i.e. 'broken link'),
      ## 'continue' to the next file in the loop.
      ## (We could insert a popup warning message to alert
      ##  the user of the broken link.)
      #######################################################

      if {![file exists $targetName]} {
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
            puts "targetName:   $targetName  DOES NOT EXIST."
         }
         continue
      }

      #######################################################
      ## Get the file-type of the target of the link.
      #######################################################

      set targetType [file type $targetName]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts "targetType:   $targetType"
      }

      ######################################################
      ## If the file-type of the target is 'directory',
      ## add the link-filename to tempDirsLIST.
      ######################################################

      if {"$targetType" == "directory"} {
         lappend tempDirsLIST [file tail "$tempFilename"]
      }

   }
   ## END OF foreach tempFilename $tempFilesLIST

   ######################################################
   ## Return the sub-directories list, sorted.
   ######################################################

   if {[info exists tempDirsLIST]} {
      return [lsort $tempDirsLIST]
   } else {
      return ""
   }

}
## END OF PROC 'get_subdirs_inCURdir_inclLinks'


##+##############################################################
## PROC  'get_files_inCURdir_inclLinks'
##+##############################################################
## PURPOSE:
##  For given CURdir, this routine returns a Tcl list of the
##  'regular' files in CURdir --- including 'symbolic links'
##  to 'regular' files (handling broken links 'appropriately').
#################################################################
## METHOD:
## To get the files for the 'fil' listbox, for a given CURdir and
## FILmask, we can use a 'glob' like the following:
##
##     glob -nocomplain $CURdir/$FILmask
##
## In a 'foreach' loop, we could select the 'regular' files.
##
## If the user wanted to see hidden files, the user could add a
## dot-prefix in the file-mask. Alternatively, we could ALWAYS
## show the 'hidden' files by using a 'glob' like
##
##     glob -nocomplain  $CURdir/$FILmask  $CURdir/.$FILmask
##
## For example, if FILmask='*', this 'glob' would look like
##
##     glob -nocomplain  $CURdir/*  $CURdir/.*
##
## Among the files returned by the 'glob' above, there could be
## 'symbolic links' to 'regular' files. In the same 'foreach' loop
## that is used to select out the regular files, we find the
## 'link' files and figure out which links point to regular files.
##
## If there is a 'regular-file-link', we append the link to the list
## of files for the 'fil' listbox. (We can check if the link is 'broken',
## and we can choose to not append it --- or popup a warning.)
##
## ---
##
## In the 'foreach' loop, we can make use of Tcl 'file' commands like
##
##  file type $file      (Returns: file, directory, characterSpecial,
##                                 blockSpecial, fifo, link, OR socket)
##
##  file readlink $file  (Good for finding the target of a link.)
##  file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##
##  file exists $file
##
## If necessary, we could also make use of Tcl 'file' commands like
##
##  file isdirectory $file
##  file isfile $file
##
##  file link $file
##
##  file stat $file aRfileStat
##  file lstat $file aRlinkStat
##
## ...................................................................
## Ref: the Eric Johnson book " Graphical Applications with Tcl & Tk",
## page 225-226, Chapter 6 Lists, Files, and Directories.
## ---
## The 'dirview.tcl' script in chapter 6 gives an example of using
## a 'foreach' loop to extract directories and non-directories
## from 'glob' output. But that code would have to be made more
## robust to handle 'symbolic links' properly --- and exclude
## special files like fifo, socket, and device files.
##+##########################################################
## CALLED BY: 'fill_lists' proc
##+##########################################################

proc get_files_inCURdir_inclLinks {} {

   global CURdir FILmask DEBUGlink0or1

   #############################################################
   ## Get the list of files to check for 'directory' and 'link'
   ## file types.
   ############################################################

   if {"$CURdir" == "/"} {
      set tempFilesLIST [glob -nocomplain  /$FILmask  /.$FILmask]
      # set tempFilesLIST [glob -nocomplain  /$FILmask]
   } else {
      set tempFilesLIST [glob -nocomplain  $CURdir/$FILmask  $CURdir/.$FILmask]
      # set tempFilesLIST [glob -nocomplain  $CURdir/$FILmask]
   }

   ## Alternative (using 'catch' to get return-code):
   #   set tempFilesLIST ""
   #   set RETcode [ catch { set tempFilesLIST  [ .... ] } ]

   ## FOR TESTING:
   #    puts "tempFilesLIST: $tempFilesLIST"
   #    puts "RETcode: $RETcode"


   ################################################################
   ## Make sure the variable 'tempRegFilesLIST' does not exist.
   ##
   ## "You can call 'lappend' with the name of an undefined
   ## variable and the variable will be created." - page 66 of the
   ## 4th edition of 'Practical Programming in Tcl and Tk' by
   ## Welch, Jones, Hobbs.
   ###############################################################

   catch {unset tempRegFilesLIST}

   ##################################################################
   ## Peform the 'foreach' loop to append the files of type
   ## 'file' to the Tcl list 'tempRegFilesLIST'.
   ##################################################################
   ## If a file is of type 'link', we determine if the link is of
   ## type 'file'. If so, we append the link-name to 'tempRegFilesLIST'.
   ##############################################################
   ## If 'file readlink' is not sufficient,
   ## we may eventually have to handle the case where the
   ## a symbolic link points to another symbolic link and
   ## on and on until the eventual target is determined.
   ## We could put that logic in a proc with a name like
   ## 'get_link_target'.
   ##############################################################

   foreach tempFilename $tempFilesLIST {

      ###########################################################
      ## If 'tempFilename' is of type 'file', append the filename
      ## to tempRegFilesLIST and 'continue' to the next file.
      ###########################################################

      if {[file type $tempFilename] == "file"} {
         lappend tempRegFilesLIST [file tail "$tempFilename"]
         continue
      }

      ########################################################
      ## If 'tempFilename' is NOT of type 'link',
      ## 'continue' to the next file in the loop.
      ########################################################

      if {[file type $tempFilename] != "link"} {continue}

      ########################################################
      ## The remainder of this loop is to handle 'link' files.
      ########################################################
      ## Get the filename of the target of the link.
      ## (If we need to do a more complex check for the *ultimate*
      ## target of the link, we could put that logic in a proc
      ## with a name like 'get_link_target'.)
      ########################################################

      set targetName [file readlink $tempFilename]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts ""
         puts "proc 'get_files_inCURdir_inclLinks' :"
         puts "tempFilename: $tempFilename"
         puts "targetName:   $targetName"
      }

      if {[file pathtype $targetName] == "relative"} {
         set targetName "$CURdir/$targetName"
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
           puts "targetName:   $targetName  RESET"
         }
      }

      #######################################################
      ## If the target does not exist (i.e. 'broken link'),
      ## 'continue' to the next file in the loop.
      ## (We could insert a popup warning message to alert
      ##  the user of the broken link.)
      #######################################################

      if {![file exists $targetName]} {
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
            puts "targetName:   $targetName  DOES NOT EXIST."
         }

         continue
      }

      #######################################################
      ## Get the file-type of the target of the link.
      #######################################################

      set targetType [file type $targetName]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts "targetType:   $targetType"
      }

      ######################################################
      ## If the file-type of the target is 'file',
      ## add the link-filename to tempRegFilesLIST.
      ######################################################

      if {"$targetType" == "file"} {
         lappend tempRegFilesLIST [file tail "$tempFilename"]
      }

   }
   ## END OF foreach tempFilename $tempFilesLIST

   ######################################################
   ## Return the regular-files list, sorted.
   ######################################################

   if {[info exists tempRegFilesLIST]} {
      return [lsort $tempRegFilesLIST]
   } else {
      return ""
   }

}
## END OF PROC 'get_files_inCURdir_inclLinks'


##+##############################################################
## PROC 'get_selected_files'
##+##############################################################
## PURPOSE:
##  This routine loads the FULFILnames var
##  -- by prefixing the selected relative-filename(s),
##  from the files-listbox, with $CURdir of the dirfilmask.
##
##  Also puts $CURdir in FULDIRname.
##+##########################################################
## CALLED BY:
##    the  'selectANDapply_script' proc
##+#############################################################

proc get_selected_files { } {

   global DIRFILmask FULFILnames FULDIRname aRtext

   ################################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   ## (In case cursor was left as a 'watch'
   ##  after a failure in 'fill_lists' proc, like
   ##  a CURdir permission error.)
   ################################################
   # . configure -cursor { left_ptr red white }

   #######################################################
   ## FIND OUT WHICH FILE(s) ARE BEING SELECTED.
   #######################################################

   set FULFILnames ""
   set sel_indexes [ .fRlists.fRfil.listbox  curselection ]

   ####################################################
   ## RESET THE STATUS MESSAGE TO NULL.
   ####################################################

   set_status_msg ""

   #######################################################
   ## IF A LIST ITEM WAS NOT SELECTED, pop a msg & return.
   #######################################################

   if { "$sel_indexes" == "" } {
      popup_msgVarWithScroll .topErr \
"NO item was selected in the Files (right) listbox.
Select File(s) before clicking on 'Apply-a-Script' --- even if the
the script you will run requires no files or only the current directory."

      set_status_msg \
"NO item was selected in the Files (right) listbox.
Select File(s) before clicking on 'Apply-a-Script' --- even if the
the script you will run requires no files or only the current directory."

      return
   }


   #############################################
   ## EXTRACT CURdir from $DIRFILmask,
   ## and put $CURdir in FULDIRname.
   #############################################

   set CURdir [ file dirname "$DIRFILmask" ]
   set FULDIRname "$CURdir"

   ####################################################
   ## UPDATE THE FULFILnames var
   ## WITH THE NEW, FULL FILENAME(s).
   ####################################################

   set FULFILnames ""
   foreach idx $sel_indexes {
           append FULFILnames "${CURdir}/[.fRlists.fRfil.listbox get $idx]"
   }

   ## FOR TESTING:
   #   puts "FULFILnames: $FULFILnames"


   ##############################################
   ## PUT A MSG IN THE STATUS MSG AREA.
   ##############################################

   set_status_msg "$aRtext(twolineSTATUSmsg)"

}
## END OF PROC 'get_selected_files'


##+##############################################################
## PROC 'navigate'
##+##############################################################
## PURPOSE:
##  This routine essentially navigates the directory
##  structure.  It
##   1) resets the DIRFILmask entry according to a subdir
##      selected in the subdirs-listbox, and
##   2) runs the 'fill_lists' procedure, to refresh both the
##      subdirs-listbox and the relfiles-listbox.
##
##+##############################################################
## CALLED BY:
##   the  fRlists.fRdir.listbox <ButtonRelease-1> BINDING
##   WHEN MOUSE-BUTTON-1 IS SINGLE-CLICKED (& released)
##   ON A (SUB)DIRECTORY in the '.fRlists.fRdir.listbox' widget.
##+##############################################################

proc navigate { } {

   global DIRFILmask aRtext env

   ####################################################
   ## FIND OUT WHICH SUB-DIRECTORY WAS SELECTED.
   ####################################################

   set sel_index  [ .fRlists.fRdir.listbox curselection ]


   ####################################################
   ## SET THE STATUS MESSAGE TO NULL.
   ####################################################

   set_status_msg ""

    ####################################################
    ## IF A LIST ITEM WAS NOT SELECTED, pop a msg & return.
    ####################################################

    if { "$sel_index" == "" } {
      popup_msgVarWithScroll .topErr \
"Navigate Procedure: NO item was selected in the Dirs (left) listbox."

      set_status_msg \
"Navigate Procedure: NO item was selected in the Dirs (left) listbox."

      return
    }


   ####################################################
   ## GET THE 'BASENAME' of the selected SUBDIRECTORY.
   ####################################################

   set RELDIRsel [ .fRlists.fRdir.listbox get $sel_index ]


   ###################################################################
   ##  CHECK HERE IF THE USER HAS 'cd' PERMISSION TO $RELDIRsel.
   ##  Pop a msg if not -- and return.
   ###################################################################

   set CURdir [ file dirname "$DIRFILmask" ]

   if {"$CURdir" == "/"} {
      set CHECKdir "/$RELDIRsel"
   } else {
      set CHECKdir "$CURdir/$RELDIRsel"
   }

   if { ! [ file executable "$CHECKdir" ] } {

         popup_msgVarWithScroll .topErr \
"The DIRECTORY
   $CHECKdir
is NOT ACCESSIBLE TO YOU, as user $env(USER)."

         set_status_msg \
"The DIRECTORY
   $CHECKdir
is NOT ACCESSIBLE TO YOU, as user $env(USER)."

         return

   }
   ## END OF  if { ! [ file executable "$CHECKdir" ] }


   ##################################################
   ## EXTRACT FILmask from $DIRFILmask.
   ##################################################

   set FILmask [ file tail "$DIRFILmask" ]


   ##########################################################
   ## SET THE NEW FULL-DIRNAME in CURdir, using $RELDIRsel.
   ## If RELDIRsel is '..' (parent directory) instead of a
   ## sub-directory, set CURdir to its parent directory.
   ##########################################################
   ## NOTE: 'file dirname' of '/home' and '/' both yield '/'.
   ## % file dirname /home
   ## /
   ## % file dirname /
   ## /
   ## % file dirname /usr
   ## /
   #########################################################

   if { "$RELDIRsel" == ".." } then {
      ## Save where we were, before setting CURdir to the parent directory.
      ## This will be used in subdir high-lighting below.
      set relCHILDdir [ file tail $CURdir ]
      set CURdir [ file dirname "$CURdir" ]
   } else {
      if { "$CURdir" == "/" } {
         ## Avoid CURdir being double-slash.
         set CURdir "/$RELDIRsel"
      } else {
         set CURdir "${CURdir}/$RELDIRsel"
      }
   }


   #######################################################
   ## RESET $DIRFILmask in the DIRFILmask entry field, AND
   ## to be used by 'fill_lists' proc.
   ## NOTE: CURdir is extracted (again) from $DIRFILmask
   ##       in the 'fill_lists' proc.
   #######################################################

   if {"$CURdir" == "/"} {
      ## Avoid DIRFILmask starting with double-slash.
      set DIRFILmask "/$FILmask"
   } else {
      set DIRFILmask "${CURdir}/$FILmask"
   }

   ##########################################################
   ## CALL 'fill_lists' to fill the subdir & relfil listboxes.
   ##########################################################

   fill_lists

   ##########################################################
   ## If the user is navigating to a parent directory (i.e.,
   ## if '..' was clicked), position the list in the updated
   ## 'dir' listbox such that the subdirectory that the user
   ## is 'climbing out of' is seen the the 'dir' listbox.
   ##########################################################

   if { "$RELDIRsel" == ".." } then {
      set IDXsubdir [lsearch [.fRlists.fRdir.listbox get 1 end] $relCHILDdir]
      .fRlists.fRdir.listbox see $IDXsubdir
      .fRlists.fRdir.listbox selection set [expr {$IDXsubdir + 1}]
   }

   ##############################################
   ## PUT A MSG IN THE STATUS MSG AREA.
   ##############################################

   set_status_msg "$aRtext(filledSTATUSmsg)"

   ########################################################
   ## MAKE SURE THE FAR-RIGHT END OF TEXT IN THE DIRFILmask
   ## ENTRY BOX IS IN VIEW, when this 'navigate' is done.
   ########################################################

   .fRdirfilmask.entryDIRFILmask   xview end

}
## END OF PROC 'navigate'


##+##############################################################
## PROC 'jump2dir'
##+##############################################################
## PURPOSE:
##  This routine essentially changes the 'Current directory'
##  in the dirfilmask -- by allowing the user to select
##  a directory name from a list of commonly-used site
##  directories.
##
##  Uses the 'dirSelector_orEditListOfFavorites_1listbox.tk' script
##  to get the directory name, from file
##  '...whatever.../fave_dirs.lis'. The '.lis' filename
##  is set in that script, not in this script.
##
##  This 'jump2dir' proc then
##   1) resets the DIRFILmask entry
##   2) runs the 'fill_lists' procedure.
##
##+##########################################################
##  CALLED BY:  the  BUTTON  .fRdirfilmask.buttJump
##+##########################################################

proc jump2dir { } {

   global DIRFILmask env DIRscripts aRtext

   ####################################################
   ## SAVE $CURdir from the dirfilmask.
   ####################################################

   set DIRFILmask [ string trim "$DIRFILmask" ]
   set CURdir [ file dirname "$DIRFILmask" ]

   set HOLDdir "$CURdir"

   ######################################################
   ## Get the user's choice of new directory from his/her
   ## directory-favorites file.
   ######################################################

   set CURdir [ exec \
      $DIRscripts/dirSelector_orEditListOfFavoriteDirs_1listbox.tk ]

   ######################################################
   ## Check if the user made a choice of a new directory
   ## from his/her directory-favorites file.
   ######################################################

   if { "$CURdir" == "" } then {
      #############################################
      ## IF THE USER DID NOT SELECT A DIRECTORY,
      ## 'restore' the value of $CURdir.
      #############################################

      set CURdir "$HOLDdir"
      # return

   } else {
      #############################################
      ## CLEAR THE DIRECTORY & FILE LISTBOXES,
      ## in preparation for their update by 'fill_lists'
      ## i.e. 'Refresh'.
      #############################################
      .fRlists.fRdir.listbox delete 0 end
      .fRlists.fRfil.listbox delete 0 end
   }

   ####################################################
   ## GET FILE MASK  $DIRFILmask.
   ####################################################

   set FILmask [ file tail "$DIRFILmask" ]

   ####################################################
   ## RESET $DIRFILmask.
   ####################################################

   set DIRFILmask "${CURdir}/$FILmask"

   ###########################################################
   ## CALL 'fill_lists' to fill the subdir & relfil listboxes.
   ###########################################################

   fill_lists

   #####################################################
   ## PUT A MSG IN THE STATUS MSG AREA?
   ## (For now, we let the fill_lists proc set the msg.)
   #####################################################

   # set_status_msg "$aRtext(filledSTATUSmsg)"

}
## END OF PROC 'jump2dir'


##+##############################################################
## PROC 'selectANDapply_script'
##+##############################################################
## PURPOSE:
##  This routine allows the user to select a script and apply it
##  to var FULFILnames which is set via the 'get_selected_files' proc.
##  We also use FULDIRname set in that proc.
##
##  Runs the Tk script 'scriptSelector_2listboxes.tk'.
##
##+##########################################################
##  CALLED BY:  the  BUTTON   .fRcntlbutts1.buttApplyScript
##+##########################################################

proc selectANDapply_script { } {

   global FULFILnames FULDIRname DIRscripts

   ############################################################
   ## PUT selected files in var FULFILnames
   ## and put the directory of those files in FULDIRname.
   ############################################################

   get_selected_files


   ##############################################################
   ## IF A FILES-LIST ITEM WAS NOT SELECTED, (pop a msg &) return.
   ## (A msg should have been popped by 'get_selected_files'.)
   ##############################################################

   if { "$FULFILnames" == "" } {

      # popup_msgVarWithScroll .topErr \
      #    "ApplyScript Procedure: NO item was selected in the Files (right) listbox."

      # set_status_msg \
      #    "ApplyScript Procedure: NO item was selected in the Files (right) listbox."

      return
   }


   #######################################################
   ## Get a scriptname.
   #######################################################

   set FULSCRIPTname [exec $DIRscripts/scriptSelector_2listboxes.tk]

   if { "$FULSCRIPTname" == "" } {
      return
   }

   #########################################################
   ## Execute the selected script on the selected filenames
   ## with the selected directory, FULDIRname, being the
   ## 'current working directory'.
   ##
   ## Reference: an example in the 'pwd' section of a Tcl-Tk
   ## 8.4 (or 8.5) Reference Manual, namely:
   ##
   ## "Sometimes it is useful to change to a known directory when
   ## running some external command using exec, but it is important
   ## to keep the application usually running in the directory
   ## that it was started in (unless the user specifies otherwise)
   ## since that minimizes user confusion. The way to do this is to
   ## save the current directory while the external command is being run:
   ##
   ## set tarFile [file normalize somefile.tar]
   ## set savedDir [pwd]
   ## cd /tmp
   ## exec tar -xf $tarFile
   ## cd $savedDir
   ##
   ####################################################################
   ## SOME SYNTAX NOTES on running a program via the Tcl 'exec' command:
   ####################################################################
   ## On page 105 of the 4th edition of 'Practical Programming in Tcl & Tk',
   ## is the following quote on the Tcl 'exec' command:
   ##
   ## "The 'exec' command runs programs from your Tcl script. For example:
   ##      set d [exec date]
   ## The standard output of the program is returned as the value of
   ## the 'exec' command. However, if the program writes to its standard
   ## error channel or exits with a nonzero status code, then 'exec'
   ## raises an error. If you do not care about the exit status, or you
   ## use a program that insists on writing to standard error, then you
   ## can use 'catch' to mask the errors:
   ##   catch {exec program arg arg} result"
   ##
   ## Unfortunately, running a player program in 'foreground' mode
   ## like this makes the widgets on the tkScriptApplicator GUI unavailable
   ## --- for example, the listboxes go blank and the buttons become
   ## unavailable (for example, the 'Help' button).
   ##
   ## A 'foreground' run per page 105 of 'Practical Programming in Tcl & Tk'.
   ## NOT USED, because it 'locks up' the GUI.
   #######################################################################
   ## On page 107 of the 4th edition of 'Practical Programming in Tcl & Tk',
   ## 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'."
   ##
   ## Page 83 of the same book says:
   ## "'catch' returns zero if there was no error caught,
   ##   or a nonzero error code if it did catch an error."
   ####################################################################
   ## A couple of examples of using a PID (process ID) with Tcl:
   ##
   ## catch {eval exec $feREADER_text \"$FULFILname\"  &} ProcessPID
   ##
   ## set RETcode [ catch {eval exec ${feDIR}/tkGUIs/shofil.tk \
   ##    "$FULFILname" &} ProcessPID ]
   ##
   ####################################################################

   #########################################################
   ## Save the current directory and switch to the directory
   ## containing the selected files.
   #########################################################

   set DIRsave [pwd]
   cd "$FULDIRname"

   ##################################################
   ## Exec the selected script on the selected files.
   ##################################################

   set RETcode [ catch {eval exec "$FULSCRIPTname" $FULFILnames &} scriptPID ]

   if {$RETcode != 0} {
      set ERRtext \
"Proc 'selectANDapply_script' encountered an error on trying to 'exec'
the script
   $FULSCRIPTname
on the selected filename(s).

RETcode: $RETcode
Message: $scriptPID
Stopping processing."
      popup_msgVarWithScroll .topERR "$ERRtext"
      return
   }
   ## END OF if {$RETcode != 0}

   ###########################################################
   ## Set the current directory back to the 'saved directory'.
   ###########################################################

   cd "$DIRsave"

}
## END OF PROC 'selectANDapply_script'


##+########################################################################
## PROC 'set_status_msg'
##+########################################################################
## PURPOSE: To put an informational message to the user into the
##          bottom 'status-msg' area of the GUI.
## CALLED BY: various procs
##+########################################################################
## MESSAGE-CONTENT NOTE: The message string can include line-feeds.
##+########################################################################

proc set_status_msg { message } {

   #############################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   ## (In case cursor was left as a 'watch'
   ##  after a failure in 'fill_list' proc, like
   ##  a subidr permission error.)
   #############################################
   # . configure -cursor { left_ptr red white }

   .fRstatusmsg.lab configure -text "$message"

}
##END OF PROC 'set_status_msg'


##+#################################################################
## PROC  'dir_listbox_width_increase'
##+#################################################################
## PURPOSE:
##  This routine increases the width of the directory listbox frame
##  (measured in pixels).
##
##  Note that our intent is to increase the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the widening in pixels-only --- using an 'INCRpixelsRIGHT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'dir_listbox_width_reduce',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butDirListRight .
##+##############################################################

proc dir_listbox_width_increase {} {

   global INCRpixelsRIGHT SCREENwidthPx wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsRIGHT
   ## An alternate method could be to use 'INCRcharsRIGHT' instead of
   ## 'INCRpixelsRIGHT', on the frame listbox instead of on the frame
   ## --- to do the increase in chars-only instead of pixels-only.

   set winXlen [ winfo width . ]

   set DIRwidthPX [winfo width .fRlists.fRdir]
   set FILwidthPX [winfo width .fRlists.fRfil]

   ## FOR TESTING:
   if {0} {
       puts "*******************************************************"
       puts "proc 'dir_listbox_width_increase' : (just entered proc)"
       puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels, on entering proc."
       puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
       puts "window-width  via winfo = $winXlen    pixels."
       puts "INCRpixelsRIGHT = $INCRpixelsRIGHT    pixels."
   }

   #######################################################################
   ## We do not try to increase the dir-listbox width if the entire window
   ## is already at the screen width. I.e. we only do the following
   ## computations if there is some room to expand the dir-listbox.
   #######################################################################

   if { $winXlen < $SCREENwidthPx } {

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ##############################################################################
      ## Increase the window width about as much as the increase in dir-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen + $INCRpixelsRIGHT}]

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}


      #####################################################################
      ## After we request an increase to the window size, we increase
      ## the size of the dir-listbox-frame.
      #####################################################################

      set DIRwidthPX [expr {$DIRwidthPX + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'dir_listbox_width_increase' : (new-DIR & same-FIL width parms)"
          puts "DIRwidthPX: $DIRwidthPX"
          puts "FILwidthPX: $FILwidthPX"
      }

      ######################################################################
      ## Increase the dir-frame width, but keep the fil-frame width the same.
      ######################################################################
      ## Strangely, the fil-listbox expands unless I add the
      ## 'pack propagate ... false' statement --- on the dir-listbox,
      ## NOT the fil-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the fil-listbox, NOT the dir-listbox. None of this section works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRdir false

   }
   ## END OF  if { $winXlen < $SCREENwidthPx }

}
## END OF PROC  'dir_listbox_width_increase'


##+##############################################################
## PROC  'dir_listbox_width_reduce'
##+##############################################################
## PURPOSE:
##  This routine reduces the width of the directory listbox frame
##  (measured in pixels).
##
##  Note that our intent is to decrease the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the narrowing in pixels-only --- using an 'INCRpixelsLEFT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'dir_listbox_width_increase',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butDirListLeft .
##+##############################################################

proc dir_listbox_width_reduce {} {

   global INCRpixelsLEFT DIRminwidthPX wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsLEFT
   ## An alternate method could be to use 'INCRcharsLEFT' instead of
   ## 'INCRpixelsLEFT', on the frame contents instead of on the frame
   ## --- to do the reduction in chars-only instead of pixels-only.

   set DIRwidthPX [winfo width .fRlists.fRdir]
   set FILwidthPX [winfo width .fRlists.fRfil]

   set winXlen [ winfo width . ]

   ## FOR TESTING:
   if {0} {
      puts "*****************************************************"
      puts "proc 'dir_listbox_width_reduce' : (just entered proc)"
      puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels."
      puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
      puts "window-width  via winfo = $winXlen    pixels."
      puts "INCRpixelszLEFT = $INCRpixelsLEFT    pixels."
   }

   ######################################################################
   ## We do not try to decrease the dir-listbox width if it is
   ## already at a designated minwidth. I.e. we only do the following
   ## computations if there is some latitude to decrement the dir-listbox.
   ######################################################################

   if { $DIRwidthPX > $DIRminwidthPX } {

      set DIRwidthPX [expr {$DIRwidthPX - $INCRpixelsLEFT}]
      set deltaDIRwidthPX $INCRpixelsLEFT

      if { $DIRwidthPX < $DIRminwidthPX } {
         set deltaDIRwidthPX [expr {($DIRwidthPX + $INCRpixelsLEFT) - $DIRminwidthPX}]
         set DIRwidthPX $DIRminwidthPX
      }

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'dir_listbox_width_reduce' : (new-DIR & same-FIL width parms)"
         puts "DIRwidthPX: $DIRwidthPX"
         puts "FILwidthPX: $FILwidthPX"
      }

      ######################################################################
      ## Decrease the dir-frame width, but keep the fil-frame width the same.
      ######################################################################
      ## Strangely, the fil-listbox decrements unless I add the
      ## 'pack propagate ... false' statement --- on the dir-listbox,
      ## NOT the fil-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the fil-listbox, NOT the dir-listbox. None of this works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRdir false


      #########################################################
      ## We narrow the window by the amount we narrow the
      ## directory box --- as follows.
      #########################################################

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      set winXlen [ winfo width . ]
      set winYlen [ winfo height . ]

      ##############################################################################
      ## Reduce the window width about as much as the decrease in dir-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winXlen [expr {$winXlen - $deltaDIRwidthPX}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'dir_listbox_width_reduce' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   }
   ## END OF   if { $DIRwidthPX > $DIRminwidthPX }

}
## END OF PROC  'dir_listbox_width_reduce'


##+##############################################################
## PROC  'fil_listbox_width_increase'
##+##############################################################
## PURPOSE:
##  This routine increases the width of the 'fil' listbox frame
##  (measured in pixels).
##
##  Note that our intent is to increase the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the widening in pixels-only --- using an 'INCRpixelsRIGHT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'fil_listbox_width_reduce',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butFilListRight .
##+##############################################################

proc fil_listbox_width_increase {} {

   global INCRpixelsRIGHT SCREENwidthPx wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsRIGHT
   ## An alternate method could be to use 'INCRcharsRIGHT' instead of
   ## 'INCRpixelsRIGHT', on the frame contents instead of on the frame
   ## --- to do the increase in chars-only instead of pixels-only.

   set winXlen [ winfo width . ]

   set FILwidthPX [winfo width .fRlists.fRfil]
   set DIRwidthPX [winfo width .fRlists.fRdir]

   ## FOR TESTING:
   if {0} {
       puts "*******************************************************"
       puts "proc 'fil_listbox_width_increase' : (just entered proc)"
       puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
       puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels."
       puts "window-width  via winfo = $winXlen    pixels."
       puts "INCRpixelsRIGHT = $INCRpixelsRIGHT    pixels."
   }

   ########################################################################
   ## We do not try to increase the fil-listbox width if the entire window
   ## is already at the screen width. I.e. we only do the following
   ## computations if there is some room to expand the fil-listbox.
   ########################################################################

   if { $winXlen < $SCREENwidthPx } {

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ##############################################################################
      ## Increase the window width about as much as the increase in fil-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'fil_listbox_width_increase' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

      ###################################################################
      ## After we request an increase to the window size, we increase the
      ## size of the fil-listbox-frame.
      ###################################################################

      set FILwidthPX [expr {$FILwidthPX + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'fil_listbox_width_increase' : (new-FIL & same-DIR width parms)"
         puts "FILwidthPX: $FILwidthPX"
         puts "DIRwidthPX: $DIRwidthPX"
      }

      ######################################################################
      ## Increase the fil-frame width, but keep the dir-frame width the same.
      ######################################################################
      ## Strangely, the dir-lisbox expands until I added the
      ## 'pack propagate ... false' statement --- on the fil-listbox,
      ## NOT the dir-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the dir-listbox, NOT the fil-listbox. None of this section works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRfil false

   }
   ## END OF  if { $winXlen < $SCREENwidthPx }

}
## END OF PROC  'fil_listbox_width_increase'


##+##############################################################
## PROC  'fil_listbox_width_reduce'
##+##############################################################
## PURPOSE:
##  This routine reduces the width of the 'fil' listbox frame
##  (measured in pixels).
##
##  Note that our intent is to decrease the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the narrowing in pixels-only --- using an 'INCRpixelsLEFT' var.
##
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'fil_listbox_width_increase',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butFilListLeft .
##+##############################################################

proc fil_listbox_width_reduce {} {

   global INCRpixelsLEFT FILminwidthPX wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsLEFT
   ## An alternate method could be to use 'INCRcharsLEFT' instead of
   ## 'INCRpixelsLEFT', on the frame contents instead of on the frame
   ## --- to do the reduction in chars-only instead of pixels-only.

   set FILwidthPX [winfo width .fRlists.fRfil]
   set DIRwidthPX [winfo width .fRlists.fRdir]

   set winXlen [ winfo width . ]

   ## FOR TESTING:
   if {0} {
      puts "*****************************************************"
      puts "proc 'fil_listbox_width_reduce' : (just entered proc)"
      puts "filFRAMEwidth via winfo = $FILwidthPX pixels, on entering proc."
      puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels, on entering proc."
      puts "window-width  via winfo = $winXlen    pixels."
      puts "INCRpixelszLEFT = $INCRpixelsLEFT    pixels."
   }

   ######################################################################
   ## We do not try to decrease the fil-listbox width if it is
   ## already at a designated minwidth. I.e. we only do the following
   ## computations if there is some latitude to decrement the fil-listbox.
   ######################################################################

   if { $FILwidthPX > $FILminwidthPX } {

      set FILwidthPX [expr {$FILwidthPX - $INCRpixelsLEFT}]
      set deltaFILwidthPX $INCRpixelsLEFT

      if { $FILwidthPX < $FILminwidthPX } {
         set deltaFILwidthPX [expr {($FILwidthPX + $INCRpixelsLEFT) - $FILminwidthPX}]
         set FILwidthPX $FILminwidthPX
      }

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'fil_listbox_width_reduce' : (new-FIL & same-DIR width parms)"
         puts "FILwidthPX: $FILwidthPX"
         puts "DIRwidthPX: $DIRwidthPX"
      }

      ######################################################################
      ## Decrease the fil-frame width, but keep the dir-frame width the same.
      ######################################################################
      ## Strangely, the dir-listbox decrements (along with the fil-listbox)
      ## unless I add the 'pack propagate ... false' statement --- on the
      ## fil-listbox, NOT the dir-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the dir-listbox, NOT the fil-listbox. None of this works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRfil false


      #########################################################
      ## We narrow the window by the amount we narrow the
      ## file listbox --- as follows.
      #########################################################

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ############################################################################
      ## Reduce the window width about as much as the decrease in fil-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen - $deltaFILwidthPX}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'fil_listbox_width_reduce' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   }
   ## END OF   if { $FILwidthPX > $FILminwidthPX }

}
## END OF PROC  'fil_listbox_width_reduce'



##+#####################################################################
## PROC  'tall_win'
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'TALL' the Tk window.
##   Several methods could be used.
##   For now, we query the current width and height of the window
##   (with 'winfo') and upsize the height by about 20%.
##
##   The user can keep clicking the button to downsize ~20% per click.
##
## ARGUMENTS: see the global vars
##
## CALLED BY:  .fRcntlbutts2.buttUPwin
##+#####################################################################

proc tall_win {} {

   global INCRfactor wmPIXELS_top  wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'tall_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Increase the window height about 20%.

   set winYlen [expr {int(floor ( $INCRfactor * $winYlen ))} ]

   ## Adjust the 'loc' vars for the window manager border.

   set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
   set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

   ## Apply the new window height.

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Leaving 'tall_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC  'tall_win'


##+#####################################################################
## PROC  'short_win'
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'SHORTEN' the Tk window.
##   Several methods could be used.
##   For now, we query the current height (and location) of the window
##   (with 'winfo') and downsize the height by ~20%.
##
##   The user can keep clicking the button to downsize ~20% per click.
##
## ARGUMENTS: see the global vars
##
## CALLED BY:  .fRcntlbutts2.buttDWNwin
##+#####################################################################

proc short_win {} {

   global INCRfactor wmPIXELS_top  wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'short_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Reduce the window height about 20%.

   set winYlen [expr {int(floor ( $winYlen / $INCRfactor ))} ]

   ## Adjust the 'loc' vars for the window manager border.

   set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
   set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

   ## Apply the new window height.

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Leaving 'short_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC  'short_win'


##+##############################################################
## PROC 'toggle_fontsize'
##+##############################################################
## PURPOSE:
##  This routine switches the size of fonts for about 20 different
##  widgets.
##+##############################################################
## CALLED BY:  the button command of the button
##             .fRcntlbutts1.butFontSize .
##+##############################################################

set FONT_FLAG "large"

proc toggle_fontsize { } {

   global  FONT_FLAG

   ## FOR TESTING: (must be commented for 'production' use)
   #  puts "Changing font size."

   if { "$FONT_FLAG" == "small" } then {

      set FONT_FLAG "large"

      .fRtitle.lab                  configure -font fontTEMP_varwidth
      .fRdirmaskHead.lab            configure -font fontTEMP_varwidth
      .fRdirfilmask.buttJump2Dir    configure -font fontTEMP_varwidth
      .fRdirfilmask.entryDIRFILmask configure -font fontTEMP_fixedwidth
      .fRlists.fRdir.lab            configure -font fontTEMP_varwidth
      .fRlists.fRdir.listbox        configure -font fontTEMP_fixedwidth
      .fRlists.fRfil.lab            configure -font fontTEMP_varwidth
      .fRlists.fRfil.listbox        configure -font fontTEMP_fixedwidth
      .fRstatusmsg.lab              configure -font fontTEMP_fixedwidth

      .fRcntlbutts1.buttExit        configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttHelp        configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttApplyScript configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttRefresh     configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttFontSize    configure -font fontTEMP_varwidth

      if {0} {
      ## These 'file management' buttons have not been implemented, yet.
      .fRcntlbutts1.butRmFile       configure -font fontTEMP_varwidth
      .fRcntlbutts1.butAllFilesInfo configure -font fontTEMP_varwidth
      .fRcntlbutts1.butFileInfo     configure -font fontTEMP_varwidth
      .fRcntlbutts1.butViewFile     configure -font fontTEMP_varwidth
      .fRcntlbutts1.butEditFile     configure -font fontTEMP_varwidth
      }

      .fRcntlbutts2.butDirListLeft  configure -font fontTEMP_varwidth
      .fRcntlbutts2.butDirListRight configure -font fontTEMP_varwidth
      .fRcntlbutts2.butFilListLeft  configure -font fontTEMP_varwidth
      .fRcntlbutts2.butFilListRight configure -font fontTEMP_varwidth
      .fRcntlbutts2.butTallWin      configure -font fontTEMP_varwidth
      .fRcntlbutts2.butShortWin     configure -font fontTEMP_varwidth

   } else {

      set FONT_FLAG "small"

      .fRtitle.lab                  configure -font fontTEMP_SMALL_varwidth
      .fRdirmaskHead.lab            configure -font fontTEMP_SMALL_varwidth
      .fRdirfilmask.buttJump2Dir    configure -font fontTEMP_SMALL_varwidth
      .fRdirfilmask.entryDIRFILmask configure -font fontTEMP_SMALL_fixedwidth
      .fRlists.fRdir.lab            configure -font fontTEMP_SMALL_varwidth
      .fRlists.fRdir.listbox        configure -font fontTEMP_SMALL_fixedwidth
      .fRlists.fRfil.lab            configure -font fontTEMP_SMALL_varwidth
      .fRlists.fRfil.listbox        configure -font fontTEMP_SMALL_fixedwidth
      .fRstatusmsg.lab              configure -font fontTEMP_SMALL_fixedwidth

      .fRcntlbutts1.buttExit        configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttHelp        configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttApplyScript configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttRefresh     configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttFontSize    configure -font fontTEMP_SMALL_varwidth

      if {0} {
      ## These 'file management' buttons have not been implemented, yet.
      .fRcntlbutts1.butRmFile       configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.butAllFilesInfo configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.butFileInfo     configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.butViewFile     configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.butEditFile     configure -font fontTEMP_SMALL_varwidth
      }

      .fRcntlbutts2.butDirListLeft  configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.butDirListRight configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.butFilListLeft  configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.butFilListRight configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.butTallWin      configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.butShortWin     configure -font fontTEMP_SMALL_varwidth

   }

}
## END OF PROC  'toggle_fontsize'


########################################################################
## PROC  'toggle_ybars'
########################################################################
## PURPOSE: To switch the side of the 2 y-scrollbars, 
##          on the sides of the 'dir' and 'fil' listboxes. 
## 
## CALLED BY: .fRcntlbutts1.buttYbarsSIDE
########################################################################

set SIDE_Ybars "left"

proc toggle_ybars {} {

   global SIDE_Ybars

   if { "$SIDE_Ybars" == "left" } {
      set SIDE_Ybars "right"
      set SIDE_other "left"
   } else {
      set SIDE_Ybars "left"
      set SIDE_other "right"
   }

   ## FOR TESTING:
   #  puts "SIDE_Ybars: $SIDE_Ybars"
   #  puts "SIDE_other: $SIDE_other"

   pack forget .fRlists.fRdir.scrbary .fRlists.fRdir.listbox \
      .fRlists.fRfil.scrbary .fRlists.fRfil.listbox

   pack .fRlists.fRdir.scrbary \
      -side $SIDE_Ybars \
      -anchor n \
      -fill y \
      -expand 0

   pack .fRlists.fRdir.listbox \
      -side $SIDE_other \
      -anchor n \
      -fill both \
      -expand 1

   pack .fRlists.fRfil.scrbary \
      -side $SIDE_Ybars \
      -anchor n \
      -fill y \
      -expand 0

   pack .fRlists.fRfil.listbox \
      -side $SIDE_other \
      -anchor n \
      -fill both \
      -expand 1

}
## END of proc 'toggle_ybars'


##+########################################################################
## 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} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_fixedwidth \
         -width  $VARwidth \
         -height 30 \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      ##  -height $VARheight \

      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_fixedwidth \
         -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} {
      ## 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'


##+####################################################################
##+####################################################################
##  END OF procs SECTION.
##+####################################################################
##+####################################################################

##+########################
## Set HELPtext var.
##+########################

set HELPtext \
"        ** HELP for this 'tkScriptApplicator' Utility **

This utility provides a Graphical User Interface (GUI) for
SELECTING A FILE OR FILES --- and this GUI is then used to call
a second, auxiliary GUI used for SELECTING AND APPLYING A SHELL
SCRIPT TO THE SELECTED FILE(S).

An 'Apply-a-Script' button on this GUI is used by the user to invoke
the auxiliary script-selector GUI with which to select the script
to be applied to the selected file(s).

*** This GUI utility is intended to offer an alternative to the
*** Nautilus file-manager of the Gnome 2 desktop environment
*** (or the Caja file-manager of the MATE desktop environment).
***
*** In particular, this utility is an alternative to the 'Nautilus Scripts'
*** 'select-file(s)-and-choose-a-script-to-run' capability
*** that was rather unique to the Nautilus 2.28 file-manager
*** that was available circa 2009 (but has since suffered 'regressions').

This initial GUI is presented by the Tk script
'tkScriptApplicator_multifilesSelector_2listboxes.tk'.

The script-selector GUI that is invoked by the 'Apply-a-Script' button
is presented by the Tk script 'scriptSelector_2listboxes.tk'.

The 'Jump2Dir' button on the file(s)-selector GUI provides another GUI via
a third Tk script --- 'dirSelector_orEditListOfFavoriteDirs_1listbox.tk'.

This set of three Tk GUI's does not provide the shell scripts
that are to be applied to the selected file(s), but
such scripts can be found in the typical locations for those
scripts in the Gnome 2 and MATE systems:

       \$HOME/.gnome2/nautilus-scripts
or
       \$HOME/.config/caja/scripts

Either of those two directory names (or your own directory name)
can be set in a 'set DIRshellScripts' statement at the bottom of
the script-selector Tk script ('scriptSelector_2listboxes.tk').

The 'Jump2Dir' button allows the user to quickly 'jump' to
another directory of file(s)-to-be-selected. The 'Jump2Dir'
GUI  allows the user to easily edit a list of 'directory favorites'
to which the user may quickly jump. This 'directory favorites'
feature is similar to the 'directory-Bookmarks' feature in the
Nautilus file manager (and in the Gnome 2 desktop top panel).


************
INSTALLATION:
************

The set of several files for this utility consists of three Tk scripts
and a 'directory-favorites' list file:

- this main Tk script 'tkScriptApplicator_multifilesSelector_2listboxes.tk'

- the Tk script 'scriptSelector_2listboxes.tk'

- the Tk script 'dirSelector_orEditListOfFavorites_1listbox.tk'

- the text file 'fave_dirs.lis'.

These Tk scripts and list file can be put in a sub-directory of the
user's home directory --- such as \$HOME/apps/tkScriptApplicator.

Then the user can use his/her desktop system (such as Gnome or KDE)
to set up the 'main' Tk script as an icon on the desktop (or in a
desktop 'panel').

Then, whenever the user wants to apply a 'Nautilus script' to one or
more files, the user can click on the icon to startup the 'main' Tk script.

The user can change the 'set' statement at the bottom of the
'scriptSelector_2listboxes.tk' Tk script to point to the desired
directory that holds the 'Nautilus scripts'.

Typically, the 'set' statements look like

       set DIRshellScripts \"\$env(HOME)/.gnome2/nautilus-scripts\"
 OR
       set DIRshellScripts \"\$env(HOME)/.config/caja/scripts\"

but the 'Nautilus scripts' could be placed in any directory.
Example: \$env(HOME)/ScriptsThatMakeMeHappy


********************
The BASIC OPERATIONS:
********************

A message in the 'main' GUI window indicates to the user the main options:

   - Navigate-dirs                      (click in the left listbox)
   - Change the current-directory path  (click on the 'Jump2dir' button)
   - Select-file(s)                     (click in the right listbox)
   - Select-and-Apply a shell script    (click on the 'Apply-a-Script' button)

In addition, the user can
   - show Help                          (click on the 'Help' button)
   - Exit                               (click on the 'Exit' button)

Other options (such as Rename, Delete, Copy, Move, Properties, etc.)
could be added (eventually) to the file(s)-selector GUI to make it
a more full-featured file manager.

---

There is a file-mask in a CurrentDirectory-and-FileMask entry field,
defaulted to '*'. The user can set that file-mask to filter
the filenames being shown. Example masks: *.htm*  OR
*.jpg  OR  *.sh  OR  \[a-m\]*

(The file(s)-selector, as initially released, is/was set to always
show 'hidden' directories and files. This could be changed rather easily.
This 'feature' reflects a preference of the author.)

---

The two main Tk GUI's of this utility (the 'file(s)-selector' and the
'script-selector') could be implemented in many different ways.

For example, a SINGLE listbox could be used to hold both
sub-directory names and filenames of a 'current directory'.
(Such a single-listbox GUI 'paradigm' could be used in an
alternative implementation of the 'tkScriptApplicator' utility.)

But these file(s)-selector and script-selector GUI's use a
TWO-listbox layout --- ONE listbox for sub-directory names and
ONE listbox for 'regular' filenames.

There are two, SIDE-BY-SIDE listboxes in these two GUI's:
  - a LEFT listbox for the sub-directories in the
    current directory (specified at the top of the GUI)
and 
  - a RIGHT listbox for the 'regular' (non-directory) files
    in the current directory.

'Hidden' (dot prefixed) directories and files are shown in
the two listboxes --- we do not hide them.

File(s) are selected by clicking on filename(s) in the right-hand,
files-listbox. If the user wants/needs to select more than one file,
the Ctrl and Shift keys can be used as needed.

---

A 'Jump2dir' button on the file(s)-selector GUI allows the user to
choose (jump to) a directory from a list of 'favorites'. Once a
'favorite' is chosen, the user is returned to the main GUI which
shows the sub-directories and files of the newly selected directory.

The user can also edit the 'fave_dirs.lis' file via that GUI
--- to add/change/delete directory names in the list.

A different text editor can be set at the bottom of the
'dirSelector_orEditListOfFavorites_1listbox.tk' Tk script ---
in a 'set EDITORtext' statement.

**************************
A NOTE ON 'symbolic links':
**************************

This Tk script-system is being released in 2014jul in a preliminary
form that is intended to handle 'symbolic links' in a robust fashion.

Some testing has been done in an attempt to implement 'robust navigation'
of symbolic-links to directories --- even when the links are 'broken' (not
pointing to an existing directory anymore).

   (The 'file(s)-selector' and 'script-selector' GUI's could notify the user
    of 'broken links' that are encountered. But that has not been done in
    the 2014jul release. Broken links are simply not shown in the listboxes.)

Navigating directories that are symbolic-links will typically not be
an issue, because the 'file(s)-selector' Tk script is usually selecting
files from a user's home directory, and the user is not typically making
'symbolic links' in the various sub-directories of his/her home
directory.

Furthermore, navigating symbolic-links will typically not be an issue for
the 'script-selector' Tk script, because the sub-directories containing
the 'Nautilus' shell scripts are typically not symbolic-links.

HOWEVER, there are times when it may be nice to make a symbolic link for
a directory --- for example, to point to a directory with a longer name,
that is, with many more directory levels.

If it turns out that there is a type of symbolic-link (for example,
a symbolic-link that points to a series of symbolic-links, relative
and/or absolute paths) that is not handled successfully/properly,
note that the user may work around this issue by navigating to the
actual target of a 'symbolic link'.
"


##+####################################################################
## 'ADDITIONAL GUI INITIALIZATION' SECTION.
##+####################################################################

##+#####################################################################
## Set a global variable to turn on messages for testing of
## 'symbolic link' processing in procs
## 'get_subdirs_inCURdir_inclLinks' & and 'get_files_inCURdir_inclLinks'.
##+#####################################################################

set DEBUGlink0or1 0
# set DEBUGlink0or1 1

##+##############################################################
## Set some global variables for the dir and fil listbox width
## incrementing and decrementing procs.
##+##############################################################

set CHARWIDTHpixels [font measure fontTEMP_fixedwidth "0"]

## FOR TESTING:
#  puts "CHARWIDTHpixels = $CHARWIDTHpixels"

set INCRpixelsLEFT 90
set INCRpixelsRIGHT 60

# set INCRpixelsLEFT 50
# set INCRpixelsRIGHT 40

set SCREENwidthPx  [winfo screenwidth .]

##+##############################################################
## Set some global variables for the window-height
## incrementing and decrementing procs.
##+##############################################################

# set INCRfactor 1.1
  set INCRfactor 1.2

# set SCREENheightPx [winfo screenheight .]


##+##################################################################
## Get the directory that this Tk script is in. That will be the
## directory that any 'external' utility shell or Tk script
## should be in.
##+#################################################################

## FOR TESTING:
#  puts "argv0: $argv0"

# set DIRscripts "."
# set DIRscripts "[pwd]"
# set DIRscripts "$env(HOME)/apps/tkScriptApplicator"
  set DIRscripts "[file dirname $argv0]"


##+####################################################################
## Set the 'DIRFILmask' variable and run the 'fill_lists' proc, to
## initialize the sub-directories and relative-filenames listboxes.
##+####################################################################
## NOTE: 'fill_lists' fills the dir and fil listboxes
##        according to the value of DIRFILmask.
## Must have DIRFILmask set properly when calling 'fill_lists'.
##+###################################################################

set DIRFILmask "$env(HOME)/*"

fill_lists

##+###################################################
## Initialize the selected filenames var to null.
##+###################################################

set FULFILnames ""

##+####################################################################
##+####################################################################
##  END OF FINAL GUI-INITIALIZATION SECTION
##  *AND*
##  END OF THIS Tk SCRIPT.
##+####################################################################
##+####################################################################


 Code for Tk script 'scriptSelector_2listboxes.tk' :
#!/usr/bin/wish
##
## If 'wish' is at a location like /usr/local/bin/wish, not /usr/bin/wish,
## you can do 'sudo ln -s /usr/local/bin/wish /usr/bin/wish' on the
## appropriate host(s).
##
##+########################################################################
## Tk SCRIPT NAME:  scriptSelector_2listboxes.tk
##
## WHERE:           typically installed in an 'apps' directory
##                  such as $HOME/apps/tkScriptApplicator      
##
##+########################################################################
## PURPOSE: Provides a Graphical User Interface (GUI) for
##          SELECTING A SCRIPT, from a hierarchy of subdirectories of scripts.
##
##          Intended to be called from a file(s)-selector utility, the Tk script
##          'tkScriptApplicator_multifilesSelector_2listboxes.tk'. Via that
##          GUI, the user can select one or more files in a listbox
##          and then click on an 'Apply-a-Script' button to bring up this GUI,
##          with which to select a script. The selected shell script
##          is to be run, by feeding the selected file(s) to the
##          chosen shell script.
##
##          This set of 'tkScriptApplicator' GUI's does not provide the 'Nautilus'
##          shell scripts that are to be applied to the selected file(s), but
##          such shell scripts can be found in the typical locations for those
##          scripts in the Gnome 2 and MATE desktop-environment systems:
##
##                 $HOME/.gnome2/nautilus-scripts
##          or 
##                 $HOME/.config/caja/scripts
##
#################################
## CONFIGURING THIS Tk GUI SCRIPT:
##
##          The user can change the 'set' statement at the bottom of this
##          'scriptSelector_2listboxes.tk' Tk script to point to the
##          desired directory that holds the 'Nautilus scripts'.
##
##          Typically, the 'set' statements look like
##
##          set DIRshellScripts "$env(HOME)/.gnome2/nautilus-scripts"
##           OR 
##          set DIRshellScripts "$env(HOME)/.config/caja/scripts"
##
##          but the 'Nautilus scripts' could be placed in any directory. 
##          Example: $env(HOME)/ScriptsThatMakeMeHappy
##
##          A package of more than 450 'Nautilus scripts' is available at
##          www.freedomenv.com, for installation in any of those
##          shell-scripts directories.
##
####################################
## AN OVERVIEW OF THIS Tk GUI SCRIPT:
## (how this Tk script fits in the 'tkScriptApplicator' mini-system)
##
##          The set of several Tk scripts constituting the 'tkScriptApplicator'
##          mini-system is intended to serve as an alternative to the
##          'select-files-and-right-click-to-choose-a-script-to-apply-
##          to-the-selected-files' capability of the Gnome2-Nautilus
##          file manager.
##
##          This 'script-selector' GUI is intended to supply the 'choose-a-script
##          to-apply-to-the-selected-files' part of that capability.
##
##+###################################
## The GUI DESIGN and OPERATION BASICS:
##
##          The two main Tk GUI's of this utility (the 'file(s)-selector' and
##          the 'script-selector') could be implemented in many different ways.
##
##          For example, a SINGLE listbox could be used to hold both
##          sub-directory names and filenames of a 'current directory'.
##          But these GUI's use a TWO-listbox layout --- ONE listbox for
##          sub-directory names and ONE listbox for 'regular' filenames.
##
##          There are TWO, SIDE-BY-SIDE LISTBOXES in this 'script-selector' GUI:
##            - a LEFT listbox for SUB-DIRECTORIES of the 'Nautilus' scripts directory
##          and
##            - a RIGHT listbox for the SCRIPTS (files) in a user-selected
##              'Nautilus' scripts sub-directory.
##
##          A script is selected by clicking on a filename in the right-hand,
##          files-listbox. The GUI supports a mask for 'filtering' filenames
##          such as '.sh'.
##
##          In other words, this utility is based on assuming the scripts all
##          have the same suffix, like '.sh', '.ksh', or '.bsh' or whatever.
##          (This avoids having to do a lot of extra processing, say using
##          a 'file' command, to determine the files that are scripts and
##          displaying, or accepting-selection-of, only those files.)
##
##          This utility is initialized to a mask of '*.sh' under the assumption
##          that all the 'Nautilus scripts' will have a suffix of '.sh'. But
##          that mask can be changed in a 'set DIRFILmask' statement at the
##          bottom of this 'script-selector' script.
##
##          A script is selected by clicking on a filename in the right-hand,
##          files-listbox. (Using the Ctrl and Shift keys to select more than
##          one script is not supported in the 'script-selector' GUI.)
##
##          Unlike the 'file(s)-selector' Tk script GUI, this 'script-selector'
##          Tk script GUI does NOT provide a 'Jump2dir' button --- because the
##          user is typically not going to want to 'jump' to another
##          directory hierarchy when using this 'script-selector' GUI.
##
##          'Hidden' (dot prefixed) directories and files could be shown in
##          the two listboxes --- we could choose not to hide them.
##
##   A message in the GUI window indicates to the user the main options:
##        - Navigate-dirs                (click in the left listbox)
##        - Select-a-script              (click in the right listbox)
##        - Run-the script               (click on the 'UseIt' button)
##
##          NOTE: Actually this Tk script does NOT run the selected script.
##                This Tk script writes the script-name to 'stdout' and thus
##                passes the scriptname back to the calling Tk script.
##                The calling 'file(s)-selector' Tk script executes the selected
##                shell script, with the selected filename(s) as arguments.
##
##   In addition, the user can
##        - show Help                    (click on the 'Help' button)
##        - Cancel                       (click on the 'Cancel' button)
##
##           The options available to the user in this implmentation
##           are indicated by the following 'sketch' of the GUI:
##
## ---------------------------------------------------------------------
##
## THE SKETCH CONVENTIONS for GUI sketch below:
##
## SQUARE-BRACKETS indicate a comment not to be included on the GUI.
## BRACES          indicate a Tk 'button' widget.
## UNDERSCORES     indicate a Tk 'entry' widget.
## A COLON         indicates that the text before the colon is on
##                 a 'label' widget.
## CAPITAL-O       indicates a Tk 'radiobutton' widget.
## CAPITAL-X       indicates a Tk 'checkbutton' widget.
##
##  ----------------------------------------------------------------------------------------
##  Script Selector - verYYYYMMMDD
##  [window title]
##  ----------------------------------------------------------------------------------------
##
##   TopLevel Frame
##   and SubFrame
##    names
##     |
##     V
##
## .fRdirmaskHead  CURRENT DIRECTORY & FILE MASK:
##
## .fRdirfilmask   $env(HOME)/.gnome2/nautilus-scripts/*.sh________________________________            
##
## .fRlists  [This 'fRlists' frame is to contain two subframes --- 'fRdir' and 'fRfil' ---
##            with 'fRdir' on the left of 'fRfil'.]
##
## .fRlists.fRdir   This frame is to contain a listbox with x-and-y scrollbars.
##                  This frame is also to contain a heading (in a label) above the listbox.
##  
## .fRlists.fRfil   This frame is to contain a listbox with x-and-y scrollbars.
##                  This frame is also to contain a heading (in a label) above the listbox.
##
## .fRscriptHead  SCRIPT SELECTION:
##
## .fRscriptSel   __________________________________________________
##
## .fRcntlbutts1   {UseIt} {Cancel} {Help} {Refresh}    
##  
## .fRcntlbutts2   {naroDir} {wideDir} {naroFil} {wideFil} {tallWin} {shortWin}
##
## .fRstatusmsg    Click on a Script (on the right) and click 'UseIt'  -OR-   Click on a Dir (on
##                 the left) to Navigate  -OR-  Cancel.
##
##                 [This initial message in a text widget may be changed in response to various
##                  user actions on the GUI --- directory selection, etc.]
##
##+##############
## GUI components:
##
## From the GUI 'sketch' above, it is seen that the GUI consists of ABOUT:
##
##  10 'button' widgets
##   5 'label'  widgets (mostly for headings)
##   2 'entry'  widgets (for the current-directory-and-file-mask and for selected-script)
##   2 'listbox' widgets (with scrollbars)
##   1 'text' widget
##   0 'checkbutton' widgets
##   0 'radiobutton' widgets
##   0 'scale'  widgets
##   0 'canvas' widgets
##
##+###############################
## NOTE on the 'CURRENT DIRECTORY':
##
##    This script does not change the 'current-working-directory' (with the
##    Tcl 'cd' command) at any point in processing user actions, such as
##    during directory navigation.
##
##    This script is intended to simply write the fully-qualified filename
##    of the selected script to 'stdout'.
##
##    There is no need to change the CWD (current working directory) in
##    the process of navigating to and selecting the 'Nautilus' shell script.
##
##+########################################################################
## NOTES on 'PLATFORMS' (operating systems):
##
##    The Tcl 'glob' command is used to get the filenames --- both
##    directory names and 'regular' file names --- to fill the 2 listboxes
##    of this 'script-selector' GUI.
##
##    Furthermore, Tcl 'file' commands, such as
##
##     file type $file      (Returns: file, directory, characterSpecial,
##                                    blockSpecial, fifo, link, OR socket)
##     file readlink $file  (Good for finding the target of a link.)
##     file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##     file exists $file
##
##    are used to get the directory names and 'regular' file names for
##    the current directory specified at the top of the GUI.
##
##    Although there are typically no 'symbolic links' in a Nautilus
##    scripts directory structure, we use 'fill_lists' code like that
##    in the 'file(s)-selector' Tk script --- with the intent of trying
##    to handle 'link' files as well as 'directory' and 'file' files ---
##    even if a link is 'broken' (pointing to a file that no longer exists).
##
##    With these factors in mind, this 'tkScriptApplicator' mini-system
##    utility will probably support a variety of Linux and Unix hosts
##    (most Linux distros, BSD flavors, HP-UX, IBM-AIX, Sun-Solaris,
##     etc. --- and probably the Apple Mac OS, since it is BSD-based).
##
##    Note that a main requirement is that the 'Nautilus' scripts (that
##    are to be applied to the selected files) are available and run on
##    the user's operating system. ALSO, any utility programs called by
##    those shell scripts need to be available on the the user's
##    operating system and need to be runnable on that operating system.
##
##    So the nature of the 'Nautilus' scripts is a key factor in
##    determining on which operating systems this 'tkScriptApplicator'
##    mini-system can be run.
##
##+###########################
## NOTE on 'SYMBOLIC LINKS':
##
##    With respect to the Tcl 'file' statements used in this
##    'file(s)-selector' Tk script, an example of how the listbox-filling
##    code could be constructed can be seen in the 'dirview' script on
##    pages 229-234 of the first edition of the book
##    'Graphical Applications with Tcl & Tk' (1996) by Eric F. Johnson.
##
##    However, that code needs quite a bit of enhancement to handle
##    'symbolic-links' robustly (whether they are broken or intact).
##
##    This Tk script-system is being released in 2014jul in a preliminary
##    form that is intended to handle 'symbolic links' in a robust fashion.
##
##    Some testing has been done --- especially with the 'file(s)-selector'
##    Tk script --- in an attempt to implement 'robust navigation' of
##    symbolic-links to directories --- even when the links are 'broken' (not
##    pointing to an existing directory anymore).
##
##       (The 'file(s)-selector' and 'script-selector' GUI's could notify the user
##        of 'broken links' that are encountered. But that has not been done in
##        the 2014jul release. Broken links are simply not shown in the listboxes.)
##
##    Navigating directories that are symbolic-links will typically not be
##    an issue, because the 'file(s)-selector' Tk script is usually selecting
##    files from a user's home directory, and the user is not typically making
##    'symbolic links' in the various sub-directories of his/her home
##    directory.
##
##    Furthermore, navigating symbolic-links will typically not be an issue for
##    the 'script-selector' Tk script, because the sub-directories containing
##    the 'Nautilus' shell scripts are typically not symbolic-links.
##
##    HOWEVER, there are times when it may be nice to make a symbolic link for
##    a directory --- for example, to point to a directory with a longer name,
##    that is, with many more directory levels.
##
##    If it turns out that there is a type of symbolic-link (for example,
##    a symbolic-link that points to a series of symbolic-links, with relative
##    and/or absolute paths) that is not handled successfully/properly,
##    note that the user may work around this issue by navigating to the
##    actual target of a 'symbolic link'.
##
##+########################################################################
## STRUCTURE OF THIS CODE:
##
##  0) Set general window & widget parms (win-name, win-position,
##     win-color-scheme, fonts, widget-geom-parms, text-array-for-labels-etc,
##     win-size-control).
##
##  1a) Define ALL frames (& sub-frames, if any).
##  1b) Pack the frames.
##
##  2) Then define & pack all widgets in the frames -- frame by frame.
##     After all the widgets for a frame are defined, pack them in the frame.
##
##  3) Define keyboard and/or mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (typically with one or more of
##     the procs), if needed.
##
## In more detail, for this particular Tk script:
##
##  1a) Define ALL frames -- and sub-frames:
##
##  '.fRdirmaskHead' to hold a label widget.
##
##  '.fRdirfilmask'  to hold an entry widget.
##   
##  '.fRlists'       to contain the following two subframes --- 'fRdir' and 'fRfil' ---
##                   with 'fRdir' on the left of 'fRfil'.
##
##  '.fRlists.fRdir'  to contain a listbox with x-and-y scrollbars ---
##                    with a label widget above the listbox.
##  
##  '.fRlists.fRfil'  to contain a listbox with x-and-y scrollbars ---
##                    with a label widget above the listbox.
##
##  '.fRscriptHead'   to hold a label widget.
##
##  '.fRscriptSel'    to hold an entry widget.
##
##  '.fRcntlbutts1'   to contain about 4 button widgets.   
##  
##  '.fRcntlbutts2'   to contain about 6 button widgets.
##
##  '.fRstatusmsg'    to contain a label widget.
##
##       This is about 8 top-level frames.
##       Most of these toplevel-frames are 1 character high.
##       Exceptions:  '.fRlists' and '.fRstatusmsg'
##
##  1b) Pack ALL these frames and sub-frames.
##
##  2) Define & pack all widgets in the frames -- basically going through
##     frames & their interiors in top-to-bottom and/or left-to-right order.
##
##  3) Define BINDINGS. See BINDINGS section below.
##
##     SOME examples:
##       - Enter key press in DirFilMask entry field (run 'fill_lists' proc)
##       - Single-click (button release) on dir-listbox (run 'navigate' proc)
##       - Single-click (button release) on fil-listbox (run 'get_selected_file' proc)
##       - Enter key press in ScriptSelection entry field (run 'put_var_exit' proc)
##
##  4) Define PROCS.   See the PROCS section below.
##
##     SOME examples:
##
##       'fill_lists'        - fills the 2 listboxes based on the current dir and mask.
##       'navigate'          - chgs ref-dir & runs 'fill_lists'.
##       'get_selected_file' - uses selected rel-filename to set FULFILname.
##       'set_status_msg'    - puts a message in the StatusMsg label.
##       'put_var_exit'      - puts $FULFILname to stdout and exits.
##
##   And for the 6 'window-size' buttons:
##    'dir_listbox_width_increase'
##    'dir_listbox_width_reduce'
##    'fil_listbox_width_increase'
##    'fil_listbox_width_reduce'
##    'tall_win'
##    'short_win'
##
##  5) ADDITIONAL GUI INITIALIZATION section.
##     See this section at the bottom of this script.
##
##+#####################################################################
## OUTPUT:
##          A single, fully-qualified filename to standard out.
##          If no script is selected (Cancel button or user Closes window),
##          nothing is sent to stdout.
##
##     In a script calling this Tk script, the programmer would typically
##     capture stdout in a variable like SCRIPTNAME and, if SCRIPTNAME were
##     null, typically do nothing with SCRIPTNAME or exit the application.
##
##     See the 'CALL FORMAT' section below for an example of this script
##     setting SCRIPTNAME.
##
##+########################################################################
## CALL FORMAT:
##    (Here are two examples -- one in a Tcl-Tk script and one in a
##     Linux/Unix shell script.)
##
##-----------------------
## EXAMPLE1 (in Tk script):
##-----------------------
##
##    set SCRIPTname [ $DIRscripts/scriptSelector_2listboxes.tk ]
##
##    if { "$SCRIPTname" == "" } {
##       return      (or some other processing)
##    }
##
##    exec $SCRIPTname ... filenames ...
##
##---------------------------
## EXAMPLE2 (in shell script):
##---------------------------
##
##   SCRIPTNAME=`$DIR_SCRIPTS/scriptSelector_2listboxes.tk`
##
##   if test "$SCRIPTNAME" = ""
##   then
##      exit      (or some other processing)
##   fi
##
##   $SCRIPTNAME ... filenames ...
##
##+#####################################################################
## MAIN INTERNAL (GLOBAL & LOCAL) VARS:
##
##   DIRFILmask  Set initially to a 'base' scripts directory name, with
##               a files mask such as '*' or '*.sh'. Each time there is a
##               single click on a subdir name, the directory portion of
##               this DIRFILmask var is changed, while the mask portion
##               is not changed.
##
##   CURdir      Extracted when needed from DIRFILmask.
##
##   FILmask     Holds the mask, '*'  OR  '*.sh'  OR  whatever.
##
##   RELDIRsel   Extracted from 'dir' listbox, in the
##               'navigate' proc.    (Should NOT contain an
##               ending '/' (slash), nor preceding slashes.
##               No slashes at all.)
##
##   FULFILname  Set from file-listbox, in 'get_selected_file' proc.
##
##  NOTE:  In various parts of this script, at places indicated by a
##         '## FOR TESTING:' line, there are commented 'puts' statements
##         to help in development and testing. In particular,
##         there are commented 'puts' statements in the 'fill_lists' proc
##         (and procs that it calls) that can be uncommented to test/verify
##         the settings of the various 'dir' and 'fil' (and 'mask') vars
##         in various navigation scenarios.
##+#####################################################################
## WIDGET NAMING CONVENTION:
##
##        In this script, in a widget hierarchy, the names of
##
##           frames      are prefixed by 'fR'
##           buttons     are prefixed by 'butt' or 'but'
##           labels      are prefixed by 'lab' or 'label'
##           entry-boxes are prefixed by 'ent' or 'entry'
##           listboxes   are prefixed by 'listbox' or 'lbox'
##           scrollbars  are prefixed by 'scrbar'
##           text        are prefixed by 'text'
##
##    Although not used in this script, conventions for other widgets:
##
##        radio-buttons  are prefixed by 'radbutt' or 'radbut'
##        check-buttons  are prefixed by 'chkbutt' or 'chkbut'
##
##        A 'generic' example for an entry widget and its parent frames:
##           .fRtop.fRdir.entDir
##+########################################################################
## NOTE ON SEPARATING CODE FROM COMMENTS:
##
##       You can see only the non-commented, executable lines of this
##       script by using
##              egrep -v '^ *##|^ *# ' <script-name>
##       or
##              grep -v '^ *##' <script-name> | grep -v '^ *# '
##
##       OR, extract most of the comments with
##              egrep '^ *##|^ *# ' <script-name>
##
##+#######################################################################
## 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
## but this script should work in most previous 8.x versions, and probably
## even in some 7.x versions (if font handling is made 'old-style').
##+########################################################################
## MAINTENANCE HISTORY:
## Started by: Blaise Montandon 2014jul18 Started developing this script
##                                        and auxiliary Tk scripts
##                                        based on similar FE scripts from
##                                        the 'feHappyScripts' system.
##                                        Started putting code in the
##                                        'fill_lists' proc.
## Changed by: Blaise Montandon 2014jul27 Replaced an 'ls -Ap' technique
##                                        by a 'glob' technique.
## Changed by: Blaise Montandon 2014jul29 A cleanup pass on this script. 
##+########################################################################
## HEY, ME!
## IF YOU UPDATE THIS SCRIPT, REMEMBER TO CHANGE THE FOLLOWING VERSION ID!!!
##+########################################################################
## For window title:
set VERSIONfilesel "ver2014jul29"

##+######################################################
## Set WINDOW TITLE and POSITION.
##+######################################################

wm title . \
"Script-Selector - $VERSIONfilesel"

wm iconname . "ScriptSelector"

wm geometry . +30+45


##+######################################################
## Set the COLOR SCHEME for the window and its widgets ---
## such as listbox and entry field background color.
##+######################################################

tk_setPalette "#e0e0e0"

# set headBKGD  "#b0b0b0"
# set headBKGD  "#99ff99"

set entryBKGD   "#ffffff"
set listboxBKGD "#ffffff"

# set STATUSmsgBKGD "#f0f0f0"
set STATUSmsgBKGD "#ff9999"

# set radbuttBKGD "#c0c0c0"
# set chkbuttBKGD "#c0c0c0"
# set scaleBKGD   "#f0f0f0"


##+########################################################
## DEFINE (temporary) FONT NAMES.
##
## We use a VARIABLE-WIDTH font for text on LABEL and
## BUTTON widgets.
##
## We use a FIXED-WIDTH font for LISTBOX lists,
## for text in ENTRY fields --- and often for text in
## TEXT widgets.
##
## 'weight' can be 'bold' or 'normal'.
## 'slant'  can be 'roman' or 'italic'.
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -12 \
   -weight normal \
   -slant roman

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

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


font create fontTEMP_fixedwidth  \
   -family {droid sans mono} \
   -size -12 \
   -weight normal \
   -slant roman

font create fontTEMP_SMALL_fixedwidth  \
   -family {droid sans mono} \
   -size -10 \
   -weight normal \
   -slant roman

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


##+###########################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. width and height of canvas, and padding for Buttons)
##+###########################################################

## Some WINDOW-MANAGER BORDER width settings:
## (We could try to set these parameters via a re-usable proc
##  that uses the 'wm' and 'winfo' commands. But that may not
##  work for all window managers and operating systems.
##  So we simply set the values here.)

set wmPIXELS_left 3
set wmPIXELS_top 23


## LABEL widget geom settings:

set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2
set RELIEF_label_lo "flat"


## BUTTON widget geom settings:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2
## We use '-relief raised' for all 'button' widgets.


## ENTRY widget geom settings:

set BDwidthPx_entry 2
## We use '-relief sunken' for all 'entry' widgets.


## LISTBOX widget geom settings:

set initWIDTHchars_dir_listbox 25
## This width was set so that the label at the top of
## the listbox is not clipped.

set initWIDTHchars_fil_listbox 85
## This width was set to accomodate the long script filenamse
## in some FE NautilusScripts directories, such as 'AUDIOtools'.

set initHEIGHTchars_fil_listbox 15
set minHEIGHTchars_fil_listbox 1

set BDwidthPx_listbox 2
## We use '-relief sunken' for the 'listbox' widget.


## TEXT widget geom settings:

set BDwidthPx_text 2
# set RELIEF_numtext "ridge"


## We nullify the following settings. NOT USED, yet.
if {0} {

## RADIOBUTTON widget geom settings:

set PADXpx_radbutton 0
set PADYpx_radbutton 0
set BDwidthPx_radbutt 2
set RELIEF_radbutt_hi "raised"


## CHECKBUTTON widget geom settings:

set PADXpx_chkbutton 0
set PADYpx_chkbutton 0
set BDwidthPx_chkbutt 2
set RELIEF_chkbutt_hi "raised"


## SCALE widget geom parameters:

# set BDwidthPx_scale 2
# set scaleThicknessPx 10

}
## END OF if {0}  (to nullify these settings)


##+####################################################################
## 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 the 'fRdirfilmask' frame:

set aRtext(labCurDirMask) "CURRENT DIRECTORY & FILE MASK:"


## For the 'fRlists.fRdir' frame:

set aRtext(labListSubdirs) \
"Parent Dir \[..\]  & N Sub-Dirs
\[ Navigate = Single-Click \]"


## For the 'fRlists.fRfil' frame:

set aRtext(labListFiles) \
"N Files
\[ Select = Single-Click \]"


## For the 'fRscriptHead' frame:

set aRtext(labScrSelection) "SCRIPT SELECTION:"


## For the 'fRcntlbutts1' frame:

set aRtext(buttOK)       "UseIt"
set aRtext(buttCancel)   "Cancel"
set aRtext(buttHelp)     "Help"
set aRtext(buttFilter)   "Refresh"
set aRtext(buttFontSize) "TogFontSize" 
set aRtext(YbarsSIDE)    "<-Ybars->"


## For the 'fRcntlbutts2' frame:

set aRtext(buttDirListLeft)  "naroDir"
set aRtext(buttDirListRight) "wideDir"
set aRtext(buttFilListLeft)  "naroFil"
set aRtext(buttFilListRight) "wideFil"
set aRtext(buttTallWin)      "tallWin"
set aRtext(buttShortWin)     "shortWin"


## For the 'fRstatusmsg' frame:

set aRtext(filledSTATUSmsg) \
"Click on a Script (on the right) and click 'UseIt'  -OR-
Click on a Dir (on the left) to Navigate  -OR-  Cancel."

set aRtext(twolineSTATUSmsg) \
"Select-File(s)-and-Apply-a-Script  -OR-  Select-Subdir-to-Navigate
-OR-  Jmp2dir  -OR-  Exit."

set aRtext(unfilledSTATUSmsg) \
"If 'Current-Directory-and-FileMask' is OK, click the 'Refresh' button
to fill the two listboxes.  Then click on a Script (on the right) and
click 'UseIt'  -OR-  Cancel."

set aRtext(maskExamplesSTATUSmsg) \
"If the Directory-and-File-Mask is OK,  **CLICK 'Refresh' TO START**.

Examples of valid file masks:
  * (all files)   OR   *.sh   OR   *CROP*.sh"

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


##+######################################################################
## Set a MIN-SIZE of the window (roughly).
##
## For WIDTH, allow for a min-width for the side-by-side sub-directories
## and files listboxes.
##
## For HEIGHT, allow for the stacked frames:
##      1 char   high for the '.fRdirmaskHead'  frame
##      1 char   high for the '.fRdirfilmask'   frame   
##      N chars  high for the '.fRlists'        frame
##      1 char   high for the '.fRscriptHead'   frame
##      1 char   high for the '.fRscriptSel'    frame
##      1 char   high for the '.fRcntlbutts1'   frame     
##      1 char   high for the '.fRcntlbutts2'   frame
##      2 chars  high for the '.fRstatusmsg'    frame
## where N =  $minHEIGHTchars_fil_listbox + 3  chars, where
## 3 chars allows for a 2-line label above the listboxes and
## the horizontal scrollbar of the 2 listboxes.
##+#####################################################################

## FOR WIDTH:
## We set the min-width according to character units.
## We allow for about 10 characters minimum for the directories
## listbox and about 20 chars minimum for the files listbox:

set charWidthPx [font measure fontTEMP_varwidth "W"]
set DIRminwidthPX [expr {10 * $charWidthPx}]
set FILminwidthPX [expr {20 * $charWidthPx}]
set minWinWidthPx [expr {$DIRminwidthPX + $FILminwidthPX}]

## We will use the DIRminwidthPX,FILminwidthPX vars in
## some procs below for the 'naroDir' and 'naroFil' buttons.

##+#################################################
## For HEIGHT --- for
##      1 char   high for the '.fRdirmaskHead'  frame
##      1 char   high for the '.fRdirfilmask'   frame   
##      N chars  high for the '.fRlists'        frame
##      1 char   high for the '.fRscriptHead'   frame
##      1 char   high for the '.fRscriptSel'    frame
##      1 char   high for the '.fRcntlbutts1'   frame     
##      1 char   high for the '.fRcntlbutts2'   frame
##      2 chars  high for the '.fRstatusmsg'    frame
##    --------
##     8+N chars  high for the 8 frames
## where N =  $minHEIGHTchars_fil_listbox + 3 chars, where
## 3 chars allows for a 2-line label above the listboxes and
## allows (roughly) for the horizontal scrollbar of the 2 listboxes.
##+#################################################

set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {(11 + $minHEIGHTchars_fil_listbox) * $charHeightPx}]

## Add about 25 pixels for top-bottom window decoration --
## and some pixels for top-and-bottom of frame/widget borders
## (~8 widgets x 4 pixels/widget = 32 pixels).

set minWinHeightPx [expr {57 + $minWinHeightPx}]


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

wm minsize . $minWinWidthPx $minWinHeightPx

## We need to allow the window to be resizable ---
## so that the 'fRdir' and 'fRfil' frames can expand,
## both horizontally and vertically.

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

## If you want the window to resize in x-direction, but not y.
#   wm resizable . 1 0


##+#######################################################################
##+#######################################################################
##
##  DEFINE *ALL* TOP-LEVEL frames -- (top to bottom):
##
##     - 'fRdirmaskHead'  to contain a label widget
##     - 'fRdirfilmask'   to contain an entry widget
##     - 'fRlists'        to contain 'dir' & 'files' frames, side-by-side
##     - 'fRscriptHead'   to contain a label widget
##     - 'fRscriptSel'    to contain an entry widget
##     - 'fRcntlbutts1'   to contain bottom buttons - UseIt,Cancel,Help,...
##     - 'fRcntlbutts2'   to contain bottom buttons - naroDir,wideDir,...,tallWin,shortWin
##     - 'fRstatusmsg'    to contain a label widget
##
##  Also define the two side-by-side frames within 'lists' --
##  'fRlists.fRdir'  &  'fRlists.fRfil'.
##+#######################################################################
##+#######################################################################

set RELIEF_frame flat
set BDwidthPx_frame 0

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

frame .fRdirmaskHead    -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame
frame .fRdirfilmask     -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame

frame .fRlists          -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame

frame .fRscriptHead     -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame
frame .fRscriptSel      -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame

frame .fRcntlbutts1     -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame
frame .fRcntlbutts2     -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame
frame .fRstatusmsg      -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame

## The two SUB-FRAMES:

frame .fRlists.fRdir    -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame
frame .fRlists.fRfil    -relief $RELIEF_frame   -borderwidth $BDwidthPx_frame


##+########################################################
## PACK *ALL* the top-level FRAMES.
##+########################################################
##  The following direct, top-to-bottom approach
##  would be nice, but it does not have
##  good control of various frames.
##
##  We want 'fRlists' to 'fill y' and y-expand (as well as
##  'fill x' and x-expand)  -- but we do not want 'fRdirmaskHead',
##  'fRdirfilmask', 'fRscriptHead', 'fRscriptSel',
##  'fRcntlbutts1', 'fRcntlbutts2', and 'fRstatusmsg' to y-expand.
##
##+########################################################
## WE WILL HAVE TO PACK THEM IN SUB-GROUPS, *NOT* like this:
##
## pack  .fRdirmaskHead \
##       .fRdirfilmask \
##       .fRlists \
##       .fRscriptHead \
##       .fRscriptSel \
##       .fRcntlbutts1 \
##       .fRcntlbutts2 \
##       .fRstatusmsg \
##    -side top \
##    -anchor nw \
##    -fill both \
##    -expand 0
##
##+########################################################

##+########################################################
## We want the following for the 'fRlists.fRdir' &
## 'fRlists.fRfil' SUB-frames:
##             -fill both  -expand 1
## so that the listboxes within these frames can expand.
##+########################################################
## GENERAL 'pack' RULE FOR A LISTBOX & ITS SCROLLBARS:
##
##    To allow a listbox  to expand-xy
##    and its y-scrollbar to expand-y
##    and its x-scrollbar to expand-x,
##
##    any 'parent'-frames containing these widgets
##    should use parms:
##            -fill both   -expand 1
##
##    For packing the widgets themselves, we use:
##       y-scrollbar:  -fill y      -expand 0
##       x-scrollbar:  -fill x      -expand 0
##       listbox:      -fill both   -expand 1
##    The order of packing these widgets is important.
##    See comments below, where these widgets are packed.
##+########################################################


##+#######################################################
## PACK 'fRdirmaskHead' & 'fRdirfilmask' FRAMEs --
## at TOP of window.
##+#######################################################
## DO NOT USE '-expand 1' HERE.
## THAT WOULD ALLOW these FRAMES TO Y-EXPAND.
##+#######################################################

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


##+############################################################
## PACK 'fRscriptHead' & 'fRscriptSel' &
##      'fRcntlbutts1' & 'fRcntlbutts2' & 'fRstatusmsg' FRAMEs
## at BOTTOM of window.
##+############################################################
## DO NOT USE '-expand 1' HERE; THAT WOULD KEEP THE
## 'fRlists' LISTBOX FRAME FROM Y-EXPANDING fully.
##+############################################################

pack .fRstatusmsg \
     .fRcntlbutts2 \
     .fRcntlbutts1 \
     .fRscriptSel \
     .fRscriptHead \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0


##+#################################################################
## PACK 'fRlists' FRAME  --
## *UNDER* 'fRdirmaskHead' & 'fRdirfilmask' FRAMEs
## AND
## *OVER* 'fRscriptHead' & 'fRscriptSel' &
##        'fRcntlbutts1' & 'fRcntlbutts2' & 'fRstatusmsg' & FRAMEs.
##+################################################################
## NOTE: We pack the 'fRlists' frame AFTER the bottom 3 frames,
## to keep the bottom 3 frames from 'scrunching up'
## when the user grabs the bottom edge of the window
## and moves it up. I.e. we want to make the 'fRlist' frame
## scrunch up before the bottom 3 frames scrunch up.
##+############################################################

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

##+##############################################################
## PACK THE two SUB-FRAMES of the 'fRlists' FRAME --
## 'fRdir' and 'fRfil'.
##+##############################################################
##  '-fill both'  AND  '-expand 1'
##  SHOULD BE USED HERE, TO ALLOW THE LISTBOX IN '.fRlists.fRdir'
##  and THE LISTBOX IN '.fRlists.fRfil' TO FILL AND XY-EXPAND.
##+##############################################################

pack .fRlists.fRdir \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

pack .fRlists.fRfil \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1


##+################################################################
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES.
##+################################################################

##+##################################################
##  IN THE 'fRdirmaskHead' FRAME --
##  DEFINE a LABEL WIDGET. THEN PACK IT.
##+##################################################

label .fRdirmaskHead.lab \
   -text "$aRtext(labCurDirMask)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd 0

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


##+##################################################
##  IN THE 'fRdirfilmask' FRAME --
##  DEFINE an ENTRY WIDGET. THEN PACK IT.
##+##################################################

entry .fRdirfilmask.entryDIRFILmask \
   -textvariable DIRFILmask \
   -font fontTEMP_fixedwidth \
   -relief sunken \
   -borderwidth $BDwidthPx_entry \
   -bg $entryBKGD

## Protect the refdir-and-mask from 'direct' change by the user.
## The user changes the directory here by clicking on the 'dirs' listbox.

.fRdirfilmask.entryDIRFILmask config -state disabled
.fRdirfilmask.entryDIRFILmask config -disabledbackground "#ccffcc"
.fRdirfilmask.entryDIRFILmask config -disabledforeground "#999999"

## Pack the widget in frame '.fRdirfilmask'.

pack .fRdirfilmask.entryDIRFILmask \
   -side left \
   -anchor w \
   -fill x \
   -expand 1


##+###############################################################
## USE '-fill x' ONLY IF YOU WANT WIDGETS (like entry) TO FILL OUT
## THE ENTIRE 'fRdirfilmask' FRAME (for a very wide entry field).
## We use '-fill x' on this entry widget, but NOT on button widgets
## and NOT on label widgets that precede a widget like an entry widget.
##
## YOU CAN USE '-expand 1' TO ALLOW SOME WIDGETS (like entry)
## TO SPREAD OUT. THEN YOU NEED TO BE SURE TO USE '-expand 0' IN
## PARENT FRAMES,  like 'fRdirfilmask', TO KEEP WIDGETS (button,
## entry, and most label widgets) FROM Y-EXPANDING.
##+###############################################################


##+####################################################################
## In the 'fRlists.fRdir' FRAME -- DEFINE WIDGETS:
##     - LABEL (to be packed at top)
##     - SCROLLBAR-Y and LISTBOX  (to be left-to-right)
##     - SCROLLBAR-X (to be at bottom).
## THEN PACK THEM.
##+####################################################################
## NOTE: Set 'selectmode' of dir-listbox  to 'single'.
##+####################################################################

label .fRlists.fRdir.lab \
   -text "$aRtext(labListSubdirs)" \
   -font fontTEMP_varwidth \
   -justify left \
   -width $initWIDTHchars_dir_listbox \
   -anchor w \
   -relief flat \
   -bd 0

listbox .fRlists.fRdir.listbox \
   -width $initWIDTHchars_dir_listbox \
   -font fontTEMP_fixedwidth \
   -relief sunken \
   -borderwidth $BDwidthPx_listbox \
   -bg $listboxBKGD \
   -selectmode single \
   -yscrollcommand ".fRlists.fRdir.scrbary set" \
   -xscrollcommand ".fRlists.fRdir.scrbarx set"

scrollbar .fRlists.fRdir.scrbary \
   -orient vertical \
   -command ".fRlists.fRdir.listbox yview"

scrollbar .fRlists.fRdir.scrbarx \
   -orient horizontal \
   -command ".fRlists.fRdir.listbox xview"

##+#################################################
## PACK all the widgets in frame 'fRlists.fRdir' ---
## lab, scrbarx,scrbary,listbox -- in that order.
##+#################################################
## DO NOT USE '-expand 1' for the X-SCROLLBAR.
## THAT WOULD KEEP THE LISTBOX FROM Y-EXPANDING.
##+#################################################
## DO NOT USE '-expand 1' for Y-SCROLLBAR.
## THAT WOULD ALLOW the Y-SCROLLBAR TO X-EXPAND, WHICH
## PUTS BLANK SPACE BETWEEN Y-SCROLLBAR & ITS LISTBOX.
##+#################################################

pack .fRlists.fRdir.lab \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRlists.fRdir.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRlists.fRdir.scrbary \
   -side left \
   -anchor nw \
   -fill y \
   -expand 0

##+###################################################
## NOTE: WE PACK THE SCROLL BARS BEFORE THE LISTBOX.
##       THE LISTBOX WOULD TAKE ALL THE FRAME-SPACE,
##       WITH '-fill both -expand 1'.
##+###################################################

pack .fRlists.fRdir.listbox \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

##+####################################################################
## In the 'fRlists.fRfil' FRAME -- DEFINE WIDGETS:
##     - label (to be packed at top)
##     - listbox and scrollbar-y (to be packed left-to-right)
##     - scrollbar-x (to be packed at bottom).
## THEN PACK THEM.
##+####################################################################
## NOTE: Set 'selectmode' of fil-listbox  to 'single', rather than
## 'extended'. We want to select only one script at a time.
##+####################################################################

label .fRlists.fRfil.lab \
   -text "$aRtext(labListFiles)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd 0

listbox .fRlists.fRfil.listbox \
   -width $initWIDTHchars_fil_listbox \
   -height $initHEIGHTchars_fil_listbox \
   -font fontTEMP_fixedwidth \
   -relief sunken \
   -borderwidth $BDwidthPx_listbox \
   -bg $listboxBKGD \
   -selectmode single \
   -yscrollcommand ".fRlists.fRfil.scrbary set" \
   -xscrollcommand ".fRlists.fRfil.scrbarx set"

##+#######################################################
## We specify an initial HEIGHT of the 'fil' listbox.
## That height (in conjunction with '-fill' and -expand'
## pack parameters) determines the height of the 'dir' listbox
## as well as the height of the 'fil' listbox.
##+#######################################################

scrollbar .fRlists.fRfil.scrbary \
   -orient vertical \
   -command ".fRlists.fRfil.listbox yview"

scrollbar .fRlists.fRfil.scrbarx \
   -orient horizontal \
   -command ".fRlists.fRfil.listbox xview"

##+###############################################
## PACK all the widgets in frame 'fRlists.fil' ---
## label, scrbarx,scrbary,listbox -- in that order.
##+###############################################
## DO NOT USE '-expand 1' for the X-SCROLLBAR.
## THAT WOULD KEEP THE LISTBOX FROM Y-EXPANDING
##+###############################################
## DO NOT USE '-expand 1' for the Y-SCROLLBAR.
## THAT WOULD ALLOW Y-SCROLLBAR TO X-EXPAND, which
## PUTS BLANK SPACE BETWEEN Y-SCROLLBAR & ITS LISTBOX.
##+###############################################

pack .fRlists.fRfil.lab \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRlists.fRfil.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRlists.fRfil.scrbary \
   -side left \
   -anchor nw \
   -fill y \
   -expand 0


##+###################################################
## NOTE: PACK THE SCROLL BARS BEFORE THE LISTBOX.
##       THE LISTBOX WOULD TAKE ALL THE FRAME-SPACE,
##       WITH '-fill both -expand 1'.
##+###################################################

pack .fRlists.fRfil.listbox \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1



##+###########################################
## In the 'fRscriptHead' FRAME --
## DEFINE a LABEL WIDGET.  THEN PACK IT.
##+###########################################

label .fRscriptHead.lab \
   -text "$aRtext(labScrSelection)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd 0

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


##+###########################################
## In the 'fRscriptSel' FRAME --
## DEFINE an ENTRY WIDGET.  THEN PACK IT.
##+###########################################

entry .fRscriptSel.entry \
   -font fontTEMP_fixedwidth \
   -textvariable FULFILname \
   -relief sunken \
   -borderwidth $BDwidthPx_entry \
   -bg $entryBKGD

pack .fRscriptSel.entry \
   -side top \
   -anchor nw \
   -fill x \
   -expand 1


##+#############################################
## In the 'fRcntlbutts1' FRAME --
## DEFINE some BUTTON WIDGETS:
## 'OK/UseIt', 'Cancel', 'Help', 'Refresh', etc.
## THEN PACK THEM.
##+#############################################

button .fRcntlbutts1.buttOK \
   -text "$aRtext(buttOK)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {put_var_exit}

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

button .fRcntlbutts1.buttHelp \
   -text "$aRtext(buttHelp)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {popup_msgVarWithScroll .topHELP "$HELPtext"}

button .fRcntlbutts1.buttFilter \
   -text "$aRtext(buttFilter)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {fill_lists}

button .fRcntlbutts1.buttFontSize \
   -text "$aRtext(buttFontSize)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {toggle_fontsize}

button .fRcntlbutts1.buttYbarsSIDE \
   -text "$aRtext(YbarsSIDE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {toggle_ybars}

##+#################################################
## PACK all the widgets of frame '.fRcntlbutts1'.
## NOTE: You can change the left-to-right order of the
##       buttons by changing this packing order.
##+#################################################

pack .fRcntlbutts1.buttCancel \
     .fRcntlbutts1.buttOK \
     .fRcntlbutts1.buttHelp \
     .fRcntlbutts1.buttFilter \
     .fRcntlbutts1.buttFontSize \
     .fRcntlbutts1.buttYbarsSIDE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+###############################################
## In the 'fRcntlbutts2' FRAME --
## DEFINE about 6 BUTTON WIDGETS:
## 'naroDir', 'wideDir', ..., 'tallWin', 'shortWin'
## THEN PACK THEM.
##+###############################################

button .fRcntlbutts2.buttDirListLeft \
   -text "$aRtext(buttDirListLeft)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {dir_listbox_width_reduce}                
                
button .fRcntlbutts2.buttDirListRight \
   -text "$aRtext(buttDirListRight)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {dir_listbox_width_increase}

button .fRcntlbutts2.buttFilListLeft \
   -text "$aRtext(buttFilListLeft)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {fil_listbox_width_reduce}

button .fRcntlbutts2.buttFilListRight \
   -text "$aRtext(buttFilListRight)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {fil_listbox_width_increase}

button .fRcntlbutts2.buttTallWin \
   -text "$aRtext(buttTallWin)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {tall_win}

button .fRcntlbutts2.buttShortWin \
   -text "$aRtext(buttShortWin)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command  {short_win}

##+#################################################
## PACK all the widgets in frame '.fRcntlbutts2'.
## NOTE: You can change the left-to-right order of
##       the buttons by changing this packing order.
##+#################################################

pack .fRcntlbutts2.buttDirListLeft \
     .fRcntlbutts2.buttDirListRight \
     .fRcntlbutts2.buttFilListLeft \
     .fRcntlbutts2.buttFilListRight \
     .fRcntlbutts2.buttTallWin \
     .fRcntlbutts2.buttShortWin \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+#######################################
## In 'fRstatusmsg' FRAME --
## DEFINE A LABEL WIDGET. THEN PACK IT.
##+#######################################

label .fRstatusmsg.lab \
   -text "$aRtext(filledSTATUSmsg)" \
   -font fontTEMP_fixedwidth \
   -justify left \
   -anchor w \
   -bd $BDwidthPx_label \
   -relief raised \
   -bg "$STATUSmsgBKGD"

pack .fRstatusmsg.lab \
   -side top \
   -anchor w \
   -fill x \
   -expand 1

## Note:
## If frame '.fRstatusmsg' is packed with '-expand 0', that can be
## used to keep the '.fRstatusmsg.lab' widget from y-expanding.

##+#####################################################################
##+#####################################################################
## END OF MAIN SECTION TO SETUP THE GUI.
##+#####################################################################
##+#####################################################################


##+#####################################################################
##+#####################################################################
## DEFINE BINDINGS (to procs):
##
##  Fill  -and-  Navigate(chgdir)  -and-  Return-key bindings:
##
##   - .fRdirfilmask.entryDIRFILmask  <Key-Return>    ('fill_lists')
##               (Not used if we disable changes to that entry field.)
##
##   - .fRlists.fRdir.listbox <ButtonRelease-1>       ('navigate', which uses 'fill_lists')
##
##   - .fRlists.fRfil.listbox <ButtonRelease-1>       ('get_selected_file')
##   - .fRscriptSel.entry  <Key-Return>               ('put_var_exit')
##
##  Listbox *keyboard*-bindings:
##
##   - .fRlists.fRdir.listbox  <Enter>   (set focus when mouse enters listbox)
##   - .fRlists.fRfil.listbox  <Enter>   (set focus when mouse enters listbox)
##
##   - .fRlists.fRdir.listbox  <Home>    (go to top of listbox)
##   - .fRlists.fRfil.listbox  <Home>    (go to top of listbox)
##   - .fRlists.fRdir.listbox  <End>     (go to bottom of listbox)
##   - .fRlists.fRfil.listbox  <End>     (go to bottom of listbox)
##
##   - Listbox    <Control-Home>         (override default behavior)
##   - Listbox    <Control-End>          (override default behavior)
##
##   - .fRlists.fRdir.listbox  <Prior>     (page up)
##   - .fRlists.fRfil.listbox  <Prior>     (page up)
##   - .fRlists.fRdir.listbox  <Next>      (page down)
##   - .fRlists.fRfil.listbox  <Next>      (page down)
##
##   - .fRlists.fRdir.listbox  <Key-Up>      (1 line up)
##   - .fRlists.fRfil.listbox  <Key-Up>      (1 line up)
##   - .fRlists.fRdir.listbox  <Key-Down>    (1 line down)
##   - .fRlists.fRfil.listbox  <Key-Down>    (1 line down)
##
##+#####################################################################
##+#####################################################################

##+#########################################################
##  .fRdirfilmask.entryDIRFILmask <Key-Return> BINDING
##+#########################################################
## PURPOSE: Fills the subdirs & fils listboxes.
##          Simply calls the 'fill_lists' proc.
##+#########################################################
## CALLED BY:
##   THE USER HITTTING RETURN AFTER EDITING THE DIR&FILE-MASK
##   ENTRY FIELD in the '.fRdirfilmask.entryDIRFILmask' widget.
##+#########################################################
##   (Not used if we disable changes to that entry field.)
##+#########################################################

bind .fRdirfilmask.entryDIRFILmask <Key-Return> {fill_lists}


##+##########################################################
##  .fRlists.fRdir.listbox <ButtonRelease-1> BINDING
##+##########################################################
## PURPOSE: Runs the 'navigate' proc, which uses a selected
##          subdir and the current 'reference directory' to
##          change the 'reference directory' and exec the
##          'fill_lists' proc to refresh the listboxes.
##+#########################################################
## CALLED BY:
##      MOUSE-BUTTON-1 IS SINGLE-CLICKED (& released)
##      ON A (SUB)DIRECTORY in the
##      '.fRlists.fRdir.listbox' widget.
##+##########################################################
##  This routine essentially navigates the directory
##  structure.  It
##   1) resets the DIRFILmask entry
##   2) runs the 'fill_lists' procedure.
##+##########################################################

bind .fRlists.fRdir.listbox <ButtonRelease-1> {navigate}


##+#########################################################
##  .fRscriptSel.entry <Key-Return> BINDING
##+#########################################################
## PURPOSE: Runs the 'put_var_exit' proc, to return the
##          selected filename to the calling application.
##+#########################################################
## 'put_var_exit' should
##   1) write $FULFILname to stdout (if it is a valid name)
##   2) exit
##+#########################################################
## CALLED BY:
##      THE USER HITTING RETURN AFTER PASTING/EDITING
##      THE CURRENT Script Selection, in the
##      '.fRscriptSel.entry' widget.
##+#########################################################

bind .fRscriptSel.entry <Key-Return> {put_var_exit}


##+#############################################################
##  .fRlists.fRfil.listbox <ButtonRelease-1> BINDING
##+#############################################################
## PURPOSE: Runs the 'get_selected_file' proc, which uses a selected
##          relative-filename and the current 'reference directory'
##          to build a fully-qualified filename and put that
##          name into the 'filselection' entry box.
##
##+############################################################
## CALLED BY:
##      MOUSE-BUTTON-1 IS SINGLE-CLICKED (& released)
##      ON A FILE in the '.fRlists.fRfil.listbox' widget.
##+############################################################

bind .fRlists.fRfil.listbox <ButtonRelease-1> {get_selected_file}


##+################################################################
##  KEYBOARD BINDINGS FOLLOW:
##+################################################################


##+################################################################
##  all <Escape> BINDING          (not implemented currently)
##+################################################################
## Executes if THE ESCAPE KEY IS HIT (while cursor is in the window).
##+################################################################

# bind all <Escape> {
#   exit
# }


##+#################################################
## FOR TESTING KEY-BINDINGS:
##     TO SHOW THE 'KEYSYM' FOR A PARTICULAR KEY
##     on this system.   Uses a binding to the widget  
##     '.' and event  <Any-Key>  .
##+#################################################
## Reference: the Brent Welch book, 'Practical
## Programming in Tcl and Tk', Chapter 23, 'Binding
## Commands to Events, page 291.  
##+#################################################
# 
#  bind . <Any-Key>  {
#    
#         ## FOR TESTING:
#         # puts "In widget '.' : Keysym for key pressed: %K"
#
#         puts "In widget '.' , for a key press :"
#         puts "Keysym:  %K    Print-char-and-modifiers: %A"
#         puts ""
#
#  }
#  ## END OF BINDING for . <Any-Key>



##+########################################################
## TEST: Show default bindings of  .fRlists.fRdir.listbox
##       with 'bindtags': 
##+#################################################
## Reference: the Brent Welch book, 'Practical
## Programming in Tcl and Tk', Chapter 23, 'Binding
## Commands to Events, page 287.  
##+########################################################
## SHOWS:   BINDtags: .fRlists.fRdir.listbox Listbox . all
##+########################################################
#  
#  set BINDtags [ bindtags .fRlists.fRdir.listbox ] 
#  puts ".fRlists.fRdir.listbox BINDtags: $BINDtags"


##+#######################################################
## If it were needed, we could
##       OVER-RIDE THE Class 'Listbox' BINDINGS,
## WITH THE BINDINGS WE SET FOR '.fRlists.fRdir.listbox'.
##+#######################################################
#   bindtags .fRlists.fRdir.listbox [ list .fRlists.fRdir.listbox ]
##  OR:
#   bindtags .fRlists.fRdir.listbox \
#                             [ list Listbox . .fRlists.fRdir.listbox all ]

##+##################################################
## TEST: Show new bindings with 'bindtags': 
##+##################################################
# 
#    set BINDtags [ bindtags .fRlists.fRdir.listbox ] 
#    puts ".fRlists.fRdir.listbox BINDtags: $BINDtags"


##+#########################################################
## <Enter> BINDINGs:
##
##         - .fRlists.fRdir.listbox   <Enter> 
##         - .fRlists.fRfil.listbox   <Enter> 
##
## PURPOSE:
##     To put keyboard focus into one of these two
##     listboxes when the mouse cursor enters it
##     --- SO THAT THE FOLLOWING KEYBOARD BINDINGS
##     CAN TAKE EFFECT --- for keys:
##     Home, End, Prior, Next, up-arrow, down-arrow.
##+#########################################################

bind .fRlists.fRdir.listbox <Enter>  {
   focus .fRlists.fRdir.listbox
   ## FOR TESTING:
   #   puts "Entered widget:  .fRlists.fRdir.listbox"
}
## END OF BINDING for .fRlists.fRdir.listbox <Enter>

bind .fRlists.fRfil.listbox <Enter>  {
   focus .fRlists.fRfil.listbox
   ## FOR TESTING:
   #   puts "Entered widget:  .fRlists.fRfil.listbox"
}
## END OF BINDING for .fRlists.fRfil.listbox <Enter>


##+####################################################
## <Home> key BINDINGs: 
##         - .fRlists.fRdir.listbox   <Home> 
##         - .fRlists.fRfil.listbox   <Home> 
##+####################################################

bind .fRlists.fRdir.listbox <Home>  {
   .fRlists.fRdir.listbox see 1
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Home>

bind .fRlists.fRfil.listbox <Home>  {
   .fRlists.fRfil.listbox see 1
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Home>


##+####################################################
## <End> key BINDINGs: 
##         - .fRlists.fRdir.listbox   <End> 
##         - .fRlists.fRfil.listbox   <End> 
##+####################################################

bind .fRlists.fRdir.listbox <End>  {
   .fRlists.fRdir.listbox see end 
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <End>


bind .fRlists.fRfil.listbox <End>  {
   .fRlists.fRfil.listbox see end 
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <End>


##+####################################################
##  De-activate the default <Control-Home> binding for
##  Listboxes -- to avoid selecting top entry when
##  <Home> key is used.
##+####################################################

bind Listbox <Control-Home> {}

##+####################################################
##  De-activate the default <Control-End> binding for
##  Listboxes -- to avoid selecting bottom entry when
##  <End> key is used.
##+####################################################

bind Listbox <Control-End> {}


##+####################################################
## <Prior> BINDINGs:      (Page-Up key)
##         - .fRlists.fRdir.listbox   <Prior> 
##         - .fRlists.fRfil.listbox   <Prior> 
##+####################################################

bind .fRlists.fRdir.listbox <Prior>  {
   .fRlists.fRdir.listbox  yview scroll -1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Prior>

bind .fRlists.fRfil.listbox <Prior>  {
   .fRlists.fRfil.listbox  yview scroll -1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Prior>


##+####################################################
## <Next> BINDINGs:      (Page-Down key)
##         - .fRlists.fRdir.listbox   <Next> 
##         - .fRlists.fRfil.listbox   <Next> 
##+####################################################

bind .fRlists.fRdir.listbox <Next>  {
   .fRlists.fRdir.listbox yview scroll +1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Next>


bind .fRlists.fRfil.listbox <Next>  {
   .fRlists.fRfil.listbox yview scroll +1 page
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Next>


##+####################################################
## <Key-Up> BINDINGs:      (Up-arrow key)
##         - .fRlists.fRdir.listbox   <Key-Up> 
##         - .fRlists.fRfil.listbox   <Key-Up> 
##+####################################################

bind .fRlists.fRdir.listbox <Key-Up>  {
   .fRlists.fRdir.listbox yview scroll -1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Key-Up>

bind .fRlists.fRfil.listbox <Key-Up>  {
   .fRlists.fRfil.listbox yview scroll -1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Key-Up>


##+####################################################
## <Key-Down> BINDINGs:      (Down-arrow key)
##         - .fRlists.fRdir.listbox   <Key-Down> 
##         - .fRlists.fRfil.listbox   <Key-Down> 
##+####################################################

bind .fRlists.fRdir.listbox <Key-Down>  {
   .fRlists.fRdir.listbox  yview scroll +1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRdir.listbox <Key-Down>

bind .fRlists.fRfil.listbox <Key-Down>  {
   .fRlists.fRfil.listbox  yview scroll +1 unit
   ## FOR TESTING:
   #  puts "Key pressed: %K"
}
## END OF BINDING for .fRlists.fRfil.listbox <Key-Down>


##+######################################################
## FOR TESTING: (show current bindings ---
## to bindtags '.fRlists.fRdir' & 'Frame' & '.' & 'all').
##+######################################################
# 
#   set BINDings [ bind .fRlists.fRdir ] 
#   puts ".fRlists.fRdir BINDings: $BINDings"
# 
#   set BINDings [ bind .fRlists.fRdir <End> ] 
#   puts ".fRlists.fRdir <End> BINDings: $BINDings"
# 
#   set BINDings [ bind Frame ] 
#   puts "Frame BINDings: $BINDings"
# 
#   set BINDings [ bind . ] 
#   puts ". BINDings: $BINDings"
# 
#   set BINDings [ bind all ] 
#   puts "all BINDings: $BINDings"


##+#################################################################
##+#################################################################
##  END OF mouse/touchpad/touch-sensitive-screen and keyboard BINDINGS
##+#################################################################
##+#################################################################



##+#################################################################
##+#################################################################
## DEFINE PROCEDURES:
##
##  'fill_lists'           - fills the dirs and fils listboxes based on
##                           the current value of $DIRFILmask
##
##  'get_subdirs_inCURdir' - called by 'fill_lists' proc
##  'get_files_inCURdir'   - called by 'fill_lists' proc
##
##  'get_selected_file'    - uses the selected relative-filename to set
##                           the $FULFILname entry field
##
##  'navigate'             - changes the directory in the $DIRFILmask entry field
##                           and runs proc 'fill_list'
##
##  'set_status_msg'       - puts supplied msg into the Status msg frame
##
##  'put_var_exit'         - puts $FULFILname (the name of the selected script)
##                           to stdout and exits.
##
##  The following 6 procs are used for the 'naroDir', ..., 'shortWin' buttons.
##
##   'dir_listbox_width_increase'
##   'dir_listbox_width_reduce'
##   'fil_listbox_width_increase'
##   'fil_listbox_width_reduce'
##   'tall_win'
##   'short_win'
##
##  'toggle_fontsize'     - for the 'TogFontSize' button
##  'toggle_ybars'        - for the '<-Ybars->' button
##
##  'popup_msgVarWithScroll' - used to show the $HELPtext var ; also used
##                              to show user various popup messages for
##                              info, warnings, errors, etc.
##
##+#################################################################
##+#################################################################


##+###################################################################
## PROC 'fill_lists'
##+###################################################################
## PURPOSE:
##   Fills the two listboxes (dirs & fils), according to the contents
##   of var DIRFILmask.
##
## CALLED BY: the 'navigate' proc  -OR-
##            the 'Filter'/'Refresh'/'ReFill' button  -OR-
##            the fRdirfilmask.entryDIRFILmask <Key-Return> BINDING ).
##+###################################################################
## LOGIC NOTES:
##  Basically, 'fill_lists' should accept DIRFILmask (a global var) and
##   1) Set CURdir  from DIRFILmask (chars before last "/")
##   2) Set FILmask from DIRFILmask (chars after  last "/")
##   3) Fill the 'dir' listbox with directory names under/in $CURdir
##   4) Fill the 'fil' listbox with relative-filenames matching
##      $DIRFILmask = $CURdir/$FILmask
## Ideally, steps 3 and 4 will support 'symbolic links' (broken or not).
##+####################################################################

proc fill_lists {  } {

   global DIRFILmask FULFILname CURdir FILmask aRtext env

   ####################################################
   ## TRIM DIRFILmask (of leading or trailing blanks).
   ####################################################

   set DIRFILmask [ string trim "$DIRFILmask" ]

   #############################################
   ## EXTRACT CURdir & FILmask from $DIRFILmask.
   #############################################

   set CURdir [ file dirname "$DIRFILmask" ]

   set FILmask [ file tail "$DIRFILmask" ]

   if { "$FILmask" == "" } { set FILmask {*} }

   #############################################
   ## FOR TESTING: (show CURdir & FILmask vars)
   #############################################
   if {0} {
      puts "proc 'fill_lists' : (just entered the proc)"
      puts "   DIRFILmask: $DIRFILmask"
      puts "   CURdir: $CURdir"
      puts "   FILmask: $FILmask"
   }

   ###################################################################
   ## CHECK IF THE DIRECTORY in the DIRFILmask var EXISTS.
   ## Pop an error msg if it does not -- and get out.
   ###################################################################

   if { ! [ file isdirectory $CURdir ] } {

      popup_msgVarWithScroll .topErr \
"The DIRECTORY in the DirFileMask entry field
    $CURdir
WAS NOT FOUND.
Change it and use the 'Refresh' button or Enter key.  Or 'Cancel'."

      set_status_msg \
"The DIRECTORY in the DirFileMask entry field
    $CURdir
was NOT FOUND.
Change it and use the 'Refresh' button or Enter key.  Or 'Cancel'."

      return

   }
   ## END OF  if { ! [ file isdirectory $CURdir ] }


   ###################################################
   ## TURN THE CURSOR INTO A WATCH.
   ## (Not needed? The listboxes fill rapidly,
   ## even for directories with more than 1500 files.)
   ###################################################
   # . configure -cursor watch
   ## Needed only to change the cursor?
   # update

   ##################################################
   ## RESET THE FULFILname (selected script) TO NULL.
   ##  (no; the user may want it even if it goes
   ##   with a previously navigated-to directory.)
   ##################################################
   ##  If necessary, do this at appropriate place
   ##  in 'navigate' proc.
   ##################################################
   ##  set FULFILname ""

   #############################################
   ## RESET THE STATUS MESSAGE TO NULL.
   ## (or indicate that filling is starting)
   #############################################
   # set_status_msg ""
   set_status_msg "Filling listbox ..."

   #######################################################################
   ## (RE)FILL THE DIRECTORY LISTBOX.
   ## (See the code in proc 'get_subdirs_inCURdir'.)
   #######################################################################

   # if { "$CURdir" == "" } { set CURdir "/" }

   set SubDirsLIST [get_subdirs_inCURdir_inclLinks]


   ###################################################################
   ## APPEND $SubDirsLIST TO THE 'dir' LISTBOX. (without using a loop)
   ###################################################################
   ## Ref: Brent Welch, Practical Programming in Tcl & Tk,
   ##      Chapter 29, The Listbox Widget, page 359
   ##############################################################
   ## To get this to work WITHOUT USING A listbox-insert LOOP,
   ## we use a Tcl list of dirs.
   ##############################################################

   .fRlists.fRdir.listbox  delete 0 end

   eval .fRlists.fRdir.listbox  insert end $SubDirsLIST


   #############################################
   ## GET THE NUMBER OF SUBDIRS IN THE LISTBOX.
   #############################################

   set num_dirs [ .fRlists.fRdir.listbox size ]

   ######################################################
   ## RESET THE HEADING OF THE DIRECTORY ('dir') LISTBOX.
   ######################################################

   .fRlists.fRdir.lab  configure -text \
"$num_dirs Dirs - ParentDir\[..\] & SubDirs
\[ Navigate = Single-Click \]"


   #######################################################################
   ## (RE)FILL THE non-directory-FILES ('fil') LISTBOX.
   ## (See the code in proc 'get_files_inCURdir'.)
   #######################################################################

   set FilesLIST [get_files_inCURdir_inclLinks]


   ######################################################################
   ## APPEND FILENAMES TO THE 'fil' LISTBOX (avoiding a 'foreach' loop).
   ######################################################################
   ## Ref: Brent Welch, Practical Programming in Tcl & Tk,
   ##      Chapter 29, The Listbox Widget, page 359
   ##############################################################
   ## To get this to work WITHOUT USING A listbox-insert LOOP,
   ## we use a Tcl list of filenames.
   ##############################################################

   .fRlists.fRfil.listbox delete 0 end

   eval  .fRlists.fRfil.listbox  insert end $FilesLIST

   #############################################
   ## GET THE NUMBER OF FILES IN THE LISTBOX.
   #############################################

   set num_files [ .fRlists.fRfil.listbox size ]

   ################################################
   ## RESET THE HEADING OF THE FILES ('fil) LISTBOX.
   ################################################

   .fRlists.fRfil.lab configure -text \
"    $num_files File(s) matching the mask
   \[ Select = Single-Click \]"


   ##################################################################
   ## PUT A MSG IN THE STATUS MSG AREA?
   ## (Yes, for when the 'Refresh' button is clicked.  For other cases,
   ##  where other procs call this 'fill_lists' proc -- like
   ##  'navigate' -- the other proc can overwrite the status msg area.)
   ##################################################################

   set_status_msg "$aRtext(filledSTATUSmsg)"

   #############################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   #############################################
   # . configure -cursor { left_ptr red white }

}
## END OF PROC 'fill_lists'


##+##############################################################
## PROC 'get_subdirs_inCURdir_inclLinks'
##+##############################################################
## PURPOSE:
##  For given CURdir, this routine returns a Tcl list of the
##  directory files in CURdir --- including 'symbolic links'
##  to directories (handling broken links 'appropriately').
#################################################################
## METHOD:
## To find the sub-directories of CURdir, for the 'dir' listbox,
## we use the Tcl 'glob' command.
##
## For example, to find the sub-directories to put in the 'dir'
## listbox, the following seems to work to show get both non-hidden
## and hidden files:
##
##         glob -nocomplain  $CURdir/*  $CURdir/.*
##
## (The '-nocomplain' allows for returning an empty list without
##  raising an error.)
##
## We show 'hidden' sub-directories (dot-prefix on the filenames)
## as well as 'non-hidden' sub-directories. (The showing of 'hidden'
## files could be made an option via a checkbutton on the GUI.)
##
## From this list of filenames, in a 'foreach' loop, we can select
## the 'directory' files.
##
## Among the files returned by the 'glob' above, there could be
## 'symbolic links' to directories. In the same 'foreach' loop
## that is used to select out the directory files, we find the
## 'link' files and figure out which links point to directories.
##
## If there is a 'directory-link', we append the link to the list
## of directories, for display in the 'dir' listbox. (We can
## check if the link is 'broken', and we can choose to NOT
## append it --- or popup a warning.)
##
## ---
##
## In the 'foreach' loop, we can make use of Tcl commands like
##
##  file type $file     (Returns: file, directory, characterSpecial,
##                                blockSpecial, fifo, link, OR socket)
##  file readlink $file         (Good for finding the target of a link.)
##  file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##
##  file exists $file
##
## If necessary, we could also make use of Tcl 'file' commands like
## 
##  file isdirectory $file
##  file isfile $file
##
##  file link $file
##  file stat $file aRfileStat
##  file lstat $file aRlinkStat
## .......................................................................
## Ref: the Eric Johnson book " Graphical Applications with Tcl & Tk",
## page 225-226, Chapter 6 Lists, Files, and Directories.
## ---
## The 'dirview.tcl' script in chapter 6 gives an example of using
## a 'foreach' loop to extract directories and non-directories
## from 'glob' output. But that code would have to be made more
## robust to handle 'symbolic links' properly --- and exclude
## special files like fifo, socket, and device files.
##+##########################################################
## CALLED BY: 'fill_lists' proc
##+##########################################################

proc get_subdirs_inCURdir_inclLinks {} {

   global CURdir DEBUGlink0or1

   #############################################################
   ## Get the list of files to check for 'directory' and 'link'
   ## file types. (We do not expect any of the sub-directories
   ## of the scripts to be 'hidden' via a dot-prefix.)
   ############################################################

   if {"$CURdir" == "/"} {
      # set tempFilesLIST [glob -nocomplain  /*  /.*]
      set tempFilesLIST [glob -nocomplain  /*]
   } else {
      # set tempFilesLIST [glob -nocomplain  $CURdir/*  $CURdir/.*]
      set tempFilesLIST [glob -nocomplain  $CURdir/*]
      lappend tempFilesLIST ".."
   }

   ## Alternative (using 'catch' to get return-code):
   #   set tempFilesLIST ""
   #   set RETcode [ catch { set tempFilesLIST  [ .... ] } ]

   ## FOR TESTING:
   #    puts "tempFilesLIST: $tempFilesLIST"
   #    puts "RETcode: $RETcode"

   ################################################################
   ## Make sure the variable 'tempDirsLIST' does not exist.
   ##
   ## "You can call 'lappend' with the name of an undefined
   ## variable and the variable will be created." - page 66 of the
   ## 4th edition of 'Practical Programming in Tcl and Tk' by
   ## Welch, Jones, Hobbs.
   ###############################################################

   catch {unset tempDirsLIST}


   ###################################################################
   ## Peform the 'foreach' loop to append the files of type
   ## 'directory' to the Tcl list 'tempDirsLIST'.
   ###################################################################
   ## If a file is of type 'link', we determine if the link is of type
   ## 'directory'. If so, we append the link-name to 'tempDirsLIST'.
   ###################################################################
   ## If 'file readlink' is not sufficient,
   ## we may eventually have to handle the case where the
   ## a symbolic link points to another symbolic link and
   ## on and on until the eventual target is determined.
   ## We could put that logic in a proc with a name like
   ## 'get_link_target'.
   ###################################################################

   foreach tempFilename $tempFilesLIST {

      ############################################################
      ## If 'tempFilename' is of type 'directory', append the
      ## filename to tempDirsLIST and 'continue' to the next file.
      ############################################################
      ## Exceptions are the sub-dirs "." and "..". We do not
      ## show "." and we show ".." for all directories except root.
      ############################################################

      if {[file type $tempFilename] == "directory"} {
         set tempTail [file tail "$tempFilename"]
         if {"$tempTail" == "."} {continue}
         if {"$tempTail" == ".." && "$CURdir" == "/"} {continue}
         lappend tempDirsLIST "$tempTail"
         continue
      }

      ########################################################
      ## If 'tempFilename' is NOT of type 'link',
      ## 'continue' to the next file in the loop.
      ########################################################

      if {[file type $tempFilename] != "link"} {continue}

      ########################################################
      ## The remainder of this loop is to handle 'link' files.
      ########################################################
      ## Get the filename of the target of the link.
      ## (If we need to do a more complex check for the *ultimate*
      ## target of the link, we could put that logic in a proc
      ## with a name like 'get_link_target'.)
      ########################################################

      set targetName [file readlink $tempFilename]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts ""
         puts "proc 'get_subdirs_inCURdir_inclLinks' :"
         puts "tempFilename: $tempFilename"
         puts "targetName:   $targetName"
      }

      if {[file pathtype $targetName] == "relative"} {
         set targetName "$CURdir/$targetName"
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
           puts "targetName:   $targetName  RESET"
         }
      }

      #######################################################
      ## If the target does not exist (i.e. 'broken link'),
      ## 'continue' to the next file in the loop.
      ## (We could insert a popup warning message to alert
      ##  the user of the broken link.)
      #######################################################

      if {![file exists $targetName]} {
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
            puts "targetName:   $targetName  DOES NOT EXIST."
         }
         continue
      }

      #######################################################
      ## Get the file-type of the target of the link.
      #######################################################

      set targetType [file type $targetName]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts "targetType:   $targetType"
      }

      ######################################################
      ## If the file-type of the target is 'directory',
      ## add the link-filename to tempDirsLIST.
      ######################################################

      if {"$targetType" == "directory"} {
         lappend tempDirsLIST [file tail "$tempFilename"]
      }

   }
   ## END OF foreach tempFilename $tempFilesLIST

   ######################################################
   ## Return the sub-directories list, sorted.
   ######################################################

   if {[info exists tempDirsLIST]} {
      return [lsort $tempDirsLIST]
   } else {
      return ""
   }

}
## END OF PROC 'get_subdirs_inCURdir_inclLinks'


##+##############################################################
## PROC  'get_files_inCURdir_inclLinks'
##+##############################################################
## PURPOSE:
##  For given CURdir, this routine returns a Tcl list of the
##  'regular' files in CURdir --- including 'symbolic links'
##  to 'regular' files (handling broken links 'appropriately').
#######################################################################
## METHOD:
## To get the files for the 'fil' listbox, for a given CURdir and
## FILmask, we can use a 'glob' like the following:
##
##     glob -nocomplain $CURdir/$FILmask
##
## In a 'foreach' loop, we could select the 'regular' files.
##
## If the user wanted to see hidden files, the user could add a
## dot-prefix in the file-mask. Alternatively, we could ALWAYS
## show the 'hidden' files by using a 'glob' like
##
##     glob -nocomplain  $CURdir/$FILmask  $CURdir/.$FILmask
##
## For example, if FILmask='*', this 'glob' would look like
##
##     glob -nocomplain  $CURdir/*  $CURdir/.*
##
## Among the files returned by the 'glob' above, there could be
## 'symbolic links' to 'regular' files. In the same 'foreach' loop
## that is used to select out the regular files, we find the
## 'link' files and figure out which links point to regular files.
##
## If there is a 'regular-file-link', we append the link to the list
## of files for the 'fil' listbox. (We can check if the link is 'broken',
## and we can choose to not append it --- or popup a warning.)
##
## ---
##
## In the 'foreach' loop, we can make use of Tcl 'file' commands like
##
##  file type $file     (Returns: file, directory, characterSpecial,
##                                blockSpecial, fifo, link, OR socket)
##
##  file readlink $file         (Good for finding the target of a link.)
##  file pathtype $file  (Returns: relative, absolute, OR volumerelative)
##
##  file exists $file
##
## If necessary, we could also make use of Tcl 'file' commands like
##
##  file isdirectory $file
##  file isfile $file
##
##  file link $file
##
##  file stat $file aRfileStat
##  file lstat $file aRlinkStat
##
## ...................................................................
## Ref: the Eric Johnson book " Graphical Applications with Tcl & Tk",
## page 225-226, Chapter 6 Lists, Files, and Directories.
## ---
## The 'dirview.tcl' script in chapter 6 gives an example of using
## a 'foreach' loop to extract directories and non-directories
## from 'glob' output. But that code would have to be made more
## robust to handle 'symbolic links' properly --- and exclude
## special files like fifo and device files --- and exclude
## special files like fifo, socket, and device files.
##+##########################################################
## CALLED BY: 'fill_lists' proc
##+##########################################################

proc get_files_inCURdir_inclLinks {} {

   global CURdir FILmask DEBUGlink0or1

   #############################################################
   ## Get the list of files to check for 'directory' and 'link'
   ## file types. (We do not expect any of the filenames
   ## of the scripts to be 'hidden' via a dot-prefix, so we
   ## do not glob for hidden files.)
   ## (We don't expect to navigate up the the root directory
   ## either but we include that case.)
   ############################################################

   if {"$CURdir" == "/"} {
      # set tempFilesLIST [glob -nocomplain  /$FILmask  /.$FILmask]
      set tempFilesLIST [glob -nocomplain  /$FILmask]
   } else {
      # set tempFilesLIST [glob -nocomplain  $CURdir/$FILmask  $CURdir/.$FILmask]
      set tempFilesLIST [glob -nocomplain  $CURdir/$FILmask]
   }

   ## Alternative (using 'catch' to get return-code):
   #   set tempFilesLIST ""
   #   set RETcode [ catch { set tempFilesLIST  [ .... ] } ]

   ## FOR TESTING:
   #    puts "tempFilesLIST: $tempFilesLIST"
   #    puts "RETcode: $RETcode"


   ################################################################
   ## Make sure the variable 'tempRegFilesLIST' does not exist.
   ##
   ## "You can call 'lappend' with the name of an undefined
   ## variable and the variable will be created." - page 66 of the
   ## 4th edition of 'Practical Programming in Tcl and Tk' by
   ## Welch, Jones, Hobbs.
   ###############################################################

   catch {unset tempRegFilesLIST}

   ##################################################################
   ## Peform the 'foreach' loop to append the files of type
   ## 'file' to the Tcl list 'tempRegFilesLIST'.
   ##################################################################
   ## If a file is of type 'link', we determine if the link is of
   ## type 'file'. If so, we append the link-name to 'tempRegFilesLIST'.
   ##############################################################
   ## If 'file readlink' is not sufficient,
   ## we may eventually have to handle the case where the
   ## a symbolic link points to another symbolic link and
   ## on and on until the eventual target is determined.
   ## We could put that logic in a proc with a name like
   ## 'get_link_target'.
   ##############################################################

   foreach tempFilename $tempFilesLIST {

      ###########################################################
      ## If 'tempFilename' is of type 'file', append the filename
      ## to tempRegFilesLIST and 'continue' to the next file.
      ###########################################################

      if {[file type $tempFilename] == "file"} {
         lappend tempRegFilesLIST [file tail "$tempFilename"]
         continue
      }

      ########################################################
      ## If 'tempFilename' is NOT of type 'link',
      ## 'continue' to the next file in the loop.
      ########################################################

      if {[file type $tempFilename] != "link"} {continue}

      ########################################################
      ## The remainder of this loop is to handle 'link' files.
      ########################################################
      ## Get the filename of the target of the link.
      ## (If we need to do a more complex check for the *ultimate*
      ## target of the link, we could put that logic in a proc
      ## with a name like 'get_link_target'.)
      ########################################################

      set targetName [file readlink $tempFilename]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts ""
         puts "proc 'get_files_inCURdir_inclLinks' :"
         puts "tempFilename: $tempFilename"
         puts "targetName:   $targetName"
      }

      if {[file pathtype $targetName] == "relative"} {
         set targetName "$CURdir/$targetName"
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
           puts "targetName:   $targetName  RESET"
         }
      }

      #######################################################
      ## If the target does not exist (i.e. 'broken link'),
      ## 'continue' to the next file in the loop.
      ## (We could insert a popup warning message to alert
      ##  the user of the broken link.)
      #######################################################

      if {![file exists $targetName]} {
         ## FOR TESTING:
         if {$DEBUGlink0or1} {
            puts "targetName:   $targetName  DOES NOT EXIST."
         }

         continue
      }

      #######################################################
      ## Get the file-type of the target of the link.
      #######################################################

      set targetType [file type $targetName]

      ## FOR TESTING:
      if {$DEBUGlink0or1} {
         puts "targetType:   $targetType"
      }

      ######################################################
      ## If the file-type of the target is 'file',
      ## add the link-filename to tempRegFilesLIST.
      ######################################################

      if {"$targetType" == "file"} {
         lappend tempRegFilesLIST [file tail "$tempFilename"]
      }

   }
   ## END OF foreach tempFilename $tempFilesLIST

   ######################################################
   ## Return the regular-files list, sorted.
   ######################################################

   if {[info exists tempRegFilesLIST]} {
      return [lsort $tempRegFilesLIST]
   } else {
      return ""
   }

}
## END OF PROC 'get_files_inCURdir_inclLinks'


##+##############################################################
## PROC  'get_selected_file'
##+##############################################################
## PURPOSE:
##  This routine essentially reloads the FULFILname entry
##  field -- by prefixing the selected relative-filename,
##  from the files-listbox, with $CURdir.
##+##########################################################
## CALLED BY:
##      the  .fRlists.fRfil.listbox <ButtonRelease-1> BINDING
##      WHEN MOUSE-BUTTON-1 IS SINGLE-CLICKED
##      ON A FILE in the '.fRlists.fRfil.listbox' widget.
##+#############################################################

proc get_selected_file { } {

   global DIRFILmask FULFILname aRtext

   ################################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   ## (In case cursor was left as a 'watch'
   ##  after a failure in 'fill_lists' proc, like
   ##  a CURdir permission error.)
   ################################################
   # . configure -cursor { left_ptr red white }

   #######################################################
   ## FIND OUT WHICH FILE IS BEING SELECTED.
   #######################################################

   set FULFILname ""
   set sel_index [ .fRlists.fRfil.listbox  curselection ]

   ####################################################
   ## RESET THE STATUS MESSAGE TO NULL.
   ####################################################

   set_status_msg ""

   #######################################################
   ## IF A LIST ITEM WAS NOT SELECTED, pop a msg & return.
   #######################################################

   if { "$sel_index" == "" } {
      popup_msgVarWithScroll .topErr \
"NO item was selected in the listbox."

      set_status_msg \
"NO item was selected in the listbox."

      return
   }

   ####################################################
   ## GET THE 'RELATIVE' FILENAME.
   ####################################################

   set RELFILsel [ .fRlists.fRfil.listbox get $sel_index ]

   #############################################
   ## EXTRACT CURdir from $DIRFILmask.
   #############################################

   set CURdir [ file dirname "$DIRFILmask" ]

   ####################################################
   ## UPDATE THE CURRENT FILE-SELECTION ENTRY
   ## WITH THE NEW, FULL FILENAME.
   ####################################################

   set FULFILname "${CURdir}/$RELFILsel"


   ##############################################
   ## PUT A MSG IN THE STATUS MSG AREA.
   ##############################################

   set_status_msg "Click 'UseIt' to accept the 'Script Selection' entry."

   ####################################################
   ## MAKE SURE THE FAR-RIGHT END OF THE TEXT IN THE
   ## FILE-SELECTION ENTRY BOX IS IN VIEW.
   ####################################################

   .fRscriptSel.entry xview end

}
## END OF PROC  'get_selected_file'


##+##############################################################
## PROC  'navigate'
##+##############################################################
## PURPOSE:
##  This routine essentially navigates the directory
##  structure.  It
##   1) resets the DIRFILmask entry according to a subdir
##      selected in the subdirs-listbox, and
##   2) runs the 'fill_lists' procedure, to refresh both the
##      subdirs-listbox and the relfiles-listbox.
##
##+##############################################################
## CALLED BY:
##   the  fRlists.fRdir.listbox <ButtonRelease-1> BINDING
##   WHEN MOUSE-BUTTON-1 IS SINGLE-CLICKED (& released)
##   ON A (SUB)DIRECTORY in the '.fRlists.fRdir.listbox' widget.
##+##############################################################

proc navigate { } {

   global DIRFILmask aRtext env

   ####################################################
   ## FIND OUT WHICH SUB-DIRECTORY WAS SELECTED.
   ####################################################

   set sel_index  [ .fRlists.fRdir.listbox curselection ]


   ####################################################
   ## RESET THE STATUS MESSAGE TO NULL.
   ####################################################

   set_status_msg ""

    ######################################################
    ## IF A LIST ITEM WAS NOT SELECTED, pop a msg & return.
    ######################################################

    if { "$sel_index" == "" } {
      popup_msgVarWithScroll .topErr \
"Navigate Procedure: NO item was selected in the Dirs (left) listbox."

      set_status_msg \
"Navigate Procedure: NO item was selected in the Dirs (left) listbox."

      return
    }


   ####################################################
   ## GET THE 'BASENAME' of the selected SUBDIRECTORY.
   ####################################################

   set RELDIRsel [ .fRlists.fRdir.listbox get $sel_index ]


   ###################################################################
   ##  CHECK HERE IF THE USER HAS 'cd' PERMISSION TO $RELDIRsel.
   ##  Pop a msg if not -- and return.
   ###################################################################

   set CURdir [ file dirname "$DIRFILmask" ]

   if {"$CURdir" == "/"} {
      set CHECKdir "/$RELDIRsel"
   } else {
      set CHECKdir "$CURdir/$RELDIRsel"
   }

   if { ! [ file executable "$CHECKdir" ] } {

         popup_msgVarWithScroll .topErr \
"The DIRECTORY
   $CHECKdir
is NOT ACCESSIBLE TO YOU, as user $env(USER)."

         set_status_msg \
"The DIRECTORY
   $CHECKdir
is NOT ACCESSIBLE TO YOU, as user $env(USER)."

         return

   }
   ## END OF  if { ! [ file executable "$CHECKdir" ] }


   ##################################################
   ## EXTRACT FILmask from $DIRFILmask.
   ##################################################

   set FILmask [ file tail "$DIRFILmask" ]



   ##########################################################
   ## SET THE NEW FULL-DIRNAME in CURdir, using $RELDIRsel.
   ## If RELDIRsel is '..' (parent directory) instead of a
   ## sub-directory, set CURdir to its parent directory.
   ##########################################################
   ## NOTE: 'file dirname' of '/home' and '/' both yield '/'.
   ## % file dirname /home
   ## /
   ## % file dirname /
   ## /
   ## % file dirname /usr
   ## /
   #########################################################

   if { "$RELDIRsel" == ".." } then {
      ## Save where we were, before setting CURdir to the parent directory.
      ## This will be used in subdir high-lighting below.
      set relCHILDdir [ file tail $CURdir ]
      set CURdir [ file dirname "$CURdir" ]
   } else {
      if { "$CURdir" == "/" } {
         ## Avoid CURdir being double-slash.
         set CURdir "/$RELDIRsel"
      } else {
         set CURdir "${CURdir}/$RELDIRsel"
      }
   }


   #######################################################
   ## RESET $DIRFILmask in the DIRFILmask entry field, AND
   ## to be used by 'fill_lists' proc.
   ## NOTE: CURdir is extracted (again) from $DIRFILmask
   ##       in the 'fill_lists' proc.
   #######################################################

   if {"$CURdir" == "/"} {
      ## Avoid DIRFILmask starting with double-slash.
      set DIRFILmask "/$FILmask"
   } else {
      set DIRFILmask "${CURdir}/$FILmask"
   }

   ##############################################################
   ## CALL 'fill_lists' to fill the subdir & relfil listboxes.
   ##############################################################

   fill_lists

   ##########################################################
   ## If the user is navigating to a parent directory (i.e.,
   ## if '..' was clicked), position the list in the updated
   ## 'dir' listbox such that the subdirectory that the user
   ## is 'climbing out of' is seen the the 'dir' listbox.
   ##########################################################

   if { "$RELDIRsel" == ".." } then {
      set IDXsubdir [lsearch [.fRlists.fRdir.listbox get 1 end] $relCHILDdir]
      .fRlists.fRdir.listbox see $IDXsubdir
      .fRlists.fRdir.listbox selection set [expr {$IDXsubdir + 1}]
   }

   ##############################################
   ## PUT A MSG IN THE STATUS MSG AREA.
   ##############################################

   set_status_msg "$aRtext(filledSTATUSmsg)"

   #######################################################
   ## MAKE SURE THE FAR-RIGHT END OF TEXT IN THE
   ## ENTRY BOXES IS IN VIEW, when this 'navigate' is done.
   #######################################################

   .fRdirfilmask.entryDIRFILmask    xview end
   .fRscriptSel.entry  xview end

}
## END OF PROC  'navigate'


##+########################################################################
## PROC  'set_status_msg'
##+########################################################################
## PURPOSE: To put an informational message to the user into the
##          bottom 'status-msg' area of the GUI.
## CALLED BY: various procs
##+########################################################################
## MESSAGE-CONTENT NOTE: The message string can include line-feeds.
##+########################################################################

proc set_status_msg { message } {

   #############################################
   ## RETURN THE CURSOR TO A 'LEFT POINTER'.
   ## (In case cursor was left as a 'watch'
   ##  after a failure in 'fill_list' proc, like
   ##  a subdir permission error.)
   #############################################
   # . configure -cursor { left_ptr red white }

   .fRstatusmsg.lab configure -text "$message"

}
## END OF PROC  'set_status_msg'


##+#################################################################
## PROC  'put_var_exit'
##+#################################################################
## PURPOSE:
##  This procedure contitutes the main purpose of this file-selector
##  GUI -- sending a fully-qualified filename to stdout (and exiting).
##
##  This procedure checks the validity of the filename.
##  If invalid, there is no exit -- an error message
##  line is shown in a status message widget.
##
##+#################################################################
## CALLED BY:  The 'OK'/'UseIt' button   -OR-
##             the .fRfilselection.entry <Key-Return> BINDING
##+####################################################################

proc put_var_exit {  } {

   global FULFILname DIRFILmask

   ##############################################
   ## GET RID OF LEADING AND TRAILING WHITE SPACE
   ## IN THE SELECTED FILE, that comes
   ## from the 'filselection.entry' widget.
   ##############################################

   set FULFILname [ string trim "$FULFILname" ]


   ##############################################
   ## IF THE 'SELECTION' FILE FIELD OS EMPTY,
   ## POPUP A MSG AND RETURN.
   ##############################################

   if { "$FULFILname" == "" } then {

      popup_msgVarWithScroll .topErr \
"The 'Script Selection' entry field is empty.
Select-a-filename or Cancel."

      set_status_msg \
"The 'Script Selection' entry field is empty.
Select-a-filename or Cancel."

      return

    }


   ##############################################
   ## IF THE 'SELECTION' FILE DOES NOT EXIST,
   ## POPUP A MSG AND RETURN.
   ##############################################

   if { ! [ file exists "$FULFILname" ] } then {

      popup_msgVarWithScroll .topErr \
"The file specified in the 'Script Selection' entry field
   $FULFILname
CANNOT BE FOUND.  Try again or Cancel."

      set_status_msg \
"The file specified in the 'Script Selection' entry field
   $FULFILname
CANNOT BE FOUND.  Try again or Cancel."

      return

   }

   ##############################################################
   ## IF THE 'SELECTION' FILE IS A DIRECTORY,
   ## POPUP A MSG AND RETURN.
   ##############################################################
   ## This should not be the case if the user
   ## selected the file via the files-listbox.
   ## But the user could paste/enter the name.
   ##############################################################

   if { [ file isdirectory "$FULFILname" ] } then {

      popup_msgVarWithScroll .topErr \
"The file selected in the 'Script Selection' entry field
   $FULFILname
is a DIRECTORY, not a regular file.  Try again or Cancel."

      set_status_msg \
"The file selected in the 'Script Selection' entry field
   $FULFILname
IS A DIRECTORY, not a regular file.  Try again or Cancel."

      return

     }

   ################################################################
   ## If the 'SELECTION' FILEname has a trailing '/',
   ## ISSUE A WARNING AND RETURN.
   ################################################################

   if { "[ file tail \"$FULFILname\" ]" == "" } {

      # set FULFILname [ file dirname "$FULFILname" ]

      popup_msgVarWithScroll .topErr \
"The file specified in the 'Script Selection' entry field
   $FULFILname
ends with a slash (/).  Remove the slash, try again, or Cancel."

      set_status_msg "The file specified in the 'Script Selection' entry field
   $FULFILname
ends with a slash (/).  Remove the slash, try again or Cancel."

      ####################################################
      ## MAKE SURE THE FAR-RIGHT END OF THE TEXT IN THE
      ## SELECTION ENTRY BOX IS IN VIEW.  (needed?)
      ####################################################

      .fRscriptSel.entry  xview end

      return
   }

   ## All looks OK. Put out the filename.
   puts "$FULFILname"
  
   exit

}
## END OF PROC 'put_var_exit'


##+##############################################################
## PROC 'toggle_fontsize'
##+##############################################################
## PURPOSE:
##  This routine toggles the size of fonts for about 18 different 
##  widgets --- between a small font and a larger font.
##+##############################################################
## CALLED BY:  the button command of the button
##             .fRcntlbutts1.buttFontSize .
##+##############################################################

set FONT_sizeFLAG "large"

proc toggle_fontsize { } {

   global  FONT_sizeFLAG

   ## FOR TESTING: (must be commented for 'production' use)
   #  puts "Changing font size."

   if { "$FONT_sizeFLAG" == "small" } then {

      set FONT_sizeFLAG "large"

      .fRdirmaskHead.lab             configure -font fontTEMP_varwidth
      .fRdirfilmask.entryDIRFILmask  configure -font fontTEMP_fixedwidth
      
      .fRlists.fRdir.lab           configure -font fontTEMP_varwidth
      .fRlists.fRdir.listbox       configure -font fontTEMP_fixedwidth
      .fRlists.fRfil.lab           configure -font fontTEMP_varwidth
      .fRlists.fRfil.listbox       configure -font fontTEMP_fixedwidth

      .fRscriptHead.lab            configure -font fontTEMP_varwidth
      .fRscriptSel.entry           configure -font fontTEMP_fixedwidth
      
      .fRcntlbutts1.buttOK          configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttCancel      configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttFilter      configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttHelp        configure -font fontTEMP_varwidth
      .fRcntlbutts1.buttFontSize    configure -font fontTEMP_varwidth
      
      .fRcntlbutts2.buttDirListLeft  configure -font fontTEMP_varwidth
      .fRcntlbutts2.buttDirListRight configure -font fontTEMP_varwidth
      .fRcntlbutts2.buttFilListLeft  configure -font fontTEMP_varwidth
      .fRcntlbutts2.buttFilListRight configure -font fontTEMP_varwidth
      .fRcntlbutts2.buttTallWin      configure -font fontTEMP_varwidth
      .fRcntlbutts2.buttShortWin     configure -font fontTEMP_varwidth
      
      .fRstatusmsg.lab               configure -font fontTEMP_fixedwidth
      
   } else {

      set FONT_sizeFLAG "small"

      .fRdirmaskHead.lab            configure -font fontTEMP_SMALL_varwidth
      .fRdirfilmask.entryDIRFILmask configure -font fontTEMP_SMALL_fixedwidth
      
      .fRlists.fRdir.lab           configure -font fontTEMP_SMALL_varwidth
      .fRlists.fRdir.listbox       configure -font fontTEMP_SMALL_fixedwidth
      .fRlists.fRfil.lab           configure -font fontTEMP_SMALL_varwidth
      .fRlists.fRfil.listbox       configure -font fontTEMP_SMALL_fixedwidth
      
      .fRscriptHead.lab            configure -font fontTEMP_SMALL_varwidth
      .fRscriptSel.entry          configure -font fontTEMP_SMALL_fixedwidth
      
      .fRcntlbutts1.buttOK          configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttCancel      configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttFilter      configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttHelp        configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts1.buttFontSize    configure -font fontTEMP_SMALL_varwidth
      
      .fRcntlbutts2.buttDirListLeft  configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.buttDirListRight configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.buttFilListLeft  configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.buttFilListRight configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.buttTallWin      configure -font fontTEMP_SMALL_varwidth
      .fRcntlbutts2.buttShortWin     configure -font fontTEMP_SMALL_varwidth
      
      .fRstatusmsg.lab               configure -font fontTEMP_SMALL_fixedwidth
      
   }

}
## END OF PROC 'toggle_fontsize'


########################################################################
## PROC  'toggle_ybars'
########################################################################
## PURPOSE: To switch the side of the 2 y-scrollbars, 
##          on the sides of the 'dir' and 'fil' listboxes. 
## 
## CALLED BY: .fRcntlbutts1.buttYbarsSIDE
########################################################################

set SIDE_Ybars "left"

proc toggle_ybars {} {

   global SIDE_Ybars

   if { "$SIDE_Ybars" == "left" } {
      set SIDE_Ybars "right"
      set SIDE_other "left"
   } else {
      set SIDE_Ybars "left"
      set SIDE_other "right"
   }

   ## FOR TESTING:
   #  puts "SIDE_Ybars: $SIDE_Ybars"
   #  puts "SIDE_other: $SIDE_other"

   pack forget .fRlists.fRdir.scrbary .fRlists.fRdir.listbox \
      .fRlists.fRfil.scrbary .fRlists.fRfil.listbox

   pack .fRlists.fRdir.scrbary \
      -side $SIDE_Ybars \
      -anchor n \
      -fill y \
      -expand 0

   pack .fRlists.fRdir.listbox \
      -side $SIDE_other \
      -anchor n \
      -fill both \
      -expand 1

   pack .fRlists.fRfil.scrbary \
      -side $SIDE_Ybars \
      -anchor n \
      -fill y \
      -expand 0

   pack .fRlists.fRfil.listbox \
      -side $SIDE_other \
      -anchor n \
      -fill both \
      -expand 1

}
## END of proc 'toggle_ybars'


##+#################################################################
## PROC  'dir_listbox_width_increase'
##+#################################################################
## PURPOSE:
##  This routine increases the width of the directory listbox frame
##  (measured in pixels).
##
##  Note that our intent is to increase the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the widening in pixels-only --- using an 'INCRpixelsRIGHT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'dir_listbox_width_reduce',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butDirListRight .
##+##############################################################

proc dir_listbox_width_increase {} {

   global INCRpixelsRIGHT SCREENwidthPx wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsRIGHT
   ## An alternate method could be to use 'INCRcharsRIGHT' instead of
   ## 'INCRpixelsRIGHT', on the frame contents instead of on the frame
   ## --- to do the increase in chars-only instead of pixels-only.

   set winXlen [ winfo width . ]

   set DIRwidthPX [winfo width .fRlists.fRdir]
   set FILwidthPX [winfo width .fRlists.fRfil]

   ## FOR TESTING:
   if {0} {
       puts "*******************************************************"
       puts "proc 'dir_listbox_width_increase' : (just entered proc)"
       puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels, on entering proc."
       puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
       puts "window-width  via winfo = $winXlen    pixels."
       puts "INCRpixelsRIGHT = $INCRpixelsRIGHT    pixels."
   }

   #######################################################################
   ## We do not try to increase the dir-listbox width if the entire window
   ## is already at the screen width. I.e. we only do the following
   ## computations if there is some room to expand the dir-listbox.
   #######################################################################

   if { $winXlen < $SCREENwidthPx } {

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ##############################################################################
      ## Increase the window width about as much as the increase in dir-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen + $INCRpixelsRIGHT}]

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}


      #####################################################################
      ## After we request an increase to the window size, we increase
      ## the size of the dir-listbox-frame.
      #####################################################################

      set DIRwidthPX [expr {$DIRwidthPX + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'dir_listbox_width_increase' : (new-DIR & same-FIL width parms)"
          puts "DIRwidthPX: $DIRwidthPX"
          puts "FILwidthPX: $FILwidthPX"
      }

      ######################################################################
      ## Increase the dir-frame width, but keep the fil-frame width the same.
      ######################################################################
      ## Strangely, the fil-listbox expands unless I add the
      ## 'pack propagate ... false' statement --- on the dir-listbox,
      ## NOT the fil-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the fil-listbox, NOT the dir-listbox. None of this section works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRdir false

   }
   ## END OF  if { $winXlen < $SCREENwidthPx }

}
## END OF PROC  'dir_listbox_width_increase'


##+##############################################################
## PROC  'dir_listbox_width_reduce'
##+##############################################################
## PURPOSE:
##  This routine reduces the width of the directory listbox frame
##  (measured in pixels).
##
##  Note that our intent is to decrease the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the narrowing in pixels-only --- using an 'INCRpixelsLEFT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'dir_listbox_width_increase',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butDirListLeft .
##+##############################################################

proc dir_listbox_width_reduce {} {

   global INCRpixelsLEFT DIRminwidthPX wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsLEFT
   ## An alternate method could be to use 'INCRcharsLEFT' instead of
   ## 'INCRpixelsLEFT', on the frame contents instead of on the frame
   ## --- to do the reduction in chars-only instead of pixels-only.

   set DIRwidthPX [winfo width .fRlists.fRdir]
   set FILwidthPX [winfo width .fRlists.fRfil]

   set winXlen [ winfo width . ]

   ## FOR TESTING:
   if {0} {
      puts "*****************************************************"
      puts "proc 'dir_listbox_width_reduce' : (just entered proc)"
      puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels."
      puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
      puts "window-width  via winfo = $winXlen    pixels."
      puts "INCRpixelszLEFT = $INCRpixelsLEFT    pixels."
   }

   ######################################################################
   ## We do not try to decrease the dir-listbox width if it is
   ## already at a designated minwidth. I.e. we only do the following
   ## computations if there is some latitude to decrement the dir-listbox.
   ######################################################################

   if { $DIRwidthPX > $DIRminwidthPX } {

      set DIRwidthPX [expr {$DIRwidthPX - $INCRpixelsLEFT}]
      set deltaDIRwidthPX $INCRpixelsLEFT

      if { $DIRwidthPX < $DIRminwidthPX } {
         set deltaDIRwidthPX [expr {($DIRwidthPX + $INCRpixelsLEFT) - $DIRminwidthPX}]
         set DIRwidthPX $DIRminwidthPX
      }

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'dir_listbox_width_reduce' : (new-DIR & same-FIL width parms)"
         puts "DIRwidthPX: $DIRwidthPX"
         puts "FILwidthPX: $FILwidthPX"
      }

      ######################################################################
      ## Decrease the dir-frame width, but keep the fil-frame width the same.
      ######################################################################
      ## Strangely, the fil-listbox decrements unless I add the
      ## 'pack propagate ... false' statement --- on the dir-listbox,
      ## NOT the fil-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the fil-listbox, NOT the dir-listbox. None of this works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRdir false


      #########################################################
      ## We narrow the window by the amount we narrow the
      ## directory box --- as follows.
      #########################################################

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      set winXlen [ winfo width . ]
      set winYlen [ winfo height . ]

      ##############################################################################
      ## Reduce the window width about as much as the decrease in dir-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winXlen [expr {$winXlen - $deltaDIRwidthPX}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'dir_listbox_width_reduce' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   }
   ## END OF   if { $DIRwidthPX > $DIRminwidthPX }

}
## END OF PROC  'dir_listbox_width_reduce'


##+##############################################################
## PROC  'fil_listbox_width_increase'
##+##############################################################
## PURPOSE:
##  This routine increases the width of the 'fil' listbox frame
##  (measured in pixels).
##
##  Note that our intent is to increase the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the widening in pixels-only --- using an 'INCRpixelsRIGHT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'fil_listbox_width_reduce',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butFilListRight .
##+##############################################################

proc fil_listbox_width_increase {} {

   global INCRpixelsRIGHT SCREENwidthPx wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsRIGHT
   ## An alternate method could be to use 'INCRcharsRIGHT' instead of
   ## 'INCRpixelsRIGHT', on the frame contents instead of on the frame
   ## --- to do the increase in chars-only instead of pixels-only.

   set winXlen [ winfo width . ]

   set FILwidthPX [winfo width .fRlists.fRfil]
   set DIRwidthPX [winfo width .fRlists.fRdir]

   ## FOR TESTING:
   if {0} {
       puts "*******************************************************"
       puts "proc 'fil_listbox_width_increase' : (just entered proc)"
       puts "filFRAMEwidth via winfo = $FILwidthPX pixels."
       puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels."
       puts "window-width  via winfo = $winXlen    pixels."
       puts "INCRpixelsRIGHT = $INCRpixelsRIGHT    pixels."
   }

   ########################################################################
   ## We do not try to increase the fil-listbox width if the entire window
   ## is already at the screen width. I.e. we only do the following
   ## computations if there is some room to expand the fil-listbox.
   ########################################################################

   if { $winXlen < $SCREENwidthPx } {

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ##############################################################################
      ## Increase the window width about as much as the increase in fil-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ##############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'fil_listbox_width_increase' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

      ###################################################################
      ## After we request an increase to the window size, we increase the
      ## size of the fil-listbox-frame.
      ###################################################################

      set FILwidthPX [expr {$FILwidthPX + $INCRpixelsRIGHT}]

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'fil_listbox_width_increase' : (new-FIL & same-DIR width parms)"
         puts "FILwidthPX: $FILwidthPX"
         puts "DIRwidthPX: $DIRwidthPX"
      }

      ######################################################################
      ## Increase the fil-frame width, but keep the dir-frame width the same.
      ######################################################################
      ## Strangely, the dir-lisbox expands until I added the
      ## 'pack propagate ... false' statement --- on the fil-listbox,
      ## NOT the dir-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the dir-listbox, NOT the fil-listbox. None of this section works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRfil false

   }
   ## END OF  if { $winXlen < $SCREENwidthPx }

}
## END OF PROC  'fil_listbox_width_increase'


##+##############################################################
## PROC  'fil_listbox_width_reduce'
##+##############################################################
## PURPOSE:
##  This routine reduces the width of the 'filenames' listbox frame
##  (measured in pixels).
##
##  Note that our intent is to decrease the width of the frame's
##  2 interior widgets (label and listbox). Those widths are
##  specified in chars, not pixels.
##
##  To avoid conversions between pixels and chars, we try to do
##  the narrowing in pixels-only --- using an 'INCRpixelsLEFT' var.
##
##  There are many ways this could be implemented. This is just one.
##  This routine, and its partner 'fil_listbox_width_increase',
##  are just routines that seem to work reasonably well.
##
## ARGUMENTS: See the global vars.
##
## CALLED BY:  the button command of the button
##             .fRcntlbutts2.butFilListLeft .
##+##############################################################

proc fil_listbox_width_reduce {} {

   global INCRpixelsLEFT FILminwidthPX wmPIXELS_top  wmPIXELS_left

   # global CHARWIDTHpixels INCRcharsLEFT
   ## An alternate method could be to use 'INCRcharsLEFT' instead of
   ## 'INCRpixelsLEFT', on the frame contents instead of on the frame
   ## --- to do the reduction in chars-only instead of pixels-only.

   set FILwidthPX [winfo width .fRlists.fRfil]
   set DIRwidthPX [winfo width .fRlists.fRdir]

   set winXlen [ winfo width . ]

   ## FOR TESTING:
   if {0} {
      puts "*****************************************************"
      puts "proc 'fil_listbox_width_reduce' : (just entered proc)"
      puts "filFRAMEwidth via winfo = $FILwidthPX pixels, on entering proc."
      puts "dirFRAMEwidth via winfo = $DIRwidthPX pixels, on entering proc."
      puts "window-width  via winfo = $winXlen    pixels."
      puts "INCRpixelszLEFT = $INCRpixelsLEFT    pixels."
   }

   ######################################################################
   ## We do not try to decrease the fil-listbox width if it is
   ## already at a designated minwidth. I.e. we only do the following
   ## computations if there is some latitude to decrement the fil-listbox.
   ######################################################################

   if { $FILwidthPX > $FILminwidthPX } {

      set FILwidthPX [expr {$FILwidthPX - $INCRpixelsLEFT}]
      set deltaFILwidthPX $INCRpixelsLEFT

      if { $FILwidthPX < $FILminwidthPX } {
         set deltaFILwidthPX [expr {($FILwidthPX + $INCRpixelsLEFT) - $FILminwidthPX}]
         set FILwidthPX $FILminwidthPX
      }

      ## FOR TESTING:
      if {0} {
         puts ""
         puts "proc 'fil_listbox_width_reduce' : (new-FIL & same-DIR width parms)"
         puts "FILwidthPX: $FILwidthPX"
         puts "DIRwidthPX: $DIRwidthPX"
      }

      ######################################################################
      ## Decrease the fil-frame width, but keep the dir-frame width the same.
      ######################################################################
      ## Strangely, the dir-listbox decrements (along with the fil-listbox)
      ## unless I add the 'pack propagate ... false' statement --- on the
      ## fil-listbox, NOT the dir-listbox.
      ##
      ## It seems that the 'pack propagate' should not be necessary (since
      ## I am specifically configuring the width of the 2 listboxes). AND
      ## it seems the 'pack propagate ... false' should be applied to
      ## the dir-listbox, NOT the fil-listbox. None of this works the
      ## way I would expect.
      ######################################################################

      .fRlists.fRdir configure -width $DIRwidthPX
      .fRlists.fRfil configure -width $FILwidthPX

      pack propagate .fRlists.fRfil false


      #########################################################
      ## We narrow the window by the amount we narrow the
      ## file listbox --- as follows.
      #########################################################

      set winXloc [ winfo rootx . ]
      set winYloc [ winfo rooty . ]

      ## Adjust the 'loc' vars for the window manager border.

      set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
      set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

      ############################################################################
      ## Reduce the window width about as much as the decrease in fil-frame-width,
      ## while keeping the location of the upper-left corner constant.
      ############################################################################

      set winYlen [ winfo height . ]
      set winXlen [expr {$winXlen - $deltaFILwidthPX}]

      ## FOR TESTING:
      if {0} {
          puts ""
          puts "proc 'fil_listbox_width_reduce' : (new WINwidth parm)"
          puts "winXlen: $winXlen"
      }

      wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   }
   ## END OF   if { $FILwidthPX > $FILminwidthPX }

}
## END OF PROC  'fil_listbox_width_reduce'


##+#####################################################################
## PROC  'tall_win'
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'TALL' the Tk window.
##   Several methods could be used.
##   For now, we query the current height (and location) of the window
##   (with 'winfo') and upsize the height by ~20%.
##
##   The user can keep clicking the button to upsize ~20% per click.
##
## ARGUMENTS: see the global vars
##
## CALLED BY:  .fRcntlbutts2.buttTallWin
##+#####################################################################

proc tall_win {} {

   global INCRfactor wmPIXELS_top wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'tall_win' proc :"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Increase the window height about 20%.

   set winYlen [expr {int(floor ( $INCRfactor * $winYlen ))} ]

   ## Adjust the 'loc' vars for the window manager border ---
   ## because 'winfo rootx' and 'winfo rooty' do not get the right values.

   set winXloc [ expr $winXloc - $wmPIXELS_left ]
   set winYloc [ expr $winYloc - $wmPIXELS_top ]

   ## Apply the new window height.

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Exiting 'tall_win' proc :"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC  'tall_win'


##+#####################################################################
## PROC  'short_win'
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'SHORTEN' the Tk window.
##   Several methods could be used.
##   For now, we query the current height (and location) of the window
##   (with 'winfo') and downsize the height by ~20%.
##
## The user can keep clicking the button to downsize ~10% per click.
##
## ARGUMENTS: see the global vars
##
## CALLED BY:  .fRcntlbutts2.buttShortWin
##+#####################################################################

proc short_win {} {

   global INCRfactor wmPIXELS_top wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'short_win' proc :"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Reduce the window height about 20%.

   set winYlen [expr {int(floor ( $winYlen / $INCRfactor ))} ]

   ## Adjust the 'loc' vars for the window manager border ---
   ## because 'winfo rootx' and 'winfo rooty' do not get the right values.

   set winXloc [ expr $winXloc - $wmPIXELS_left ]
   set winYloc [ expr $winYloc - $wmPIXELS_top ]

   ## Apply the new window height.

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Exiting 'short_win' proc :"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC  'short_win'


##+########################################################################
## 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} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_fixedwidth \
         -width  $VARwidth \
         -height 30 \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      ##  -height $VARheight \

      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_fixedwidth \
         -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} {
      ## 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'


##+####################################################################
##  END OF procs SECTION.
##+####################################################################

##+########################
## Set HELPtext var.
##+########################

set HELPtext \
"        ** HELP for this 'Script-Selector' Utility GUI **

This utility provides a Graphical User Interface (GUI) for
SELECTING A SCRIPT, from a hierarchy of subdirectories of scripts.

This 'script-selector' GUI is presented via a Tk script ---
'scriptSelector_2listboxes.tk'.

This 'script-selector' Tk script is intended to be called from a 'file(s)-selector'
utility --- the Tk script 'tkScriptApplicator_multifilesSelector_2listboxes.tk'.

Via that 'file(s)-selector' GUI, the user can select one or more files
(from a listbox) and then click on an 'Apply-a-Script' button to bring up
this 'script-selector' GUI, with which to select a script.

The selected shell script is to be run, by feeding the selected file(s)
to the selected shell script.

This set of Tk GUI's does not provide the 'Nautilus' shell scripts
that are to be applied to the selected file(s), but such
shell scripts can be found in the typical locations for those
scripts in the Gnome 2 and MATE desktop-environment systems:

       \$HOME/.gnome2/nautilus-scripts
or 
       \$HOME/.config/caja/scripts


******************************
CONFIGURING THIS Tk GUI SCRIPT:
******************************

The user can change the 'set' statement at the bottom of this
'scriptSelector_2listboxes.tk' Tk script to point to the
desired directory that holds the 'Nautilus scripts'.

Typically, the 'set' statements look like

set DIRshellScripts \"\$env(HOME)/.gnome2/nautilus-scripts\"
 OR 
set DIRshellScripts \"\$env(HOME)/.config/caja/scripts\"

but the 'Nautilus scripts' could be placed in any directory. 
Example: \$env(HOME)/ScriptsThatMakeMeHappy


*********************************
AN OVERVIEW OF THIS Tk GUI SCRIPT:
*********************************

The set of several Tk scripts constituting the 'tkScriptApplicator'
mini-system is intended to serve as an alternative to the
'select-files-and-right-click-to-choose-a-script-to-apply-
to-the-selected-files' capability of the Gnome2-Nautilus
file manager.

This 'script-selector' GUI is intended to supply the 'choose-a-script
to-apply-to-the-selected-files' part of that capability.

There are TWO, SIDE-BY-SIDE LISTBOXES in this 'script-selector' GUI:
   - a LEFT listbox for SUB-DIRECTORIES of the 'Nautilus' scripts directory
and
   - a RIGHT listbox for the SCRIPTS (files) in a user-selected
     'Nautilus' scripts sub-directory.

A script is selected by clicking on a filename in the right-hand,
files-listbox. The GUI supports a mask for 'filtering' filenames
such as '.sh'.

In other words, this utility is based on assuming the scripts all
have the same suffix, like '.sh', '.ksh', or '.bsh' or whatever.
(This avoids having to do a lot of extra processing, say using
a 'file' command, to determine the files that are scripts and
displaying, or accepting-selection-of, only those files.)

This utility is initialized to a mask of '*.sh' under the assumption
that all the 'Nautilus scripts' will have a suffix of '.sh'. But
that mask can be changed in a 'set DIRFILmask' statement at the
bottom of this 'script-selector' script.
"


##+####################################################################
## ADDITIONAL GUI-INITIALIZATION SECTION.
##+####################################################################

##+#####################################################################
## Set a global variable to turn on messages for testing of
## 'symbolic link' processing in procs
## 'get_subdirs_inCURdir_inclLinks' & and 'get_files_inCURdir_inclLinks'.
##+#####################################################################

set DEBUGlink0or1 0
# set DEBUGlink0or1 1

##+##############################################################
## Set some global variables for the dir and fil listbox width
## widening and narrowing procs.
##+##############################################################

set CHARWIDTHpixels [font measure fontTEMP_fixedwidth "0"]

## FOR TESTING:
#  puts "CHARWIDTHpixels = $CHARWIDTHpixels"

set INCRpixelsLEFT 90
set INCRpixelsRIGHT 80

# set INCRpixelsLEFT 50
# set INCRpixelsRIGHT 40

set SCREENwidthPx  [winfo screenwidth .]

##+##############################################################
## Set some global variables for the window-height
## incrementing and decrementing procs.
##+##############################################################

# set INCRfactor 1.1
  set INCRfactor 1.2

# set SCREENheightPx [winfo screenheight .]

##+####################################################################
## Set the directory that contains the 'Nautilus scripts'.
##+####################################################################

  set DIRshellScripts "$env(HOME)/.gnome2/nautilus-scripts"
# set DIRshellScripts "$env(HOME)/.config/caja/scripts" 
# set DIRshellScripts "$env(HOME)/ScriptsThatMakeMeHappy"

##+####################################################################
## Set the initial value of the 'DIRFILmask' variable.
##+####################################################################

set DIRFILmask "$DIRshellScripts/*.sh"

##+####################################################################
## Run the 'fill_lists' proc.
##+####################################################################
## NOTE: 'fill_lists' fills the dir and fil
##        listboxes according to the value of DIRFILmask.
##        Should have DIRFILmask set properly when calling 'fill_lists'.
##+###################################################################

fill_lists

##+####################################################################
##  END OF FINAL GUI-INITIALIZATION SECTION
##  *AND*
##  END OF this Tk SCRIPT.
##+####################################################################


 Code for Tk script 'dirSelector_orEditListOfFavoriteDirs_1listbox.tk' :
#!/usr/bin/wish -f
##
## Tk SCRIPT NAME:   dirSelector_orEditListOfFavoriteDirs_1listbox.tk
##
## WHERE:            typically installed in an 'apps' directory
##                   such as $HOME/apps/tkScriptApplicator
##
##+#######################################################################
## PURPOSE: This Tk script provides a GUI for selecting a directory
##          from a list of 'favorite' directories in a user favorites file:
##                      fave_dirs.lis
##
##          The GUI includes ONE scrolling listbox to present the
##          directory names that are extracted from the '.lis' file
##          when this Tk script starts up.
##
##          Clicking on a 'UseIt' button on the GUI causes a selected
##          directory name to be put to 'stdout'.
##
##          An 'EditDirs' button on the GUI allows for editing the '.lis' file.
##
## ---
##
##          This Tk script is intended (originally) to be called from a
##          'file(s)-selector' utility, the Tk script
##                  'tkScriptApplicator_multifilesSelector_2listboxes.tk'.
##          This Tk script is called via a 'Jump2Dir' button on the
##          'file(s)-selector' GUI.
## ---
## The main function of the 'tkScriptApplicator' mini-system:
##
##          Via the 'file(s)-selector' GUI,
##          the user can select one or more files in a listbox,
##          and then click on an 'Apply-a-Script' button to execute another
##          Tk script, 'scriptSelector_2listboxes.tk', to bring up a GUI
##          from which to select a SHELL SCRIPT filename.
##
##          The selected shell script is then run, by feeding the selected
##          file(s) to the selected shell script.
##
##+############
## INSTALLATION:
##
##  This Tk script and the other 2 Tk scripts ---
##  'tkScriptApplicator_multifilesSelector_2listboxes.tk' and
##  'scriptSelector_2listboxes.tk' --- may be installed in a
##  a sub-directory of the user's home directory. Example:
##                     $HOME/apps/tkScriptApplicator
##
##  The 'fave_dirs.lis' file may also be installed in a sub-directory
##  of the user's home directory. For example, if the several Tk scripts
##  of the 'tkScriptApplicator' mini-system are installed in directory
##                     $HOME/apps/tkScriptApplicator
##  the 'fave_dirs.lis' file may be installed in that same directory.
##
##  A 'DIRscripts' variable at the bottom of this script is used to
##  hold the name of the directory that contains this script. That
##  same directory is used to specify the location of the
## 'fave_dirs.lis' file, but that location can be changed.
##
##  For example, in a large site with workstations that have an
##  '/apps' directory NFS-mounted from a central server, the 3 Tk scripts
##  could be installed in a central directory such as
##        /apps/tkScriptApplicator
##
##  Then the fully-qualified name of the '.lis' file in this script
##  could be set to
##       $env(HOME)/apps/tkScriptApplicator/fave_dirs.lis
##  This would allow each user at the large site to have their own
##  directory-favorites file.
##
##+##################
## OTHER APPLICATIONS:
##
##   Note that although this Tk GUI script was written to be used
##   in the 'tkScriptApplicator' mini-system, this Tk GUI script could
##   be used in other file-selector applications.
##
##   This script is useful to a Tcl-Tk app developer so that
##   he/she can offer a user a choice of directories via a 'Jump2Dir'
##   or 'FaveDir' or 'DirBookmarks' button in a Tk GUI script.
##
##   In addition to being called from a Tk script,
##   this Tcl-Tk script may be called from any *SHELL* script which
##   needs to prompt the user for a directory name from a list of
##   'favorite directories'.
##
##+#####################################################################
## CALL FORMAT:
##
## EXAMPLE CALL in a TCL-TK script)
##
## set DIRnew [ exec $DIRscripts/dirSelector_orEditListOfFavorites_1listbox.tk ]
##
##                   (with no input parameters)
##
## ------------
## EXAMPLE CALL in a SHELL script:
##
## DIR_NEW=`$DIR_SCRIPTS/dirSelector_orEditListOfFavorites_1listbox.tk`
##
##
##+###################################
## The GUI DESIGN and OPERATION BASICS:
##
##   This GUI shows several buttons including:
##        'UseIt' (OK), 'Cancel', 'EditDirs', and several others.
##
##   The 'EditDirs' button allows for editing the 'fave_dirs.lis' file.
##
##   The following 'sketch' shows the GUI.
##
## -----------------------------------------------------------------------
##
## THE SKETCH CONVENTIONS for GUI sketch below:
##
## SQUARE-BRACKETS indicate a comment not to be included on the GUI.
## BRACES          indicate a Tk 'button' widget.
## UNDERSCORES     indicate a Tk 'entry' widget.
## A COLON         indicates that the text before the colon is on
##                 a 'label' widget.
## CAPITAL-O       indicates a Tk 'radiobutton' widget.
## CAPITAL-X       indicates a Tk 'checkbutton' widget.
##
##  ----------------------------------------------------------------------------------------
##  Directory Selector - 'Favorites'
##  [window title]
##  ----------------------------------------------------------------------------------------
##
##   TopLevel Frame
##    names (no SubFrames)
##     |
##     V
##
## .fRbuttons    {UseIt} {Cancel} {EditDirs} {Help} {tallWin} {shortWin}
##
## .fRmsg       SELECT a directory and click 'UseIt' --- or 'Cancel'.
##              If you 'EditDirs', this window may close when done. Start
##              this selector interface again to select from the edited file.
##
##              [This initial message is in a label widget.]
##
## .fRlistbox   ------------------------------------------------------------
##              | ## Fully-qualified DIRECTORY NAMES -                     A
##              | ## 'favorites'.                                          |
##              | ######################                                   |
##              | ## My Home Directories:                                  |
##              | ######################                                   |
##              | ~                                                        |
##              | ~/Documents                                              |
##              | ~/Downloads                                              |
##              | ~/Pictures                                               |
##              | ~/Desktop                                                |
##              | ~/apps                                                   |
##              | ~/IMAGE_CAPTURE                                          |
##              | ~/.mozilla                                               |
##              | ~/.thumbnails                                            |
##              | ####################                                     |
##              | ## Other Directories:                                    |
##              | ####################                                     |
##              | /tmp                                                     |
##              | /usr                                                     |
##              | /usr/share                                               |
##              | /usr/lib                                                 |
##              | /lib                                                     |
##              | /etc                                                     |
##              | /dev                                                     |
##              | /var                                                     |
##              | /                                                        |
##              |                                                          V
##              <---------------------------------------------------------->
##
##  That is the GUI --- simply 3 frames, top to bottom.
##
##  The listbox has x and y scrollbars --- shown here on the bottom and on
##  the right of the listbox.
##
##  The contents of the listbox are the lines of the 'fave_dirs.lis' file.
##  As indicated here, the '.lis' file can include comment lines indicated
##  by a '#' sign in column one. Clicking on a comment line does NOT
##  result in a selection.
##
##  Clicking on the 'UseIt' button causes a selected directory name to
##  be passed back to the calling script. A 'puts' statement sends the
##  directory name to 'stdout'.
##
##  Clicking on the 'EditDirs' button causes the 'fave_dirs.lis' file
##  to be brought up in a text editor. The text editor can be set in
##  a 'set EDITORtext' statement at the bottom of this Tk script.
##
##+##############
## GUI components:
##
## From the GUI 'sketch' above, it is seen that the GUI consists of ABOUT:
##
##   6 'button' widgets
##   1 'label'  widget (in the 'fRmsg' frame)
##   1 'listbox' widget (with scrollbars)
##   0 'entry'  widgets
##   0 'text' widgets (except for the text widget in the proc used to show Help)
##   0 'checkbutton' widgets
##   0 'radiobutton' widgets
##   0 'scale'  widgets
##   0 'canvas' widgets
##
##+#####################################################################
## INPUTS:  User selects a directory in the listbox and clicks the
##          'UseIt' button.
##
## OUTPUT:  A directory name (fully-qualified) to stdout.
##
##+########################################################################
## STRUCTURE OF THIS CODE:
##
##  0) Set general window & widget parms (win-name, win-position,
##     win-color-scheme, fonts, widget-geom-parms, text-array-for-labels-etc,
##     win-size-control).
##
##  1a) Define ALL frames (and sub-frames, if any).
##  1b) Pack the frames.
##
##  2) Define & pack all widgets in the frames -- frame by frame.
##     After all the widgets for a frame are defined, pack them in the frame.
##
##  3) Define keyboard and/or mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) ADDITIONAL GUI INITIALIZATION  (typically with one or more of
##     the procs), if needed.
##
## In more detail, for this particular Tk script:
##
##  1a) Define ALL frames:
##
##      Top-level : 'fRbuttons', 'fRmsg', 'fRlistbox'
##                            (packed top-to-bottom, not left-to-right)
##
##      Sub-frames: none
##
##  1b) Pack ALL frames.
##
##  2) Define & pack all widgets in the frames -- basically going through
##     frames & their interiors in  left-to-right and/or top-to-bottom order:
##
##  3) Define BINDINGS:  See BINDINGS section in code below.
##
##  4) Define PROCS:
##
##       'loaddirs2listbox'  - to read the '.lis' file into the listbox.
##
##       'put_vars'          - called by the 'UseIt' button, to put the
##                             selected dirname to stdout.
##
##  'popup_msgVarWithScroll' - used to show the $HELPtext var ; also can
##                             be used to show user various popup messages
##                             for info, warnings, errors, etc.
##
##   And for 2 'window-size' buttons:
##       'short_win'         - to shorten  the window with a single click
##       'tall_win'          - to lengthen the window with a single click
##
##  5) ADDITIONAL GUI INITIALIZATION:  run 'loaddirs2listbox'
##
##+#######################################################################
## 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
## but this script should work in most previous 8.x versions, and probably
## even in some 7.x versions (if font handling is made 'old-style').
##+########################################################################
## MAINTENANCE HISTORY:
## Started by: Blaise Montandon 2014jul21 Started developing this script
##                                        and auxiliary Tk scripts
##                                        based on similar FE scripts from
##                                        the 'feHappyScripts' system.
## Updated by: Blaise Montandon 2014jul29 A cleanup pass on this script.
##+#######################################################################
## HEY, ME!
## IF YOU UPDATE THIS SCRIPT, REMEMBER TO CHANGE THE FOLLOWING VERSION ID!!!
##+########################################################################
## For window title:
set VERSIONdirsel "ver2014jul29"

##+######################################################
## Set WINDOW TITLE and POSITION.
##+######################################################

wm title . \
"Directory-Selector - 'Favorites' - $VERSIONdirsel"

wm iconname . "DirSelector"

wm geometry . +30+45


##+######################################################
## Set the COLOR SCHEME for the window and its widgets ---
## such as listbox and entry field background color.
##+######################################################

tk_setPalette "#f0f0f0"

set listboxBKGD "#ffffff"

# set entryBKGD   "#ffffff"

# set msgBKGD "#e0e0e0"
  set msgBKGD "#ff9999"

# set radbuttBKGD "#c0c0c0"
# set chkbuttBKGD "#c0c0c0"
# set scaleBKGD   "#f0f0f0"


##+########################################################
## DEFINE (temporary) FONT NAMES.
##
## We use a VARIABLE-WIDTH font for text on LABEL and
## BUTTON widgets.
##
## We use a FIXED-WIDTH font for LISTBOX lists,
## for text in ENTRY fields --- and often for text in
## TEXT widgets.
##
## 'weight' can be 'bold' or 'normal'.
## 'slant'  can be 'roman' or 'italic'.
##
## "You can specify pixel-based sizes with negative numbers."
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -12 \
   -weight normal \
   -slant roman

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

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


font create fontTEMP_fixedwidth  \
   -family {droid sans mono} \
   -size -12 \
   -weight normal \
   -slant roman

font create fontTEMP_SMALL_fixedwidth  \
   -family {droid sans mono} \
   -size -10 \
   -weight normal \
   -slant roman

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


##+###########################################################
## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS.
## (e.g. width and height of canvas, and padding for Buttons)
##+###########################################################

## Some WINDOW-MANAGER BORDER width settings:
## (We could try to set these parameters via a re-usable proc
##  that uses the 'wm' and 'winfo' commands. But that may not
##  work for all window managers and operating systems.
##  So we simply set the values here.)

set wmPIXELS_left 3
set wmPIXELS_top 23


## LABEL widget geom settings:

set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2
set RELIEF_label_lo "flat"


## BUTTON widget geom settings:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2
## We use '-relief raised' for all 'button' widgets.


## LISTBOX widget geom settings:

set initWIDTHchars_listbox 25
## This width can be set to accomodate any longer
## 'favorite directory' names of the user(s).

set initHEIGHTchars_listbox 30

set minHEIGHTchars_listbox 1

set BDwidthPx_listbox 2
## We use '-relief sunken' for the 'listbox' widget.


## TEXT widget geom settings:

set BDwidthPx_text 2
# set RELIEF_numtext "ridge"


## We nullify the following settings. NOT USED, yet.
if {0} {

## ENTRY widget geom settings:

set BDwidthPx_entry 2
## We use '-relief sunken' for all 'entry' widgets.


## RADIOBUTTON widget geom settings:

set PADXpx_radbutton 0
set PADYpx_radbutton 0
set BDwidthPx_radbutt 2
set RELIEF_radbutt_hi "raised"


## CHECKBUTTON widget geom settings:

set PADXpx_chkbutton 0
set PADYpx_chkbutton 0
set BDwidthPx_chkbutt 2
set RELIEF_chkbutt_hi "raised"


## SCALE widget geom parameters:

# set BDwidthPx_scale 2
# set scaleThicknessPx 10

}
## END OF if {0}  (to nullify these settings)


##+####################################################################
## 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 the 'fRbottons' frame:

set aRtext(buttOK)          "UseIt"
set aRtext(buttCANCEL)      "Cancel"
set aRtext(buttEDITdirs)    "EditDirs"
set aRtext(buttHELP)        "Help"
set aRtext(buttTallWin)     "tallWin"
set aRtext(buttShortWin)    "shortWin"

# set aRtext(buttFontSize)    "TogFontSize"

## For the 'fRmsg' frame:

set aRtext(labelMSG)  \
"SELECT a directory and click 'UseIt' --- or 'Cancel'. If you click
'EditDirs', this window may close when the editor starts up. You can
start this selector interface again to select from the edited file."


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


##+######################################################################
## Set a MIN-SIZE of the window (roughly).
##
## For WIDTH, allow for the buttons in frame '.fRbuttons'.
##
## For HEIGHT, allow for the stacked frames:
##      1 char   high for the '.fRbuttons'  frame
##      3 chars  high for the '.fRmsg'      frame
##      N chars  high for the '.fRlistbox'  frame
## where N =  $minHEIGHTchars_listbox + 1  char, where the 1 char
## allows some room for the horizontal scrollbar of the listbox.
##+#####################################################################

## FOR WIDTH: (allow for the several buttons in frame '.fRbuttons')

set minWinWidthPx [font measure fontTEMP_varwidth \
"$aRtext(buttOK) $aRtext(buttCANCEL) $aRtext(buttEDITdirs) \
$aRtext(buttHELP) $aRtext(buttTallWin) $aRtext(buttShortWin)"]


##+#############################################################
## For HEIGHT --- for
##      1 char   high for the '.fRbuttons'  frame
##      3 chars  high for the '.fRmsg'      frame
##      N chars  high for the '.fRlistbox'  frame
##    --------
##    4+N chars  high for the 3 frames
## where N =  $minHEIGHTchars_listbox + 1 char, where the 1 char
## allows some room for the horizontal scrollbar of the listbox.
##+############################################################

set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {(5 + $minHEIGHTchars_listbox) * $charHeightPx}]

## Add about 20 pixels for top-bottom window decoration --
## and some pixels for top-and-bottom of frame/widget borders
## (~3 widgets x 4 pixels/widget = 12 pixels).

set minWinHeightPx [expr {32 + $minWinHeightPx}]


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

wm minsize . $minWinWidthPx $minWinHeightPx

## We need to allow the window to be resizable ---
## so that the 'fRlistbox' frame can expand,
## both horizontally and vertically.

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

## If you want the window to resize in x-direction, but not y.
#   wm resizable . 1 0



##+####################################################################
##+####################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : 'fRbuttons', 'fRmsg', 'fRlistbox'
##
##   Sub-frames: none
##+####################################################################
##+####################################################################

set RELIEF_frame flat
set BDwidthPx_frame 0

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

frame .fRbuttons   -relief $RELIEF_frame  -bd $BDwidthPx_frame

frame .fRmsg       -relief $RELIEF_frame  -bd $BDwidthPx_frame

frame .fRlistbox   -relief $RELIEF_frame  -bd $BDwidthPx_frame

##+########################################################
## PACK *ALL* the FRAMES.
##+########################################################

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

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

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


##+################################################################
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES.
##+################################################################
##+################################################################


##+#####################################################################
## IN THE '.fRbuttons' frame -- DEFINE several buttons
## --- UseIt (OK), Cancel, EditDirs, ...  THEN PACK THEM.
##+#####################################################################

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

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

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

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

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

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


## Here is a label Widget on which to write the number of directories
## read in by the 'loaddirs2listbox' proc.

label .fRbuttons.labelNdirs \
   -text "" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief  flat \
   -bd $BDwidthPx_label

##+##########################################
## Pack all the widgets in frame '.fRbuttons'.
##+##########################################
## NOTE: You can rearrange the buttons by
##       simply changing this packing order.
##+##########################################

pack .fRbuttons.buttCANCEL \
     .fRbuttons.buttOK \
     .fRbuttons.buttHELP \
     .fRbuttons.buttEDITdirs \
     .fRbuttons.buttTallWin \
     .fRbuttons.buttShortWin \
     .fRbuttons.labelNdirs \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+#####################################################################
## IN THE '.fRmsg' frame -- DEFINE a LABEL widget and PACK IT.
##+#####################################################################

label .fRmsg.label \
   -text "$aRtext(labelMSG)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief raised \
   -bd $BDwidthPx_label \
   -bg $msgBKGD

pack .fRmsg.label \
   -side left \
   -anchor w \
   -fill x \
   -expand 0


##+########################################################
## IN THE 'fRlistbox' frame -- DEFINE 1 listbox widget,
## with vertical and horizontal scrollbars.
##+########################################################

listbox .fRlistbox.listbox \
   -width $initWIDTHchars_listbox \
   -height $initHEIGHTchars_listbox \
   -font fontTEMP_fixedwidth \
   -relief sunken \
   -borderwidth $BDwidthPx_listbox \
   -bg $listboxBKGD \
   -selectmode single \
   -yscrollcommand ".fRlistbox.scrbary set" \
   -xscrollcommand ".fRlistbox.scrbarx set"

scrollbar .fRlistbox.scrbary \
   -orient vertical \
   -command ".fRlistbox.listbox yview"

scrollbar .fRlistbox.scrbarx \
   -orient horizontal \
   -command ".fRlistbox.listbox xview"

## Pack the scrollbars, then the listbox.

pack .fRlistbox.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRlistbox.scrbary \
   -side right \
   -anchor ne \
   -fill y \
   -expand 0

pack .fRlistbox.listbox \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1


##+#####################################################################
## END OF  MAIN SECTION TO SETUP THE GUI.
##+#####################################################################

##+#####################################################################
##+#####################################################################
## DEFINE BINDINGS: none
##+#####################################################################
##+#####################################################################


##+#####################################################################
##+#####################################################################
## DEFINE PROCEDURES:
##    'edit_dirslist'       - for 'EditDirs' button
##
##    'loaddirs2listbox'    - for the 'ADDITIONAL GUI INITIALIZATION'
##                            section at the bottom of this script
##
##    'put_vars'            - for the 'UseIt' button
##
## 'popup_msgVarWithScroll' - for the 'Help' button
##
##+#####################################################################
##+#####################################################################


##+#####################################################################
## PROC  'edit_dirslist'
##+#####################################################################
## PURPOSE: Edits the dirs list file, then reloads the listbox.
##
## CALLED BY:  .fRbuttons.buttEDITdirs
##+#####################################################################

proc edit_dirslist { } {

   global EDITORtext DIRSFILEfullname
   # global RETCODEedit

   ## I can't get 'gedit' to run in foreground mode.
   ## It wants to start up and then allow following Tcl-Tk
   ## statements to execute.
   ##
   ## The statements following the following statement, like
   ## 'loaddirs2listbox', proceed to execute
   ## (try to refill the listbox) while user is editing.

   # exec $EDITORtext "$DIRSFILEfullname"

   ## CLUTZY WORKAROUND METHOD to run in foreground mode
   ## (avoid executing following Tcl-Tk statements while the
   ##  editor is 'up') : Use 'xterm -hold -e'.

   # exec xterm -hold -e $EDITORtext "$DIRSFILEfullname"
   # loaddirs2listbox

   ## 'update' is NOT needed after the loaddirs call.

   #############################################################
   ## A CLEANER WORKAROUND: Start editor and issue exit command.
   #############################################################
   ## NOTE: For some editors, if an instance is already 'up',
   ## a new edit window may not pop up. This '.lis' file may
   ## appear in an already-opened edit window. This may not
   ## be obvious to the user. A popup message could be added here.
   ##############################################################

   exec $EDITORtext "$DIRSFILEfullname"

   ## FOR TESTING:
   # puts "Exiting proc 'edit_dirslist'"

   exit

}
## END OF PROC  'edit_dirslist'



##+#####################################################################
## PROC 'loaddirs2listbox'
##+#####################################################################
## PURPOSE: Loads directory names to listbox.
##          Done once, at GUI initialization.
##
## CALLED BY:  in the 'ADDITIONAL GUI INITIALIZATION' section
##             at bottom of this Tk script
##+#####################################################################

proc loaddirs2listbox { } {

   global  DIRSFILEfullname env

   ## FOR TESTING:
   #   puts "Entering proc 'loaddirs2listbox'."

   ## Make sure listbox is empty.

   .fRlistbox.listbox delete 0 end

   ## Open the directory-favorites file and load it into the listbox.

   set f [open "$DIRSFILEfullname"]

   while {![eof $f]} {

      set rec [gets $f]

      set char1 [ string index $rec 0 ]

      ## We could avoid loading comment lines into the listbox.
      ## But we go ahead and load them.

      # if { "$char1" == "#" } {continue}

      ## We could avoid loading blank lines into the listbox.
      ## But we go ahead and load them.
      # if { "$rec" == "" } {continue}

      ## Replace string '$HOME', if any, by the contents of $env(HOME).

      set HOMEstartIDX [string first {$HOME} $rec]
      set newrec "$rec"
      if { $HOMEstartIDX != -1 && "$char1" != "#"} {
         set HOMEendIDX [expr $HOMEstartIDX + 4]
         set newrec [string replace $rec $HOMEstartIDX $HOMEendIDX $env(HOME)]
      }

      .fRlistbox.listbox insert end $newrec

   }
   ## END of read-file loop

   close $f

   ## Show the number of lines loaded in a label on the GUI.

   set numdirs [.fRlistbox.listbox size]
   # set numdirs [expr $numdirs - 1]
   ## Not needed if $rec empty check is added.

   .fRbuttons.labelNdirs configure -text "$numdirs Lines in Listbox"


   ####################################################
   ## INITIALIZE THE POSITION IN THE LISTBOX so that
   ## a particular line is in view.
   ################################
   ## COMMENTED for now.
   ####################################################

   # .fRlistbox.listbox see $seeINDEX

}
## END OF PROC  'loaddirs2listbox'


##+#####################################################################
## PROC  'put_vars'
##+#####################################################################
## PURPOSE: Puts parms (just dirname for now) to stdout.
##
## CALLED BY:  button .fRbuttons.buttOK ('UseIt')
##+#####################################################################

proc put_vars { } {

   global env

   ## Get the directory from a selected line in the listbox.

   set sel_index [ .fRlistbox.listbox curselection ]

   if { $sel_index != "" } {
      set dirname [ .fRlistbox.listbox get $sel_index ]
   } else {
      # set dirname ""
      return
   }

   ## If the line selected is a comment line, return nothing.

   set char1 [ string index $dirname 0 ]
   if { "$char1" == "#" } {return}

   ## If the line selected is a null line, return nothing.

   if { "$dirname" == "" } {return}

   ## If the line selected starts with '~', replace the '~'
   ## with the user's home directory --- and return the dirname,
   ## and exit the GUI.

   if { "$char1" == "~" } {
      set dirname "$env(HOME)[string range $dirname 1 end]"
   }

   puts "$dirname"

   exit

}
## END OF PROC  'put_vars'


##+#####################################################################
## PROC 'tall_win' PROCEDURE
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'TALL-en' (lengthen) the Tk window.
##   Several methods could be used.
##   For now, we query the current width and height of the window
##   (with 'winfo') and upsize the height by 20%.
##
## ARGUMENTS: See the globals.
##
## CALLED BY:  .fRbuttons.buttUPwin
##
## NOTE: The user can keep clicking the button to downsize ~20% per click.
##+#####################################################################

# set INCRfactor 1.2
  set INCRfactor 1.5

proc tall_win {} {

   global INCRfactor wmPIXELS_top  wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'tall_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Increase the window height about 20%
   # set winXlen [expr {int(floor ( $INCRfactor * $winXlen ))} ]
   set winYlen [expr {int(floor ( $INCRfactor * $winYlen ))} ]

   ## Adjust the 'loc' vars for the window manager border.
   set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
   set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Leaving 'tall_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC 'tall_win'


##+#####################################################################
## 'short_win' PROCEDURE
##+#####################################################################
## PURPOSE:
##   This procedure is invoked to 'SHORTEN' the Tk window.
##   Several methods could be used.
##   For now, we query the current height (and location) of the window
##   (with 'winfo') and downsize the height by ~20%.
##
## ARGUMENTS: See the globals.
##
## CALLED BY:  .fRbuttons.buttDWNwin
##
## NOTE: The user can keep clicking the button to downsize ~10% per click.
##
##+#####################################################################

proc short_win {} {

   global INCRfactor wmPIXELS_top  wmPIXELS_left

   set winXlen [ winfo width . ]
   set winYlen [ winfo height . ]

   set winXloc [ winfo rootx . ]
   set winYloc [ winfo rooty . ]

   ## FOR TESTING:
   #    puts "Entering 'short_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

   ## Reduce the window height about 20%
   # set winXlen [expr {int(floor ( $winXlen / $INCRfactor ))} ]
   set winYlen [expr {int(floor ( $winYlen / $INCRfactor ))} ]

   ## Adjust the 'loc' vars for the window manager border.
  set winXloc [ expr {$winXloc - $wmPIXELS_left} ]
  set winYloc [ expr {$winYloc - $wmPIXELS_top} ]

   wm geometry . ${winXlen}x${winYlen}+${winXloc}+${winYloc}

   ## FOR TESTING:
   #    puts "Leaving 'short_win' proc:"
   #    puts "winXlen : $winXlen"
   #    puts "winYlen : $winYlen"
   #    puts "winXloc : $winXloc"
   #    puts "winYloc : $winYloc"

}
## END OF PROC  'short_win'


##+########################################################################
## 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} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_fixedwidth \
         -width  $VARwidth \
         -height 30 \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      ##  -height $VARheight \

      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_fixedwidth \
         -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} {
      ## 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'


##+####################################################################
##+####################################################################
##  END OF procs SECTION.
##+####################################################################
##+####################################################################

##+########################
## Set HELPtext var.
##+########################

set HELPtext \
"   ** HELP for this 'Directory Selector (Favorites)' Utility **

This utility provides a Graphical User Interface (GUI) for
selecting a directory from a list of 'favorite' directories in
a user favorites file:
            fave_dirs.lis

This GUI is presented by a 'directory-selector' Tk script ---
'dirSelector_orEditListOfFavoriteDirs_1listbox.tk'.

The GUI includes ONE scrolling listbox to present the
directory names that are extracted from the '.lis' file
when this Tk script starts up.

Clicking on a 'UseIt' button on the GUI causes a selected
directory name to be put to 'stdout'.

An 'EditDirs' button on the GUI allows for editing the '.lis' file.

---

This Tk script is intended (originally) to be called from a
'file(s)-selector' utility of the 'tkScriptApplicator' mini-system
--- the Tk script
      'tkScriptApplicator_multifilesSelector_2listboxes.tk'.

Via that 'file(s)-selector' GUI,
the user can select one or more files in a listbox,
and then click on an 'Apply-a-Script' button to execute another
Tk script, 'scriptSelector_2listboxes.tk', to bring up a GUI
from which to select a SHELL SCRIPT filename.

The selected shell script is then run, by feeding the selected
file(s) to the selected shell script.

In other words, the 'tkScriptApplicator' mini-system consists
mainly of 3 Tk GUI scripts:
   - a 'file(s)-selector' GUI
   - a 'script-selector' GUI
   - a 'directory-selector' GUI
and
   - a 'fave_dirs.lis' text file.


************
INSTALLATION:
************

This Tk script and the other 2 Tk scripts ---
'tkScriptApplicator_multifilesSelector_2listboxes.tk' and
'scriptSelector_2listboxes.tk' --- may be installed in a
a sub-directory of the user's home directory. Example:
                   \$HOME/apps/tkScriptApplicator

The 'fave_dirs.lis' file may also be installed in a sub-directory
of the user's home directory. For example, if the several Tk scripts
of the 'tkScriptApplicator' mini-system are installed in directory
                   \$HOME/apps/tkScriptApplicator
the 'fave_dirs.lis' file may be installed in that same directory.

A 'DIRscripts' variable at the bottom of this script is used to
hold the name of the directory that contains this script. That
same directory is used to specify the location of the
'fave_dirs.lis' file, but that location can be changed.

For example, in a large site with workstations that have an '/apps'
directory NFS-mounted from a central server, the 3 Tk scripts
could be installed in a central file-server directory such as
      /apps/tkScriptApplicator

Then the fully-qualified name of the '.lis' file in this Tk script
could be set to 'home directory' filename
     \$env(HOME)/apps/tkScriptApplicator/fave_dirs.lis

This can be done by simply changing a 'set DIRSFILEfullname'
statement at the bottom of this Tk script.

This would allow each user at the large site to have their own
directory-favorites file --- whether the user home-directories
were 'mounted' from a central file-server OR on local workstations.

******************
OTHER APPLICATIONS:
******************

Note that although this Tk GUI script was written to be used
in the 'tkScriptApplicator' mini-system, this Tk GUI script
could be used in other file-selector applications where a
directory-selector is needed.

This script is useful to a Tcl-Tk app developer so that
he/she can offer a user a choice of directories via a 'Jump2Dir'
or 'FaveDir' or 'DirBookmarks' button in a Tk GUI script.

In addition to being called from a *Tk* script,
this Tcl-Tk script may be called from any *SHELL* script which
needs to prompt the user for a directory name from a list of
'favorite directories'.
"


##+######################################################
## ADDITIONAL GUI INITIALIZATION section:
##+######################################################

##+###################################################
## Set the GUI text-editor to use on the '.lis' file.
##+###################################################

# set EDITORtext "/usr/bin/kedit"
# set EDITORtext "/usr/bin/gedit"
# set EDITORtext "/usr/bin/scite"
set EDITORtext "$env(HOME)/apps/gscite_2.27/SciTE"


##+###############################################
## Get the directory that contains this Tk script.
##+###############################################

## FOR TESTING:
#  puts "argv0: $argv0"

# set DIRscripts "."
# set DIRscripts "[pwd]"
# set DIRscripts "$env(HOME)/apps/tkScriptApplicator"
  set DIRscripts "[file dirname $argv0]"


##+#################################################
## PUT the full filename of the 'fave_dirs.lis' file
## in a var, for use in a couple of procs.
##+#################################################

# set DIRSFILEfullname "$env(HOME)/apps/tkScriptApplicator/fave_dirs.lis"
set DIRSFILEfullname  "$DIRscripts/fave_dirs.lis"


##+#################################################
## Load the listbox.
##+#################################################

loaddirs2listbox


 Text file 'fave_dirs.lis' :
## Fully-qualified DIRECTORY NAMES -
## 'favorites'.
####################################
## My Home Directories:
## (Could use $HOME instead of '~'.)
####################################
~
~/Documents
~/Downloads
~/Pictures
~/Desktop
~/apps
~/IMAGE_CAPTURE
~/.mozilla
$HOME/.thumbnails
####################
## Other Directories:
####################
/tmp
/usr
/usr/share
/usr/lib
/lib
/etc
/dev
/var
/


INSTALLING THE SCRIPTS:

The 3 Tk scripts and the 'lis' file could be put in a sub-directory of the user's home directory, such as $HOME/apps/tkScriptApplicator.

Then the user can use their desktop system (such as Gnome or KDE) to set up the 'main' 'file(s)-selector' Tk script as an icon on the desktop.

Then, whenever the user wants to apply some Nautilus scripts to one or more files, the user can click on the icon to startup the main script of the 'tkScriptApplicator' mini-system.

---

In addition, a few variables may need to be set in the Tk scripts.

In the 'file(s)-selector' Tk script, the statement

   set DIRFILmask "$env(HOME)/*"

at the bottom of that script can be changed to have the file(s)-selector GUI start up positioned at a different directory from the user's home directory.

In the 'script-selector' Tk script, the location of the 'Nautilus scripts' can be changed in a 'set DIRshellScripts' statement near the bottom of that script.

In the 'directory-selector' Tk script, the text editor can be set in a 'set EDITORtext' statement near the bottom of that Tk script.

Furthermore, the 'fave_dirs.lis' text file could be edited to start off with a set of 'favorite' directories suited to the user-installer.


SOME POSSIBLE ENHANCEMENTS

The copious amount of code here lends itself to many possible enhancements. Some examples:

1) Add some buttons to the 'file(s)-selector' Tk script (such as Rename, Delete, Copy, Move, Properties, etc.) to make it a more full-featured file manager.

2) Move some of the info/warning/error messages into the 'aRtext' array.

3) Change from a 2-listbox format to a 1-listbox format, for both the 'file(s)-selector' Tk script and the 'script-selector' Tk script.


CODE SNIPPETS

NOTE that even if you do not ever implement these scripts, there may be some code here that would be of use. For example, the code that implements the 'fill_lists' procs is a big step forward from the similar code of the 'dirview' script in the Eric Johnson book "Graphical Applications with Tcl & Tk", first edition (1996), page 225-226, Chapter 6 'Lists, Files, and Directories'.

The 'dirview.tcl' script in chapter 6 gives an example of using a 'foreach' loop to extract directories and non-directories from 'glob' output. But that code would have to be made more robust to handle 'symbolic links' properly --- and exclude special files like fifo, socket, and device files.

Another example: The code in the 'toggle_ybars' proc may be of interest to some Tcler's.


IN CONCLUSION

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.