package provide TextMenu 23.07.13
#
# <20230713.1919.53>
#
# This is the code to create a menu widget from a text window.
# The advantage of this is to allow icons in the menu, allow
# the user to size and scroll the menu as well as
# to address the issue of cascading not working as it should in
# MSW systems. The way we cascade is not to create aother widget,
# but rather to open up the line in the text widget and insert
# lines (the cascade) that are indented from the beginning of the
# line that requests the cascade (much like the directory section
# at the left of the windows explorer window).
#
#
# A textmenu is created with the command:
#
# textmenu name ?options?
#
# where "name" is a path name for the window and should be a legal window
#              (save the '.<nam>' at the end. If the part of the name
#              before the last dot is a toplevel window a toplevel menu
#              is built. If it is 3052not toplevel a widget menu is built which
#              will be grided into place (should we let the called do this?).
# options are the same as for a "text" & "menu" together with the
#              following exceptions:
#
#        Only top level menus support the -tearoff, -tearoffcommand and
#             -title options.
# TBD
#    ********** need info here on indicators and indention options ************
# Indicators:
#     Indicators are made by using special characters in the utf8 character set
#     We include a small sampling of what is available and a way to enter
#     the characters using the \u notation. These are always used in the
#     indicators for cascade, checkbutton and radiobutton. They may also be
#     used in the indention. The subset we have choosen is in the option
#     '-specialchars' (a read only option). These may be referenced in the
#     options -cascadeindicators, -checkbuindicators and -radiobuindicators.
#     Each of these indicator entries is a two element list where element 0
#     means, respectively, not expanded, off and not this one.
#     Each of these indicators is, itself, a list where the first element is
#     one of {up-carit down-carit right-carit box box-check circle eye
#            separator tearoff indent identbold literal}
#     followed by a number that indexes into the '-specialchars' list, except
#     for 'literal' which is followed by the character itself.
#     In the literal case the character may be any legal TCL character
#     representation includeing \u notation.
 
# The following commands are used by the scrolling code
# pathname xposition index
# pathname yposition index

# Scrolling options are:
#
# pathname configure -scrollsides ?<op>?   Where <op> is any combination of
#                                          the characters n,s,w,e and specifies
#                                          where scrollbars are to be located.
# pathname configure -autohide  ?<op>?     Where <op> is boolean and true means
#                                          scroll bars are hidden unless needed.
#
#
# In addition to the standard (somewhat curtailed) "text" commands
# the following "menu" commands are provided:
#
#
# <how the index is formed> TBD
#
# pathname activate index
# pathname add type ?option value option value...?
# pathname cget option
# pathname configure ?option? ?value option value ...?
# pathname delete index1 ?index2?
# pathname destroy
# pathname entryconfigure index ?options...?
# pathname index
# pathname insert index (differs from the text version)
# pathname post args (args are ignored)  posts a cascade menu.
# pathname postcascade index
# pathname type index
# pathname unpost          Unpost a cascade menu
#
# In the "add" command with type "cascade" the -menu option creates a new name
# for the cascade window. This name is used with the "add" command to
# fill in the cascade entries (just like the std. menu command)
#
# post args are ignored since each cascade menu is posted indented in the
#           menu window so it has a fixed position.
#
# The "destroy" command will destroy this window, however, it many not destroy
#               a cascade window. To destroy any part of the window, the
#               pathname destroy is the best option. This cleans up all the
#               loose ends.
#
# The following are the options we add for 'textmenu's
# these are our own options. Mostly these are standard menu ops
# These are the default value. If an entry does not have a value
# for an option, it should come from here.

# For command, cascade, checkbutton, radiobutton we have 2 new options
# unique to the 'textmenu' named '-tagbind' and '-tagconfig. Each entry
# in the menu has a tag. The tags for the above menu entries can be bound
# to a user script. The syntax is:
#
# TAGS:

# -tagbind {<event> {script} ?<event> {script}?}  I.e. binding can be for
#                                                 several <events>.
# Binding scripts may use the "%" codes describe in the 'tcl bind' manpage.
# In addition we have added the following codes:
#   %I is replaced by the tagname
#   %p is the pathname of the entry. (this is overloaded)
#   %n is the entry number (for pathname entryconfig, etc.)

#
# The tagconfig option is:

# -tagconfig {tag configure {options}}
#
# and accepts any of the 'text' widget tag configure options. Note that
# the appearence of the menu including font size, color (fg & bg) as well
# as other things can be changed.


#
# We support most of the standard "menu" ops as well as all the "text" widget
# "tag" ops.


# INDENTION:
#
# Indention is complicated. The simple indention is just a set of spaces
# with possible indicators (boxes for checkbox, circles for radiobuttons,
# open/close for cascade). More complicated indention includes lines and
# tees (for cascade). (More complicated yet is to put corners to the right
# on the bottom of a line, but not yet.)
# So, each level of a menu has an indention with 4 possibilities
#(check, radio, cascade and other). Also, the indicators need to be managed.
# Oh, and by the way, we use characters from the extended char. set so

# a.) you can choose what you like, and
# b.) they change with the font size.

# We use a fixed space font for the indention/indicators. (We derive this from
# the font used when the menu is first create.)
#
# So here is what we do. The initial menu call has the option -indent which
# ought to be at least 3 chars wide. By default this will be 3 spaces.
# We also have -indent options for (cascade, check & radio) which are
# lists of two characters (open, checked, selected) respectively. By default
# cascade indention is carried to the next cascade level, but it can be
# changed.
# Indention can be set to 'nil' (i.e. {}) which will work find if you avoid
# the cascade, check and radio entries (and is infact nil in this case).

# Modes: We have already mentioned that both a toplevel menu and an embedded
#        menu is supported.

#        Another mode is 'menu' mode and 'edit' mode.
#        Menu mode presents a menu and allows you to select cascades. It is
#        terminated and the window (if toplevel) is destroyed. (If it is not
#        toplevel, or is torn-off its disposition is up to user code.)
#
#        Selection: If a menu entry has no command associated with it selecting
#                   it with a right click (or other normal text selection
#                   activity) adds the entries id to a selection list.
# 'pathname curselection' returns this list.
#                   The list contains two elements for each selected entry,
#                   the 'menu' command that created it and its index.
#                   If '-exportselection' is true, the selction is exported
#                   as the selected '-label' entries seperated by linefeeds.
#                   This is also available with the
#  'pathname selection list' command.

#        Selection bindings: All selection operations only work with the mouse
#                            pointing to the label area (not the indention or
#                            indicators).
#                            <1> clears all existing selections and selects the
#                            entry under the mouse.
#                            Dragging <1> selects all passed over
#                            <Control-1> toggles the selection of the entry
#                            under the mouse.
#                            <Shift-1> extends the selection. (This extends
#                            the result of the last selection event which,
#                            if it was a toggle to off will extend that.
#
#                            The order of selection is kept and for duplicate
#                            selections the last entry will eliminate the
#                            earlier.
#
#        Edit mode differs in that all, except exempt, entries may be selected
#        i.e. the commands, if present are ignored. In this mode each 
#        menu entry has an opt-out option (-editmodeexempt), where rather than
#        affecting selection, the -command option is active.
#        This allows one to write rather simple routines to edit the menu,
#        delete items, add new, rearrange etc. all controlled by the
#        entries that have opt-out set.
#        The 'edit' mode can be set at any time.
#
# Notes:
# the pathname entryconfig n   command returns not only the user info on the
#                              entry but the working data for the entry.
#

#################################### debug stuff ##############################
if {1} {
  catch {destroy .m}
  catch {namespace delete textmenu}
  catch {namespace delete textMenu }
  catch {namespace delete menulist}
  catch {namespace delete snit}
  catch {interp delete [interp slaves]}

  package forget snit
}
################################### end of debug stuff ########################
package require snit
package require scrodget
  
# The 'textmenu' command (a simple proc) kicks things off by checking the
# requested menu name to determine if the menu is to be a toplevel window.
# If so it calls the 'snit' 'TextMenu' widget which handles all the toplevel
# options (-title, -tearoff, -tearoffcommand) and the calls the 'snit'
# 'textMenu' widgetadaptor.

# If the window is not a toplevel window, 'textmenu' directly calls 
# the 'snit' 'textMenu' widgetadaptor.

# 'textMenu' installs both the 'text' window and the 'snit' widget 'scrodget'
# which handles scrolling of the text window.
#
# 'textMenu' provides a set of constant options (the defaults) which both
# define what is allowed as well as their defaults. Each instance of
# 'textMenu' provides a set of options that are first configured by merging
# the 'Menu' options, the 'text' options and the user options. 'textMenu'
# also directs user requests to the 'text' widget, 'scrodget' or the actual
# menu code which is a 'snit' 'type' called 'menulist'.

# Mostly we intend 'textmenu' to provide something that is as menu like
# as possible. This means, among other things, that it is a toplevel window
# which, by default, has no border and is posted where it is desired. Also
# we honor the tearoff menu option. We also allow it to be a widget that is
# included (grid or pack) in a parent window.
#
#
# in both cases, the returned value from 'textmenu' is used to address the
# window.

# Stand alone, windows can either have a border or not. This is usually 
# called tearoff.
# Tearoff can be added using the -tearoff configure option.
# You can also define '-tearoff' and '-tearoffcommand' as in normal windows.
# You can NOT, however, tearoff a parented window.

# Option inheritance is as follows:
# Options that apply to menu entries passed to the "textmenu" command  or
# by configure requests to this level will
# be inherited by first level  entries, including cascade
# entries. These may be overridden by options supplied by the "add",
# "insert" or "entryconfigure" pathname commands.
# Entries below a "cascade" entry (set up by "add", "insert") inherit
# the options the "cascade" entry has when the entry is added or inserted.

# Menu width and height are set as needed when ever a post is done. The
# option -maxwidth and -maxheight set the maximum width or heigth that
# will be allowed. If -autohide 1 a vertical
# scroll bar will be added when the heigth exceeds the -maxheight. Likewise
# if a horizontal
# scroll bar will be added when the width exceeds the -maxwidth.
#
# The mouse wheel will also be set to do vertical scrolling and the mouse
# button <2> hold will scroll in both axes.
#
# as part of the initial set up we collect the current default values
# for the various options.

########################### Data Templates and steering info ##################

# We put all our structure and fixed value constructs in
# snit::macro definitions. This allows us to have the definition
# in one place and then use them to build typevariables and delegate
# things from one definition.

# inferior Commands are the working Menu commands. We pass these to
# an inferior snit 'type' 'menulist'.


snit::macro inferiorCommands {} {
  return {
    activate
    add
    debug
    delete
    entrycget
    entryconfig
    entryconfigure
    index
    invoke
    insert
    post
    postcascade
    type
    unpost
  }
}
################################# Option management ##############

# Options options OPTIONS  lets see if we can organize them...
# First, lets let the snit code apply them,,, except those that we
# only allow at turn on (i.e. in the command line), which, of course,
# means we need to separate them (we throw an error if not).

# scrodget option defaults...
snit::macro scrodgetOptions {} {
  return {
    -autohide       1
    -scrollsides    se
  }
}
# Options we do NOT want from the text widget
# These ops are forced and unsupported...
# Forced means we override any user attempt to mess with them.
# We also claim them and report them as not existing
# (I have not yet figured out how to tell Snit that they are illegal.)
# trap these:

 
snit::macro trapOps {} {
  return {
    -endline           {}
    -startline         {}
    -undo              0
    -wrap              none
  }
}


# These options in the menuOps list we want to pass
# to the text widget 
# The following ops are copied from the standard 'menu' value to
# the text window, i.e. we want to pick up the 'menu' defaults for these.
 

snit::macro textOptionsDefaults {} {
  return {
    -background        {}
    -borderwidth       {}
    -cursor            {}
    -font              {}
    -foreground        {}
    -insertunfocussed  hollow
    -insertofftime     0  
    -insertwidth       20 
    -insertbackground  red
    -relief            {}
  }
}

snit::macro fontOp {} {return -font}

# tagOptions are text widget options we realize with tags in 'text' widget
# we want to track them so we can build a set of tag options ('tagBase')
# that mimics the normal text menu. We use this definition to except 
# these options from the hull and provide a method to relay them to the
# hull while triggering a rebuild of the 'tagBase'.

snit::macro tagOptionsDefaults {} {
  return {
    -foreground         {}
    -background         {}
    -borderwidth        {}
    -font               {}
    -relief             {} 
  }
}

snit::macro tagOptions {} {return [opsOnly [tagOptionsDefaults]]}


############################### end of text and scrodget opts ##########

# All the following ops are only used in the 'menulist' type
# There are three types here:
# 1.) ops used in tag configuration
# 2.) global menu ops which become defaults for
# 3.) menu entry ops which are arranged by entry type



# 'textmenuOps' are given this default and updated with the
# cascade ops when an add cascade command is processed.
# These are private ops and only appear in the 'menulist' type

# indention is what the user wants to fill the indention to
#              move a cascade posting to the right
# masterindent is a flag that indicates the there is one or
#              more entries of type cascade, radiobutton or checkbutton
#              this means that entries of type command and seperator
#              should be moved to the right so the labels line up.
#              This is only in the root entry. Cascade entries will
#              always have this indention (to accommodate the cascade flag)

# masterindentsize is the number of character to use for the
#                  indicator part of the cascade, radiobutton or checkbutton
#                  it must be at least 1. It is defaulted to 3

snit::macro textmenuOps {} {return [opsOnly [textmenuOpsDefaults]]}
  
snit::macro textmenuOpsDefaults {} {
  return {
    -active              {none}
    -activeforeground    {}
    -activebackground    {}
    -activeborderwidth   {}
    -cascadeindicators   {{right-carit 1} {down-carit 1}}
    -checkboindicators   {{box 0} {box-check 0}}
    -entrycount          0
    -editmode            0
    -entrywindowops      {}
    -indention           {{indent 0}  }
    -indentionfont       {}
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -indenttagconfig     {}
    -indicatorsize       3
    -masterindent        0
    -masterindentsize    3
    -maxheight           {}
    -maxwidth            {}
    -multipleopen        0
    -postcommand         {}
    -radiobuindicators   {{circle 0} {eye 0}}
    -toplevel            {}
  }
}

# Changes to the following ops require a relayout.
snit::macro textmenuRepostOps {} {
  return {
    -activeforeground
    -activebackground 
    -activeborderwidth
    -cascadeindicators
    -checkboindicators 
    -entrywindowops
    -indention
    -indentionfont
    -indicator1tagconfig
    -indicator2tagconfig
    -indenttagconfig
    -indicatorsize
    -masterindent
    -masterindentsize
    -multipleopen
    -postcommand
    -radiobuindicators
  }
}



# These ops do NOT need to be propagated to all current levels
# (this should be a subset of the above textmenuOps)
# note that readonly ops need not be considered here

snit::macro noPropTextmenuOps {} {
  return {
    -active
    -entrycount 
    -postcommand 
    -toplevel
  }
}
# Ops that the Text widget has that we want it to keep.

snit::macro saveTextOps {} {
  return {
    -selectcolor
    -selectforeground
    -selectbackground
  }
}

# Special case the font to allow our own code

snit::macro specialChars {} {
  # use subst here to convert the specialchars to the real thing.
  return [list -specialchars \
	  [subst {
	    up-carit    " \u2038 \u2bc5 \u2b99 \u2b9d "
	    down-carit  " \u2228 \u25bc \u2b9b \u2B9F \u229F"
	    right-carit " > \u25b6 \u2B9A \u2B9E \u229e"
	    box         " \u25a1 \u2610 \u2b1c"
	    box-check   " \u2612  \u25a3 \u25fc \u2bf3 \u2612"
	    circle      " \u25ef \u20dd \u2b55"
	    eye         " \u25c9 \u2b59"
	    separator   " \u2500 \u2501"
	    tearoff     " -"
	    indent      " \u2502 \u251c \u2514"
	    indentbold  " \u2503 \u2523 \u2517"
	  }]]
}

# A place to get utf8 special chars
# https://www.utf8-chartable.de/unicode-utf8-table.pl?start=9472&unicodeinhtml=dec

# should have been able to just use these in the {  } but, no...
# up-carit    "\u2038" ; # ‸ \u2bc5 -> ⯅ \u2b99 -> ⮙ \u2b9d -> ⮝
# down-carit   "\u2228" -> ∨ \u2bc6 -> ⯆ \u2b9b -> ⮛ \u2B9F -> ⮟ \u229F -> ⊟
# right-carit ">"      ; # > \u2bc8 -> ⯈ \u2B9A -> ⮚ \u2B9E -> ⮞ \u229e -> ⊞ 
# box         "\u25a1" ; # □ \u2610 -> ☐ \u2b1c -> ⬜ 
# box-check   "\2612"  ; # ☒ \u25a3 -> ▣ \u2bf3 -> ⯳ \u25fc -> ◼ \u2612 -> ☒
# circle      "\u25ef" ; # ◯ \u20dd -> ⃝ \u2b55 -> ⭕
# eye         "\u25c9" ; # ◉ \u2b59 -> ⭙  
# separator   "\u2500" ; # ─ \u2501 -> ━
# tearoff     "-"      ; # -
# indent      "\u2502" ; # │ \u251c  -> ├ \u2514 -> └
# indentbold  "\u2503" ; # ┃ \u2523  -> ┣ \u2517 -> ┗
#  others   \u22B9 -> ⊹ \u25ba -> ►


# These are menu options which are applied via bind and/or tag
# The following groups will depend on the menu "type"
# The comments are possible additions...

# -accelerator       ;# not supported (yet?)
# -bitmap            ;#
# -compound          ;# 
# -image             ;# 
# -state             ;# not supported
# -underline         ;# must use bind on the char not supported

# The following are the options we add for 'textmenu's
# these are our own options. Mostly these are standard menu ops
# These are the default value. If an entry does not have a value
# for an option, it should come from here.

snit::macro commandOps {} {
  return {
    -command {}
    -label   {}
    -tagbind {}
    -type    "command"
  }
}

snit::macro cascadeOps {} {
  return {
    -command             {}
    -entrycount          0
    -masterindent        0
    -cascadeindicators   {}
    -indicatorsize       3
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -indicatoron         1
    -menu                {}
    -indention           {}
    -indentionfont       {}
    -indenttagconfig     {}
    -multipleopen        0
    -parent              {}
    -post                0
    -postcommand         {}
    -tags                {}
    -type                "cascade"
  }
}

# These are the minimum ops that must be set in 'menulist's args
# that are not set in the cascad
snit::macro minLocalOps {} {
  return {
    -toplevel 
    hull
    instVault
  }
}

snit::macro checkbuttonOps {} {
  return {
    -checkbuindicators   {}
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -command             {}
    -indicatoron         1
    -indicatortag        {}
    -label               {}
    -offvalue            0
    -onvalue             1
    -selectcolor         {}
    -type                "checkbutton"
    -variable            {}   
  }
}
snit::macro radiobuttonOps {} {
  return {
    -command             {}
    -indicatoron         0
    -indicatortag        {}
    -label               {}
    -radiobuindicators   {}
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -selectcolor         {}
    -type                "radiobutton"
    -value               {}
    -variable            {}
  }
}
snit::macro separatorOps {} {
  return {
    -foreground "black"
    -label      {}
    -state      "normal"
    -type       "separator"
  }
}

snit::macro tearoffOps {} {
  return {
    -command        {}
    -state          "normal"
    -tearoffcommand {}
    -type           "tearoff"
    -foreground     "black"
  }
}

# commonOps are ops that are common to all menu entry types except:
# -entrywindowname   {} separator and tearoff.
# Note: while -type is common, its value is not.


snit::macro commonOps {} {
  return {
    -state             "normal"
    -label             {}
    -entry             0
    -entrywindowops    {}
    -font              {}
    -activebackground  {}
    -activeforeground  {}
    -activeborderwidth {}
    -foreground        {}
    -background        {}
    -borderwidth       {}
    -editmodeexempt    0
    -selectcolor       {}
    -tagbind           {}
    -tagconfig         {}
  }
}

######################### the following list is used to define the
#                         verify code for each op that we verify

# The opsVerify list provides the type for each entry. If an op is not
# in this list but is in one of the 'menu' ops, it has no restrictions.
# The restrictions are: boolean, integer, stringtype, list, color

snit::macro opsVerify {} {
  return {
    -active                     {readonlyTest  %s}
    -activebackground           {colorTest     %s}
    -activeforeground           {colorTest     %s}
    -activeborderwidth          {snit::integer %s -min 0 -max 10}
    -background                 {colorTest     %s}
    -borderwidth                {integerTest   %s -min 0 -max 10}
    -indenttagconfig            {tagconfigTest {%s}}
    -indicator1tagconfig        {tagconfigTest {%s}}
    -indicator2tagconfig        {tagconfigTest {%s}}
    -disabledforeground         {colorTest     %s}
    -entry                      {booleanTest   %s}
    -entrycount                 {readonlyTest  %s}
    -entrywindowname            {readonlyTest  %s}
    -entrywindowops             {tagconfigTest {%s}}
    -editmode                   {booleanTest   %s}
    -editmodeexempt             {booleanTest   %s}
    -font                       {fontTest      %s}
    -foreground                 {colorTest     %s}
    -indenttagconfig            {tagconfigTest {%s}}
    -indentionfont              {fontTest      %s}
    -indicatoron                {booleanTest   %s}
    -indicatorsize              {integerTest   %s -min 1 -max 10}
    -locationtag                {readonlyTest  %s}
    -multipleopen               {booleanTest   %s}
    -masterindent               {readonlyTest  %s}
    -parent                     {readonlyTest  %s}
    -post                       {readonlyTest  %s}
    -selectcolor                {colorTest     %s}
    -selectbackground           {colorTest     %s}
    -specialchars               {readonlyTest  %s}
    -state                      {enumTest      %s {normal active disabled}}
    -tags                       {readonlyTest  %s}
    -tagbind                    {tagbindTest   {%s}}
    -tagconfig                  {tagconfigTest {%s}}
    -tearoff                    {readonlyTest  %s}
    -toplevel                   {readonlyTest  %s}
    -type                       {readonlyTest  %s}
  }
}

# The following is a list of all the 'text' options that we want to pass
# to 'peer's on tearoff.

snit::macro textTearOffOps {} {
  return {
    -autoseparators
    -background
    -blockcursor
    -borderwidth
    -cursor
    -endline
    -exportselection
    -font
    -foreground
    -height
    -highlightbackground 
    -highlightcolor
    -highlightthickness
    -inactiveselectbackground
    -insertbackground
    -insertborderwidth
    -insertofftime
    -insertontime
    -insertunfocussed
    -insertwidth
    -maxundo
    -padx
    -pady
    -relief
    -selectbackground
    -selectborderwidth
    -selectforeground
    -setgrid
    -spacing1
    -spacing2
    -spacing3
    -startline
    -state
    -tabs
    -tabstyle
    -takefocus
    -undo
    -width
    -wrap
  }
}
# This list names all the above definitions that should be in our array.

#    offLimitOps
#    passThrough
#    menuOnly
#    disInherit
#    bindTranslateTable
#    textmenuTopLevelDefaults

snit::macro arrayParts {} {
  return {
    cascadeOps
    checkbuttonOps
    commandOps
    commonOps
    inferiorCommands
    minLocalOps
    noPropTextmenuOps
    opsVerify
    radiobuttonOps
  }
}

#    -tearoff                      0
# -foreground          {}
# -background          {}
# -borderwidth         {}
# -cursor              {}
# -relief              {}
# -selectforeground    {}
# -selectbackground    {}

# Special case the font to allow our own code

snit::macro specialChars {} {
  # use subst here to convert the specialchars to the real thing.
  return [list -specialchars \
	  [subst {
	    up-carit    " \u2038 \u2bc5 \u2b99 \u2b9d "
	    down-carit  " \u2228 \u25bc \u2b9b \u2B9F \u229F"
	    right-carit " > \u25b6 \u2B9A \u2B9E \u229e"
	    box         " \u25a1 \u2610 \u2b1c"
	    box-check   " \u2612  \u25a3 \u25fc \u2bf3 \u2612"
	    circle      " \u25ef \u20dd \u2b55"
	    eye         " \u25c9 \u2b59"
	    separator   " \u2500 \u2501"
	    tearoff     "{ -}"
	    indent      " \u2502 \u251c \u2514"
	    indentbold  " \u2503 \u2523 \u2517"
	  }]]
}

# A place to get utf8 special chars
# https://www.utf8-chartable.de/unicode-utf8-table.pl?start=9472&unicodeinhtml=dec

# should have been able to just use these in the {  } but, no...
# up-carit    "\u2038" ; # ‸ \u2bc5 -> ⯅ \u2b99 -> ⮙ \u2b9d -> ⮝
# down-carit   "\u2228" -> ∨ \u2bc6 -> ⯆ \u2b9b -> ⮛ \u2B9F -> ⮟ \u229F -> ⊟
# right-carit ">"      ; # > \u2bc8 -> ⯈ \u2B9A -> ⮚ \u2B9E -> ⮞ \u229e -> ⊞ 
# box         "\u25a1" ; # □ \u2610 -> ☐ \u2b1c -> ⬜ 
# box-check   "\2612"  ; # ☒ \u25a3 -> ▣ \u2bf3 -> ⯳ \u25fc -> ◼ \u2612 -> ☒
# circle      "\u25ef" ; # ◯ \u20dd -> ⃝ \u2b55 -> ⭕
# eye         "\u25c9" ; # ◉ \u2b59 -> ⭙  
# separator   "\u2500" ; # ─ \u2501 -> ━
# tearoff     "-"      ; # -
# indent      "\u2502" ; # │ \u251c  -> ├ \u2514 -> └
# indentbold  "\u2503" ; # ┃ \u2523  -> ┣ \u2517 -> ┗
#  others   \u22B9 -> ⊹ \u25ba -> ►


# These are menu options which are applied via bind and/or tag
# The following groups will depend on the menu "type"
# The comments are possible additions...

# -accelerator       ;# not supported (yet?)
# -bitmap            ;#
# -compound          ;# 
# -image             ;# 
# -state             ;# not supported
# -underline         ;# must use bind on the char not supported

# The following are the options we add for 'textmenu's
# these are our own options. Mostly these are standard menu ops
# These are the default value. If an entry does not have a value
# for an option, it should come from here.

snit::macro commandOps {} {
  return {
    -command {}
    -label   {}
    -tagbind {}
    -type    "command"
  }
}

snit::macro cascadeOps {} {
  return {
    -command             {}
    -entrycount          0
    -masterindent        0
    -cascadeindicators   {}
    -indicatorsize       3
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -indicatoron         1
    -menu                {}
    -indention           {}
    -indentionfont       {}
    -indenttagconfig     {}
    -multipleopen        0
    -parent              {}
    -post                0
    -postcommand         {}
    -tags                {}
    -type                "cascade"
  }
}

# These are the minimum ops that must be set in 'menulist's args
# that are not set in the cascad
snit::macro minLocalOps {} {
  return {
    -toplevel 
    hull
    instVault
  }
}

snit::macro checkbuttonOps {} {
  return {
    -checkbuindicators   {}
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -command             {}
    -indicatoron         1
    -indicatortag        {}
    -label               {}
    -offvalue            0
    -onvalue             1
    -selectcolor         {}
    -type                "checkbutton"
    -variable            {}   
  }
}
snit::macro radiobuttonOps {} {
  return {
    -command             {}
    -indicatoron         0
    -indicatortag        {}
    -label               {}
    -radiobuindicators   {}
    -indicator1tagconfig {}
    -indicator2tagconfig {}
    -selectcolor         {}
    -type                "radiobutton"
    -value               {}
    -variable            {}
  }
}
snit::macro separatorOps {} {
  return {
    -foreground "black"
    -label      {}
    -state      "normal"
    -type       "separator"
  }
}

snit::macro tearoffOps {} {
  return {
    -command        {}
    -state          "normal"
    -tearoffcommand {}
    -type           "tearoff"
    -foreground     "black"
  }
}

# commonOps are ops that are common to all menu entry types except:
# -entrywindowname   {} separator and tearoff.
# Note: while -type is common, its value is not.


snit::macro commonOps {} {
  return {
    -state             "normal"
    -label             {}
    -entry             0
    -entrywindowops    {}
    -font              {}
    -activebackground  {}
    -activeforeground  {}
    -activeborderwidth {}
    -foreground        {}
    -background        {}
    -borderwidth       {}
    -editmodeexempt    0
    -selectcolor       {}
    -tagbind           {}
    -tagconfig         {}
  }
}

######################### the following list is used to define the
#                         verify code for each op that we verify

# The opsVerify list provides the type for each entry. If an op is not
# in this list but is in one of the 'menu' ops, it has no restrictions.
# The restrictions are: boolean, integer, stringtype, list, color

snit::macro opsVerify {} {
  return {
    -active                     {readonlyTest  %s}
    -activebackground           {colorTest     %s}
    -activeforeground           {colorTest     %s}
    -activeborderwidth          {snit::integer %s -min 0 -max 10}
    -background                 {colorTest     %s}
    -borderwidth                {integerTest   %s -min 0 -max 10}
    -indenttagconfig            {tagconfigTest {%s}}
    -indicator1tagconfig        {tagconfigTest {%s}}
    -indicator2tagconfig        {tagconfigTest {%s}}
    -disabledforeground         {colorTest     %s}
    -entry                      {booleanTest   %s}
    -entrycount                 {readonlyTest  %s}
    -entrywindowname            {readonlyTest  %s}
    -entrywindowops             {tagconfigTest {%s}}
    -editmode                   {booleanTest   %s}
    -editmodeexempt             {booleanTest   %s}
    -font                       {fontTest      %s}
    -foreground                 {colorTest     %s}
    -indenttagconfig            {tagconfigTest {%s}}
    -indentionfont              {fontTest      %s}
    -indicatoron                {booleanTest   %s}
    -indicatorsize              {integerTest   %s -min 1 -max 10}
    -locationtag                {readonlyTest  %s}
    -multipleopen               {booleanTest   %s}
    -masterindent               {readonlyTest  %s}
    -parent                     {readonlyTest  %s}
    -post                       {readonlyTest  %s}
    -selectcolor                {colorTest     %s}
    -selectbackground           {colorTest     %s}
    -specialchars               {readonlyTest  %s}
    -state                      {enumTest      %s {normal active disabled}}
    -tags                       {readonlyTest  %s}
    -tagbind                    {tagbindTest   {%s}}
    -tagconfig                  {tagconfigTest {%s}}
    -tearoff                    {readonlyTest  %s}
    -toplevel                   {readonlyTest  %s}
    -type                       {readonlyTest  %s}
  }
}

# The following is a list of all the 'text' options that we want to pass
# to 'peer's on tearoff.

snit::macro textTearOffOps {} {
  return {
    -autoseparators
    -background
    -blockcursor
    -borderwidth
    -cursor
    -endline
    -exportselection
    -font
    -foreground
    -height
    -highlightbackground 
    -highlightcolor
    -highlightthickness
    -inactiveselectbackground
    -insertbackground
    -insertborderwidth
    -insertofftime
    -insertontime
    -insertunfocussed
    -insertwidth
    -maxundo
    -padx
    -pady
    -relief
    -selectbackground
    -selectborderwidth
    -selectforeground
    -setgrid
    -spacing1
    -spacing2
    -spacing3
    -startline
    -state
    -tabs
    -tabstyle
    -takefocus
    -undo
    -width
    -wrap
  }
}
# This list names all the above definitions that should be in our array.

#    offLimitOps
#    passThrough
#    menuOnly
#    disInherit
#    bindTranslateTable
#    textmenuTopLevelDefaults

snit::macro arrayParts {} {
  return {
    cascadeOps
    checkbuttonOps
    commandOps
    commonOps
    inferiorCommands
    minLocalOps
    noPropTextmenuOps
    opsVerify
    radiobuttonOps
    saveTextOps
    scrodgetOptions
    separatorOps
    specialChars
    tagOptions
    tagOptionsDefaults
    tearoffOps
    textOptionsDefaults
    textmenuOps
    textmenuOpsDefaults
    textmenuRepostOps
    textTearOffOps
    trapOps
  }
}


################################# end of data templates #####################

################################ macros to build the widgetadaptor ##########

# The 'typeDef' macro is used to build the typevariable(s) #################

snit::macro typeDef {what with} {
  eval $what \{ $with \{[$with] \}\}
}

# The 'buildArray' macro builds our constant array given a list of its parts

snit::macro buildArray {parts} {
  foreach part $parts {
    typeDef {typevariable {} -array} $part
  }
}

# The 'opsOnly' macro takes an {options value} list and returns the options

snit::macro opsOnly {name} {
  foreach {op val} $name {
    lappend newlist $op
  }
  return $newlist
}

# The 'optionSet' macro builds an option entry for each {op val} pair in
# 'optionList'

snit::macro optionSet {optionList method cgmethod} {
  foreach {op val} $optionList {
    option $op \
	-configuremethod $method\
	-cgetmethod $cgmethod\
	-default $val
  }
}

# The dead letter version...
snit::macro deadOptionSet {optionList} {
  foreach {op val} $optionList {
    option $op\
	-readonly 1\
	-default {option "$op" does not exist}\
	-cgetmethod deadCget\
	-configuremethod deadConfig
  }
}
    

snit::macro delegateto {what to} {
  foreach elm $what {
    delegate method $elm to $to
  }
}

################################# end of macros #####################

################################## textMenu widgetadaptor ####################
::snit::widgetadaptor textMenu {
  
  # This code implements the 'textmenu' selection management.  All this code
  # will be in the '::textMenu' namespace and is driven by bindings to 
 
  # tags in the underlying 'text' window. The basic managment of 'textmenu'
  # selections is similar to the 'listbox' windows selection management
  # with the 'extended mode' for the mouse.
  # For button management of selection we go in our own direction.
  # ########### Movement only ##############
  # <Key-Right>          scrolls window to the right 1 character
  # <Key-Left>           scrolls window to the left 1 character
  # <Shift-Key-Right>    scrolls window fully to the right 
  # <Shift-Key-Left>     scrolls window fully to the left
  # <Key-Up>             scrolls cursor up 
  # <Key-Down>           scrolls cursor down 
  # <Home>               scrolls cursor to the top line
  # <End>                scrolls cursor to the last line

  # ########### Selection only ############
  # <Key-space>          set selection anchor at the current cursor line
  # <Control-Space>      toggels selection of cursor line
  # <Shift-Space>        extends selection to cursor line
  
  ############# Move & selection ##########
  # <Control-Home>       scrolls to the top line and selects only that line
  # <Control-End>        scrolls to the last line and selects only that line
  # <Control-Shift-Home> scrolls to the top line extending the selection
  # <Control-Shift-End>  scrolls to the last line extending the selection
  
  # <Shift-up/down>      scrolls & extends the selection (like <Shift-1>)
  # <Control-up/down>    toggeles cursor line & scrolls to a new line

  
  # We id tagged entries by line number (starting at line 1). The additional
  # constraint is that we want to produce the tagged lines in the order
  # selected. We use the left/right arrow keys to scroll the window
  # (left & right).

  # Here is a routine to set up all the bindings.
  
  proc setSelectBindings {w} {
 
    # Movement keys
    bind $w <Home>          {::textMenu::simpleHomeEnd  %W 1.0}
    bind $w <End>           {::textMenu::simpleHomeEnd  %W {end-2 lines}}
    bind $w <<PrevChar>>    {%W xview scroll -1 unit}  ;# Key-Left
    bind $w <<NextChar>>    {%W xview scroll  1 unit}  ;# Key-Right
    bind $w <<NextLine>>    {::textMenu::UpDown %W 1}  ;# Key-Down
    bind $w <<PrevLine>>    {::textMenu::UpDown %W -1} ;# Key-Up
    
    # Selection mouse
    bind $w <1>           {::textMenu::beginSelect %W [%W masterindex current]}
    bind $w <B1-Motion>   {::textMenu::Motion      %W [%W masterindex @%x,%y]}
    bind $w <Control-1>   {::textMenu::beginToggle %W [%W masterindex current]}
    bind $w <Shift-1>     {::textMenu::beginExtend %W [%W masterindex current]}
    # Selection keys
    bind $w <Key-space>   {::textMenu::beginSpace  %W [%W masterindex insert]}
    bind $w <Shift-Key-space> \
	                  {::textMenu::Motion      %W [%W masterindex insert]}
    bind $w <Control-Key-space> \
	                  {::textMenu::beginToggle %W [%W masterindex insert]}
    
    # Selection and movement

    bind $w <Control-Home>       {::textMenu::homeEnd %W 1}
    bind $w <Control-End>        {::textMenu::homeEnd %W end}
    bind $w <Control-Shift-End>  {::textMenu::dataExtend %W\
				      [%W selectindex end]}
    bind $w <Control-Shift-Home> {::textMenu::dataExtend %W 1}
    bind $w <<SelectPrevLine>>   {::textMenu::ExtendUpDown %W -1} ;#Shift-Key-U
    bind $w <<SelectNextLine>>   {::textMenu::ExtendUpDown %W 1}  ;#Shift-Key-D
    bind $w <<SelectAll>>        {%W selection set 1 end}         ;#Control-/
    bind $w <<SelectNone>>       {%W selection clear 1 end}       ;#Control-\ .
    bind $w <<Selection>>        {::textMenu::lostSelection %W}

    # Execution

    bind $w <Return>             {::textMenu::exReturn %W}
  }
  
  # The instance array 'v' contains the selection info, i.e.:
  # v(mouseMoved) is cleared on <1> and set in Motion to allow us to defer
  #               selection until after movement.
  # v(anchor)     is the current anchor
  # v(Prev)       is used only here to help manage the selection(s)
  # v(Selection)  is a list (in selection order) of the current selection
  #
  # '$w selection anchor' Queries and sets v(anchor)
  # '$w selection clear and set' change v(selection) (and do other management)

  # The simpleHomeEnd moves the insert to Home/End and makes it visible, only.
  
  proc simpleHomeEnd {w el} {
    $w mark set insert $el
    # $w mark set insert [lsearch -inline [$w tag names $el] *user].first
    $w see $el
  }
  
  # On <1> && window exists.. 'beginSelect' window-name index(line.char)

  # beginSelect called on <1>.
  # Move cursor & keep track of where we are. This patterned on 'text'
  # window mouse, however, I don't see there how the selection is cleared.
  
  proc beginSelect {w el args} {
    variable [$w instance]::v
    frputs "[array get v] " el
    # only do this if in the label area
    set utag [lsearch -inline [$w tag names [expr {int($el)}].end] *priv]
    # if no user tag here, we are not in the active area of the window
    # or selection is not in play here (i.e. <1> is doing something else)
    if {$utag == {} || [$w tag bind $utag <1>] != {}} {return 0}
    
    # In active area, move the insert mark
    $w mark set insert $utag.first
    # If not in the text area we are done
    if {[$w compare $el < $utag.first]} {return 0}
    
    $w selection clear
    set v(mouseMoved) 0
    set v(Prev) $el
    $w selection anchor $el
  
    # Allow focus in any case on Windows, because that will let the
    # selection be displayed even for state disabled text widgets.
    if {$::tcl_platform(platform) == "windows" \
	    || [$w cget -state] eq "normal"} {
      focus $w
      $w see insert
    }
    return 1
  }
  
  # beginSpace handles the "space" key which:
  #   does every thing 'beginSelect' does and then
  #   sets the selection anchor and
  #   selects the line under the cursor (insert mark)
  #   
  proc beginSpace {w el} {
    variable [$w instance]::v
    if {[beginSelect $w $el]} {
      incr v(mouseMoved)
      $w selection set $v(Prev)
      $w selection anchor $v(Prev)
    }
  }
  #
  # on motion..
  # Again, a cut down version of text TextSelectTo
  # bind tag <B1-Motion> {
  #                      Motion %W [%W masterindex current]}
  

  proc Motion {w el} {
    variable [$w instance]::v
    frputs "[array get v] " el
    # no movement...
    # frputs v(mouseMoved) el v(Prev)
    if {$el == $v(Prev)} {return}
    # if {![info exists v(mouseMoved)]} {
    #   set v(mouseMoved) 0
    # }
    if {!$v(mouseMoved)} {
      # If this is <Shift-Key-space> with no previous begin, ignor it.
      if {$v(Prev) == {}} {return}
      incr v(mouseMoved)
      $w selection clear
      $w selection set $v(Prev)
      $w selection anchor $v(Prev)
    }
    # Lets cut our overhead. Insist that the movement is to a new line
    if {int($el) == int($v(Prev))} {return}
    if {[catch {
      $w mark set insert\
          [lsearch -inline [$w tag names [$w selectindex $el].end] *user].first
    }] > 0} {return}
    
    set anchor [$w selection anchor]
    # frputs el v(Prev) anchor v(anchor) v(Selection)
	
    
    if {$v(Prev) eq ""} {
      set el [set i [$w selection set $el]]
    } else {
      # run thru selectindex to check bounds
      set i [$w selectindex $v(Prev)]
    }
    set v(Prev) $el
    if {[$w selection includes anchor]} {
      $w selection clear $i $el
      $w selection set anchor $el
    } else {
      # frputs v(Selection) anchor
      $w selection clear $i $el
      $w selection clear anchor $el
    }
    set last $v(Selection)
    while {($i < $el) && ($i < $anchor)} {
      if {$i in $last} {
	$w selection set $i
      }
      incr i
    }
    while {($i > $el) && ($i > $anchor)} {
      if {$i in $last} {
	$w selection set $i
      }
      incr i -1
    }
  }
  
  # and now the toggle
  
  # bind tag <Control-1> beginToggle %W [%W index  current]

  proc beginToggle {w el} {
    variable [$w instance]::v
    set el [expr {int($el)}]
  ## frputs el
    # set v(Selection) [$w curselection]
    set v(Prev) $el
    $w selection anchor $el
    if {[$w selection includes $el]} {
      $w selection clear $el
    } else {
      $w selection set $el
    }
  }
  # and extend the selection
  
  # bind tag <Shift-1> beginExtend %W [%W index current]

  proc beginExtend {w el} {
    variable [$w instance]::v
    if {(!$v(mouseMoved) && $v(Prev) != {}) || [$w selection includes anchor]} {
      Motion $w $el
    } else {
      # No selection yet; simulate the begin-select operation.
      beginSelect $w $el
    }
  }

  # Implements the Control-Home and Control-End selections

  proc homeEnd {w el} {
    variable [$w instance]::v
    $w selection clear
    set el [$w selection set $el]
    $w mark set insert [lsearch -inline [$w tag names $el.end] *user].first
    $w see $el.end
  }

  # UpDown --
  #
  # This has two forms depending on editmode
  #
  # if 'editmode' is false (0) this works with the active entry
  #
  # otherwise we work with select entries
  #
  # Moves the active entry or location cursor (insert mark)  up or down by
  # one element and changes the selection if editmode on.
  #
  #
  # Arguments:
  # w -		The textMenu widget.
  # amount -	+1 to move down one item, -1 to move back one item.

  proc UpDown {w amount} {
    variable [$w instance]::v
    frputs v(Selection) v(Prev) v(anchor) v(activeTag) v(mouseMoved)

    if {![$w cget -editmode]} {
      # This is a bit messy as we really don't know the line number
      set end [expr {int ([$w masterindex end]) - 2}]
      set line [lindex [$w tag ranges $v(activeTag)] 0]
      # make sure we have a line number and there is more than one menu entry
      if {$end <= 1 } {return}
      if {$line == {}} {
        set line 1
      }
      set line [expr {int($line) + $amount}]
      # test for wrap around
      
      if {$line < 1} {
        set line $end
      }
      if {$line > $end} {
        set line 1
      }
      set tags [$w tag names $line.end]
      foreach tag $tags {
        if {[string match "*priv" $tag]} {
          set cmd [$w tag bind $tag <Enter>]
          $w see $line.0
          # we need to wait till after the 'see' as it moves the relative mouse
          # which will activate where ever that is.
          after 1 $cmd
          return
        }
      }
    }
                      
    # set new [$w selectindex [expr {int([$w masterindex insert]) + $amount}]]
    if {[set new [$w index  active]] == "none"} {
     set new 0
    }
    frputs new amount "[$w active] "
    incr new $amount
    # while {[set tmp [lsearch -inline [$w tag names $new.end] *user]] == {}} {
    #   if {[incr new $amount] >= 0} {continue}
    #   return
    # }
    
    # skip separators and wrap beginning/end
    frputs new
    while {1} {
      if {$new < 0} {
        incr failSafe
        set new [$w index end]
      }
      if {$new > [$w index end]} {
        incr failSafe
        set new 0
      }
      # this keeps us from hanging in menues with nothing but separators
      if {[incr failSafe] > 2} {return}
     
      if {[$w type $new] == "separator"} {
        incr new  $amount
      } else {
        break
      }
    } 
    frputs new
    $w activate $new
    $w see ${new}.0
    if {0} {
      $w selection clear 1 end
      $w selection set $new
      $w mark set insert $tmp.first
      $w see insert
    }
  }

  # extendUpDown --
  #
  # It moves the location cursor (insert element) up or down by
  # one element, and extends the selection to that point.
  # If no selection, just move the cursor.
  #
  # Arguments:
  # w -		The textMenu widget.
    # amount -	+1 to move down one item, -1 to move back one item.
  
  proc ExtendUpDown {w amount} {
    variable [$w instance]::v
    set new [$w selectindex [expr {int([$w masterindex insert]) + $amount}]] 
    $w mark set insert [lsearch -inline [$w tag names $new.end] *user].first
    $w see $new.end
    if {$v(Selection) == {}} {return}
    Motion $w $new
  }
  
  # dataExtend
  #
  # This procedure is called for key-presses such as Shift-End
  # It moves the insert element to el and extends the selection to that point.
  #
  # Arguments:
  # w -		The listbox widget.
  # el -		An integer element number.
  

  # dataExtend is used for both

  proc dataExtend {w el} {
    variable [$w instance]::v
    $w see $el.end
    $w mark set insert [lsearch -inline [$w tag names $el.end] *user].first
    if {[$w selection includes anchor]} {
      Motion $w $el
    }
  }
  # selectAll
  #
  # This procedure is invoked to handle the "select all" operation.
  # It selects everything in the widget.
  #
  # Arguments:
  # w -		The listbox widget.

  proc selectAll {w} {
    $w selection set 1 end
  }
  #
  # lostSelection is bound to <<Selection>> which triggers when ever
  # the text window selection changes. We use it to:
  # a.) Notice that we have lost the selection (to some other window) and
  # b.) In all cases, notify using our <<textMenuSelect>>
  
  proc lostSelection {w} {
    variable [$w instance]::v
    # frputs v(Selection)
    if {$v(Selection) != {} && [$w tag nextrange sel 1.0] == {}} {
      $w selection clear
      set v(Prev) {}
      set v(mouseMoved) 0
    }
    if {[$w cget -state] eq "normal"} {
      event generate $w <<textMenuSelect>>
    }
  }

  # Return (editmode checked later)

  proc exReturn {w} {
    variable [$w instance]::v
    
    if {$v(activeTag) != {}} {
      set cmd [$w tag bind $v(activeTag) <ButtonRelease-1>]
      set cmd2 [$w tag bind $v(activeTag) <Enter>]
      eval $cmd
      # The following sets the active entry back to what it was
      # Useful on opening/closing sub levels.
      eval $cmd2
    }
  }
  
  ########################### End of selection routines ###############

  # The array unset is for debug cases where we re-source the code
  array unset ::textMenu::
  # buildArray is a snit macro that builds a typevariable. This array is
  # common to all textMenus and should be considered read only.

  buildArray [arrayParts]
  
  
  # A place to get utf8 special chars
  # https://www.utf8-chartable.de/unicode-utable.pl?start=9472&unicodeinhtml=dec

  
  # The commands, if not here send to the text handler.
  # We allow abbreviations but don't want to collide with the text handler
  # The means we need more chars for some. Rather we include the possible
  # text commands and just send it through if not one we want to handle.
  # (We mark the pass throughs with a # below)
  # Here we have the list of commands. There are several lists
  # 1. commands      commands for 'text' widget
  # 2. passThrough   commands we immediately pass through to the 'text' widget
  # 3. entryCommands menu commands that address a menu entry & have options
    
  
  # ############################## end of init constants  #################
  #

  # ############### Some general routines #################################

  # These routines will be in the '::textMenu::' namespace 
  #########################################################################
  
  
  ################################## getIndicator #######################

  # toActive gets called with 'w' set to the master or peer as appropriate
  # # we use 'select' highlight as it is independent for each peer
  
  # proc toActive {w tagpriv entLev tagOps} {
  #   frputs  w tagpriv entLev tagOps
  #   if {[catch {variable [$w instance]::v}] != 0} {
  #     variable [[winfo parent $w] instance]::v
  #   }
  #   if {$v(activeTag) != {}} {
  #     if {$entLev == "leave"} {
  # 	if {$tagpriv != $v(activeTag)} {
  # 	  frputs ERROR tagpriv v(activeTag)
  # 	  return -code error "tagpriv miss match $tagpriv vs $v(activeTag)"
  # 	}
  # 	# $hull tag configure $tagpriv {*}$tagOps
  # 	# $hull tag lower $tagpriv
  # 	$w tag remove sel 1.0 end
  # 	set v(activeTag) {}
  #     } else {
  # 	# frputs v(activeTag) 
  # 	eval [$hull tag bind $v(activeTag) <Leave>]
  #     }
  #   }
  #   if {$entLev == "enter"} {
  #     $w tag configure sel {*}$tagOps
  #     set v(activeTag) $tagpriv
  #     $w tag add sel $tagpriv.first $tagpriv.last
  #     $w tag raise sel
  #     # $hull tag configure $tagpriv {*}$tagOps
  #     # set v(activeTag) $tagpriv
  #     # $hull tag raise $tagpriv
  #   }
  #   return
  # }
 
  # The getIndicators proc takes the -indicator info 
  # and returns the two indicator characters. Indicator
  # syntax is a 2 part list:
  #
  # one of {up-carit down-carit right-carit box box-check circle eye
  #    separator tearoff indent identbold literal}
  # followed by a number that indexes into the list referenced, except for
  # 'literal' which is followed by the character itself
  # For indicators there are two such, for indention there is one but it
  # may be several characters.
  
  proc getIndicators {given {prefix {}}} {
    set result {}
    foreach selList $given {
      # there are two indicators (should be single chars)
      # frputs given selList
      lassign $selList sym val
      lappend result ${prefix}[transLateSp $sym $val]
     }
    # if we only found one indicator, return the same one over again
    if {[llength $result] < 2} {
      lappend result {*}$result
    }
    return $result
  }
  ############################### getIndention ###############################
  # We should be given a string with possible specialChars in it. We are to
  # translate those.
  
  proc getIndention {given} {
    append result [string range $given 0 [set start [string first \{ $given]]-1]
    if {$start == -1} {return $given}
    set end [string first \} $given]
    if {$end == -1 || $end < $start} {
      return -code error "Malformed indention string: $given"
    }
    set ind [string range $given $start+1 $end-1]
    # frputs ind start end given
    foreach {sym indx} $ind {
      append result [transLateSp $sym $indx]
    }
    append result [string range $given $end+1 end]
    return $result
  }
  ################################# translate special ########################
  proc transLateSp {sym indx} {
    variable ::textMenu::
    if {$sym == "literal"} {
      return [subst $indx]
    }
    
    array set specialChars [lindex $(specialChars) 1]
    if {![info exists specialChars($sym)]} {
      return -code error \
      "\"$sym\" is not a valid indicator reference, must be one of\
      \"[array names specialChars] literal\""
    }
    if {$indx >= [llength $specialChars($sym)]} {
      set indx end
    }
    return [lindex $specialChars($sym) $indx]
  }
  
  
  # this proc cleans up the option lists. Pass it the 'possible' & and the
  # 'coded'. 'type' will be used in the error code if ambiguous. It resolves
  # abbreviations and returns the expanded option.
  
  proc cleanOp {coded possible type} {
    if {$coded in $possible} {
      return $coded
    }
    set act [lsearch -glob -all -inline $possible ${coded}*]
    if {[llength $act] != 1} {
      return -code error\
	  "Ambiguous $type ($coded). should be one of \"$possible\""
    }
    # We don't catch nil here as sometimes it is legal
    return $act
  }
  
  # verifyOps checks each op that is in the 'opsVerify' list to make
  # sure it, at least on the surface, looks reasonable.

  # Support functions...
  #
  # Verify routines: Except for readonly, these will either throw an error
  # or return an error message.  Nil return is pass. Routines that return
  # other than nil return the reason for the failure assuming the caller
  # will add more context to the error message.
  
  proc colorTest {color} {
    winfo rgb . $color
    return {}
  }

  # A better enum test might be 'cleanOp' above...
  proc enumTest {val legal} {
    if {$val in $legal} {return {}}
    return "must be one of \"$legal\""
  }
  proc integerTest {val args} {
    if {![string is integer -strict $val]} {return "must be integer"}
    
    array set tAy $args
    if {[info exists tAy(-min)] && $val < $tAy(-min)} {
      return "must be >= $tAy(-min)"
    }
    if {[info exists tAy(-max)] && $val > $tAy(-max)} {
      return "must be <= $tAy(-max)"
    }
    return {}
  }
  
  proc readonlyTest {val} {return "is readonly"}

  proc booleanTest {val} {
    if {![string is boolean -strict $val]} {return "must be boolean"}
    return {}
  }
  
  proc tagbindTest {val} {
    # this should be <event> {script}. We check the events are enclosed in <>
    foreach {event script} $val {
    ## frputs event script
      if {[string index $event 0] != "<" || [string index $event end] != ">"} {
	return -code error \
	    "'-tagbind' \"$val\" must have syntax '<event> script'."
      }
    }
    return {}
  }
  proc fontTest {args} {
    # This is bit more complicated that it ought to be.
    set first [lindex $args 0]
    if {$first in [font names] && [llength $args] == 1} {return {}}
    set fams [font families]
    if {[string index $first 0] == "-" && \
	    [catch {array set tmpAry $args}] == 0 &&\
	    [info exists tmpAry(-family)] &&\
	    [lsearch -exact -nocase $fams $tmpAry(-family)] != -1} {return {}}
    if {[lsearch -exact -nocase $fams $first] != -1} {return {}}
    return -code error "Font \"$args\" not recognized."
  }
  
  proc tagconfigTest {val} {
    variable ::textMenu::opsVerifyAry
    foreach {op value} $val {
      if {$op ni $(tagOptions)} {
	return -code error\
	    "\"$op\" is not a recognized 'tag option'.\
             Must be one of $(tagOptions)."
      }
      # Note: no readonly ops in the tagOptions so no need to catch that
      if {[info exists opsVerifyAry($op)]} {
	eval [format $opsVerifyAry($op) $value]
      }
    }
    return {}
  }

    
  proc verifyOp {op val} {
    variable ::textMenu::opsVerifyAry

    if {[info exists opsVerifyAry($op)]} {
      if {[set ms [eval [format $opsVerifyAry($op) $val]]] != {}} {
	return -code error "Option \"$op\" $ms"
      }
    }
    return {}
  }
			       

      
  # Here we check for properly formed option value pairs given a list
  # of requested and supported. The returned list will have any
  # abbreviations expanded.
  # 
  #
  
  proc checkOpPairs {request supported {oddOk 0}} {
    if {([llength $request] & 1) != 0 && ! $oddOk} {
      return -code error "Option value pairs not of even length ($request)"
    }
    set result {}
    foreach {op value} $request {
      set fo $op
      if {$op ni $supported && \
  	      [string index $op 0] != [lindex $supported end] &&\
  	      [llength [set fo [lsort -unique\
		    [lsearch -glob -all -inline $supported ${op}*]]]] != 1} {
  	return -code error "Option ($op) ambiguous or not supported ($fo)."
      }
      lappend result $fo $value
    }
    if {[llength $request] & 1} {
      # if we got an 'odd' list, return an odd one.
      set result [lreplace $result end end]
    }
    return $result
  }
  
  # 'toPairs' takes a list of lists of option/values and drops values that
  # start with '-' (such as the -bg option). It then returns a list of
  # option/value pairs where the value is the last entry in each sublist.
  # If there happens to be no value part in the source list, {} is returned
  # as the value. Thus it works with both 'configure' return quads and ops only.
  
  proc toPairs {list} {
    set result {}
    foreach ent $list {
      set value [lindex [lassign $ent op] end]
      if {[string index $value 0] == "-"} {continue}
      lappend result $op $value
    }
    return $result
  }

  # The buildDefaultList command takes two lists of op/value pairs
  # and builds the 'namePairs' list which enumerates the options 
  # with the value from the 'vals' list.  
  #

  
  proc buildDefaultList {vals defaults namepairs} {
    # frputs name selfns vals
    upvar $namepairs namePairs
    array set locAry $defaults
    set ofInterest [array names locAry]
    foreach {op value} $vals {
      # If it has something to contribute and is one of the
      # ops that we are interested in, update the value
      if {$value != {} && $op in $ofInterest} {
  	set locAry($op) $value
      }
    }
    set namePairs [array get locAry]
  }

  ############################# check-radioIndicators ###############
  
  # The check-radioIndicators proc examines the state of checkbox and
  # radio button indicators and updates them as needed. It is called by
  # masterLayout and has a trace code function as well.
  # The masterLayout call may be redundant as
  # we repost the menu when cascades change and such, or it may be new.
  # For each check box and radio button variable we keep an array in
  # the menus global space to keep track of what is happening. This
  # proceedure, itself, is in that space.

  # parameters: selfns   this should be the callers 'selfns'
  #             entryNum the entry number of the menu 

  proc check-radioIndicators {entryNum args} {
    # we keep our data in the 'v' array
    upvar selfns selfns
    upvar ${selfns}::options options
    upvar $options(instVault) v
    upvar [lindex $options(entryList) $entryNum] entAry
  ## frputs options(entryList)
  ## frputs "=[lindex $options(entryList) $entryNum]< "\
	entryNum options(instVault) selfns
  ## frputs "=[myproc check-radioTrace foo]< "
    # can't do anything if the variable does not exist 
    if {![info exists entAry(-variable)]} {return}
    
    if {![info exist v([set var $entAry(-variable)])] ||
	[lindex $options(entryList) $entryNum] ni $v(rc,$var)} {
      lappend v(rc,$var) [lindex $options(entryList) $entryNum]
    }
    # set up the trace... but first eliminate any old ones
    # We want to be able to deconstruct these when the menu is destroyed
    # also, we only need one per var. We will use the first one we see.
    
  ## frputs "[myproc check-radioTrace $selfns $var] " v(trace,$var)
    if {![info exists v(trace,$var)] || $v(trace,$var) == {}} {
      set v(trace,$var) [myproc check-radioTrace $selfns $var]
      trace add variable $var write [myproc check-radioTrace $selfns $var]
    }
    # trigger an update
    # if var does not exist, do nothing. This should resolve when it is created
    catch {set $var [set $var]}
  }

  # This routine is called when we are to delete a radio entry. This should
  # be rare, but in the process we will get rid of any dangling traces.

  proc delete-radioEntry {entryNum} {
    upvar selfns selfns
    upvar ${selfns}::options options
    upvar $options(instVault) v
    upvar [lindex $options(entryList) $entryNum] entAry
    # can't do anything if the variable does not exist 
    if {![info exists entAry(-variable)]} {return}
    if {[info exist v([set var $entAry(-variable)])] &&
	[set indx [lsearch -exact $v($var) \
		       [lindex $options(entryList) $entryNum]]] != -1} {
      set v(rc,$var) [lreplace $v(rc,$var) $indx $indx]
      if {$v(rc,$var) == {}} {
	trace remove variable $var write $cmd
	unset v(rc,$val)
	unset v(trace,$var)
      }
    }
  }  
    

  # This is called by the deconstructor to remove all active traces
  proc check-radioClear {vv} {
    upvar $vv v
    foreach {val cmd} [array get v trace,*] {
      set var [lindex $cmd end]
    ## frputs  val cmd
      trace remove variable $var write $cmd
      unset v(rc,$val)
    }
  }
  
  proc check-radioTrace {args} {
    # Somebody has changed one of the variables. If we have an indicator
    # we need to update it.
    # We are NOT tracking the options namespace for each entry, assume any
    # 'options' is ok.
  ## frputs args "[namespace current] "
    lassign $args selfns var
    upvar ${selfns}::options options
    upvar $options(instVault) v
    
    # get the new value...
    set newVal [set $var]
    foreach ent $v(rc,$var) {
    ## frputs ent v(rc,$var)
      upvar $ent entAry
    ## frputs "[array get entAry] "
      lassign $entAry(-decodedindicators) onInd offInd
      set remainingTags [lassign $entAry(-indicatortags) tagindict]
      set inqueryFlag \
	  [expr {$entAry(-type) == "checkbutton" ? "-onvalue" : "-value"}]
      set indic [expr {$newVal != $entAry($inqueryFlag) ? $onInd : $offInd}]
    ## frputs indic inqueryFlag onInd offInd
      $options(hull) replace $tagindict.first $tagindict.last ${indic}\t \
	  $entAry(-indicatortags)
      set tagConfig [expr {$indic == $onInd ? "1" : "2"}]
      
      $options(hull) tag configure $tagindict\
	  {*}[buildTagUsing -indicator${tagConfig}tagconfig]
    }
  }
  if {$::tcl_platform(platform) == "windows"} {
    # The focusTrack code is only used with MSW systems which have a
    # different way of handeling when a menu should be withdrawn.
    # On linux systems it is handled by the tk menu code.
    
    # focusTrack is called when the focus out event happens on the menu or
    # when the toplevel focus changes.
    
    # toplevel owner both loose focus, we need to unpost the menu.
    # This routine is called when focus in/out or trace happens
    # Parameters are:
    #    w   i.e. mem, the menu name
    #    why  "out" if focus out, trace if the parent window focus changes
    #    args 'place holder' 'master window name' op (read/write)

    #
    proc focusTrack {var baseName master args} {
      upvar $var v
    ## frputs #4 #3 #2 args v baseName master v(focusTrack)
      if {$v(focusTrack) || $v(menuFocus) || $v(masterFocus)} {return}
      incr v(focusTrack)
      update
      if {[set focus [focus]] == {} ||\
	      [winfo toplevel $focus] ni "$baseName $master"} {	
	set r [catch {::tk::MenuUnpost $baseName} rr]
	frputs UNPOSTCALLED tk::Priv(postedMb)
      }
      set v(focusTrack) 0
    ## frputs r focus
    }
    # Set up a focus indicator for the master. This may be used by several
    # instances but not all and only a posted menu (only one at a time).
    # Master, in this case is the top level. This is
    # NOT needed for embedded menus as we don't unpost when focus is lost.
    proc setFocusTrack {var} {
      upvar $var v
      set master [winfo toplevel [winfo parent [set baseName $v(baseName)]]]
      set v(focusTrack)  0
      set v(masterFocus) 0
      bind $master <FocusIn>  [list set ${var}(masterFocus) 1] 
      bind $master <FocusOut> [list set ${var}(masterFocus) 0]
      bind $master <FocusOut> +[list ::textMenu::focusTrack\
				    $var $baseName $master m]
      
      # and here is the binding for the individual menu
      # Only one menu may be posted at once, so we use a common var
      set v(menuFocus) 0
      
      # We assume that we completely control these as it is our window
      # so we will not attempt to save or restore them
      bind $baseName <FocusIn>  [list set ${var}(menuFocus) 1]
      bind $baseName <FocusOut> [list set ${var}(menuFocus) 0]
      bind $baseName <FocusOut> +[list ::textMenu::focusTrack\
				      $var $baseName $master b]
      # trace add variable v(focus) write\
	  # 	[list ::textMenu::focusTrack $master trace]
    }

    proc clearFocusTrack {var} {
      upvar $var v
      set master [winfo toplevel [winfo parent $v(baseName)]]
    ## frputs master var
      resetBind $master FocusIn  $var
      resetBind $master FocusOut $var
    }
    
    proc resetBind {w ty pat} {
      set cur [split [bind $w <$ty>] \n]
      bind $w <$ty> {}
      foreach ent $cur {
	if {$ent != {} || [string match *$pat $ent]} {continue}
	bind $w <$ty> +$ent
      }
    }
  }
  ################################ end of pre-constructor code ###########
  
  
  # ########################### delegations/to menuList and scrodget #######
  component menuList
  delegate method {*} to hull except [list \
					  {*}[inferiorCommands]\
					  masterindex\
					  clone]
  
  delegate method masterindex to hull as index
  
  # 'delegateto' is a Snit macro (as is 'inferiorCommands') which thus allow
  # delegation of all the commands...
  delegateto [inferiorCommands] menuList
  # [opsOnly [textmenuTopLevelDefaults]]

  delegate option * to hull except [list \
					{*}[opsOnly [scrodgetOptions]]\
					{*}[tagOptions]\
					{*}[textmenuOps]\
					{*}[opsOnly [trapOps]]\
					{*}[fontOp]\
					]
  # Define the trapOps out of existance
  
  deadOptionSet [trapOps]
  
  variable v
  
  ######################### scrodget stuff ###############################
  component scrodget
  delegate option -autohide    to scrodget
  delegate option -scrollsides to scrodget
  component grip
  ######################### end of scrodget set up ######################
  
  ###################### textMenu ## CONSTRUCTOR #########################

  constructor {args} {
  ## frputs args
    # options variable "v" is an array of lists...
    # This is a list of all the options and their default values
    # as obtained by introspection. Each list has {op value} entries.
    # There is a list for the master menu entry v(menuOptionsPairs)
    # and one for each menu entry type.
    
    lassign {}  v(menuOptionsPairs) v(commandMenuOptionsPairs)\
	v(cascadeMenuOptionsPairs)     v(checkbuttonMenuOptionsPairs) \
	v(radiobuttonMenuOptionsPairs) v(separatorMenuOptionsPairs)\
	v(tearoffMenuOptionsPairs)
    
    # Set up the verify array
    
    array set ::textMenu::opsVerifyAry $(opsVerify)
    
    # 'win' is the passed in name and will be the name of the text window. 
    # frputs start "[array get {}] "
    # Here we set up (root) and verify its existance.
    if {![winfo exist [set tl [regsub -start 1 {\.?[^.]+$} $win {}]]]} {
      return -code error "Window \"$tl\" does not exist"
    }
    # set top to indicate if this is a top level window
    set top [expr {[set parentTopLevel [winfo toplevel $tl]] == $tl}]
    # Lets get the default configure values
    set v(baseName) $tl
    set v(sibling)  $tl
    if {!$top} {
      set v(baseName) $win
    }
    set v(self) $self
    set v(textWindowName) $win
    set v(tabAryName) ${selfns}::tabsAry
    set configDefaultOps {}
    set defaultOps {}
  ## frputs win tl top args
    
    ############################
    # use the 'menu' command to get menu defaults
    set wt [menu $tl.wt -tearoff 1]
    #############################
    set menuDefaults [toPairs [$wt config]]
    lappend ::vault {*}$menuDefaults
    foreach en [$wt configure] {
      lappend ::vault2 $en
    }
  ## frputs TEXTMENU menuDefaults "[$wt configure] "
    buildDefaultList $menuDefaults $(textmenuOpsDefaults) v(menuOptionsPairs)
    

    # Here we build the options list and defaults for each type of menu entry.
    # This is a composit of the 'menu' options and our 'textmenu' additions.
    foreach var {tearoff separator command cascade checkbutton radiobutton} {
      if {$var != "tearoff"} {
	$wt add $var
      }
    ## frputs var win tl
      buildDefaultList {} \
	  [concat [toPairs [$wt entryconfig end]]\
	       $(${var}Ops)\
	       [expr {$var == "separator" ? {} : $(commonOps)}]]\
	  v(${var}MenuOptionsPairs) 
    # puts "Ops $var are $v(${var}MenuOptionsPairs)"
    }
    # Set up a set of ops to look like a menu widget to pass to the text widget
    # This gets the menu color and font stuff, for example.

    buildDefaultList $menuDefaults $(textOptionsDefaults) configDefaultOps
    
  ## frputs DEFAULTOPS configDefaultOps
    # We are done with the 'menu' window, destroy it.
    destroy $wt
    
   
    # ################################ install hull (text) ##############
    # We need to put the trapOps here as from here
    # from now on we will trap them
    installhull using text {*}$(trapOps)
    # Get rid of the Text bindings...
    bindtags $win [regsub {Text} [bindtags $win] {}]
    # lets grab the 'Text' widget scroll options. These reference
    # 'MouseWheel' 'Button-4' or 'Button-5'
    foreach binding [bind Text] {
      if {[string match {*MouseWheel*} $binding] ||\
	      [string match {*Button-4*} $binding] ||\
	      [string match {*Button-5*} $binding]} {
	#frputs binding win
	bind $v(baseName) $binding [bind Text $binding]
      }
    }
    # Here we set up the bindings arrow keys...
    lassign {{} {} 0 {} 0} \
	v(Selection) \
	v(Prev) \
	v(anchor) \
	v(activeTag) \
	v(mouseMoved)
    
    setSelectBindings $v(baseName)

    # ##############################################
    
    # use scrodget for the scroll bars
    # we need a sibling to win...
    
    set scrollCmd $v(sibling).scrollBars
    # ############################### install scrodget ################
    install scrodget using scrodget $scrollCmd
    $scrollCmd associate $win
    grid $scrollCmd   -sticky news
    grid rowconfig    $v(sibling) 0 -weight 1
    grid columnconfig $v(sibling) 0 -weight 1
    # The following are not needed if toplevel
    # It is also not clear if this is presumptuous
    if {!$top} {
      grid $v(baseName) -sticky news
      grid rowconfig    [winfo parent $v(sibling)] all -weight 1
      grid columnconfig [winfo parent $v(sibling)] all -weight 1
    }
    # ################################ install the grip ###############
    # ... we make a child of the scrollCmd window
    # This makes it easy to modify the bindtags with out knowing
    # what they are actually called (named).
    set gripCmd $scrollCmd.grip
    install grip using ttk::sizegrip $gripCmd
    grid $gripCmd -in $scrollCmd -row 2 -column 2 -sticky se
    
    # Keep scroll events to the scroll bars (don't forget the parent)
    foreach sWin [list {*}[winfo children $scrollCmd] $scrollCmd] {
      set newTags {}
      foreach bindTag [bindtags $sWin] {
	if {$bindTag == "all" || $bindTag == $v(sibling)} {continue}
	lappend newTags $bindTag
      }
      bindtags $sWin $newTags
    }
  ## frputs  selfns v(tagOptions)
    # always start a menu as, well, a menu, not a tearoff...
  
  ## frputs hull win "mymethod >[mymethod cleanup $win] &&  proc >[myproc cleanup $win] "
  ## frputs v(menuOptionsPairs)
    #
    # set up our options...
    # configDefaultOps set above will all go to the hull or our deversion
    # and then to the hull.
    
    # These will all go to 'menulist' or to 'scrodget' and, we pickup args.
    # first, the 'array set' will eliminate dups while keeping the last
    array set allOps [concat $(textmenuOpsDefaults)\
			  $v(menuOptionsPairs)\
			  $configDefaultOps\
			  $(scrodgetOptions)\
			  $args]
    # now separate those that must be passed at turn on (i.e. read only stuff)
    # and those that are nil (as this may wipe out useful stuff)
    foreach op [array names allOps] {
      if {[catch {verifyOp $op $allOps($op)}] != 0} {
	lappend initOnlyOps $op $allOps($op)
	frputs op
	unset allOps($op)
      }
      if {$op in $(saveTextOps)} {
	unset allOps($op)
      }	
      if {$op == "-insertwidth"}  {
	#unset allOps($op)
      }

    }
    # array set options [array get myopsAry]
    ##############################################################
    install menuList using menulist ::$v(baseName).inthebegining\
	hull $hull level 0 instVault [myvar v] {*}$initOnlyOps
	
    ##############################################################
    #[array get textOpsAry]
  ## frputs "text>[array get textOpsAry]< " self
    
  ## frputs "resultOps>[array get allOps] "
    #$self configurelist $configDefaultOps
    # $self configurelist $(textOptionsDefaults)
    $self configurelist [array get allOps]
    $self configurelist $(specialChars)
    #
    # And finally, lets set up a 'cursor' color that is a few shades
    # different from the background.
  ## frputs fLevel win "where the dirt is [myvar {}] ops [myvar options] "
    return $win
  }

  destructor {
  ## frputs v(baseName) v(sibling)
    check-radioClear v
    if {[info exists v(cascadeNames)]} {
      foreach name [lreverse $v(cascadeNames)] {
	catch {rename $name {}} er
	frputs name er
      }
    }
    if {$::tcl_platform(platform) == "windows"} {
      clearFocusTrack v
    }
    #catch {rename $v(baseName).inthebegining {}} err
    catch {rename $v(sibling).scrollBars {}} er2
  ## frputs  er2
  }
  #############################################################
  option {-specialchars {} {}} -readonly 1 
  option -toplevel -readonly 1
  
  optionSet [textmenuOpsDefaults] optionConfig optionConfig
  optionSet [tagOptionsDefaults]  tagMonMethod cgetPass
  # The following replaces the -font op in the above.
  option [fontOp] -configuremethod fontConfig -cgetmethod cgetPass
  option -type -default {normal} -configuremethod typeconfig\
      -cgetmethod typeconfig -readonly 1

  # ######################### METHODS ##########################

  # short hand...
  method config {args} {$self configure {*}$args}
  
  method optionConfig {args} {
    #set bn [set ${selfns}::baseName] "[array get options] "
  ## frputs "OPTIONCONFIG " args 
    $v(baseName).inthebegining myconfigure {*}$args
  }
  # We come to the 'tagMonMethod's for options that are sent to the
  # text component (hull) and which we use to set up the tagBase
  #
  method tagMonMethod {args} {
    set result [$hull configure {*}$args]
    if {[llength $args] >= 2} {
      # changed something
      foreach {op val} $(tagOptionsDefaults) {
	lappend newBase $op [$hull cget $op]
      }
      set v(tagBase) $newBase
    }
  ## frputs args result
    return $result
  }
  
  method cgetPass {args} {
    # do nothing here if just looking
  ## frputs args "[$hull cget {*}$args] "
    return [$hull cget {*}$args]
  }
  # Only come here for font changes. We find a compatable fixed font
  # for the indention.
  method fontConfig {args} {
    if {[llength $args] >= 2} {
      set val [lindex $args 1]
      set indFont $val
      if {![font metrics $indFont -fixed]} {
	set indFont "TkFixedFont"
	array set fontAry [font actual $val]
	array set tmpAry  [font actual TkFixedFont]
	set tmpAry(-size)   $fontAry(-size)
	set tmpAry(-weight) $fontAry(-weight)
	set indFont "TkFixedFont$fontAry(-size)"
	if {$indFont ni [font names]} {
	  font create $indFont {*}[array get tmpAry]
	}
      }
      $self optionConfig -indentionfont $indFont
    }
  ## frputs FONTCONFIG val indFont
    return [$self tagMonMethod {*}$args]
  }
  method typeconfig {args} {return $options(-type)}
    
  # It appears that the 'cget' method is called first for configure
  # so its returning an error takes care of the configure as well.
  # But still, the 'option' command seems to require a configure method.
  method deadConf {args} {frputs}
  
  method deadCget {args} {return "unknown option \"$args\""}

  method instance {args} {return $selfns}

  proc getUserTagForLine {el {pat "*user"} } {
    upvar hull hull
    set tmp [$hull tag names $el.end]
    set tag [lsearch -inline $tmp $pat]
    # if {[llength $tag] != 1} {
    #   return -code error "Line \"$el\" does not contain a menu line."
    # }
    return [lindex $tag 0]
  }
  
  proc tagRangeForLine {el} {
    upvar hull hull
    set tag [getUserTagForLine $el]
    if {$tag == {}} {return {}}
    return [list $tag.first $tag.last]
  }

  proc getWinEntNoFor {el} {
    upvar hull hull
    set tag [getUserTagForLine $el]
    set indx [string last "." $tag]
    set entWin   [string range $tag 0 $indx-1]
    set entCount [string range $tag $indx+1 end-4]
    return [list $entWin $entCount]
  }
  
  # convert a range to an inclusive list
  # we also have a few sanity tests here.
  proc firstLastToList {first last} {
    if {$last == {}} {
      set last $first
    }
    set tmp $first
    if {$first == {} || $first == 0} {
      return -code error "Illegal element \"$first\" on 'selection'"
    }
    set inc [expr {$first <= $last ? 1 : -1}]
    # frputs inc first last
    if {$inc == -1 && $first == 1} {return {}}
    while {$tmp != $last} {
      lappend result $tmp
      incr tmp $inc
    }
    lappend result $tmp
    return $result 
  }
  #
  # This is the 'textmenu' active command, not to be confused with
  # the 'activate' command. This is accessed from the 'textmenu' master
  # (not the 'menulist' which handles the 'activate' command by
  # calling us. It deals in text tags.

  # It is not clear that this routine is needed or useful. !!!!!!!!!!!!!!

  method active {args} {
    if {[llength $args] == 0} {
      # Not clear what to return here, we settle on the menu and entry #
    ## frputs v(activeTag) v(baseName)
      if {$v(activeTag) == {} ||\
	      [set tag [$hull tag ranges $v(activeTag)]] == {}} {
	return "none"
      }
      set dotLoc [string last "." $v(activeTag)]
      set umem [string range $v(activeTag) 0 $dotLoc-1]
      if {$umem == "::$v(baseName).inthebegining"} {
	set umen "::$v(baseName)"
      }
      return [list  $umen [string range $v(activeTag) $dotLoc+1 end-4]]
    }
    # First we need to de-activate the current active entry (if any)
    # frputs args
    if {$v(activeTag) != {}} {
      set cmd [$hull tag bind $v(activeTag) <Leave>]
    ## frputs v(activeTag) cmd
      eval $cmd
     }
    if {[lindex $args 0] == "none"} {return}
    set el [$self selectindex [lindex $args 0] 1]
    set tagv [getUserTagForLine $el "*priv"]
    set cmd [$hull tag bind $tagv <Enter>]
    # frputs cmd
    eval $cmd
  }

  # toActive gets called with 'w' set to the master or peer as appropriate
  # we use 'select' highlight as it seems to be independent for each peer
  # by the nature of active, only one item in all the peers is active.
  
  method toActive {w tagpriv entLev tagOps} {
  ## frputs  tagpriv entLev v(activeTag) tagOps
    if {$w == "%W"} {
      set w $self
    }
    if {$v(activeTag) != {}} {
      if {$entLev == "leave"} {
  	if {$tagpriv != $v(activeTag)} {
          ## frputs ERROR tagpriv v(activeTag)
          return
  	  return -code error "tagpriv miss match $tagpriv vs $v(activeTag)"
  	}
   	$w tag remove sel 1.0 end
  	set v(activeTag) {}
      } else {
  	# frputs v(activeTag) 
  	eval [$hull tag bind $v(activeTag) <Leave>]
      }
    }
    if {$entLev == "enter"} {
      # grab focus so we don't use the inactive colors.
      focus $w
      $w tag configure sel {*}$tagOps
      if {[catch {$w tag add sel $tagpriv.first $tagpriv.last} er]} {
	frputs ACTIVEERROR er
	return
      } 
      set v(activeTag) $tagpriv
      $w tag raise sel
    }
    return
  }
  method selectindex {el {strict 0}} {
    set end [expr {int([$hull index end]) - 2}]
    if {$el == "anchor"} {return $v(anchor)}
    if {$el == "active"} {
      # here we settle on a line number...
      if {$v(activeTag) == {}} {return "none"}
      set tag [$hull tag ranges $v(activeTag)]
      if {$tag == {}} {return "none"}
      return [expr {int([lindex $tag 0])}]
    }
    if {$el == "end"} {return $end}
    if {![string is double -strict $el]} {return 1}
    if {$strict && ($el < 1 || $el > $end)} {return "none"}
    if {$el < 1}                    {return 1}
    if {$el > $end}                 {return $end}
    return [expr {int($el)}]
  }

  # The selection command.
  #
  # The selection is kept in the instance array 'v', v(Selection) exactly.
  #
  method selection {args} {
    # we have several flavors...
    lassign $args stype element lastElement
    set element [$self selectindex $element]
    if {[llength $args] < 3} {
      set lastElement $element
    } else {
      set lastElement [$self selectindex $lastElement]
    }
    # frputs element lastElement args
    switch [cleanOp $stype {anchor clear includes set list} \
		"selection subtype"] {
      anchor   {
	if {[llength $args] < 2} {
	return $v(anchor)
	}
	set v(anchor) $element
      }
      clear    {
	if {[llength $args] == 1} {
	  $hull tag remove sel 1.0 end
	  set v(Selection) {}
	} else {
	  foreach el [firstLastToList $element $lastElement] {
	    set val [lsearch -all  -exact $v(Selection) $el]
	    foreach ent [lreverse $val] {
              if {[set tmp [tagRangeForLine $el]] == {}} {continue}
	      $hull tag remove sel {*}$tmp
	      set v(Selection) [lreplace $v(Selection) $ent $ent]
	    }
	  }
	}
      }
      includes {
	if {[llength $args] != 2} {
	  return -code error "\"selection includes\" must have exactly 1 index."
	}
	return [expr {[lsearch -exact $v(Selection) $element] != -1}]
      }
      set    {
	if {[llength $args] > 3} {
	  return -code error "Too many arguments for \"selection set\"."
	}
	# frputs element lastElement
	foreach el [firstLastToList $element $lastElement] {
	  set val [lsearch -all -exact $v(Selection) $el]
	  # frputs el val
	  foreach ent [lreverse $val] {
	    set v(Selection) [lreplace $v(Selection) $ent $ent]
	  }
	  # frputs el v(Selection) "[tagRangeForLine $el] "
          if {[set tmp [tagRangeForLine $el]] == {}} {continue}
	  lappend v(Selection) $el
	  $hull tag add   sel {*}$tmp
	  $hull tag raise sel
	}
	return $lastElement
      }
	
      list     {
	# we have two options, the 'raw' which returns a list of dublets the
	# first being the 'command name' to query and the second being the
	# element and 'cooked' which returns the label for each selected item.
	# We return 'cooked' when call with no args, raw when there are args
	set result {}
	foreach el $v(Selection) {
	  lassign [getWinEntNoFor $el] entWin entCount
	  if {[llength $args] > 1} {
	    lappend result $entWin $entCount
	  } else {
	    # by using the -label we get it right even if an -entry is true.
	    lappend result [$entWin entrycget $entCount -label]
	  }
	}
	return $result
      }
      default  {return -code error "Selection subtype not supplied"}
    }
  }

  method curselection {args} {return $v(Selection)}

  # The clone method is called to create a tearoff of the
  # menu. Argument is the new window pathname and, the type.
  # We support only the tearoff.
  # The main part of this is done by the text 'peer' method.

  method clone {args} {
    lassign $args path type
    # if {![winfo exists $path] || $path != [winfo toplevel $path]} {
    #   return -code error "Clone path not a toplevel window."
    # }
    if {$type ni {{} tearoff}} {
      return -code error "Only type \"tearoff\" is supported by clone."
    }
    # create the toplevel and the frame
    set t [textMenuClone $path $v(textWindowName) $hull\
	       -title [$v(baseName) cget -title]]
  ## frputs CLONE t path
  }
}

	  
########################### end of textMenu widget adaptor ###############

########################### textMenuClone widget adaptor   ###############
::snit::widgetadaptor textMenuClone {
  # The textMenuClone implements the 'textmenu clone' command.  The clone
  # function is usually invoked to create a tearoff menu.
  # Since the user is not likely to interacte problematically with the
  # actual clone (i.e. tearoff) we are mainly concerned with the scrolling
  #
  # We should be called with a window name and the 'textmenu' widget to be
  # cloned. In many ways this will be like the 'textmenu' widget except
  # in the instantiation of the component.

  # What we get is the name for a toplevel window with the hull for the
  # main window to clone.

  delegate method {*} to hull except [list \
					  {*}[opsOnly [scrodgetOptions]]\
					  post\
					 ]
  
  delegate option * to textmenu except {-title -type}
    
  ######################### scrodget stuff ###############################
  component scrodget
  delegate option -autohide    to scrodget
  delegate option -scrollsides to scrodget
  delegate option -xscrollcommand to scrodget
  delegate option -yscrollcommand to scrodget
  component grip
  component textmenu
  ######################### end of scrodget set up ######################
  
  ###################### textMenuClone ## CONSTRUCTOR ###################
  constructor {args} {
    set args [lassign $args options(textmenuName) masterhull]
    set options(textmenuName) [regsub {::} $options(textmenuName) {}]
    # 'win' is the passed in name and will be the name of the text window. 
    # frputs start "[array get {}] "
    # Here we set up (root) and verify its existance.
    if {![winfo exist [set tl [regsub -start 1 {\.?[^.]+$} $win {}]]]} {
      return -code error "Window \"$tl\" does not exist"
    }
    installhull using toplevel

    $masterhull peer create $win.x
    # grab the textmenu options we want
    textMenu::buildDefaultList \
	[textMenu::toPairs [$masterhull configure]] \
	[textMenu::toPairs $textMenu::(textTearOffOps)] pasAry
  ## frputs TEXTMENUCLONE args win hull tl\
	">[array get pasAry]< " options(textmenuName)
    set textmenu  $win.x
    # Get rid of the Text bindings...
    bindtags $win.x [regsub {Text} [bindtags $win] {}]
    # lets grab the 'Text' widget scroll options. These reference
    # 'MouseWheel' 'Button-4' or 'Button-5'
    foreach binding [bind Text] {
      if {[string match {*MouseWheel*} $binding] ||\
	      [string match {*Button-4*} $binding] ||\
	      [string match {*Button-5*} $binding]} {
	#frputs binding win
	bind $win $binding [bind Text $binding]
      }
    }
    # setSelectBindings $win
    
    # use scrodget for the scroll bars
    # we need a sibling to win...
    set scrollCmd $win.scrollBars
    # ############################### install scrodget ################
    install scrodget using scrodget $scrollCmd
    catch {$scrollCmd associate $win.x} err
  ## frputs LOSTIT  err ::errorInfo
    grid $scrollCmd   -sticky news
    grid rowconfig    $win 0 -weight 1
    grid columnconfig $win 0 -weight 1
    ######################### end of scrodget set up ######################
  ## frputs CLONETEXT win scrollCmd hull tl
    # ################################ install the grip ###############
    # ... we make a child of the scrollCmd window
    # This makes it easy to modify the bindtags with out knowing
    # what they are actually called (named).
    set gripCmd $scrollCmd.grip
    install grip using ttk::sizegrip $gripCmd
    grid $gripCmd -in $scrollCmd -row 2 -column 2 -sticky se
    
    # Keep scroll events to the scroll bars (don't forget the parent)
    foreach sWin [list {*}[winfo children $scrollCmd] $scrollCmd] {
      set newTags {}
      foreach bindTag [bindtags $sWin] {
	if {$bindTag == "all" || $bindTag == $tl} {continue}
	lappend newTags $bindTag
      }
      bindtags $sWin $newTags
    }
    # -startline 2 eliminates the tearoff entry.
    $self configurelist [list {*}$pasAry {*}$args -startline 2]
  }

  destructor {
    #catch {rename $v(baseName).inthebegining {}} err
    catch {rename [winfo parent $win].scrollBars {}} er2
  ## frputs  DESTRUCTORCLONE er2

  }
  option -type -default tearoff -configuremethod typeconfig\
      -cgetmethod typeconfig
  option -title -default {} -configuremethod setTitle -cgetmethod setTitle

  method setTitle {args} {
  ## frputs args win hull
    lassign $args op val
    if {[llength $args] == 1} {return [wm title [winfo toplevel $win]]}
    set options($op) $val
  ## frputs op win val "[array get options] "
    wm title [winfo toplevel $win] $val
    return
  }
  method post {args} {
    # should get x & y
    if {[lassign $args x y] != {} || $x == {} || $y == {}} {
      return -code error "Post take two arguments (x & y)."
    }
    # Lets use the master's size here
    lassign \
	[split [wm geo [winfo toplevel $options(textmenuName)]] x+] width height
    wm geo [winfo toplevel $win] ${width}x${height}+$x+$y
    wm deiconify [winfo toplevel $win]
    # we seem to need this to get the menu reliably on top
    # if {[tk windowingsystem] eq "win32"} {
    #   set parent [winfo parent $win]
    #   set parent [winfo toplevel $parent]
    #   while {[winfo class $parent] in "TextMenu Menu"} {
    # 	set parent [winfo toplevel [winfo parent $parent]]
    #   }
    #   wm transient $win [winfo toplevel $parent]
    #   wm attributes $win -toolwindow 1
    # }

  }
  method instance {args} {return $selfns}
}
  

###################### end of textMenuClone widget adaptor   #############


############################### TextMenu widget ##########################
# This is the main entry into the textmenu widget for toplevel menus.
# All it really does is define the toplevel frame and take care of toplevel
# chores such as title and tearoff.

# This is where we set up the menu clone (which is a text peer) inside of
# the scroll bars, etc. The clone set up is indicated by argument
#
# In clone mode, most all options are ignored... it is intended that most
# operations are actually directed to the main menu. The only exception is
# the title.

::snit::widget TextMenu {

  hulltype toplevel
  #component options
  #component methods
  component realWin
  delegate option * to realWin except \
      {-title -tearoff -tearoffcommand -toplevel}
  delegate method * to realWin except {setTitle config setTearoff}

  constructor {args} {
    set top [winfo toplevel $win]
  ## frputs TextMenu args top
    array set argsAry $args
    foreach op {-title -tearoff -tearoffcommand -toplevel} {
    ## frputs op
      unset -nocomplain argsAry($op)
    }
    # set argsAry(-insertwidth) 10
    set argsP [array get argsAry]
    install realWin using textMenu $win.x {*}$argsP -toplevel $top
    
  ## frputs win hull realWin self args ::errorInfo
    wm overrideredirect   $top 1
    # Lets try for a default title
    set title {}
  ## frputs LIGHTUP win "1>[winfo parent $win] 2>[winfo  parent [winfo parent $win]] "
    if {[winfo class [set parent [winfo parent $win]]] == "Menubutton"} {
      lappend title -title [$parent cget -text]
    }
  ## frputs parent title
    wm withdraw $top
    $self configurelist [list {*}$title {*}$args]
    array unset args
    set options(-toplevel) $top
  }

  option -title   -default {} -configuremethod setTitle   -cgetmethod setTitle
  option -tearoff -default 0  -configuremethod setTearoff -cgetmethod setTearoff
  option -tearoffcommand -default {}
  option -toplevel -readonly 1
  #  -configuremethod setToplevel -cgetmethod setToplevel 
  #  -configuremethod setTearoff -cgetmethod setTearoff
  
  destructor {
  }
  # This method is called for -title
  method setTitle {args} {
  ## frputs args win hull
    lassign $args op val
    if {[llength $args] == 1} {return [wm title $win]}
    set options($op) $val
  ## frputs op win val "[array get options] "
    wm title $win $val
    return
  }
  # allow configure to be shortened to config
  method setTearoff {args} {
    lassign $args op val
  ## frputs "do tearoff" args options($op) op val self hull
    if {[llength $args] == 1} {return $options($op)}
    if {[set er [::textMenu::booleanTest $val]] != {}} {
      return -code error "-tearoff value ($val) $er."
    }
    if {$val} {
      $self add tearoff
      set options($op) $val
    } elseif {$options($op)} {
      if {[$self entrycget 0 -type] == "tearoff"} {
	$self entryconfigure 0 -state disabled
      }
    }
  }
  method setToplevel {args} {
    # This is read only...
  ## frputs args
    if {[llength $args] == 1} {return $options(-toplevel)}
    return -code error "Option \"-toplevel\" is read only."
  }
  # This also takes care of option in the hull (i.e. textMenu windows)
  method config {args} {$win configure {*}$args}
  
}
########################### end of TextMenu ##########################


################################### menulist #########################

# the menulist type is the manager of the textmenu entries.

# it is first enabled by the textmenu constructor and re-entered
# by each cascade entry. The options data structures thus pertains
# only to a given cascade within a set of cascade menus.
# And, of course there may be more than one cascade on each level.
# Each cascade will get a new 'menulist' entry.
# The fixed data is the default data structure for each type which
# is a 'typevariable' we call the 'entryTypeAry' array. (By convention
# we will tack 'Ary' on the end of each array name.)


::snit::type menulist {


  # These are menu options which are applied via bind and/or tag
  # The following groups will depend on the menu "type"
  # The comments are possible additions...
  
  # -accelerator       ;# not supported (yet?)
  # -bitmap            ;#
  # -compound          ;# 
  # -image             ;# 
  # -state             ;# not supported
  # -underline         ;# must use bind on the char not supported

  
  # There are three levels of data.
  # 1. The global constant (we hope) typevariable, with name "" (i.e. nil)
  #    in ::textMenu namespace. (variable ::textMenu::) which contains 
  #    constants used to set defaults and such. Each menu 'type' has a
  #    data structure based on its type which define its options.
  #    This data is kept in a list of lists. 
  
  # 2. A global array for each 'textmenu'. This is an instance variable
  #    for textMenu (an array named v) and is passed in on first call
  #    as the 'defaults' value (i.e. ends up as 'options(instVault)' and
  #    each cascade passes it to the next. This array contains the default
  #    options and values for each menu entry type as well as
  #    what is needed to easily find each cascade name and the
  #    structure of cascading so that we can track the full path of
  #    any given menu entry and so we can easily delete a cascade and all
  #    below it. The easy way to set this up is:
  
  #    upvar $options(instVault) v
  
  #    This makes the array available as 'v' in the given proc/method.  
  #
  # v(cascadeNames)  a list of cascade names to prevent collision.
  # v(posted)        a boolean indicator if the menu has been posted
  #                  once posted, the menu is 'live' until unposted
  #                  structures to keep track of 'radiobuttons'
  # v(rc,<var>)      a radio/check <var> name a list of fully qualified
  #                  entry arrays referencing the <var>
  # v(trace,<var>)   trace function for <var>


  # 3. A 'options' array for each cascade which contains the options
  #    for each of the entries of a given cascade. (Snit gives us a new
  #    unique namespace for each level of the menu.)

  #    The 'setEntAry' proc below sets up an array in the levels namespace
  #    for each entry at that level. That array contains all the public and
  #    private (actually readonly) info needed to manage the entry and its
  #    options. These arrays have arbitrary names which the action routines
  #    never use as 'setEntAry' sets up a options name for the entries array
  #    when ever it is called. The actual fully qualified names are kept in
  #    'options(entryList)' (a list) which is kept in the same order as the
  #    entries. Thus when 'setEntAry' is asked to set up a options name for
  #    an entry, it uses the entry number to index into the 'options(entryList)'
  #    to get the fully qualified name and then uses 'upvar' to set up a
  #    options name for the array. Any method and procs that have 'selfns'
  #    defined may set up a options array name using setEntAry.
  
  #    These are things like (hull) which is needed talk to
  #    the underlying text window. The 'options' array contains all the info
  #    passed in from the 'textMenu' widgetadapter.

  # We keep the following in 'options' for ease of access:

  # (master)         is the level 0 command name. We use it to 'post'
  # (-post)          status of post at the given level
  #           0      nothing yet
  #           1      any prepost command has been called, ready to post
  #           2      posted
  #           3      unposted because parent is unposted or to update
  #                  entries above it in the cascade. These will be
  #                  posted when the reason clears.
  
  # (indicatorcount) the number of entries at this level that require indicators

  # For procedures: the namespace ::textMenu:: contains procedures
  # that me be needed here. We access them with the full namespace names.

  # a typevariable
  #
  # 'options' is options to each instance.  As such it ties all the methods
  # together
  #
  # variable options 

  # we are passed
  # hull             the text widget we are working with
  # level          0 is the first level menu, a cascade is 1, etc.
  #                (there may be multiple occurances of levels other
  #                than 0)
  # bits of the parent cascade option list such as:
  
  # multipleopen   boolean, 1 if more than one cascade may be open on a level.
  # incomingindent is where we are in indention. At level 0 it should be 0
  optionSet [textmenuOpsDefaults] optionConfig optionConfig

  constructor {args} {
    # frputs "menuList " args self win
    array set options $args
    upvar $options(instVault) v
    # set cleanHull [regsub {^::} $options(hull) {}]
    # variable ::textMenu::$cleanHull
    
    if {$options(level) == 0} {
      set v(cascadeNames) {}
      set v(posted) 0
    }
   
    array set options $v(menuOptionsPairs)
    array set options $args
    unset -nocomplain options(-type)

    
    # Since this is the first we know of this level of the menu, it is
    # not posted.
    set options(-post) 0
    set options(-active) "none"
    
  ## frputs win hull
    lappend v(cascadeNames) $win
    set options(master) [lindex $v(cascadeNames) 0]
    set options(entryList) {}
    set options(-entrycount) 0
    # the 'indicatorCount' indicates how many of the entries in 'entryList'
    # have indicators (i.e. checkbox, radiobutton and cascade)
    # by counting we can track the need for indicator space without
    # having to rescan the 'entryList'.
    set options(indicatorCount) 0
    # A few useful values 
  

    # frputs "menuList " "options>[myvar options] nil>[array get {}] "\
	win self selfns
  ## frputs v(cascadeNames) options(instVault) 
  ## frputs "HELLO  "
    # array set arg $args
  }
  
  destructor  {
    $self delete 0 end destructor
  ## frputs self win
  }
  
  # Each menu item has a set of 'options' which we keep track of here.
  # The default values are in the '::textMenu::' array which we reach
  # via the 'variable' command. That array should not be altered by this
  # code. Each menu entry has an entry there by type and a set of common
  # entries for all types except 'separator' and 'tearoff'.
  # We combine the default ops and put them in the 'options(entryList)' array
  # Then we call out 'configure' method to add those ops passed in here.

  # The actual building of the display is done in the 'post' method using
  # the 'options(entryList)'

  ####################### First some support procs #####################
  #
  # 'buildEntryWindowFor' builds the entry window when the '-entry' option
  #                       is true. The entry window will have a name based
  #                       on the level and entry number and will have a
  #                       '-textvariable' pointing at the '-label' entry.
  #
  # By default this window will use the 'Entry' class options.
  # These may be overridden by the global option '-entrywindowops' and
  # these in turn, by entryconfigure option '-entrywindowops'

  proc buildEntryWindowFor {entCount} {
    upvar entAry entAry
    upvar options options
    upvar $options(instVault) v
    set name $v(textWindowName).$options(level)-$entCount
    set entAry(-entrywindowname) $name
    if {![winfo exists $name]} {
      entry $name -textvariable [lindex $options(entryList) $entCount](-label)
    }
    # Give him some room to work with
    $name configure -width [expr {[string length $entAry(-label)] * 2}]\
	{*}[buildTagUsing -entrywindowops {} nobase]
    return $name
  }
  
  #
  # 'getOpVal' handles option inherit from 'options' to current ent
  #
  proc getOpVal {op} {
    upvar entAry entAry
    if {[info exists entAry($op)] && $entAry($op) != {}} {return $entAry($op)}
    upvar options  options
    if {[info exists options($op)]}  {return $options($op)}
    if {[info exists entAry($op)]} {return $entAry($op)}
    return -code error \
	"Option \"$op\" is not defined in entry or options space."
  }
 
  # 'buildTagUsing' builds the tag config list for the specified option. The
  # list starts with the tagBase and then the options and entAry are used. This
  # a) normalizes, b) sets the options default and c) overrides that default if
  # an override is given. Caller MUST name the entry array 'entAry'.
  
  # If the 'op' value starts with '-' it is a collection of op/value pairs
  # otherwise it is a value and we need to put op in front.

  # If 'alias' is supplied, it is a value that should be removed from
  # 'op' when it is put in the result, thus
  # -activebackground active ->background
 
  proc buildTagUsing {args} {
    lassign $args ops alias nobase
    upvar entAry             entAry
    upvar options            options
    upvar $options(instVault) v
    set result [expr {$nobase == {} ? $v(tagBase) : {}}]
    foreach op $ops {
      if {[info exists options($op)] && $options($op) != {} } {
	lappend result {*}[expr {[string index $options($op) 0] == "-" ?\
				     {} : \
				     [regsub $alias $op {}]}] {*}$options($op)
      }
      if {[info exists entAry($op)] && $entAry($op) != {} } {
	lappend result {*}[expr {[string index $entAry($op) 0] == "-" ?\
				     {} : \
				     [regsub $alias $op {}]}] {*}$entAry($op)
      }
    }
    # frputs ops result
    # we know there are repeats here, but the last one wins, so why bother
    return $result
  }
  
  # setEntAry sets up the entry array for the given entryNum array
  # It is an error if 'entryNum' is negative or greater than the
  # size of the current 'options(entryList)'.
  # If 'type' is supplied, a new entry is created. Type MUST not be
  # abbreviated here as it is used to access the default values.
  
  # If 'entryNum' is exactly the entryList size type must be supplied.
  # If 'entryNum' exists (i.e. is > 0 and < length of entryList) that
  # entry is set up and returned unless 'type' is supplied, in which
  # case a new entry is created with the given 'entryNum' and the
  # existing entries are renumbered to accommodate the insertion.
  #
  # If a new entry is created it is set with the default values for the
  # given type as defined by {type}MenuOptionsPairs in the 'defaults'
  # 
  proc setEntAry {entryNum reqAryName {type {}} } {
    upvar selfns selfns ${selfns}::options options
    upvar $options(instVault) v
    set lnEntryList [llength $options(entryList)]
    
    if {$entryNum > $lnEntryList  || $entryNum < 0 } {
    ## frputs lnEntryList entNum
      return -code error "Entry number \"$entryNum\" out of range."
    } elseif {$type != {}} {
      # we create an array name and link it to the entryNum with the
      # options(entryList) list.
      set newAryName ${selfns}::typAry[incr options(AryCount)]
      # Insert it in the list.
     # frputs options(entryList) entryNum lnEntryList type
      set options(entryList) \
	  [lreplace $options(entryList) $entryNum $entryNum-1 $newAryName]
      set options(-entrycount) [llength $options(entryList)]
      upvar [lindex $options(entryList) $entryNum] entAry
      array set entAry $v(${type}MenuOptionsPairs)
      foreach op [array names entAry] {
	if {[info exists options($op)] && $entAry($op) != {}} {
	  set entAry($op) $options($op)
	}
      }
    }
    uplevel "
      upvar [lindex $options(entryList) $entryNum] $reqAryName
    "
  }
  
  # This routine calculates and sets indicator characters and indention
  # for a cascade level. This will be called each time an entry is made
  # at a given level and when any indicator is change or if a cascade's
  # indention is changed.  The result is kept in the level's options
  # array. If a calculation result changes the routine returns true, else
  # false (true then indicates that a level repost is needed).

  proc caculateIndention {} {
    upvar selfns selfns
    upvar ${selfns}::options options
    upvar $options(instVault) v
    upvar $v(tabAryName) tabAry
    lassign {0 0 1 1} oldResult oldWidth options(casTab)\
	indentSize options(casIndent)
    
    if {[info exists options(maxTextWidth)]} {
      set oldWidth $options(maxTextWidth)
    }
    if {[info exists options(tabStopFor)]} {
      set oldResult $options(tabStopFor)
    }
    set font [getOpVal -indentionfont]
    # entry font is a bit more work...
    set locAry(-font) [$options(hull) cget -font]
    array set locAry $v(tagBase)
    set defaultFont $locAry(-font)
    
    if {$options(level) != 0} {
      set options(casIndent) [::textMenu::getIndention $options(-indention)]
      set options(casTab) \
	  [font measure $font -displayof $v(textWindowName) $options(casIndent)]

    }
    if {$options(indicatorCount) > 0 || 1} {
      set lnEntryList [llength $options(entryList)]
      set indicSiz 0
      set indicSiz $options(-indicatorsize)
      # set tabChar "\t"
      
      set preSize [expr {$indicSiz / 2}]
      
      set  preIndicator    [string repeat " " $preSize]
      set postIndicator    [string repeat " " [expr {$indicSiz  - $preSize  -1}]]
      
      
      for {set entry 0} {$entry < $lnEntryList} {incr entry} {
	setEntAry $entry entAry
	lappend txtWidth [font measure \
			      [expr {[info exists entAry(-font)] && \
					$entAry(-font) != {} ? \
					$entAry(-font) : $defaultFont}] \
			      -displayof $v(textWindowName) $entAry(-label)]
	if {[set typeSufix [string range $entAry(-type) 0 6]] in \
		{cascade checkbu radiobu}} {
	  
	  set entAry(-decodedindicators) \
	      [::textMenu::getIndicators [getOpVal -${typeSufix}indicators] \
		   $preIndicator]
	  foreach spChar $entAry(-decodedindicators) {
	    # frputs spChar preIndicator preSize
	    if {![info exists tabAry("$spChar$postIndicator")]} {
	      set tabAry("$spChar$postIndicator") \
		  [font measure $font\
		       -displayof $v(textWindowName) "$spChar$postIndicator"]
	    }
	    lappend indentSize $tabAry("$spChar$postIndicator")
	  }
	}
      }
    }
    
    # Use 'eval' to break up the string for expr
    set options(maxTextWidth) [eval {expr max([join $txtWidth ,])}]
    set options(tabStopFor)   [eval {expr max([join $indentSize ,]) + \
					 $options(casTab)}]
    return [expr {$oldResult != $options(tabStopFor) ||\
		      $oldWidth != $options(maxTextWidth)}]
  }
  
  # ########################### PrePost ##################################
  
  # PrePost the PrePost proc is 'private' and not intended to be called by
  #         other than internal code. PrePost is called by the 'add' 'insert'
  #         'delete' and possibly 'entryconfigure' commands. Its job is to:
  
  #      a.) unpost any cascades at the level it is called from (
  #          assuming '-multipleopen' is false) except the index passed.
  #      b.) Or if called with a start location, just clear cascades from
  #          there to the end.
  #         
  #  We extend the 'menu' functionality by optionally allowing multiple
  #  cascades to be open at each level. This is the '-multipleopen' option
  #  and is present at each level. Each levels option defaults to it parents
  #  value.
  #
  #  argument is an array set list. Values recognized;
  #           entry    (0)   entry number to start from
  #           except   (nil) nil  do above only if -multipleopen is false
  #                    !nil do above regardless of -multipleopen
  #                        this also updates the indicators
  #           unpost   (3)   3 or 1, value to set -post to.
  # 
  # In all cases if we are unposting the stuff under a cascade entry,
  # not the cascade entry itself.
  
  proc PrePost {args} {
    upvar options options selfns selfns
    array set myAry {entry 0 unpost 3 except {}}
    array set myAry $args
    set entry $myAry(entry)
  ## frputs options(-multipleopen) "[array get myAry] "
    if {!$options(-multipleopen) ||\
	    $myAry(except) != {} } {
      for {} {$entry < [llength $options(entryList)]} {incr entry} {
	setEntAry $entry locAry
	if {$locAry(-type) == "cascade" && $locAry(-post) == 2} {
	  if {$myAry(except) == {}} {
	    $locAry(-menu) unpost [expr $myAry(unpost) == 1]
	  } else {
	    $locAry(-menu) TextCascadeUnpost unpost $myAry(unpost)
	  }
	  set locAry(-post) $myAry(unpost)
	}
      }
    }
  }

  # This routine is called by the insert/add methods to update the display
  # If the added entry changes the indention (by adding an entry requiring
  # an indicator) it will call to repost all, otherwise, just from the
  # entry down. Call parms are 'entry "entry number"'
  
  proc updateLayout {args} {
    upvar options options
    upvar self    self
    
    if {[set ${options(instVault)}(posted)] && $options(-post) == 2} {
       # Intent here is to layout from given down
      $self TextCascadeUnpost  {*}$args
      $self masterLayout {*}$args
    } else {
      # need to set a "master layout needed" flag here. Be nice if it contained
      # the 'args' (but they would need to be update to indicate an early entry
    }
  }
  # Here is a private routine to find the 'insert'  for the last entry posted
  # OR rather for the entry prior to the one we wish to post just
  # after it in the display. I.e. if the last entry is an open cascade
  # we need to recursively find its last entry.
  #
  # Our call...
  #
  # entry # want the location for. This should be a number or 'end' if
  #                                the last entry in the cascade is desired
  # tag   this should be "$win.$entry" of the cascade we are looking at.
  #
  # tag is used if the 'entry' has zero entries
  #
  # We depend on Snit method to pass in 'win' and 'options'

  method FindLastTag {args} {
    # frputs self args
    lassign $args entCount tag
    if {$entCount == "end"} {
      set entCount [llength $options(entryList)]
      incr entCount -1
    }
     if {$entCount >= 0} {
    
      setEntAry $entCount entAry
      set tag $win.$entCount

      if {$entAry(-type) == "cascade" && $entAry(-post)} {
	return [$entAry(-menu) FindLastTag end $tag]
      }
    }
    # frputs tag self args
    return [expr {[lindex [$options(hull) tag range $tag] end] -1}]   
  }



  
  # Private Unpost (TextCascadeUnpost)
  # This has three flavors:
  # We only unpost the current entry and children
  # If we get a start location, only from there down with those children.
  
  # If at level 0, and entry 0, clear the window and then,
  # If this is a toplevel menu, withdraw the window.
  
  # args: entry  (0)  from entry down
  #       unpost (3)  -post is set to this, must be 1 or 3
  #                   1 will leave unposted 3 will post
  #                   when parent is reposted.
  
  method TextCascadeUnpost {args} {
    array set myAry {entry 0 unpost 3 keeptags 0}
    
    array set myAry $args
    
    # PrePost will unpost all the cascades at this and lower levels
    PrePost except 1 unpost $myAry(unpost)
    # Now, clear from 'start' to the end...
    set start [$options(hull) tag range ${win}.$myAry(entry)]
    set endCo $options(-entrycount)
    # If we are called after an insert, the entrycount will be too high
    set end {}
    while {$end == {} && [incr endCo -1] >= $myAry(entry)} {
      set end   [$options(hull) tag range ${win}.$endCo]
    }
    # Not clear if we need to remove all the tags..
  ## frputs start end args
    if {$start == {} || $end == {}} {
      return    }
    # -code error "Could not find anything to delete"
    $options(hull) delete [lindex $start 0] [lindex $end end]
    if {$options(level) != 0 || $myAry(entry) != 0} {
      if {!$myAry(keeptags)} {
	set tags [$options(hull) tag names]
	foreach tag $tags {
	  if {[string match *$win.* $tag]} {
	    if {[regexp {[^0-9]+([0-9]+).*} $tag -> num]} {
	      if {$num >= $myAry(entry)} {
		$options(hull) tag delete $tag
		# frputs DELETETAG tag
	      }
	    }
	  }
	}
      }
      return
    }
    # set options(-post) $myAry(unpost)
    # set v(posted) 0
    # $options(hull) tag delete {*}[$options(hull) tag names]
    # $options(hull) delete 1.0 end
  }
  
 
  ###################### The methods ####################################
  
  # This is the user level 'unpost' which we want close to 'Menus'
  # unpost. Unpost just removes a menu from the display, it does
  # not alter it. At the same time, if the menu is in its own frame,
  # i.e. not a -toplevel we do nothing.
  #
  
  method unpost {{unpostsubs 0}} {
    set unpost [expr {$unpostsubs ? 3 : 0}]
    # This little kludge is to get around the unpost the menu handling
    # code sends us on menu button up (only in MSW)
  ## frputs #4 #3 #2 UNPOSTTEST  "[wm state  $options(-toplevel)] "
    if {$::tcl_platform(platform) == "windows"} {
      if {([catch {array set infoAry [info frame -2]}] == 0 && \
	       "::tk::MbButtonUp" == $infoAry(proc)) || \
	      ([catch {array set infoAry [info frame -3]}] == 0 && \
		   "::tk::MbButtonUp" == $infoAry(proc))} {
	after 0 [list set ::tk::Priv(postedMb) $::tk::Priv(postedMb)]
	after 0 [list set ::tk::Priv(focus) $::tk::Priv(focus)]
	frputs UNPOSTFAIL infoAry(proc) win
	frputs UNPOSTFAIL infoAry(proc) "[wm state  $options(-toplevel)] "
	return
      }
    }
  ## frputs options(level)
    if {$options(level) != 0} {
      $self TextCascadeUnpost \
	  unpost $unpost\
	  keeptags [expr {$options(level) == 1}]
      catch {$options(-parent) SetIndicatorToUnposted \
		 -menu [regsub {^::} $win {}]\
		 unpost $unpost} er
    ## frputs er
      return
    }
    # if {[tk windowingsystem] == "win32"} {return}
  ## frputs #6 #5 #4 #3 #2 unpost self
    wm withdraw $options(-toplevel)
    
    if {$::tcl_platform(platform) == "windows"} {
      upvar $options(instVault) v
      ::textMenu::clearFocusTrack v
    }
  }
  
  # A private method to switch the cascade indicator
  # we are passed the '-menu' of the entry we need to change
  
  method SetIndicatorToUnposted {args} {
    array set locAry [list unpost 1 {*}$args]
    
    for {set entry 0} {$entry < [llength $options(entryList)]} {incr entry} {
      
    ## frputs entAry(-menu) locAry(-menu)
      setEntAry $entry entAry
      if {$entAry(-type) != "cascade" ||\
	      $entAry(-menu) != $locAry(-menu)} {continue}
      if {$entAry(-indicatoron)} {
	set tags $entAry(tags)
	set tagindict [lindex $tags 0]
	$options(hull) tag configure $tagindict\
	    {*}[buildTagUsing -indicator1tagconfig]
	$options(hull) replace $tagindict.first $tagindict.last \
	    [lindex $entAry(-decodedindicators) 0]\t $tags
	set entAry(-post) $locAry(unpost)
	return
       }
    }
  }
  
  #
  # This configure code gets the 'textmenuOps' (see snit::macro by this name)
  # Some of these need to be propagated to the active levels.
  # It is not yet clear if we need to propagate to current entries.
  
  # Since this is the default when entries are created, we would need to
    # remember if the current value was the old default or an override.
    
  method optionConfig {args} {
  ## frputs args
    $self myconfigure {*}$args
  }
  
  method myconfigure {args} {
    variable ::textMenu::
    lassign $args op
    # This array is intended to direct the set up of options
    # beyond just putting them in the entrys op array.

    # frputs "MYCONFIGURE " args type self win 
    
    switch [llength $args] {
      0 {return [array get options]}
      1 {if {[info exists options($op)]} {return $options($op)}}
      default {
	upvar $options(instVault) v
	foreach {op val} $args {
	  ::textMenu::verifyOp $op $val
	  set options($op) $val
	  if {$op ni $(noPropTextmenuOps)} {
	    foreach cascade [lrange $v(cascadeNames) 1 end] {
	      $cascade optionsConfig $op $val
	    }
	    if {$op in $(textmenuRepostOps)} {
	      updateLayout entry 0
	    }
	  }
	}
      }
    }
  }

  # optionsConfig should only be called from above where all the checking
  # is done.

  method optionsConfig {args} {
    lassign $args op val
    set options($op) $val
  }

  # To reuse code, we arrange for the 'add' method to call the here
  # (the 'insert' method) so that all the set code is here.
  
  method add {args} {
    return [$self insert [llength $options(entryList)] {*}$args]
  }
 
  method insert {args} {
    variable ::textMenu::
    upvar $options(instVault) v
    set args [lassign $args index type]
    # First get the entry number
    if {[set locIndex $index] != [llength $options(entryList)]} {
      set locIndex [$self index $index]
    }
    if {$locIndex == "none"} {
	return -code error "Index \"$index\" is not a legal 'insert' index."
    }
    # ok, here is the order of things...
    # remember last one in is the one we get
    #
    set type [::textMenu::cleanOp $type \
		  {cascade checkbutton command\
		       radiobutton separator tearoff} "type"]
    
    # Lets weed out any attempt to put in an illegal 'tearoff' entry
    # Doing this here means we don't have to back it out.
    
    if {$type == "tearoff" &&\
	    ([llength $options(entryList)] != 0 || $options(level) != 0)} {
      return -code error "Tearoff entry must be first entry in main menu."
    }
    
    # Now set up the array with the defaults using 'setEntAry'

    setEntAry $locIndex entAry $type

    # frputs selfns "ary=[array get entAry]< "
    if {[llength $args] > 0} {
      if {[catch {setConfigOps {*}$args} er] != 0} {
	# frputs er locIndex args
	$self delete $locIndex
	return -code error $er
      }
    }

    set oldIndicatorCount $options(indicatorCount)
    set cname {}
    # Ok, the options are processed, now lets set up  the entry
    # refresh our array
    # set entAry(-type) $type
    switch $type {
      cascade {
 	if {$entAry(-indicatoron)} {
	  incr options(indicatorCount)
	}
	# If this is level 0 we toss the first suffix so as not to show
	# our nickers
	set namePrefix [regsub -all {^::} $win {}]
	if {$options(level) == 0} {
	  set namePrefix $v(baseName)
	}
	# frputs win namePrefix
	if {[set cname $entAry(-menu)] == {}} {
	  set cname $namePrefix.$entAry(-label)
	}
	#frputs cname win v(cascadeNames)
	if {![string match "${namePrefix}.?*" $cname]} {
	  $self delete $locIndex
	## frputs nameprefix cname
	  return -code error \
	      "Submenu path name \"$cname\" must be a child of menu,\
               i.e. start with \"${namePrefix}.\""
	}
	if {$cname == "${namePrefix}." || \
		"::$cname" in $v(cascadeNames)} {
	  # if this is a requesed name, we complain, if not just fix it
	  if {$cname == $entAry(-menu)} {
	  ## frputs cname 
	    $self delete $locIndex
	    return -code error "Menu name \"$cname\" already used."
	  }
	  # In use. lets fudge it.
	  while {[set cname $cname$locIndex] in $v(cascadeNames) &&\
		     [incr failsafe] < 10 } {}
	}
	#frputs NEWNAME cname
	set entAry(-post) 0
	# frputs locIndex addOps options(entryList)
	############################### instantiate the new level ########
	# Build the minimum pass list
	foreach op [concat [array names entAry] $(minLocalOps)] {
	  lappend minOps $op [getOpVal $op]
	}
	if {[catch {menulist ::$cname\
			{*}$minOps\
			level [expr {$options(level) + 1}]\
			-parent $win\
		      }\
		 err] != 0} {
	## frputs err
	  $self delete $locIndex

	}
	###################################################################
	
	# We put this here so that it is not inherited by the next level
	set entAry(-menu) $cname
	# frputs cname
      }
      command {
      }
      checkbutton -
      radiobutton {
 	if {$entAry(-indicatoron)} {
	  incr options(indicatorCount)
	}
      }
      separator {
	# no code required here.
      }
      tearoff {
      	set entAry(-command) [mymethod Tearoff]
      }
    }
    # frputs cname
    # We have added a new entry. Check if we need to updateLayout or just add the new
    # entry. In any case, updateLayout is only require for this level/win.
    # The following changes require updateLayout:
    #
    # An insert (to get the tags right after the changes, but  only from the
    #            insert  down). Actually just post from here to end.
    # An entry that adds the first indicator at this level/win
    # (to adjust indention)
    #
    if {[caculateIndention] || $oldIndicatorCount != $options(indicatorCount)} {
      # repost on indicator
      set locIndex 0
    }
    # frputs locIndex args win options(entryList)
    updateLayout entry $locIndex
    return $cname   
  }
  
  method activate {args} {
    upvar $options(instVault) v
    # We translate this to a line number and send it to the master
    set locIndex [$self index [lindex $args 0]]
    ## frputs HELLO args locIndex
    if {$locIndex == "none"} {
      $v(textWindowName) active "none"
    } else {
      set tag ${win}.${locIndex}priv
      ## frputs tag
      set cmd [$options(hull) tag bind $tag <Enter>]
      ## frputs cmd
      eval $cmd
    }
  }
  
  method entrycget {args} {
    set args [lassign $args index op]
    if {[llength $args] != 0 || $op == {}} {
      return -code error \
	  "Argument count wrong! Should be: \"entrycget index op\""
    }
    return [lindex [$self entryconfigure $index $op] end]
  }
  
  method entryconfig    {args} {$self entryconfigure {*}$args}
      
  
  method entryconfigure {args} {
  ## frputs args
    variable ::textMenu::
    set args [lassign $args index]
    set locIndex [$self index $index]
    # frputs args index locIndex options(entryList)
    if {$index == "none"} {
      return -code error "Entry \"$index\" does not exist."
    }
  ## frputs #2 #3 #4 args 
    # get the entry...
    setEntAry $locIndex entAry
    
    # if only one, it is a request to read it.
    if {[llength $args] == 1} {
      set op [lindex $args 0]
    ## frputs #2 #3 #4 args entAry($op)
      if {[info exists entAry($op)]} {return [list $op {} {} {} $entAry($op)]}
      # Still here...oops
      return -code error "Option \"$op\" is not in entry \"$index\""
    }
    if {[llength $args] == 0} {
      # A request for all the values
      set result {}
      foreach {op val} [lsort -stride 2 [array get entAry]] {
	lappend result [list $op {} {} {} $val]
      }
      return $result
    }
    setConfigOps {*}$args
    if {[caculateIndention]} {
      updateLayout
    }
    return {}
  }

  proc setConfigOps {args} {
    # Test the options...
    if {[llength $args] & 1} {
    ## frputs length args
      return -code error "Must have an even number of entries in \"$args\""
    }
    upvar entAry   entAry
    upvar options  options
    # set up the verify array
    foreach {op val} $args {
      if {![info exists entAry($op)]} {
	# frputs noop op
	return -code\
	    error \
	    "Option \"$op\" is not available in a \"$entAry(-type)\" entry."
      }
      ::textMenu::verifyOp $op $val

      # If still here, all is well
      # so we don't have to scan the entries each post to set up the
      # indicator count, we track it here...
      set oldVal $entAry($op)
      set entAry($op) $val
      if {$op == "-indicatoron" && $oldVal != $val} {
	# an indictoron was fipped, so the count needs to be updated
	incr options(indicatorCount) -1
	if {$val} {incr options(indicatorCount) 2}
      }
    }
  }
  
  method delete {args} {
    upvar $options(instVault) v
    lassign $args index1 index2 desFlag
    set index1 [$self index $index1]
    if {$index1 == "none"} {return}
    if {$index2 != {}} {
      set index2 [$self index $index2]
      if {$index2 == "none"} {
	set index2 [$self index "end"]
      }
    } else {
      set index2 $index1
    }
    # do not allow deletion of a tearoff
    if {$options(level) == 0 && $index1 == 0} {
      setEntAry 0 locAry
      if {$locAry(-type) == "tearoff" && $desFlag != "destructor"} {
	return -code error "The tearoff entry may not be removed"
      }
    }
    # We need to repost from the new index1 down. Lets unpost
    # those now, but first save the location incase the roll back
    # leaves nothing or zero.
    # set loc [$options(hull) tag range ${win}.$index1]
    if {[info commands $options(hull)] != {}} {
      $self TextCascadeUnpost entry $index1
    }
    # first remove the arrays...
    set entryNum $index2
    foreach aryName [lreverse [lrange $options(entryList) $index1 $index2]] {
      upvar $aryName myAry
      # adjust the indicator count if needed...
      if {[info exists myAry(-indicatoron)] && $myAry(-indicatoron)} {
	incr options(indicatorCount) -1
	if {$myAry(-type) == "radiobutton"} {
	  ::textMenu::delete-radioEntry $entryNum
	}
      }
      if {$myAry(-type) == "cascade"} {
	# frputs DELETE entryNum self v(cascadeNames) myAry(-menu)
	set savePosted $v(posted)
	set v(posted)  0
	$myAry(-menu) destroy
	set v(posted) $savePosted
	set ent [lsearch -exact $v(cascadeNames) ::$myAry(-menu)]
	if {$ent == -1} {
	  return -code error "Internal error: cascade name not found."
	}
	set v(cascadeNames) [lreplace $v(cascadeNames) $ent $ent]
      }

      
      unset myAry
      incr entryNum -1   
    }
    set options(entryList) [lreplace $options(entryList) $index1 $index2]
    set options(-entrycount) [llength $options(entryList)]
    if {$options(-post) == 2 && $desFlag != "destructor"} {
      $self masterLayout entry $index1
    }
  }
  
  # The 'index' method does the translation from the various forms of
  # indexing (see menu manpage). These are:
  #            @number (y-loc) number (index) pattern
  #            or one of the 4 strings enumerated below.
 
  method index {args} {
    upvar $options(instVault) v
    
    if {[llength $args] != 1} {
      return -code error "\"index\" call requires exactly 1 parameter."
    }
    set arg [lindex $args 0]
    switch -exact $arg {
      end -
      last {return [expr {[llength $options(entryList)] - 1}]}
      none {return $arg}
      active {
	if {$v(activeTag) == {} ||\
		[set tag [$options(hull) tag ranges $v(activeTag)]] == {} ||\
		$win != [string range $v(activeTag) 0\
			     [string last "." $v(activeTag)]-1] } {
	  return "none"
	}
	return [string range $v(activeTag)\
		    [string last "." $v(activeTag)]+1 end-4]
      }
      default {
	# Gets a bit harder now...
	if {[string index $arg 0] == "@"} {
	  # index with an @x,y is used by the select code
	  # but the menu code only uses the line number (i.e. @y)
	  # if we find a comma, lets send it to the hull to
	  # make it work for the select code
	  if {[string match "*,*" $arg]} {
	    return [$options(hull) index $arg]
	  }
	  # convert to @x,y from @y and get the text char and
	  # convert that to the line number only
	  set el [expr {int([$options(hull) index\
				 @1,[string range $arg 1 end]])}]
	  # get the tags on the end of that line
	  set taglist [$options(hull) tag names $el.end]
	  # use the 'user' tag the tag looks like:
	  # $win.<ent#>user
	  # isolate that tag
	  set val [lsearch -inline $val "*user"]
	  # if the $win part mismatches (or is absent, i.e no tag at all)
	  if {![string match "$win.*" $val]} {
	    return "none"
	  }
	  # use string range to isolate the entry number
	  return [string range $val [string length $win.] end-4]
	}
	if {[string is integer -strict $arg]} {
	  if {$arg >= 0 && $arg < [llength $options(entryList)]} {return $arg}
	  return "none"
	}
	# It must be a pattern. We are really not arranged to make this easy
	# set options(-entrycount) [llength $options(entryList)]
	
	for {set index 0} {$index < [llength $options(entryList)]}\
	    {incr index} {
	  setEntAry $index tmpAry
	  if {[string match $arg $tmpAry(-label)]} {return $index}
	}
	return "none"
      }
    }
  }

  method type {args} {
    set index [$self index {*}$args]
    return [$self entrycget $index -type]
 }
  
  method invoke {args} {
    set index [$self index {*}$args]
     if {[info exists entAry(-command)]} {
      uplevel #0 $entAry(-command)
    }
  }
  

  # ########################### post ##################################

  # The post method can only be called by the main menu which usually
  # supplies an x,y location. For cascade, call postcascade!

  # Its job is to call the postcommand and then the masterLayout method.
  # and finally (if level 0 and not done yet) to position and deiconify
  # the window. Thru watching other meuns we divine that post does not
  # return until the user has either selected an entry
  # (unpost via docommand) or has selected another menu or none at all.
  
  method post {args} {
    # The user call here on menus and submenus. We need to make sure the
    # postcommand is taken care of (i.e. not a submenu).
    upvar $options(instVault) v
  ## frputs POST options(-post)
    if {$options(level) != 0} {
      lassign $args entCount
      snit::integer $entCount -min 0 -max $options(-entrycount)
      
      #array set locAry [lindex $options(entryList) $entCount]
      setEntAry $entCount locAry
      if {$locAry(-type) != "cascade"} {
	return -code error \
	    "Entry type is \"$locAry(-type)\" but must be \"cascade\"."
      }
      # The tcl/tk menu command does not say it changes a submenu post into
      # a postcascade, but why not?
      $self postcascade $entCount
      return
    }
     if {$options(-postcommand) != {}} {
      uplevel #0 $options(-postcommand)
    }
  ## frputs PREPOST options(level) args options(-toplevel)

    set v(posted) 1
    # We ought to set up a flag that indicates we need a new layout
    # instead of doing it each post.
    $self masterLayout 
  ## frputs BACK options(level) args options(-toplevel) options(-post)
  
    if {[info exists options(-toplevel)] && \
	    [winfo exist $options(-toplevel)]} {
      set w [winfo toplevel $options(-toplevel)]
      lassign $args x y
      # set the required width & height but limit by '.' size
      set lines [$options(hull) index end]
      set font [$options(hull) cget -font]
      set reqHeight [expr {int($lines) * \
			       [font metrics $font -displayof $w -linespace]}]
      lassign [split [wm geo .] x+] maxwidth maxheight
      set txtWidth      [winfo reqwidth $v(textWindowName)]
      set topLevelWidth [winfo reqwidth $w]
    ## frputs txtWidth topLevelWidth v(width)
      wm geo $w\
	  [expr {min($maxwidth,$v(width)) +\
		     4 * [$v(textWindowName) cget -bd] +\
		     $topLevelWidth - \
		     $txtWidth}]x[expr {min($maxheight,$reqHeight)}]+$x+$y
     
      #wm geo [set w $options(-toplevel)] "+$x+$y"
      if {$::tcl_platform(platform) == "windows"} {
	::textMenu::setFocusTrack $options(instVault)
      }
      wm deiconify $w
      wm attributes $w -topmost 1
    ## frputs w "[wm geo $w] " options(hull) win
    }
    set options(-post) 2
  }
  
  
  # ########################### masterLayout ##################################
  
  # masterLayout actually builds the text window from the 'options(entryList)'
  #      entries.
  
  # Where we put things:
  #      If this is entry 0 at level 0 we set the insert location to 1
  #      (note we work with line numbers while the actual insert will be n.0
  #      where n is the line number).
  #      If the entry number is not zero, we look at entry n-1 and insert
  #      after that.
  #      If the entry number is zero and level is not 0, the call should, if
  #      feasable, pass in the insert location -1. If it does not masterLayout
  #      will search the tags for the parents location and insert after that.

  # Indention:
  
  #      There are two indentions, the 'cascade' indention and the 'indicator'
  #      indention.

  #      The 'indicator' indention is other than zero only if
  #      this 'level' has one or more 'indicator' entries in which the
  #      '-indicatoron' option is true. Also, the 'indicator' indention has
  #      a tab in it. 

  #      The 'cascade' indention is an indention applied to each cascade.
  #      This indention defaults to a given value and may contain special
  #      characters which may change from cascade to cascade. It does NOT,
  #      however, contain a tab.
  #      The 'cascade' indention starts at zero length for the level zero
  #      entries.
  #      For each additional cascade it is (usually) increased based on the
  #      given cascade (or the default if not set in the cascade).

  #      On each call to masterLayout at levels other than zero, it will look
  #      at the previous line's indention. If the call is for entry 0 at
  #      the given (non-zero) level, the indention will be set to the indention
  #      of the previous line PLUS the indention requested for this cascade.
  #      For calls that start at an entry other than 0, the previous lines
  #      indention is used. This sounds easy, but the tab make it a bit more
  #      complicated. Here is what we do.
  #
  #      The indention tag is used to tranfer info on the indention. The
  #      convention on tag usage in the 'indicator' is that it immediately
  #      follows the any special 'indicator' character. (By the way, tabs
  #      are used because the 'indicator' characters are not of fixed width.)
  #
 
  #      To post a cascade
  
  #      postcascade is normally called by clicking on the 'indicator'.
  #                  it may also be called by the user.

  #      In cascade entries, the -post option indicates its state:
  #      0 has not been opened -command not yet called
  #      1 command called, menu still not posted (or has been unposted)
  #      2 cascade is posted.

  # call args:
  #      insert (optional) 'insert location -1' (see notes above)
  #      entry             'start with this entry'

  
  # The 'options' array is the main instance data pool. Key elements are:
  #      instVault      the array name (v) which is the menu instance data pool
  #      hull           the 'text' window command path name
  #      level          the level of this instance, (only 0 and != 0 matter)
  #      -entrycount    the number of entries in this cascade
  #      entryList      list in entry order of each entries fully qualified
  #                     array name (arrays are in the 'selfns' namespace)
  
  # masterLayout returns the insert location of the last line used
  
  method masterLayout {args} {
    # frputs self  "[myvar options] " options(master)
    # frputs "MASTERLAYOUT " args options(level) selfns \
    #	${options(instVault)}(cascadeNames)
    # frputs self win
    upvar $options(instVault) v
    if {$v(posted) == 0} {return}
    array set locAry {entry 0 insert -1 parentNum -1}
    array set locAry $args
    # This should tell updateLayout that we are good to go.
    set options(-post) 2
    if {$options(level) == 0} {
      
      array set locAry  [array get options -*]
      # The caller has the option of telling us to start at an entry
      # other than 0 (which we default to). We always go from there
      # to the end of entries at the current level. While we number
      # 'levels' only level zero is unique, i.e. there may be several
      # level n's for any n greater than zero.
      
      if {$locAry(entry) == 0} {
	# level 0, clear the window
	set locAry(insert) 0
	$options(hull) delete 1.0 end
	set options(casIndent) ""
	set tabStops 1
	# v(width) will be the request max window width
	# The width of "." will be used to set max window width
	set v(width) 0
	# set locAry(-indention) {}
	::textMenu::check-radioClear $options(instVault)
      }
    }
    # Here we figure where our insert pointer should be
    # If the passed entry number is not zero, compute from there
    # If it is 0 and this is level zero it was set above
    # If it is 0 and this is NOT level zero, it should have been passed in
    # or, we look it up by finding the parent's tag.
    
    # Huston we have a problem! If the desired entry is not 0 and the
    # prior entry is an open cascade, we need to find the end of that
    # cascade, and if that is an open cascade, recur until the last
    # is not an open cascade. ** Isn't recursion fun! **
    
    set entCount $locAry(entry)
    # frputs locAry(entry) locAry(insert)
    if {$locAry(insert) == -1} {
      if {$entCount != 0} {
	# set loc [$options(hull) tag range ${win}.[expr {$entCount - 1}]]
	set queryCount [expr {$entCount -1}]
	set locAry(insert) [$self FindLastTag $queryCount $win.$queryCount]
	# frputs locAry(insert) 
      } else {
	# entry 0 in a new cascade. Look up the parent.
	set parTags [$options(hull) tag names]
	set parTag [lsearch -all -inline $parTags\
			*$options(-parent).$locAry(parentNum)*user*]
	if {[llength $parTag] != 1} {
	  return -code error \
	      "Internal error, could not find cascade parent at:\
                       $options(-parent) entry $locAry(parentNum)!"
	}
	set locAry(insert) [$options(hull) tag range  $parTag]
 	# frputs locAry(insert) parTag
      }
    }
    # better safe....
    if {$locAry(insert) < 0} {
      return -code error "Internal error, bad insert location '$locAry(insert)'"
    }
    # To simplify things we will use only the first entry and just the init
    # part. This allows callers to pass the result of a 'tag range' without
    # isolating the first value or striping the decimal part.
    
    set end [expr {int([lindex $locAry(insert) 0])}]
    if {$options(-entrycount) == 0} {return $end}
    
    # Here we figure out the 'cascade' indention #####################
    # We want two parts:
    # priorIndent is what was used last cascade including up to the last tab.
    # ourIndent   is what is after that tab plus if a new cascade, its indent.
    # 
    #      The 'text' widget code counts tabs as characters so 'get's will
    #      return the given string. There are two cases, a new cascade
    #      (entry is 0) and continuation of the current. Here are the steps:
    
    #      1.) Get the indention tag for/and the previous line. The tag (as part
    #          of its name, has the string length of the 'cascade' indention
    #          and is configured with any tab stops used.

    #      2.) If 'entry' is 0 and the prior ends in \t, fix char \t-1 to space
    #          save the tabs and set 'ourIndent' as per the option.
    #          If the prior does not end in \t, split on \t, the part after
    #          \t (or sart of line) with our option indent becomes 'outIndent'
    #          (Note: this covers the cascade with noindicator case.)

    #      3.) If 'entry' is not 0 (continuation of current cascade).
    #          In this case we either have a casIndent+ an indicatorIndent.
    #          or just the casIndent (which is what we want). The number at
    #          the end of the tag is the size. Use that to keep just the
    #          prior Indent with out the indicatorIndent.
  
    lassign {} priorIndent ourIndent tabStops
    if {$options(level) != 0} {
      # Step 1:
      set prevTags [$options(hull) tag names $end.0]
      set prevItag [lsearch -inline -all $prevTags *indent*]
      set tabStops [$options(hull) tag cget $prevItag -tabs]

      if {[llength $prevItag] != 1} {
	return -code error "Internal error:\
             Expected 1 indention tag at $end.0 but found [llength $prevItag]."
      }
      # seperate into what we want... do we need this ??????????????????????
      # we need 'ind' but there may be a better way to get it.
      if {[regexp {(.+)\.([0-9]+)indent([0-9]+)$}\
	       $prevItag => menu ent ind] == 0} {
	return -code error "Internal error: Failed to parse tag $prevItag."
      }

      set priorIndent \
	  [$options(hull) get {*}[$options(hull) tag range $prevItag]]
      if {$entCount == 0} {
	# Step 2:
	if {[string index $priorIndent end] == "\t"} {
	  set priorIndent [string replace $priorIndent end-2 end " \t"]
	} else {
	  set splIndent [split $priorIndent \t]
	  #set ourIndent [lindex $splIndent end]$casIndent
	  set priorIndent [join [lrange $splIndent 0 end-1] \t]
	}
      } else {
	# Step 3: a continuation of this cascade
	set tabStops [lrange $tabStops 0 end-1]
	set priorIndent \
	    [string range $priorIndent 0 \
		 [string range $prevItag [string last "t" $prevItag]+1 end]-1]
      }
    }

    # tagLevel is applied to the indention for all entries at a given level
    # it is used to line every thing up.
    # set tagLevel tag$options(level)
    lappend tabStops [expr {$options(tabStopFor)  + \
			    ([set lastSize [lindex $tabStops end]] != {} ?\
				 $lastSize : 0)}]
    set v(width) [expr {max( $v(width),\
			    ([lindex $tabStops end] + $options(maxTextWidth)))}]
    set curIndent "$priorIndent$options(casIndent)"
    # frputs tabStops options(tabStopFor)  priorIndent ourIndent
    # Use insert mark to keep track of where we are
    incr end
    $options(hull) mark set insert $end.0
    for {} {$entCount < $options(-entrycount)} {incr entCount} {
      setEntAry $entCount entAry
      
      # array set entAry $ent
      # Set up tag(s) here............
      # There are 4 or 5 tags per entry as follows:
      # tag 1 aka 'tag' real name '<$win>.<$entryCount>'
      #                   (Note 'win' differs for each cascade entries menu)
      # tag 2 aka 'tagpriv' name '<tag1>priv' used only by us to provide
      #                                       command & active binding
      # tag 2n aka 'tagprivN name '<tag1>privN' used to contain 'normal' i.e.
      #                                         not active tagops
      # tag 3 aka 'taguser' name '<tag1>user' users tag
      #                                         (gets -tagbind & -tagconfig)
      # tag 4 aka 'tagindent' name '<tag1>indent' tags only the indention
      # tag 5 aka 'tagindict" name '<tag1>indict' tags only the indicator
      
      # We use the 'tagpriv' on an entry to connect to <Enter> and
      #                                                <Leave> events
      # We also bind a 'tagpriv' to <1> which calls any 'command'
      #                                                attached to the entry.
      #
      # tag 1     applies to the whole entry 
      # tag 2 & 3 applies only to the -label part
      # tag 4     applies only to the indention + indicator
      # tag 5     applies only to the indicator

      # tag priority (lowest to highest) (tag 1) (tag 3) (tag 4) (tag 5)
      # The user can change the tag options on 3, 4 & 5
      # The user can change/set the bindings on 3
      
      # create the tags
      set tag ${win}.$entCount
      set tagpriv   ${tag}priv
      set tagprivN  ${tag}privN
      set taguser   ${tag}user
      set tagindent ${tag}indent[string length $priorIndent]
      set tagindict ${tag}indict
      # frputs "[mymethod toActive] "
       # We set the active info by raising and lowering tagpriv & tagprivN
      set notActiveTagConf [buildTagUsing \
				{-foreground -background -borderwidth}]
      $options(hull) tag configure $tagpriv {*}$notActiveTagConf
      
      if {!$options(-editmode) || $entAry(-editmodeexempt)} {
	$options(hull) tag bind $tagpriv <ButtonRelease-1> \
	    [mymethod doCommand $entCount]
      }

      $options(hull) tag configure $tagindent\
	  {*}[buildTagUsing -indenttagconfig]
      
      if {[info exist entAry(-tagconfig)]} {
	$options(hull) tag configure $taguser {*}[buildTagUsing -tagconfig]
      }
      if {[info exist entAry(-tagbind)]} {
	foreach {event script} [getOpVal -tagbind] {
	  $options(hull) tag bind $taguser $event [string map\
						     [list \
							  %I $taguser\
							  %p $self\
							  %n $entCount] \
						     $script]
	}
      }
      # frputs entAry(-type) entAry(-label) tabStops indentSize
      set font [getOpVal -indentionfont]
      $options(hull) tag configure $tagindent\
	  -tabs [list {*}$tabStops ]\
	  -font $font
      # set up the sel tag configuration for "active"
      set selConf [buildTagUsing {-activeforeground\
				   -activebackground \
				      -activeborderwidth} active]
      if {$entAry(-type) == {tearoff}} {
	lappend selConf  -font $font
      }
      if {$entAry(-type) != "separator"} {
        $options(hull) tag bind $tagpriv <Enter>\
            [list $v(self) toActive %W $tagpriv enter $selConf]

        $options(hull) tag bind $tagpriv <Leave>\
            [list $v(self) toActive %W $tagpriv leave {}]
      }
      set textWin [set ${options(instVault)}(textWindowName)]
	
      # frputs priorIndent options(casIndent) tabStops
	
      $options(hull) tag configure $tagindict\
	  {*}[buildTagUsing -indicator1tagconfig]

      if {[info exists entAry(-entry)] && $entAry(-entry)} {
	set entryId [buildEntryWindowFor $entCount]
      }
      if {[info exists entAry(-decodedindicators)]} {
	lassign  $entAry(-decodedindicators) spChar1 spChar2
      }
      # frputs spChar1 spChar2
      switch $entAry(-type) {
	tearoff -
	separator {
	  if {$entAry(-type) == "tearoff"} {
	    # $options(hull) tag bind $tagpriv <1> [mymethod Tearoff]
	  }
	  if {[info exist entAry(-label)] &&\
		  $entAry(-label) != {} &&\
		  $entAry(-type) != "tearoff"} {
	    set text $entAry(-label)	  
	  } else {
	    set spChar [::textMenu::transLateSp $entAry(-type) 0]
	    set text \
		[string repeat $spChar \
		     [expr {2 + ($options(maxTextWidth) / \
				     [font measure $font -displayof $textWin\
					  $spChar])}]]
	  }
	  # We include tagindent here as its font is needed to get the right
	  # line length.
	  set tags [list $tag $tagpriv $tagindent]
	  # frputs SEP spChar text "[winfo width $textWin] " textWin 
	  $options(hull) insert insert $curIndent\t $tagindent
	  sendLabel $text $tags [list $curIndent\t $tagindent]
	  
	  # Since we are starting at line 1, the peer's startline will have
	  # been reset. We set it back to 2 here so the peers do not show
	  # the tearoff entry.
	  if {$entAry(-type) == "tearoff"} {
	    foreach peer [$options(hull) peer names] {
	      $peer configure -startline 2
	    }
	  }
	}
	command {
	  $options(hull) insert insert $curIndent\t [list $tag   $tagindent ]
	  sendLabel $entAry(-label) [list $tag $tagpriv $taguser] $curIndent
	  # if {$entAry(-command) != {}} {
	  #   $options(hull) tag bind $tagpriv <1> $entAry(-command)
	  # }
	}
	checkbutton -
	radiobutton {
	  set entAry(-indicatortags) [list $tagindict $tag $tagindent]
	  set indicatChar [expr {$entAry(-indicatoron) ? \
				     "${spChar1}\t" : " "}]
	  $options(hull) tag configure \
	      $tagindict {*}[buildTagUsing -indicator1tagconfig]
	  $options(hull) insert insert $curIndent [list $tag  $tagindent ]\
	      "$indicatChar\t" [list $tag $tagindent $tagindict]
	  sendLabel $entAry(-label) [list $tag $tagpriv  $taguser] $curIndent
	  if {$entAry(-indicatoron)} {
	    ::textMenu::check-radioIndicators $entCount $tagindict
	  }
	  $options(hull) tag bind $tag <1> \
	      [mymethod check-radioTickel $entCount $tagindict]
	  
	}
	cascade {
	  # frputs preIndicator
	  if {$entAry(-post) == 2} {
	    set spChar $spChar2
	    set inTag "-indicator2tagconfig"
	  } else {
	    set spChar $spChar1
	    set inTag "-indicator1tagconfig"
	  }
	  set Char [expr {$entAry(-indicatoron) ? "${spChar}" : " "}]
	  
	  set tags [list $tagindict $tag  $tagindent]
	  set entAry(tags) $tags
	  $options(hull) insert insert $curIndent [list $tag $tagindent]\
	      "${Char}\t" $tags
	  sendLabel $entAry(-label) [list $tag $tagpriv $taguser] $curIndent
	  if {$entAry(-indicatoron)} {
	    $options(hull) tag configure $tagindict {*}[buildTagUsing $inTag]
	    $options(hull) tag bind $tagindict <1> \
		[mymethod SwitchCascade $entCount]
	  }
	  # save indention
	  #set entAry(-indention) $ourIndent$noIndicator[getOpVal -indention]
	  if {$entAry(-post) >= 2} {
	    # we are to post now...
	    # we depend on PostCascade to return the proper insert location
	    # however, we can not provide one as PostCascade may
	    # unpost other entries and thus throwing the location off.
	    # frputs SENT end
	    set end [PostCascade entry $entCount] 
	    # frputs GOT end
	  }
	}
	default {frputs entAry(-type) "DEFAULT ??? "}
      }
    }
    # options(-post) is to indicate the status of the level 2 -> posted
    set options(-post) 2
    # frputs RETURN end
    return [expr {int([$options(hull) index insert])}]
  }
  
  # This routine sends the label to the textwindow. This will be either
  # a text string or an embedded window.
  proc sendLabel {text tags indent} {
    upvar options options
    upvar entAry  entAry
    upvar end     end
    # frputs TEXT text
    if {[info exists entAry(-entry)] && $entAry(-entry)} {
      $options(hull) window create insert -window $entAry(-entrywindowname)
      $options(hull) insert insert \n $tags 
    } else {
      set lines [lassign [split $text \n] line]
      $options(hull) insert insert $line\n $tags
      foreach line $lines {
	$options(hull) insert insert {*}$indent $line\n $tags
      }
    }
  }
  # This private method does what it takes to open/close a cascade
  # Most of what it needs is in the entryList entry for this entry
  # We depend on the indicator tag being first in the 'tags' list.
  
  method SwitchCascade {entCount {unpost 3}} {
    setEntAry $entCount entAry

    # get the current presented indicator
  ## frputs win entCount "[lindex $options(entryList) $entCount] "
    # run this from the -post flag...
    if {$entAry(-post) == 2} {
      $entAry(-menu) unpost [expr {$unpost == 3}]
    } else {
      PostCascade entry $entCount unpost $unpost
    }
  }
  # The postcascade call is made with reference to a particular entry
  # which must be a -type cascade. If there is a -postcommand it needs
  # to be called.

  # This is the world facing 'postcascade'
    
  method postcascade {entCount {unpostsubs 0}} {
    set index [$self index $entCount]
    if {$index == "none"} {return}
    PostCascade entry $index unpost [expr {$unpostsubs ? 1 : 3}]
    return {}
  }

  # The internal private version of postcascade

  proc PostCascade {args} {
    upvar options options selfns selfns win win
   
    # first unpost if not multiple posts
    # ?? Intent here is to unpost others if not multiple....
    # && $entAry(-post) == 0
    
    PrePost {*}$args entry 0
  ## frputs args
    array set myAry $args
    setEntAry $myAry(entry) entAry
    if {$entAry(-type) != "cascade"} {return}
    if {$entAry(-postcommand) != {} } {
      uplevel #0 $entAry(-postcommand)
    }
    if {$entAry(-indicatoron)} {
      set spChar [lindex $entAry(-decodedindicators) 1]
      set tags $entAry(tags)
      set tagindict [lindex $tags 0]
      $options(hull) tag configure $tagindict\
	  {*}[buildTagUsing -indicator2tagconfig]
      $options(hull) replace $tagindict.first $tagindict.last $spChar\t $tags
    }
    set entAry(-post) 2
    # set ent [$options(hull) tag range ${win}.$myAry(entry)]
  ## frputs entAry(-menu) entAry(-type) args
    # we need to over ride the entry number here
    set end [$entAry(-menu) masterLayout {*}$args entry 0 parentNum $myAry(entry)]
    $options(hull) see ${end}.end
    return 
  }

  # The following method is called by a <1> on a menu item. It does one of
  # 3 things depending on the 'options':
  #
  # If '-editmode' if false  call the entries '-command' option and terminate
  # If '-editmode' and "-editmodeexempt' are true, '-command' is called
  # If '-editmode' is true and '-editmodeexempt' is false selection options
  #                                              are used (we are not called)
  #
  # 'terminate' means that if the menu is toplevel and not 'tornoff', it will be
  # 'destroy'ed. In all other cases it is up to some other user action to
  # destroy the menu.
  #
  method doCommand {args} {
    # first lets add any requeste info to the command
    upvar $options(instVault) v
    setEntAry [set entCo [lindex $args 0]] entAry
  ## frputs #4 #3 #2 DOCOMMAND1 args
    if {$entAry(-type) == "separator"} {return}
    
    if {$entAry(-command) != {}} {
      set cmd [string map [list %p $self %n $entCo] $entAry(-command)]
      # The 'after' allows us to complete prior to the action which
      # may want to put up a window where we are...
      after 0 "uplevel #0 [list $cmd]"
    } elseif {$entAry(-type) == "cascade"} {
      # if no command & this is a cascade, do the cascade thing
      $self SwitchCascade $entCo
      return
    }
    if {!$options(-editmode) && [set mw $options(-toplevel)] != {}} {
      # it is a toplevel window named 'mw'. Is it tornoff?
      if {[wm overrideredirect $mw] == 1} {
	# complete destruction runs afoul of the menubutton bindings...
	# if the menubutton code is what started us, let it finish...
	set mb $::tk::Priv(postedMb)
	# frputs mb 
	set r [catch {
	  if {($mb != {} && [$mb cget -menu] == $v(baseName)) ||\
		  $::tk::Priv(popup) == $v(baseName)} {
	    ::tk::MenuUnpost $v(baseName)
	  ## frputs mb
	  ## frputs duh "[wm state [winfo toplevel $mb]] " 
	    if {[wm state [wm toplevel $mb]] == "normal"} {
	      TextCascadeUnpost keeptags 1
	    }
	  } else {
	    incr noMb
	  }
	} er ]
	# frputs DOCOMMAND er args self mb
	#frputs "[wm state [winfo toplevel $mb]] "
	if {$r != 0 || [info exists noMb]} {
	  # Not tangled in the menubutton... do it here
	  after 0 "$options(master) unpost"
	}
      }
    }
  }

  # the following command switches the tearoff state as needed.
  # It is called by selecting the tearoff line in the window. 
  # menu entries are usually created without a frame or heading
  # This is handeled by the wm overrideredirect command.
  # The default here is to do a normal menu (no frame etc.)
    
  method Tearoff {args} {
    upvar $options(instVault) v
    setEntAry 0 tmpAry
    
    # Here we call the tk menu handeling code...
  ## frputs #4 #3 #2 TEAROFF v(baseName)
    ::tk::TearOffMenu $v(baseName)
    ::tk::MenuUnpost  $v(baseName)
    return
  }
  # We keep "-active" info in the master and each cascade entry
  # It is possible to have more than one active entry but only
  # if they are in different master/cascade sections.
  # this should only be possible by using the "active" command

  # The active/inactive colors are controlled by tags, in particular
  # tagpriv, which is pasted to us here. The tagprivN is the active version
  # of the tag. To avoid priority issues we will assume that what ever
  # sets the normal tag and we only mess with tagprivN, we do, however
  # depend on tagpriv being assigned to the item to be activated as
  # we need the indexes it provides.

  # The check-radioTickel method is called when the user clicks on the
  # indicator. It gets the entryCount and the indicator tag. It figures
  # out what, if anything, needs to be done to the variable. It does not
  # update the indicator(s), leaving that to the trace code that is traceing
  # writes to the variable.
    
  method check-radioTickel {entCount tagindict} {
    setEntAry $entCount entAry
    # For a checkbutton, we toggle the value
    if {$entAry(-type) == "checkbutton"} {
      set $entAry(-variable)\
	  [expr {[set $entAry(-variable)] == $entAry(-offvalue) ? \
		     $entAry(-onvalue) : \
		     $entAry(-offvalue)}]
    } else {
      # It is a radiobutton, we always just set these
      set $entAry(-variable) $entAry(-value)  
    }
    $self doCommand $entCount
  }
  method debug {args} {
    upvar $options(instVault) v
    # return some useful stuff
  ## frputs win options(instVault) " options->[myvar options] "
    # puts  "win->$win v->[myvar v] options->[myvar options]"
    return "win->$win v->$options(instVault) options->[myvar options]"
  }
}
# frputs SOFAR
############################ It all starts here ##########################

# # In order to define the text menu as either a toplevel menu or an embeded one
# # we start here and, by examination of the requested widget name determine
# # if we should call 'TextMenu' (gives a normal toplevel menu) of 'textMenu' which
# # gives an embedded menu (i.e. it can be grid'ed or pack'ed into another lay out).

# proc textmenu {w args} {
#   set par [regsub -start 1 {\.?[^.]+$} $w {}]
#   # First this must be a window
#   set argAry(-toplevel) 1
#   # set args [list {*}$args -insertwidth 10]
#   array set argAry $args
#   if {[winfo exists $par]} {
#   ## frputs NOTTOP args "~[list {*}$args] "
#     if {$argAry(-toplevel)} {
#       # This is to be a toplevel window...
#       set rt  [TextMenu $w {*}[array get argAry]]
#     ## frputs rt ::errorInfo
#       return $rt
#     }
#     return [textMenu $w {*}$args -toplevel {}]
#   }
#   return -code error "\"$par\" is not a window."
# }
# # frputs THEEND

frputsSnit

return

# Source will not visit here, so, lets do a manual...
[manpage_begin NAME SECTION VERSION]
[titledesc textmenu  - Create and manipulate a 'textmenu' widgets]
[description]
SYNOPSIS
testmenu pathName ?options?

STANDARD OPTIONS
      -activebackground     -borderwidth         -foreground
       -activeborderwidth    -cursor              -relief
       -activeforeground     -disabledforeground  -takefocus
       -background           -font
See the options manual entry for details on the standard options.

WIDGET-SPECIFIC OPTIONS
       Command-Line Name:-postcommand
       Database Name:  postCommand
       Database Class: Command

              If this option is specified then it provides a Tcl command to
              execute each time the menu is posted.  The command is invoked by
              the post widget command before posting the menu. Note that in Tk
              8.0 on Macintosh and Windows, all post-commands in a system of
              menus are executed before any of those menus are posted.  This
              is due to the limitations in the individual platforms' menu
              managers.

       Command-Line Name:-selectcolor
       Database Name:  selectColor
       Database Class: Background

              For menu entries that are check buttons or radio buttons, this
              option specifies the color to display in the indicator when the
              check button or radio button is selected.

       Command-Line Name:-tearoff
       Database Name:  tearOff
       Database Class: TearOff

              This option must have a proper boolean value, which specifies
              whether or not the menu should include a tear-off entry at the
              top.  If so, it will exist as entry 0 of the menu and the other
              entries will number starting at 1.  The default menu bindings
              arrange for the menu to be torn off when the tear-off entry is
              invoked.  This option is ignored under Aqua/Mac OS X, where
              menus cannot be torn off.

       Command-Line Name:-tearoffcommand
       Database Name:  tearOffCommand
       Database Class: TearOffCommand

              If this option has a non-empty value, then it specifies a Tcl
              command to invoke whenever the menu is torn off.  The actual
              command will consist of the value of this option, followed by a
              space, followed by the name of the menu window, followed by a
              space, followed by the name of the name of the torn off menu
              window.  For example, if the option's value is “a b” and menu
              .x.y is torn off to create a new menu .x.tearoff1, then the
              command “a b .x.y .x.tearoff1” will be invoked.  This option is
              ignored under Aqua/Mac OS X, where menus cannot be torn off.

       Command-Line Name:-title
       Database Name:  title
       Database Class: Title

              The string will be used to title the window created when this
              menu is torn off. If the title is NULL, then the window will
              have the title of the menubutton or the text of the cascade item
              from which this menu was invoked.

       Command-Line Name:-type
       Database Name:  type
       Database Class: Type

              This option can be one of menubar, tearoff, or normal, and is
              set when the menu is created. While the string returned by the
              configuration database will change if this option is changed,
              this does not affect the menu widget's behavior. This is used by
              the cloning mechanism and is not normally set outside of the Tk
              library.
______________________________________________________________________________

INTRODUCTION

The textmenu command creates a new top-level or embedded window
(given by the pathName argument) and makes it into a menu widget.  That menu widget can either
       be used as a pop-up window or embedded into another widget.  Additional
       options, described above, may be specified on the command line or in
       the option database to configure aspects of the textmenu such as its colors
       and font.  The menu command returns its pathName argument.  At the time
       this command is invoked, there must not exist a window named pathName,
but pathName's parent must exist.

If the pathName argument is a top-level window a top-level menu is created
otherwise a embeddable window is created.
(The window is embedded by being the object of a pack or grid command.)

       A textmenu is a megawidget that displays a collection of one-line entries
       arranged in a column.  There exist several different types
       of entries, each with different properties.  Entries of different types
       may be combined in a single textmenu.  Textmenu entries are not the same as
       entry widgets.  In fact, textmenu entries are not even distinct widgets;
       the entire textmenu is one megawidget.

       Textmenu entries are displayed with up to three separate fields.  The main
       field is a label in the form of a text string, a bitmap, or an image,
controlled by the -label,!!!!
-bitmap, and -image options for the entry.!!!
The second field is an indention which is used to indent menu items
under the cascade entries.
  The third field is an
       indicator.  The indicator is present only for cascade, checkbutton or
       radiobutton entries.  It indicates whether the entry is selected or
       not, and is displayed to the left of the entry's string.

       In normal use, an entry becomes active (displays itself differently)
       whenever the mouse pointer is over the entry.  If a mouse button is
       released over the entry then the entry is invoked.  The effect of
       invocation is different for each type of entry; these effects are
described below in the sections on individual entries.

Textmenus are scrollable both horizontally and vertically.

   !!!!    Entries may be disabled, which causes their labels and accelerators to
       be displayed with dimmer colors.  The default menu bindings will not
       allow a disabled entry to be activated or invoked.  Disabled entries
       may be re-enabled, at which point it becomes possible to activate and
       invoke them again.  !!!!

   !!!    Whenever a menu's active entry is changed, a <<MenuSelect>> virtual
       event is send to the menu. The active item can then be queried from the
       menu, and an action can be taken, such as setting context-sensitive
       help text for the entry.
   !!!
TYPES OF ENTRIES
   COMMAND ENTRIES
       The most common kind of menu entry is a command entry, which behaves
       much like a button widget.  When a command entry is invoked, a Tcl
       command is executed.  The Tcl command is specified with the -command
       option.

   SEPARATOR ENTRIES
       A separator is an entry that is displayed as a horizontal dividing
       line.  A separator may not be activated or invoked, and it has no
       behavior other than its display appearance.

   CHECKBUTTON ENTRIES
       A checkbutton menu entry behaves much like a checkbutton widget.  When
       it is invoked it toggles back and forth between the selected and
       deselected states.  When the entry is selected, a particular value is
       stored in a particular global variable (as determined by the -onvalue
       and -variable options for the entry);  when the entry is deselected
       another value (determined by the -offvalue option) is stored in the
       global variable.  An indicator box is displayed to the left of the
       label in a checkbutton entry.  If the entry is selected then the
       indicator's center is displayed in the color given by the -selectcolor
       option for the entry; otherwise the indicator's center is displayed in
       the background color for the menu.  If a -command option is specified
       for a checkbutton entry, then its value is evaluated as a Tcl command
       each time the entry is invoked;  this happens after toggling the
       entry's selected state.

   RADIOBUTTON ENTRIES
       A radiobutton menu entry behaves much like a radiobutton widget.
       Radiobutton entries are organized in groups of which only one entry may
       be selected at a time.  Whenever a particular entry becomes selected it
       stores a particular value into a particular global variable (as
       determined by the -value and -variable options for the entry).  This
       action causes any previously-selected entry in the same group to
       deselect itself.  Once an entry has become selected, any change to the
       entry's associated variable will cause the entry to deselect itself.
       Grouping of radiobutton entries is determined by their associated
       variables:  if two entries have the same associated variable then they
       are in the same group.  An indicator diamond is displayed to the left
       of the label in each radiobutton entry.  If the entry is selected then
       the indicator's center is displayed in the color given by the
       -selectcolor option for the entry; otherwise the indicator's center is
       displayed in the background color for the menu.  If a -command option
       is specified for a radiobutton entry, then its value is evaluated as a
       Tcl command each time the entry is invoked;  this happens after
       selecting the entry.

   CASCADE ENTRIES
       A cascade entry is one with an associated menu (determined by the -menu
       option).  Cascade entries allow the construction of cascading menus.
       The postcascade widget command can be used to post and unpost the
       associated menu just next to of the cascade entry.  The associated menu
       must be a child of the menu containing the cascade entry (this is
       needed in order for menu traversal to work correctly).

       A cascade entry posts its associated menu by invoking a Tcl command of
       the form
              menu post x y
       where menu is the path name of the associated menu, and x and y are the
       root-window coordinates of the upper-right corner of the cascade entry.
       On Unix, the lower-level menu is unposted by executing a Tcl command
       with the form
              menu unpost
       where menu is the name of the associated menu.  On other platforms, the
       platform's native code takes care of unposting the menu.

       If a -command option is specified for a cascade entry then it is
       evaluated as a Tcl command whenever the entry is invoked. This is not
       supported on Windows.

   TEAR-OFF ENTRIES
       A tear-off entry appears at the top of the menu if enabled with the
       -tearoff option.  It is not like other menu entries in that it cannot
       be created with the add widget command and cannot be deleted with the
       delete widget command.  When a tear-off entry is created it appears as
       a dashed line at the top of the menu.  Under the default bindings,
       invoking the tear-off entry causes a torn-off copy to be made of the
       menu and all of its submenus.
WIDGET COMMAND
       The textmenu command creates a new Tcl command whose name is pathName.
       This command may be used to invoke various operations on the widget.
       It has the following general form:
              pathName option ?arg arg ...?
       Option and the args determine the exact behavior of the command.

       Many of the widget commands for a menu take as one argument an
       indicator of which entry of the menu to operate on. These indicators
       are called indexes and may be specified in any of the following forms:

       active      Indicates the entry that is currently active.  If no entry
                   is active then this form is equivalent to none.  This form
                   may not be abbreviated.

       end         Indicates the bottommost entry in the menu.  If there are
                   no entries in the menu then this form is equivalent to
                   none.  This form may not be abbreviated.

       last        Same as end.

       none        Indicates “no entry at all”; this is used most commonly
                   with the activate option to deactivate all the entries in
                   the menu.  In most cases the specification of none causes
                   nothing to happen in the widget command.  This form may not
                   be abbreviated.

       @number     In this form, number is treated as a y-coordinate in the
                   menu's window;  the entry closest to that y-coordinate is
                   used.  For example, “@0” indicates the top-most entry in
                   the window.

       number      Specifies the entry numerically, where 0 corresponds to the
                   top-most entry of the menu, 1 to the entry below it, and so
                   on.

       pattern     If the index does not satisfy one of the above forms then
                   this form is used.  Pattern is pattern-matched against the
                   label of each entry in the menu, in order from the top
                   down, until a matching entry is found.  The rules of string
                   match are used.

       If the index could match more than one of the above forms, then the
       form earlier in the above list takes precedence.

       The following widget commands are possible for menu widgets:

       pathName activate index
              Change the state of the entry indicated by index to active and
              redisplay it using its active colors.  Any previously-active
              entry is deactivated.  If index is specified as none, or if the
              specified entry is disabled, then the menu ends up with no
active entry.  Returns an empty string. The active state is
textmenu wide, i.e. only one entry in the collective menu and submenus may be active.

       pathName add type ?option value option value ...?
              Add a new entry to the bottom of the menu.  The new entry's type
              is given by type and must be one of cascade, checkbutton,
              command, radiobutton, or separator, or a unique abbreviation of
              one of the above.  If additional arguments are present, they
              specify the options listed in the MENU ENTRY OPTIONS section
              below.  The add widget command returns an empty string.

       pathName cget option
              Returns the current value of the configuration option given by
              option.  Option may have any of the values accepted by the menu
              command.

       pathName clone newPathname ?cloneType?
              Makes a clone of the current menu named newPathName. This clone
              is a menu in its own right, but any changes to the clone are
              propagated to the original menu and vice versa. cloneType can be
              normal, menubar, or tearoff. Should not normally be called
              outside of the Tk library. See the CLONES section for more
              information.

       pathName configure ?option? ?value option value ...?
              Query or modify the configuration options of the widget.  If no
              option is specified, returns a list describing all of the
              available options for pathName (see Tk_ConfigureInfo for
              information on the format of this list).  If option is specified
              with no value, then the command returns a list describing the
              one named option (this list will be identical to the
              corresponding sublist of the value returned if no option is
              specified).  If one or more option-value pairs are specified,
              then the command modifies the given widget option(s) to have the
              given value(s);  in this case the command returns an empty
              string.  Option may have any of the values accepted by the menu
              command.

       pathName delete index1 ?index2?
              Delete all of the menu entries between index1 and index2
              inclusive.  If index2 is omitted then it defaults to index1.
              Attempts to delete a tear-off menu entry are ignored (instead,
              you should change the -tearoff option to remove the tear-off
              entry).

       pathName entrycget index option
              Returns the current value of a configuration option for the
              entry given by index.  Option may have any of the names
              described in the MENU ENTRY OPTIONS section below.

       pathName entryconfigure index ?options...?
              This command is similar to the configure command, except that it
              applies to the options for an individual entry, whereas
              configure applies to the options for the menu as a whole.
              Options may have any of the values described in the MENU ENTRY
              OPTIONS section below.  If options are specified, options are
              modified as indicated in the command and the command returns an
              empty string.  If no options are specified, returns a list
              describing the current options for entry index (see
              Tk_ConfigureInfo for information on the format of this list).

       pathName index index
              Returns the numerical index corresponding to index, or none if
              index was specified as none.

       pathName insert index type ?option value option value ...?
              Same as the add widget command except that it inserts the new
              entry just before the entry given by index, instead of appending
              to the end of the menu.  The type, option, and value arguments
              have the same interpretation as for the add widget command.  It
              is not possible to insert new menu entries before the tear-off
              entry, if the menu has one.

       pathName invoke index
              Invoke the action of the menu entry.  See the sections on the
              individual entries above for details on what happens.  If the
              menu entry is disabled then nothing happens.  If the entry has a
              command associated with it then the result of that command is
              returned as the result of the invoke widget command.  Otherwise
              the result is an empty string.  Note:  invoking a menu entry
              does not automatically unpost the menu;  the default bindings
              normally take care of this before invoking the invoke widget
              command.

       pathName post x y ?index?
              Arrange for the menu to be displayed on the screen at the root-
              window coordinates given by x and y.  If an index is specified
              the menu will be located so that the entry with that index is
              displayed at the point.  These coordinates are adjusted if
              necessary to guarantee that the entire menu is visible on the
              screen.  This command normally returns an empty string.  If the
              -postcommand option has been specified, then its value is
              executed as a Tcl script before posting the menu and the result
              of that script is returned as the result of the post widget
              command.  If an error returns while executing the command, then
              the error is returned without posting the menu.

       pathName postcascade index
              Posts the submenu associated with the cascade entry given by
              index, and unposts any previously posted submenu.  If index does
              not correspond to a cascade entry, or if pathName is not posted,
              the command has no effect except to unpost any currently posted
              submenu.

       pathName type index
              Returns the type of the menu entry given by index.  This is the
              type argument passed to the add or insert widget command when
              the entry was created, such as command or separator, or tearoff
              for a tear-off entry.

       pathName unpost
              Unmap the window so that it is no longer displayed.  If a lower-
              level cascaded menu is posted, unpost that menu.  Returns an
              empty string. This subcommand does not work on Windows and the
              Macintosh, as those platforms have their own way of unposting
              menus.
!!!
       pathName xposition index
              Returns a decimal string giving the x-coordinate within the menu
              window of the leftmost pixel in the entry specified by index.

       pathName yposition index
              Returns a decimal string giving the y-coordinate within the menu
              window of the topmost pixel in the entry specified by index.
!!!
     pathName xview option args
              This command is used to query and change the horizontal position
              of the text in the widget's window. It can take any of the
              following forms:

              pathName xview
                     Returns a list containing two elements. Each element is a
                     real fraction between 0 and 1; together they describe the
                     portion of the document's horizontal span that is visible
                     in the window. For example, if the first element is .2
                     and the second element is .6, 20% of the text is off-
                     screen to the left, the middle 40% is visible in the
                     window, and 40% of the text is off-screen to the right.
                     The fractions refer only to the lines that are actually
                     visible in the window: if the lines in the window are all
                     very short, so that they are entirely visible, the
                     returned fractions will be 0 and 1, even if there are
                     other lines in the text that are much wider than the
                     window.  These are the same values passed to scrollbars
                     via the -xscrollcommand option.

              pathName xview moveto fraction
                     Adjusts the view in the window so that fraction of the
                     horizontal span of the text is off-screen to the left.
                     Fraction is a fraction between 0 and 1.

              pathName xview scroll number what
                     This command shifts the view in the window left or right
                     according to number and what. What must be units, pages
                     or pixels. If what is units or pages then number must be
                     an integer, otherwise number may be specified in any of
                     the forms acceptable to Tk_GetPixels, such as “2.0c” or
                     “1i” (the result is rounded to the nearest integer value.
                     If no units are given, pixels are assumed). If what is
                     units, the view adjusts left or right by number average-
                     width characters on the display; if it is pages then the
                     view adjusts by number screenfuls; if it is pixels then
                     the view adjusts by number pixels. If number is negative
                     then characters farther to the left become visible; if it
                     is positive then characters farther to the right become
                     visible.

       pathName yview ?args?
              This command is used to query and change the vertical position
              of the text in the widget's window. It can take any of the
              following forms:

              pathName yview
                     Returns a list containing two elements, both of which are
                     real fractions between 0 and 1. The first element gives
                     the position of the first visible pixel of the first
                     character (or image, etc) in the top line in the window,
                     relative to the text as a whole (0.5 means it is halfway
                     through the text, for example). The second element gives
                     the position of the first pixel just after the last
                     visible one in the bottom line of the window, relative to
                     the text as a whole. These are the same values passed to
                     scrollbars via the -yscrollcommand option.

              pathName yview moveto fraction
                     Adjusts the view in the window so that the pixel given by
                     fraction appears at the top of the top line of the
                     window. Fraction is a fraction between 0 and 1; 0
                     indicates the first pixel of the first character in the
                     text, 0.33 indicates the pixel that is one-third the way
                     through the text; and so on. Values close to 1 will
                     indicate values close to the last pixel in the text (1
                     actually refers to one pixel beyond the last pixel), but
                     in such cases the widget will never scroll beyond the
                     last pixel, and so a value of 1 will effectively be
                     rounded back to whatever fraction ensures the last pixel
                     is at the bottom of the window, and some other pixel is
                     at the top.

              pathName yview scroll number what
                     This command adjust the view in the window up or down
                     according to number and what. What must be units, pages
                     or pixels. If what is units or pages then number must be
                     an integer, otherwise number may be specified in any of
                     the forms acceptable to Tk_GetPixels, such as “2.0c” or
                     “1i” (the result is rounded to the nearest integer value.
                     If no units are given, pixels are assumed). If what is
                     units, the view adjusts up or down by number lines on the
                     display; if it is pages then the view adjusts by number
                     screenfuls; if it is pixels then the view adjusts by
                     number pixels. If number is negative then earlier
                     positions in the text become visible; if it is positive
                     then later positions in the text become visible.

              pathName yview ?-pickplace? index
                     Changes the view in the widget's window to make index
                     visible. If the -pickplace option is not specified then
                     index will appear at the top of the window. If -pickplace
                     is specified then the widget chooses where index appears
                     in the window:



In the following "pathName" may refer to the toplevel menu or to cascades.

pathName postcascade index ?unpost subsubs?

As per the "menu" command:  Conditionally unposts any currently posted submenu.
              And then posts the submenu associated with the cascade entry 
              given by index. If index
              does not correspond to a cascade entry, or if pathName is not posted,
the command has no other effect.
Submenus are unposted only if the option "-muptipleopen" is false.
If submenus of these unposted submenus are posted, they will be reposted when the
containing submenu is reposted unless <unpost subsubs> is true. Returns an empty
string.

pathName unpost ?unpost subs?
  
If a lower-level cascaded menus are posted, unpost those menus.  
 lower-level cascades that were posted will again be posted when the menu is
reposted unless <unpost subs> is true. If "pathName" refers to the toplevel menu
that window will be cleared. If the menu is a toplevel window and is not 'tornoff'
it will be unmaped so that it is no longer displayed. Returns an empty string. 

[manpage_end]
