home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 December / PCWorld_2000-12_cd.bin / Komunikace / Comanche / comanche.exe / lib / iwidgets3.0.0 / scripts / menubar.itk < prev    next >
Text File  |  1999-02-24  |  71KB  |  2,241 lines

  1. #
  2. # Menubar widget
  3. # ----------------------------------------------------------------------
  4. # The Menubar command creates a new window (given by the pathName 
  5. # argument) and makes it into a Pull down menu widget. Additional 
  6. # options, described above may be specified on the command line or 
  7. # in the option database to configure aspects of the Menubar such 
  8. # as its colors and font. The Menubar command returns its pathName 
  9. # argument. At the time this command is invoked, there must not exist 
  10. # a window named pathName, but pathName's parent must exist.
  11. # A Menubar is a widget that simplifies the task of creating 
  12. # menu hierarchies. It encapsulates a frame widget, as well 
  13. # as menubuttons, menus, and menu entries. The Menubar allows 
  14. # menus to be specified and refer enced in a more consistent 
  15. # manner than using Tk to build menus directly. First, Menubar
  16. # allows a menu tree to be expressed in a hierachical "language". 
  17. # The Menubar accepts a menuButtons option that allows a list of 
  18. # menubuttons to be added to the Menubar. In turn, each menubutton
  19. # accepts a menu option that spec ifies a list of menu entries 
  20. # to be added to the menubutton's menu (as well as an option 
  21. # set for the menu).   Cascade entries in turn, accept a menu 
  22. # option that specifies a list of menu entries to be added to 
  23. # the cascade's menu (as well as an option set for the menu). In 
  24. # this manner, a complete menu grammar can be expressed to the 
  25. # Menubar. Additionally, the Menubar allows each component of 
  26. # the Menubar system to be referenced by a simple componentPathName 
  27. # syntax. Finally, the Menubar extends the option set of menu 
  28. # entries to include the helpStr option used to implement status 
  29. # bar help.
  30. #
  31. # WISH LIST:
  32. #   This section lists possible future enhancements.
  33. #
  34. # ----------------------------------------------------------------------
  35. #  AUTHOR: Bill W. Scott                 EMAIL: bscott@spd.dsccc.com
  36. #
  37. #  @(#) $Id: menubar.itk,v 1.2 1998/08/11 14:42:08 welch Exp $
  38. # ----------------------------------------------------------------------
  39. #            Copyright (c) 1995 DSC Technologies Corporation
  40. # ======================================================================
  41. # Permission to use, copy, modify, distribute and license this software 
  42. # and its documentation for any purpose, and without fee or written 
  43. # agreement with DSC, is hereby granted, provided that the above copyright 
  44. # notice appears in all copies and that both the copyright notice and 
  45. # warranty disclaimer below appear in supporting documentation, and that 
  46. # the names of DSC Technologies Corporation or DSC Communications 
  47. # Corporation not be used in advertising or publicity pertaining to the 
  48. # software without specific, written prior permission.
  49. # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
  50. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
  51. # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
  52. # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
  53. # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
  54. # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
  55. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  56. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
  57. # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
  58. # SOFTWARE.
  59. # ======================================================================
  60.  
  61.  
  62. #
  63. # Usual options.
  64. #
  65. itk::usual Menubar {
  66.     keep -activebackground -activeborderwidth -activeforeground \
  67.         -anchor -background -borderwidth -cursor -disabledforeground \
  68.         -font -foreground -highlightbackground -highlightthickness \
  69.         -highlightcolor -justify -padx -pady -wraplength
  70. }
  71.  
  72. class iwidgets::Menubar {
  73.     inherit itk::Widget
  74.  
  75.     constructor { args } {}
  76.  
  77.     itk_option define -foreground foreground Foreground Black
  78.     itk_option define -activebackground activeBackground Foreground "#ececec"
  79.     itk_option define -activeborderwidth activeBorderWidth BorderWidth 2 
  80.     itk_option define -activeforeground activeForeground Background black
  81.     itk_option define -anchor anchor Anchor center
  82.     itk_option define -borderwidth borderWidth BorderWidth 2
  83.     itk_option define \
  84.         -disabledforeground disabledForeground DisabledForeground #a3a3a3 
  85.     itk_option define \
  86.         -font font Font "-Adobe-Helvetica-Bold-R-Normal--*-120-*-*-*-*-*-*"
  87.     itk_option define \
  88.         -highlightbackground highlightBackground HighlightBackground #d9d9d9
  89.     itk_option define -highlightcolor highlightColor HighlightColor Black
  90.     itk_option define \
  91.         -highlightthickness highlightThickness HighlightThickness 0
  92.     itk_option define -justify justify Justify center
  93.     itk_option define -padx padX Pad 4p
  94.     itk_option define -pady padY Pad 3p
  95.     itk_option define -wraplength wrapLength WrapLength 0
  96.     itk_option define -menubuttons menuButtons MenuButtons {}
  97.     itk_option define -helpvariable helpVariable HelpVariable {}
  98.  
  99.     public {
  100.     method add { type path args } { }
  101.     method delete { args } { }
  102.     method index { path } { }
  103.     method insert { beforeComponent type name args }
  104.     method invoke { entryPath } { }
  105.     method menucget { args } { }
  106.     method menuconfigure { path args } { }
  107.     method path { args } { }
  108.     method type { path } { }
  109.     method yposition { entryPath } { }
  110.  
  111.     method _helpHandler { menuPath } { }
  112.     }
  113.  
  114.     private {
  115.     method menubutton { menuName args } { }
  116.     method options { args } { }
  117.     method command { cmdName args } { }
  118.     method checkbutton { chkName args } { }
  119.     method radiobutton { radName args } { }
  120.     method separator { sepName args } { }
  121.     method cascade { casName args } { }
  122.     method _addMenuButton { buttonName args} { }
  123.     method _insertMenuButton { beforeMenuPath buttonName args} { }
  124.     method _makeMenuButton {buttonName args} { }
  125.     method _makeMenu \
  126.         { componentName widgetName menuPath menuEvalStr } { }
  127.     method _substEvalStr { evalStr } { }
  128.     method _deleteMenu { menuPath {menuPath2 {}} } { }
  129.     method _deleteAMenu { path } { }
  130.     method _addEntry { type path args } { }
  131.     method _addCascade { tkMenuPath path args } { }
  132.     method _insertEntry { beforeEntryPath type name args } { }
  133.     method _insertCascade { bfIndex tkMenuPath path args } { }
  134.     method _deleteEntry { entryPath {entryPath2 {}} } { }
  135.     method _configureMenu { path tkPath {option {}} args } { }
  136.     method _configureMenuOption { type path args } { }
  137.     method _configureMenuEntry { path index {option {}} args } { }
  138.     method _unsetPaths { parent } { }
  139.     method _entryPathToTkMenuPath {entryPath} { }
  140.     method _getTkIndex { tkMenuPath tkIndex} { }
  141.     method _getPdIndex { tkMenuPath tkIndex } { }
  142.     method _getMenuList { } { }
  143.     method _getEntryList { menu } { }
  144.     method _parsePath { path } { }
  145.     method _getSymbolicPath { parent segment } { }
  146.     method _getCallerLevel { }
  147.  
  148.     variable _parseLevel 0        ;# The parse level depth
  149.     variable _callerLevel #0      ;# abs level of caller
  150.     variable _pathMap             ;# Array indexed by Menubar's path
  151.                                       ;# naming, yields tk menu path
  152.     variable _entryIndex -1       ;# current entry help is displayed
  153.                                       ;# for during help <motion> events
  154.  
  155.     variable _tkMenuPath          ;# last tk menu being added to
  156.     variable _ourMenuPath         ;# our last valid path constructed.
  157.  
  158.     variable _menuOption          ;# The -menu option
  159.     variable _helpString          ;# The -helpstr optio
  160.     }
  161. }
  162.  
  163. #
  164. # Use option database to override default resources.
  165. #
  166. option add *Menubar*Menu*tearOff         false        widgetDefault
  167. option add *Menubar*Menubutton*relief    flat         widgetDefault
  168. option add *Menubar*Menu*relief          raised       widgetDefault
  169.  
  170. #
  171. # Provide a lowercase access method for the menubar class
  172. #
  173. proc ::iwidgets::menubar { args } {
  174.     uplevel ::iwidgets::Menubar $args
  175. }
  176.  
  177. # ------------------------------------------------------------------
  178. #                           CONSTRUCTOR
  179. # ------------------------------------------------------------------
  180. body iwidgets::Menubar::constructor { args } {
  181.     component hull configure -borderwidth 0
  182.  
  183.     #
  184.     # Create the Menubar Frame that will hold the menus.
  185.     #
  186.     # might want to make -relief and -bd options with defaults
  187.     itk_component add menubar {
  188.     frame $itk_interior.menubar -relief raised -bd 2
  189.     } {
  190.     keep -cursor -background -width -height
  191.     }
  192.     pack $itk_component(menubar) -fill both -expand yes
  193.  
  194.     # Map our pathname to class to the actual menubar frame
  195.     set _pathMap(.) $itk_component(menubar)
  196.  
  197.     eval itk_initialize $args
  198.  
  199.     #
  200.     # HACK HACK HACK
  201.     # Tk expects some variables to be defined and due to some
  202.     # unknown reason we confuse its normal ordering. So, if
  203.     # the user creates a menubutton with no menu it will fail
  204.     # when clicked on with a "Error: can't read $tkPriv(oldGrab):
  205.     # no such element in array". So by setting it to null we
  206.     # avoid this error.
  207.     uplevel #0 "set tkPriv(oldGrab) {}"
  208.  
  209. }
  210.  
  211. # ------------------------------------------------------------------
  212. #                           OPTIONS
  213. # ------------------------------------------------------------------
  214. # This first set of options are for configuring menus and/or menubuttons
  215. # at the menu level.
  216. #
  217. # ------------------------------------------------------------------
  218. # OPTION -foreground
  219. #
  220. # menu
  221. # menubutton
  222. # ------------------------------------------------------------------
  223. configbody iwidgets::Menubar::foreground {
  224. }
  225.  
  226. # ------------------------------------------------------------------
  227. # OPTION -activebackground
  228. #
  229. # menu
  230. # menubutton
  231. # ------------------------------------------------------------------
  232. configbody iwidgets::Menubar::activebackground {
  233. }
  234.  
  235. # ------------------------------------------------------------------
  236. # OPTION -activeborderwidth
  237. #
  238. # menu
  239. # ------------------------------------------------------------------
  240. configbody iwidgets::Menubar::activeborderwidth {
  241. }
  242.  
  243. # ------------------------------------------------------------------
  244. # OPTION -activeforeground
  245. #
  246. # menu
  247. # menubutton
  248. # ------------------------------------------------------------------
  249. configbody iwidgets::Menubar::activeforeground {
  250. }
  251.  
  252. # ------------------------------------------------------------------
  253. # OPTION -anchor
  254. #
  255. # menubutton
  256. # ------------------------------------------------------------------
  257. configbody iwidgets::Menubar::anchor {
  258. }
  259.  
  260. # ------------------------------------------------------------------
  261. # OPTION -borderwidth
  262. #
  263. # menu
  264. # menubutton
  265. # ------------------------------------------------------------------
  266. configbody iwidgets::Menubar::borderwidth {
  267. }
  268.  
  269. # ------------------------------------------------------------------
  270. # OPTION -disabledforeground
  271. #
  272. # menu
  273. # menubutton
  274. # ------------------------------------------------------------------
  275. configbody iwidgets::Menubar::disabledforeground {
  276. }
  277.  
  278. # ------------------------------------------------------------------
  279. # OPTION -font
  280. #
  281. # menu
  282. # menubutton
  283. # ------------------------------------------------------------------
  284. configbody iwidgets::Menubar::font {
  285. }
  286.  
  287. # ------------------------------------------------------------------
  288. # OPTION -highlightbackground
  289. #
  290. # menubutton
  291. # ------------------------------------------------------------------
  292. configbody iwidgets::Menubar::highlightbackground {
  293. }
  294.  
  295. # ------------------------------------------------------------------
  296. # OPTION -highlightcolor
  297. #
  298. # menubutton
  299. # ------------------------------------------------------------------
  300. configbody iwidgets::Menubar::highlightcolor {
  301. }
  302.  
  303. # ------------------------------------------------------------------
  304. # OPTION -highlightthickness
  305. #
  306. # menubutton
  307. # ------------------------------------------------------------------
  308. configbody iwidgets::Menubar::highlightthickness {
  309. }
  310.  
  311. # ------------------------------------------------------------------
  312. # OPTION -justify
  313. #
  314. # menubutton
  315. # ------------------------------------------------------------------
  316. configbody iwidgets::Menubar::justify {
  317. }
  318.  
  319. # ------------------------------------------------------------------
  320. # OPTION -padx
  321. #
  322. # menubutton
  323. # ------------------------------------------------------------------
  324. configbody iwidgets::Menubar::padx {
  325. }
  326.  
  327. # ------------------------------------------------------------------
  328. # OPTION -pady
  329. #
  330. # menubutton
  331. # ------------------------------------------------------------------
  332. configbody iwidgets::Menubar::pady {
  333. }
  334.  
  335. # ------------------------------------------------------------------
  336. # OPTION -wraplength
  337. #
  338. # menubutton
  339. # ------------------------------------------------------------------
  340. configbody iwidgets::Menubar::wraplength {
  341. }
  342.  
  343. # ------------------------------------------------------------------
  344. # OPTION -menubuttons
  345. #
  346. # The menuButton option is a string which specifies the arrangement 
  347. # of menubuttons on the Menubar frame. Each menubutton entry is 
  348. # delimited by the newline character. Each entry is treated as 
  349. # an add command to the Menubar. 
  350. #
  351. # ------------------------------------------------------------------
  352. configbody iwidgets::Menubar::menubuttons {
  353.     if { $itk_option(-menubuttons) != {} } {
  354.  
  355.     # IF one exists already, delete the old one and create
  356.     # a new one
  357.     if { ! [catch {_parsePath .0}] } {
  358.         delete .0 .last
  359.     } 
  360.  
  361.     #
  362.     # Determine the context level to evaluate the option string at
  363.     #
  364.     set _callerLevel [_getCallerLevel]
  365.  
  366.     #
  367.     # Parse the option string in their scope, then execute it in
  368.     # our scope.
  369.     #
  370.     incr _parseLevel
  371.     _substEvalStr itk_option(-menubuttons)
  372.     eval $itk_option(-menubuttons)
  373.  
  374.     # reset so that we know we aren't parsing in a scope currently.
  375.     incr _parseLevel -1
  376.     }
  377. }
  378.  
  379. # ------------------------------------------------------------------
  380. # OPTION -helpvariable
  381. #
  382. # Specifies the global variable to update whenever the mouse is in 
  383. # motion over a menu entry. This global variable is updated with the 
  384. # current value of the active menu entry's helpStr. Other widgets 
  385. # can "watch" this variable with the trace command, or as is the 
  386. # case with entry or label widgets, they can set their textVariable 
  387. # to the same global variable. This allows for a simple implementation 
  388. # of a help status bar. Whenever the mouse leaves a menu entry, 
  389. # the helpVariable is set to the empty string {}.
  390. # ------------------------------------------------------------------
  391. configbody iwidgets::Menubar::helpvariable {
  392.     if {"" != $itk_option(-helpvariable) &&
  393.     ![string match ::* $itk_option(-helpvariable)] &&
  394.         ![string match @itcl* $itk_option(-helpvariable)]} {
  395.         set itk_option(-helpvariable) "::$itk_option(-helpvariable)"
  396.     }
  397. }
  398.  
  399.  
  400. # -------------------------------------------------------------
  401. #
  402. # METHOD: add type path args
  403. #
  404. # Adds either a menu to the menu bar or a menu entry to a
  405. # menu pane.
  406. #
  407. # If the type is one of  cascade,  checkbutton,  command,
  408. # radiobutton,  or separator it adds a new entry to the bottom
  409. # of the menu denoted by the menuPath prefix of componentPath-
  410. # Name.  The  new entry's type is given by type. If additional
  411. # arguments are present, they  specify  options  available  to
  412. # component  type  Entry. See the man pages for menu(n) in the
  413. # section on Entries. In addition all entries accept an  added
  414. # option, helpStr:
  415. #
  416. #     -helpstr value
  417. #
  418. # Specifes the string to associate with  the  entry.
  419. # When the mouse moves over the associated entry, the variable
  420. # denoted by helpVariable is set. Another widget can  bind  to
  421. # the helpVariable and thus display status help.
  422. #
  423. # If the type is menubutton, it adds a new  menubut-
  424. # ton  to  the  menu bar. If additional arguments are present,
  425. # they specify options available to component type MenuButton.
  426. #
  427. # If the type is menubutton  or  cascade,  the  menu
  428. # option  is  available  in  addition to normal Tk options for
  429. # these to types.
  430. #
  431. #      -menu menuSpec
  432. #
  433. # This is only valid for componentPathNames of  type
  434. # menubutton  or  cascade. Specifes an option set and/or a set
  435. # of entries to place on a menu and associate with  the  menu-
  436. # button or cascade. The option keyword allows the menu widget
  437. # to be configured. Each item in the menuSpec  is  treated  as
  438. # add  commands  (each  with  the  possibility of having other
  439. # -menu options). In this way a menu can be recursively built.
  440. #
  441. # The last segment of  componentPathName  cannot  be
  442. # one  of  the  keywords last, menu, end. Additionally, it may
  443. # not be a number. However the componentPathName may be refer-
  444. # enced  in  this  manner  (see  discussion  of Component Path
  445. # Names).
  446. #
  447. # -------------------------------------------------------------
  448. body iwidgets::Menubar::add { type path args } {
  449.     if ![regexp \
  450.             {^(menubutton|command|cascade|separator|radiobutton|checkbutton)$} \
  451.             $type] {
  452.         error "bad type \"$type\": must be one of the following:\
  453.             \"command\", \"checkbutton\", \"radiobutton\",\
  454.             \"separator\", \"cascade\", or \"menubutton\""
  455.     }
  456.     regexp {[^.]+$} $path segName
  457.     if [regexp {^(menu|last|end|[0-9]+)$} $segName] {
  458.         error "bad name \"$segName\": user created component \
  459.                 path names may not end with \
  460.         \"end\", \"last\", \"menu\", \
  461.                 or be an integer"
  462.     }
  463.  
  464.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  465.     # OK, either add a menu
  466.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  467.     if { $type == "menubutton" } {
  468.     # grab the last component name (the menu name)
  469.     eval _addMenuButton $segName $args
  470.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  471.     # Or add an entry
  472.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  473.     } else {
  474.     eval _addEntry $type $path $args
  475.     }
  476. }
  477.  
  478.  
  479. # -------------------------------------------------------------
  480. #
  481. # METHOD: delete entryPath ?entryPath2?
  482. #
  483. # If componentPathName is of component type MenuButton or
  484. # Menu,  delete  operates  on menus. If componentPathName is of
  485. # component type Entry, delete operates on menu entries.
  486. #
  487. # This  command  deletes  all  components  between   com-
  488. # ponentPathName  and  componentPathName2  inclusive.  If com-
  489. # ponentPathName2  is  omitted  then  it  defaults   to   com-
  490. # ponentPathName. Returns an empty string.
  491. #
  492. # If componentPathName is of type Menubar, then all menus
  493. # and  the menu bar frame will be destroyed. In this case com-
  494. # ponentPathName2 is ignored.
  495. #
  496. # -------------------------------------------------------------
  497. body iwidgets::Menubar::delete { args } {
  498.  
  499.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  500.     # Handle out of bounds in arg lengths
  501.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  502.     if { [llength $args] > 0 && [llength $args] <=2 } {
  503.  
  504.     # Path Conversions
  505.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  506.     set path [_parsePath [lindex $args 0]]
  507.  
  508.     set pathOrIndex $_pathMap($path)
  509.  
  510.     # Menu Entry
  511.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  512.     if { [regexp {^[0-9]+$} $pathOrIndex] } {
  513.         eval "_deleteEntry $args"
  514.  
  515.         # Menu
  516.         # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  517.     } else {
  518.         eval "_deleteMenu $args"
  519.     }
  520.     } else {
  521.     error "wrong # args: should be \
  522.         \"$itk_component(hull) delete pathName ?pathName2?\""
  523.     }
  524.     return ""
  525. }
  526.  
  527. # -------------------------------------------------------------
  528. #
  529. # METHOD: index path
  530. #
  531. # If componentPathName is of type menubutton or menu,  it
  532. # returns  the  position of the menu/menubutton on the Menubar
  533. # frame.
  534. #
  535. # If componentPathName is  of  type  command,  separator,
  536. # radiobutton,  checkbutton,  or  cascade, it returns the menu
  537. # widget's numerical index for the entry corresponding to com-
  538. # ponentPathName. If path is not found or the Menubar frame is
  539. # passed in, -1 is returned.
  540. #
  541. # -------------------------------------------------------------
  542. body iwidgets::Menubar::index { path } {
  543.  
  544.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  545.     # Path conversions
  546.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  547.     if { [catch {set fullPath [_parsePath $path]} ] } {
  548.     return -1
  549.     }
  550.     if { [catch {set tkPathOrIndex $_pathMap($fullPath)} ] } {
  551.     return -1
  552.     }
  553.  
  554.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  555.     # If integer, return the value, otherwise look up the menu position
  556.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  557.     if { [regexp {^[0-9]+$} $tkPathOrIndex] } {
  558.     set index $tkPathOrIndex
  559.     } else {
  560.     set index [lsearch [_getMenuList] $fullPath]
  561.     }
  562.  
  563.     return $index
  564. }
  565.  
  566. # -------------------------------------------------------------
  567. #
  568. # METHOD: insert beforeComponent type name ?option value?
  569. #
  570. # Insert a new component named name before the  component
  571. # specified by componentPathName.
  572. #
  573. # If componentPathName is of type MenuButton or Menu, the
  574. # new  component  inserted  is of type Menu and given the name
  575. # name. In this  case  valid  option  value  pairs  are  those
  576. # accepted by menubuttons.
  577. #
  578. # If componentPathName is of type  Entry,  the  new  com-
  579. # ponent inserted is of type Entry and given the name name. In
  580. # this case valid option value pairs  are  those  accepted  by
  581. # menu entries.
  582. #
  583. # name cannot be one of the  keywords  last,  menu,  end.
  584. # dditionally,  it  may  not  be  a  number. However the com-
  585. # ponentPathName may be referenced in this manner (see discus-
  586. # sion of Component Path Names).
  587. #
  588. # Returns -1 if the menubar frame is passed in.
  589. #
  590. # -------------------------------------------------------------
  591. body iwidgets::Menubar::insert { beforeComponent type name args } {
  592.      if ![regexp \
  593.             {^(menubutton|command|cascade|separator|radiobutton|checkbutton)$} \
  594.             $type] {
  595.         error "bad type \"$type\": must be one of the following:\
  596.          \"command\", \"checkbutton\", \"radiobutton\",\
  597.          \"separator\", \"cascade\", or \"menubutton\""
  598.     }
  599.     regexp {[^.]+$} $name segName
  600.     if [regexp {^(menu|last|end|[0-9]+)$} $segName] {
  601.     error "bad name \"$name\": user created component \
  602.         path names may not end with \
  603.         \"end\", \"last\", \"menu\", \
  604.         or be an integer"
  605.     }
  606.  
  607.     set beforeComponent [_parsePath $beforeComponent]
  608.  
  609.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  610.     # Choose menu insertion or entry insertion
  611.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  612.     if { $type == "menubutton" } {
  613.     eval _insertMenuButton $beforeComponent $name $args
  614.     } else {
  615.     eval _insertEntry $beforeComponent $type $name $args
  616.     }
  617. }
  618.  
  619.  
  620. # -------------------------------------------------------------
  621. #
  622. # METHOD: invoke entryPath
  623. #
  624. # Invoke  the  action  of  the  menu  entry  denoted   by
  625. # entryComponentPathName.  See  the sections on the individual
  626. # entries in the menu(n) man pages. If the menu entry is  dis-
  627. # abled  then  nothing  happens.  If  the  entry has a command
  628. # associated with it  then  the  result  of  that  command  is
  629. # returned  as the result of the invoke widget command. Other-
  630. # wise the result is an empty string.
  631. #
  632. # If componentPathName is not a menu entry, an  error  is
  633. # issued.
  634. #
  635. # -------------------------------------------------------------
  636. body iwidgets::Menubar::invoke { entryPath } {
  637.  
  638.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  639.     # Path Conversions
  640.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  641.     set entryPath [_parsePath $entryPath]
  642.     set index $_pathMap($entryPath)
  643.  
  644.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  645.     # Error Processing
  646.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  647.     # first verify that beforeEntryPath is actually a path to
  648.     # an entry and not to menu, menubutton, etc.
  649.     if { ! [regexp {^[0-9]+$} $index] } {
  650.     error "bad entry path: beforeEntryPath is not an entry"
  651.     }
  652.  
  653.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  654.     # Call invoke command
  655.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  656.     # get the tk menu path to call
  657.     set tkMenuPath [_entryPathToTkMenuPath $entryPath]
  658.  
  659.     # call the menu's invoke command, adjusting index based on tearoff
  660.     $tkMenuPath invoke [_getTkIndex $tkMenuPath $index]
  661. }
  662.  
  663. # -------------------------------------------------------------
  664. #
  665. # METHOD: menucget componentPath option
  666. #
  667. # Returns the current value of the  configuration  option
  668. # given  by  option.  The  component type of componentPathName
  669. # determines the valid available options.
  670. #
  671. # -------------------------------------------------------------
  672. body iwidgets::Menubar::menucget { path opt } {
  673.     return [lindex [menuconfigure $path $opt] 4]
  674. }
  675.  
  676. # -------------------------------------------------------------
  677. #
  678. # METHOD: menuconfigure componentPath ?option? ?value option value...?
  679. #
  680. # Query or modify the configuration options of  the  sub-
  681. # component  of the Menubar specified by componentPathName. If
  682. # no option is specified, returns a list describing all of the
  683. # available     options     for     componentPathName     (see
  684. # Tk_ConfigureInfo for  information  on  the  format  of  this
  685. # list).  If  option is specified with no value, then the com-
  686. # mand returns a list describing the one  named  option  (this
  687. # list  will  be identical to the corresponding sublist of the
  688. # value returned if no option is specified). If  one  or  more
  689. # option-value  pairs are specified, then the command modifies
  690. # the given widget option(s) to have the  given  value(s);  in
  691. # this case the command returns an empty string. The component
  692. # type of componentPathName  determines  the  valid  available
  693. # options.
  694. #
  695. # -------------------------------------------------------------
  696. body iwidgets::Menubar::menuconfigure { path args } {
  697.  
  698.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  699.     # Path Conversions
  700.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  701.     set path [_parsePath $path]
  702.     set tkPathOrIndex $_pathMap($path)
  703.  
  704.     # Case: Menu entry being configured
  705.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  706.     if { [regexp {^[0-9]+$} $tkPathOrIndex] } {
  707.     eval "_configureMenuEntry $path $tkPathOrIndex $args"
  708.  
  709.     # Case: Menu (button and pane) being configured.
  710.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  711.     } else {
  712.     eval _configureMenu $path $tkPathOrIndex $args
  713.     }
  714. }
  715.  
  716. # -------------------------------------------------------------
  717. #
  718. # METHOD: path
  719. #
  720. # SYNOPIS: path ?<mode>? <pattern>
  721. #
  722. # Returns a fully formed component path that matches pat-
  723. # tern.  If no match is found it returns -1. The mode argument
  724. # indicates how the search is to be  matched  against  pattern
  725. # and it must have one of the following values:
  726. #
  727. #     -glob     Pattern is a glob-style pattern which is
  728. #       matched  against each component path using the same rules as
  729. #       the string match command.
  730. #
  731. #     -regexp   Pattern is treated as a regular  expression  
  732. #       and matched against each component path using the same
  733. #       rules as the regexp command.
  734. #
  735. # The default mode is -glob.
  736. #
  737. # -------------------------------------------------------------
  738. body iwidgets::Menubar::path { args } {
  739.  
  740.     set len [llength $args]
  741.     if { $len < 1 || $len > 2 } {
  742.     error "wrong # args: should be \
  743.         \"$itk_component(hull) path ?mode?> <pattern>\""
  744.     }
  745.  
  746.     set pathList [array names _pathMap]
  747.  
  748.     set len [llength $args]
  749.     switch -- $len {
  750.     1 {
  751.         # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  752.         # Case: no search modes given
  753.         # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  754.         set pattern [lindex $args 0]
  755.         set found [lindex $pathList [lsearch -glob $pathList $pattern]]
  756.     } 
  757.     2 {
  758.         # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  759.         # Case: search modes present (-glob, -regexp)
  760.         # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  761.         set options [lindex $args 0]
  762.         set pattern [lindex $args 1]
  763.         set found \
  764.             [lindex $pathList [lsearch $options $pathList $pattern]]
  765.     }
  766.     default {
  767.         # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  768.         # Case: wrong # arguments
  769.         # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  770.         error "wrong # args: \
  771.             should be \"$itk_component(hull) path ?-glob? ?-regexp? pattern\""
  772.     }
  773.     }
  774.  
  775.     return $found
  776. }
  777.  
  778. # -------------------------------------------------------------
  779. #
  780. # METHOD: type path
  781. #
  782. # Returns the type of the component  given  by  entryCom-
  783. # ponentPathName.  For menu entries, this is the type argument
  784. # passed to the add/insert widget command when the  entry  was
  785. # created, such as command or separator. Othewise it is either
  786. # a menubutton or a menu.
  787. #
  788. # -------------------------------------------------------------
  789. body iwidgets::Menubar::type { path } {
  790.  
  791.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  792.     # Path Conversions
  793.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  794.     set path [_parsePath $path]
  795.  
  796.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  797.     # Error Handling: does the path exist?
  798.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  799.     if { [catch {set index $_pathMap($path)} ] } {
  800.     error "bad path \"$path\""
  801.     }
  802.  
  803.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  804.     # ENTRY, Ask TK for type
  805.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  806.     if { [regexp {^[0-9]+$} $index] } {
  807.     # get the menu path from the entry path name
  808.     set tkMenuPath [_entryPathToTkMenuPath $path]
  809.  
  810.     # call the menu's type command, adjusting index based on tearoff
  811.     set type [$tkMenuPath type [_getTkIndex $tkMenuPath $index]]
  812.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  813.     # MENUBUTTON, MENU, or FRAME
  814.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  815.     } else {
  816.     # should not happen, but have a path that is not a valid window.
  817.     if { [catch {set className [winfo class $_pathMap($path)]}] } {
  818.         error "serious error: \"$path\" is not a valid window"
  819.     }
  820.     # get the classname, look it up, get index, us it to look up type
  821.     set type [ lindex \
  822.         {frame menubutton menu} \
  823.         [lsearch { Frame Menubutton Menu } $className] \
  824.         ]
  825.     }
  826.     return $type
  827. }
  828.  
  829. # -------------------------------------------------------------
  830. #
  831. # METHOD: yposition entryPath
  832. #
  833. # Returns a decimal string giving the y-coordinate within
  834. # the  menu window of the topmost pixel in the entry specified
  835. # by componentPathName. If the  componentPathName  is  not  an
  836. # entry, an error is issued.
  837. #
  838. # -------------------------------------------------------------
  839. body iwidgets::Menubar::yposition { entryPath } {
  840.  
  841.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  842.     # Path Conversions
  843.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  844.     set entryPath [_parsePath $entryPath]
  845.     set index $_pathMap($entryPath)
  846.  
  847.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  848.     # Error Handling
  849.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  850.     # first verify that entryPath is actually a path to
  851.     # an entry and not to menu, menubutton, etc.
  852.     if { ! [regexp {^[0-9]+$} $index] } {
  853.     error "bad value: entryPath is not an entry"
  854.     }
  855.  
  856.     # ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  857.     # Call yposition command
  858.     # '''''''''''''''''''''''''''''''''''''''''''''''''''''
  859.     # get the menu path from the entry path name
  860.     set tkMenuPath [_entryPathToTkMenuPath $entryPath]
  861.  
  862.     # call the menu's yposition command, adjusting index based on tearoff
  863.     return [$tkMenuPath yposition [_getTkIndex $tkMenuPath $index]]
  864.  
  865. }
  866.  
  867. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  868. # PARSING METHODS
  869. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  870. # -------------------------------------------------------------
  871. #
  872. # PARSING METHOD: menubutton
  873. #
  874. # This method is invoked via an evaluation of the -menubuttons
  875. # option for the Menubar.
  876. #
  877. # It adds a new menubutton and processes any -menu options
  878. # for creating entries on the menu pane associated with the 
  879. # menubutton
  880. # -------------------------------------------------------------
  881. body iwidgets::Menubar::menubutton { menuName args } {
  882.     eval "add menubutton .$menuName $args"
  883. }
  884.  
  885. # -------------------------------------------------------------
  886. #
  887. # PARSING METHOD: options
  888. #
  889. # This method is invoked via an evaluation of the -menu
  890. # option for menubutton commands.
  891. #
  892. # It configures the current menu ($_ourMenuPath) with the options
  893. # that follow (args)
  894. #
  895. # -------------------------------------------------------------
  896. body iwidgets::Menubar::options { args } {
  897.     eval "$_tkMenuPath configure $args"
  898. }
  899.  
  900.  
  901. # -------------------------------------------------------------
  902. #
  903. # PARSING METHOD: command
  904. #
  905. # This method is invoked via an evaluation of the -menu
  906. # option for menubutton commands.
  907. #
  908. # It adds a new command entry to the current menu, $_ourMenuPath
  909. # naming it $cmdName. Since this is the most common case when
  910. # creating menus, streamline it by duplicating some code from
  911. # the add{} method.
  912. #
  913. # -------------------------------------------------------------
  914. body iwidgets::Menubar::command { cmdName args } {
  915.     set path $_ourMenuPath.$cmdName
  916.  
  917.     # error checking
  918.     regsub {.*[.]} $path "" segName
  919.     if [regexp {^(menu|last|end|[0-9]+)$} $segName] {
  920.      error "bad name \"$segName\": user created component \
  921.          path names may not end with \
  922.          \"end\", \"last\", \"menu\", \
  923.          or be an integer"
  924.     }
  925.  
  926.     eval _addEntry command $path $args
  927. }
  928.  
  929. # -------------------------------------------------------------
  930. #
  931. # PARSING METHOD: checkbutton
  932. #
  933. # This method is invoked via an evaluation of the -menu
  934. # option for menubutton/cascade commands.
  935. #
  936. # It adds a new checkbutton entry to the current menu, $_ourMenuPath
  937. # naming it $chkName.
  938. #
  939. # -------------------------------------------------------------
  940. body iwidgets::Menubar::checkbutton { chkName args } {
  941.     eval "add checkbutton $_ourMenuPath.$chkName $args"
  942. }
  943.  
  944. # -------------------------------------------------------------
  945. #
  946. # PARSING METHOD: radiobutton
  947. #
  948. # This method is invoked via an evaluation of the -menu
  949. # option for menubutton/cascade commands.
  950. #
  951. # It adds a new radiobutton entry to the current menu, $_ourMenuPath
  952. # naming it $radName.
  953. #
  954. # -------------------------------------------------------------
  955. body iwidgets::Menubar::radiobutton { radName args } {
  956.     eval "add radiobutton $_ourMenuPath.$radName $args"
  957. }
  958.  
  959. # -------------------------------------------------------------
  960. #
  961. # PARSING METHOD: separator
  962. #
  963. # This method is invoked via an evaluation of the -menu
  964. # option for menubutton/cascade commands.
  965. #
  966. # It adds a new separator entry to the current menu, $_ourMenuPath
  967. # naming it $sepName.
  968. #
  969. # -------------------------------------------------------------
  970. body iwidgets::Menubar::separator { sepName args } {
  971.     eval $_tkMenuPath add separator
  972.     set _pathMap($_ourMenuPath.$sepName) [_getPdIndex $_tkMenuPath end]
  973. }
  974.  
  975. # -------------------------------------------------------------
  976. #
  977. # PARSING METHOD: cascade
  978. #
  979. # This method is invoked via an evaluation of the -menu
  980. # option for menubutton/cascade commands.
  981. #
  982. # It adds a new cascade entry to the current menu, $_ourMenuPath
  983. # naming it $casName. It processes the -menu option if present,
  984. # adding a new menu pane and its associated entries found.
  985. #
  986. # -------------------------------------------------------------
  987. body iwidgets::Menubar::cascade { casName args } {
  988.     
  989.     # Save the current menu we are adding to, cascade can change
  990.     # the current menu through -menu options.
  991.     set saveOMP $_ourMenuPath
  992.     set saveTKP $_tkMenuPath
  993.  
  994.     eval "add cascade $_ourMenuPath.$casName $args"
  995.  
  996.     # Restore the saved menu states so that the next entries of
  997.     # the -menu/-menubuttons we are processing will be at correct level.
  998.     set _ourMenuPath $saveOMP
  999.     set _tkMenuPath $saveTKP
  1000. }
  1001.  
  1002. # ... A P I   S U P P O R T   M E T H O D S...
  1003.  
  1004. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1005. # MENU ADD, INSERT, DELETE
  1006. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1007. # -------------------------------------------------------------
  1008. #
  1009. # PRIVATE METHOD: _addMenuButton
  1010. #
  1011. # Makes a new menubutton & associated -menu, pack appended
  1012. #
  1013. # -------------------------------------------------------------
  1014. body iwidgets::Menubar::_addMenuButton { buttonName args} {
  1015.  
  1016.     eval "_makeMenuButton $buttonName $args"
  1017.  
  1018.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1019.     # Pack at end, adjust for help buttonName
  1020.     # ''''''''''''''''''''''''''''''''''
  1021.     if { $buttonName == "help" } {
  1022.     pack $itk_component($buttonName) -side right
  1023.     } else {
  1024.     pack $itk_component($buttonName) -side left
  1025.     }
  1026.  
  1027.     return $itk_component($buttonName)
  1028. }
  1029.  
  1030. # -------------------------------------------------------------
  1031. #
  1032. # PRIVATE METHOD: _insertMenuButton
  1033. #
  1034. # inserts a menubutton named $buttonName on a menu bar before 
  1035. # another menubutton specified by $beforeMenuPath
  1036. #
  1037. # -------------------------------------------------------------
  1038. body iwidgets::Menubar::_insertMenuButton { beforeMenuPath buttonName args} {
  1039.  
  1040.     eval "_makeMenuButton $buttonName $args"
  1041.  
  1042.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1043.     # Pack before the $beforeMenuPath
  1044.     # ''''''''''''''''''''''''''''''''
  1045.     set beforeTkMenu $_pathMap($beforeMenuPath)
  1046.     regsub {[.]menu$} $beforeTkMenu "" beforeTkMenu
  1047.     pack $itk_component(menubar).$buttonName \
  1048.         -side left \
  1049.         -before $beforeTkMenu
  1050.  
  1051.     return $itk_component($buttonName)
  1052. }
  1053.  
  1054. # -------------------------------------------------------------
  1055. #
  1056. # PRIVATE METHOD: _makeMenuButton
  1057. #
  1058. # creates a menubutton named buttonName on the menubar with args.
  1059. # The -menu option if present will trigger attaching a menu pane.
  1060. #
  1061. # -------------------------------------------------------------
  1062. body iwidgets::Menubar::_makeMenuButton {buttonName args} {
  1063.  
  1064.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1065.     # Capture the -menu option if present
  1066.     # '''''''''''''''''''''''''''''''''''
  1067.     array set temp $args
  1068.     if { [::info exists temp(-menu)] } {
  1069.     # We only keep this in case of menuconfigure or menucget
  1070.     set _menuOption(.$buttonName) $temp(-menu)
  1071.     set menuEvalStr $temp(-menu)
  1072.     } else {
  1073.     set menuEvalStr {}
  1074.     }
  1075.  
  1076.     # attach the actual menu widget to the menubutton's arg list
  1077.     set temp(-menu) $itk_component(menubar).$buttonName.menu
  1078.     set args [array get temp]
  1079.  
  1080.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1081.     # Create menubutton component
  1082.     # ''''''''''''''''''''''''''''''''
  1083.     itk_component add $buttonName {
  1084.         eval ::menubutton \
  1085.                 $itk_component(menubar).$buttonName \
  1086.                 $args 
  1087.     } {
  1088.         keep \
  1089.                 -activebackground \
  1090.                 -activeforeground \
  1091.                 -anchor \
  1092.                 -background \
  1093.                 -borderwidth \
  1094.                 -cursor \
  1095.                 -disabledforeground \
  1096.                 -font \
  1097.                 -foreground \
  1098.                 -highlightbackground \
  1099.                 -highlightcolor \
  1100.                 -highlightthickness \
  1101.                 -justify \
  1102.                 -padx \
  1103.                 -pady \
  1104.                 -wraplength
  1105.     }
  1106.     
  1107.     set _pathMap(.$buttonName) $itk_component($buttonName)
  1108.  
  1109.     _makeMenu \
  1110.         $buttonName-menu \
  1111.         $itk_component($buttonName).menu \
  1112.         .$buttonName \
  1113.         $menuEvalStr
  1114.  
  1115.     return $itk_component($buttonName)
  1116.     
  1117. }
  1118.  
  1119. # -------------------------------------------------------------
  1120. #
  1121. # PRIVATE METHOD: _makeMenu
  1122. #
  1123. # Creates a menu.
  1124. # It then evaluates the $menuEvalStr to create entries on the menu.
  1125. #
  1126. # Assumes the existence of $itk_component($buttonName)
  1127. #
  1128. # -------------------------------------------------------------
  1129. body iwidgets::Menubar::_makeMenu \
  1130.     { componentName widgetName menuPath menuEvalStr } {
  1131.  
  1132.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1133.     # Create menu component
  1134.     # ''''''''''''''''''''''''''''''''
  1135.     itk_component add $componentName {
  1136.     ::menu $widgetName
  1137.     } {
  1138.     keep \
  1139.         -activebackground \
  1140.         -activeborderwidth \
  1141.         -activeforeground \
  1142.         -background \
  1143.         -borderwidth \
  1144.         -cursor \
  1145.         -disabledforeground \
  1146.         -font \
  1147.         -foreground 
  1148.     }
  1149.  
  1150.     set _pathMap($menuPath.menu) $itk_component($componentName)
  1151.  
  1152.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1153.     # Attach help handler to this menu
  1154.     # ''''''''''''''''''''''''''''''''
  1155.     bind $itk_component($componentName) <<MenuSelect>> \
  1156.         [code $this _helpHandler $menuPath.menu]
  1157.  
  1158.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1159.     # Handle -menu
  1160.     #'''''''''''''''''''''''''''''''''
  1161.     set _ourMenuPath $menuPath
  1162.     set _tkMenuPath $itk_component($componentName)
  1163.  
  1164.     #
  1165.     # A zero parseLevel says we are at the top of the parse tree,
  1166.     # so get the context scope level and do a subst for the menuEvalStr.
  1167.     #
  1168.     if { $_parseLevel == 0 } {
  1169.         set _callerLevel [_getCallerLevel]
  1170.     }
  1171.  
  1172.     #
  1173.     # bump up the parse level, so if we get called via the 'eval $menuEvalStr'
  1174.     # we know to skip the above steps...
  1175.     #
  1176.     incr _parseLevel
  1177.     eval $menuEvalStr
  1178.  
  1179.     #
  1180.     # leaving, so done with this parse level, so bump it back down 
  1181.     #
  1182.     incr _parseLevel -1
  1183. }
  1184.  
  1185. # -------------------------------------------------------------
  1186. #
  1187. # PRIVATE METHOD: _substEvalStr
  1188. #
  1189. # This performs the substitution and evaluation of $ [], \ found
  1190. # in the -menubutton/-menus options
  1191. #
  1192. # -------------------------------------------------------------
  1193. body iwidgets::Menubar::_substEvalStr { evalStr } {
  1194.     upvar $evalStr evalStrRef
  1195.     set evalStrRef [uplevel $_callerLevel [list subst $evalStrRef]]
  1196. }
  1197.  
  1198.  
  1199. # -------------------------------------------------------------
  1200. #
  1201. # PRIVATE METHOD: _deleteMenu
  1202. #
  1203. # _deleteMenu menuPath ?menuPath2?
  1204. #
  1205. # deletes menuPath or from menuPath to menuPath2
  1206. #
  1207. # Menu paths may be formed in one of two ways
  1208. #    .MENUBAR.menuName  where menuName is the name of the menu
  1209. #    .MENUBAR.menuName.menu  where menuName is the name of the menu
  1210. #
  1211. # The basic rule is '.menu' is not needed.
  1212. # -------------------------------------------------------------
  1213. body iwidgets::Menubar::_deleteMenu { menuPath {menuPath2 {}} } {
  1214.  
  1215.     if { $menuPath2 == "" } {
  1216.     # get a corrected path (subst for number, last, end)
  1217.     set path [_parsePath $menuPath]
  1218.  
  1219.     _deleteAMenu $path
  1220.  
  1221.     } else {
  1222.     # gets the list of menus in interface order
  1223.     set menuList [_getMenuList]
  1224.  
  1225.     # ... get the start menu and the last menu ...
  1226.  
  1227.     # get a corrected path (subst for number, last, end)
  1228.     set menuStartPath [_parsePath $menuPath]
  1229.  
  1230.     regsub {[.]menu$} $menuStartPath "" menuStartPath
  1231.  
  1232.     set menuEndPath [_parsePath $menuPath2]
  1233.  
  1234.     regsub {[.]menu$} $menuEndPath "" menuEndPath
  1235.     
  1236.     # get the menu position (0 based) of the start and end menus.
  1237.     set start [lsearch -exact $menuList $menuStartPath]
  1238.     if { $start == -1 } {
  1239.         error "bad menu path \"$menuStartPath\": \
  1240.             should be one of $menuList"
  1241.     }
  1242.     set end [lsearch -exact $menuList $menuEndPath]
  1243.     if { $end == -1 } {
  1244.         error "bad menu path \"$menuEndPath\": \
  1245.             should be one of $menuList"
  1246.     }
  1247.  
  1248.     # now create the list from this range of menus
  1249.     set delList [lrange $menuList $start $end]
  1250.  
  1251.     # walk thru them deleting each menu.
  1252.     # this list has no .menu on the end.
  1253.     foreach m $delList {
  1254.         _deleteAMenu $m.menu
  1255.     }
  1256.     }
  1257. }
  1258.  
  1259. # -------------------------------------------------------------
  1260. #
  1261. # PRIVATE METHOD: _deleteAMenu
  1262. #
  1263. # _deleteMenu menuPath 
  1264. #
  1265. # deletes a single Menu (menubutton and menu pane with entries)
  1266. #
  1267. # -------------------------------------------------------------
  1268. body iwidgets::Menubar::_deleteAMenu { path } {
  1269.  
  1270.     # We will normalize the path to not include the '.menu' if
  1271.     # it is on the path already.
  1272.  
  1273.     regsub {[.]menu$} $path "" menuButtonPath
  1274.     regsub {.*[.]} $menuButtonPath "" buttonName
  1275.  
  1276.     # Loop through and destroy any cascades, etc on menu.
  1277.     set entryList [_getEntryList $menuButtonPath]
  1278.     foreach entry $entryList {
  1279.     _deleteEntry $entry
  1280.     }
  1281.  
  1282.     # Delete the menubutton and menu components...
  1283.     destroy $itk_component($buttonName-menu)
  1284.     destroy $itk_component($buttonName)
  1285.  
  1286.     # This is because of some itcl bug that doesn't delete
  1287.     # the component on the destroy in some cases...
  1288.     catch {itk_component delete $buttonName-menu}
  1289.     catch {itk_component delete $buttonName}
  1290.     
  1291.     # unset our paths
  1292.     _unsetPaths $menuButtonPath
  1293.  
  1294. }
  1295.  
  1296. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1297. # ENTRY ADD, INSERT, DELETE
  1298. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1299.  
  1300. # -------------------------------------------------------------
  1301. #
  1302. # PRIVATE METHOD: _addEntry
  1303. #
  1304. # Adds an entry to menu.
  1305. #
  1306. # -------------------------------------------------------------
  1307. body iwidgets::Menubar::_addEntry { type path args } {
  1308.  
  1309.     # Error Checking
  1310.     # ''''''''''''''
  1311.     # the path should not end with '.menu'
  1312.     # Not needed -- already checked by add{}
  1313.     # if { [regexp {[.]menu$} $path] } {
  1314.     #  error "bad entry path: \"$path\". \
  1315.     #        The name \"menu\" is reserved for menu panes"
  1316.     # }
  1317.  
  1318.     # get the tkMenuPath
  1319.     set tkMenuPath [_entryPathToTkMenuPath $path]
  1320.     if { $tkMenuPath == "" } {
  1321.     error "bad entry path: \"$path\". The menu path prefix is not valid"
  1322.     }
  1323.  
  1324.     # get the -helpstr option if present
  1325.     array set temp $args
  1326.     if { [::info exists temp(-helpstr)] } {
  1327.     set helpStr $temp(-helpstr)
  1328.     unset temp(-helpstr)
  1329.     } else {
  1330.     set helpStr {}
  1331.     }
  1332.     set args [array get temp]
  1333.  
  1334.     # Handle CASCADE
  1335.     # ''''''''''''''
  1336.     # if this is a cascade go ahead and add in the menu...
  1337.     if { $type == "cascade" } {
  1338.     eval [list _addCascade $tkMenuPath $path] $args
  1339.     # Handle Non-CASCADE
  1340.     # ''''''''''''''''''
  1341.     } else {
  1342.     # add the entry
  1343.     eval [list $tkMenuPath add $type] $args
  1344.     set _pathMap($path) [_getPdIndex $tkMenuPath end]
  1345.     }
  1346.  
  1347.     # Remember the help string
  1348.     set _helpString($path) $helpStr
  1349.  
  1350.     return $_pathMap($path)
  1351. }
  1352.  
  1353. # -------------------------------------------------------------
  1354. #
  1355. # PRIVATE METHOD: _addCascade
  1356. #
  1357. # Creates a cascade button.  Handles the -menu option
  1358. #
  1359. # -------------------------------------------------------------
  1360. body iwidgets::Menubar::_addCascade { tkMenuPath path args } {
  1361.  
  1362.     # get the cascade name from our path
  1363.     regsub {.*[.]} $path "" cascadeName
  1364.  
  1365.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1366.     # Capture the -menu option if present
  1367.     # '''''''''''''''''''''''''''''''''''
  1368.     array set temp $args
  1369.     if { [::info exists temp(-menu)] } {
  1370.     set menuEvalStr $temp(-menu)
  1371.     } else {
  1372.     set menuEvalStr {}
  1373.     }
  1374.  
  1375.     # attach the menu pane
  1376.     set temp(-menu) $tkMenuPath.$cascadeName
  1377.     set args [array get temp]
  1378.  
  1379.     # Create the cascade entry
  1380.     eval $tkMenuPath add cascade $args
  1381.     
  1382.     # Keep the -menu string in case of menuconfigure or menucget
  1383.     if { $menuEvalStr != "" } {
  1384.     set _menuOption($path) $menuEvalStr
  1385.     }
  1386.  
  1387.     # update our pathmap
  1388.     set _pathMap($path) [_getPdIndex $tkMenuPath end]
  1389.  
  1390.     _makeMenu \
  1391.         $cascadeName-menu \
  1392.         $tkMenuPath.$cascadeName \
  1393.         $path \
  1394.         $menuEvalStr
  1395.  
  1396.     #return $itk_component($cascadeName)
  1397.  
  1398. }
  1399.  
  1400. # -------------------------------------------------------------
  1401. #
  1402. # PRIVATE METHOD: _insertEntry
  1403. #
  1404. # inserts an entry on a menu before entry given by beforeEntryPath.
  1405. # The added entry is of type TYPE and its name is NAME. ARGS are
  1406. # passed for customization of the entry.
  1407. #
  1408. # -------------------------------------------------------------
  1409. body iwidgets::Menubar::_insertEntry { beforeEntryPath type name args } {
  1410.  
  1411.     # convert entryPath to an index value
  1412.     set bfIndex $_pathMap($beforeEntryPath)
  1413.  
  1414.     # first verify that beforeEntryPath is actually a path to
  1415.     # an entry and not to menu, menubutton, etc.
  1416.     if { ! [regexp {^[0-9]+$} $bfIndex] } {
  1417.     error "bad entry path: beforeEntryPath is not an entry"
  1418.     }
  1419.  
  1420.     # get the menu path from the entry path name
  1421.     regsub {[.][^.]*$} $beforeEntryPath "" menuPathPrefix
  1422.     set tkMenuPath $_pathMap($menuPathPrefix.menu)
  1423.  
  1424.     # INDEX is zero based at this point.
  1425.  
  1426.     # ENTRIES is a zero based list...
  1427.     set entries [_getEntryList $menuPathPrefix]
  1428.  
  1429.     # 
  1430.     # Adjust the entries after the inserted item, to have
  1431.     # the correct index numbers. Note, we stay zero based 
  1432.     # even though tk flips back and forth depending on tearoffs.
  1433.     #
  1434.     for {set i $bfIndex} {$i < [llength $entries]} {incr i} {
  1435.     # path==entry path in numerical order
  1436.     set path [lindex $entries $i]
  1437.  
  1438.     # add one to each entry after the inserted one.
  1439.     set _pathMap($path) [expr $i + 1]
  1440.     }
  1441.  
  1442.     # get the -helpstr option if present
  1443.     array set temp $args
  1444.     if { [::info exists temp(-helpstr)] } {
  1445.     set helpStr $temp(-helpstr)
  1446.     unset temp(-helpstr)
  1447.     } else {
  1448.     set helpStr {}
  1449.     }
  1450.     set args [array get temp]
  1451.  
  1452.     set path $menuPathPrefix.$name
  1453.  
  1454.     # Handle CASCADE
  1455.     # ''''''''''''''
  1456.     # if this is a cascade go ahead and add in the menu...
  1457.     if { [string match cascade $type] } {
  1458.  
  1459.     if { [ catch {eval "_insertCascade \
  1460.         $bfIndex $tkMenuPath $path $args"} errMsg ]} {
  1461.         for {set i $bfIndex} {$i < [llength $entries]} {incr i} {
  1462.         # path==entry path in numerical order
  1463.         set path [lindex $entries $i]
  1464.  
  1465.         # sub the one we added earlier.
  1466.         set _pathMap($path) [expr $_pathMap($path) - 1]
  1467.         # @@ delete $hs
  1468.         }
  1469.         error $errMsg
  1470.     }
  1471.  
  1472.     # Handle Entry
  1473.     # ''''''''''''''
  1474.     } else {
  1475.  
  1476.     # give us a zero or 1-based index based on tear-off menu status
  1477.     # invoke the menu's insert command
  1478.     if { [catch {eval "$tkMenuPath insert \
  1479.         [_getTkIndex $tkMenuPath $bfIndex] $type $args"} errMsg]} {
  1480.         for {set i $bfIndex} {$i < [llength $entries]} {incr i} {
  1481.         # path==entry path in numerical order
  1482.         set path [lindex $entries $i]
  1483.  
  1484.         # sub the one we added earlier.
  1485.         set _pathMap($path) [expr $_pathMap($path) - 1]
  1486.         # @@ delete $hs
  1487.         }
  1488.         error $errMsg
  1489.     }
  1490.     
  1491.  
  1492.     # add the helpstr option to our options list (attach to entry)
  1493.     set _helpString($path) $helpStr
  1494.  
  1495.     # Insert the new entry path into pathmap giving it an index value
  1496.     set _pathMap($menuPathPrefix.$name) $bfIndex
  1497.     
  1498.     }
  1499.  
  1500.     return [_getTkIndex $tkMenuPath $bfIndex]
  1501. }
  1502.  
  1503. # -------------------------------------------------------------
  1504. #
  1505. # PRIVATE METHOD: _insertCascade
  1506. #
  1507. # Creates a cascade button.  Handles the -menu option
  1508. #
  1509. # -------------------------------------------------------------
  1510. body iwidgets::Menubar::_insertCascade { bfIndex tkMenuPath path args } {
  1511.  
  1512.     # get the cascade name from our path
  1513.     regsub {.*[.]} $path "" cascadeName
  1514.  
  1515.     #,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  1516.     # Capture the -menu option if present
  1517.     # '''''''''''''''''''''''''''''''''''
  1518.     array set temp $args
  1519.     if { [::info exists temp(-menu)] } {
  1520.     # Keep the -menu string in case of menuconfigure or menucget
  1521.     set _menuOption($path) $temp(-menu)
  1522.     set menuEvalStr $temp(-menu)
  1523.     } else {
  1524.     set menuEvalStr {}
  1525.     }
  1526.  
  1527.     # attach the menu pane
  1528.     set temp(-menu) $tkMenuPath.$cascadeName
  1529.     set args [array get temp]
  1530.  
  1531.     # give us a zero or 1-based index based on tear-off menu status
  1532.     # invoke the menu's insert command
  1533.     eval "$tkMenuPath insert \
  1534.         [_getTkIndex $tkMenuPath $bfIndex] cascade $args"
  1535.     
  1536.     # Insert the new entry path into pathmap giving it an index value
  1537.     set _pathMap($path) $bfIndex
  1538.     _makeMenu \
  1539.         $cascadeName-menu \
  1540.         $tkMenuPath.$cascadeName \
  1541.         $path \
  1542.         $menuEvalStr
  1543.  
  1544.     #return $itk_component($cascadeName)
  1545. }
  1546.  
  1547. # -------------------------------------------------------------
  1548. #
  1549. # PRIVATE METHOD: _deleteEntry
  1550. #
  1551. # _deleteEntry entryPath ?entryPath2?
  1552. #
  1553. #   either
  1554. # deletes the entry entryPath
  1555. #   or
  1556. # deletes the entries from entryPath to entryPath2 
  1557. #
  1558. # -------------------------------------------------------------
  1559. body iwidgets::Menubar::_deleteEntry { entryPath {entryPath2 {}} } {
  1560.  
  1561.     if { $entryPath2 == "" } {
  1562.     # get a corrected path (subst for number, last, end)
  1563.     set path [_parsePath $entryPath]
  1564.  
  1565.     set entryIndex $_pathMap($path)
  1566.     if { $entryIndex == -1 } {
  1567.         error "bad value for pathName: \
  1568.             $entryPath in call to delet"
  1569.     }
  1570.  
  1571.     # get the type, if cascade, we will want to delete menu
  1572.     set type [type $path]
  1573.  
  1574.     # ... munge up the menu name ...
  1575.  
  1576.     # the tkMenuPath is looked up with the .menu added to lookup
  1577.     # strip off the entry component
  1578.     regsub {[.][^.]*$} $path "" menuPath
  1579.     set tkMenuPath $_pathMap($menuPath.menu)
  1580.  
  1581.     # get the ordered entry list
  1582.     set entries [_getEntryList $menuPath]
  1583.  
  1584.     # ... Fix up path entry indices ...
  1585.  
  1586.     # delete the path from the map
  1587.     unset _pathMap([lindex $entries $entryIndex])
  1588.  
  1589.     # Subtract off 1 for each entry below the deleted one.
  1590.     for {set i [expr $entryIndex + 1]} \
  1591.         {$i < [llength $entries]} \
  1592.         {incr i} {
  1593.         set epath [lindex $entries $i]
  1594.         incr _pathMap($epath) -1
  1595.     }
  1596.  
  1597.     # ... Delete the menu entry widget ...
  1598.  
  1599.     # delete the menu entry, ajusting index for TK
  1600.     $tkMenuPath delete [_getTkIndex $tkMenuPath $entryIndex]
  1601.  
  1602.     if { $type == "cascade" } {
  1603.         regsub {.*[.]} $path "" cascadeName
  1604.         destroy $itk_component($cascadeName-menu)
  1605.  
  1606.         # This is because of some itcl bug that doesn't delete
  1607.         # the component on the destroy in some cases...
  1608.         catch {itk_component delete $cascadeName-menu}
  1609.  
  1610.         _unsetPaths $path
  1611.     }
  1612.  
  1613.     } else { 
  1614.     # get a corrected path (subst for number, last, end)
  1615.     set path1 [_parsePath $entryPath]
  1616.     set path2 [_parsePath $entryPath2]
  1617.  
  1618.     set fromEntryIndex $_pathMap($path1)
  1619.     if { $fromEntryIndex == -1 } {
  1620.         error "bad value for entryPath1: \
  1621.             $entryPath in call to delet"
  1622.     }
  1623.     set toEntryIndex $_pathMap($path2)
  1624.     if { $toEntryIndex == -1 } {
  1625.         error "bad value for entryPath2: \
  1626.             $entryPath2 in call to delet"
  1627.     }
  1628.     # ... munge up the menu name ...
  1629.  
  1630.     # the tkMenuPath is looked up with the .menu added to lookup
  1631.     # strip off the entry component
  1632.     regsub {[.][^.]*$} $path1 "" menuPath
  1633.     set tkMenuPath $_pathMap($menuPath.menu)
  1634.  
  1635.     # get the ordered entry list
  1636.     set entries [_getEntryList $menuPath]
  1637.  
  1638.     # ... Fix up path entry indices ...
  1639.  
  1640.     # delete the range from the pathMap list
  1641.     for {set i $fromEntryIndex} {$i <= $toEntryIndex} {incr i} {
  1642.         unset _pathMap([lindex $entries $i])
  1643.     }
  1644.  
  1645.     # Subtract off 1 for each entry below the deleted range.
  1646.     # Loop from one below the bottom delete entry to end list
  1647.     for {set i [expr $toEntryIndex + 1]} \
  1648.         {$i < [llength $entries]} \
  1649.         {incr i} {
  1650.         # take this path and sets its index back by size of
  1651.         # deleted range.
  1652.         set path [lindex $entries $i]
  1653.         set _pathMap($path) \
  1654.             [expr $_pathMap($path) - \
  1655.             (($toEntryIndex - $fromEntryIndex) + 1)]
  1656.     }
  1657.  
  1658.     # ... Delete the menu entry widget ...
  1659.  
  1660.     # delete the menu entry, ajusting index for TK
  1661.     $tkMenuPath delete \
  1662.         [_getTkIndex $tkMenuPath $fromEntryIndex] \
  1663.         [_getTkIndex $tkMenuPath $toEntryIndex]
  1664.  
  1665.     }
  1666. }
  1667.  
  1668. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1669. # CONFIGURATION SUPPORT
  1670. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1671. # -------------------------------------------------------------
  1672. #
  1673. # PRIVATE METHOD: _configureMenu
  1674. #
  1675. # This configures a menu. A menu is a true tk widget, thus we
  1676. # pass the tkPath variable. This path may point to either a 
  1677. # menu button (does not end with the name 'menu', or a menu
  1678. # which ends with the name 'menu'
  1679. #
  1680. # path : our Menubar path name to this menu button or menu pane.
  1681. #        if we end with the name '.menu' then it is a menu pane.
  1682. # tkPath : the path to the corresponding Tk menubutton or menu.
  1683. # args   : the args for configuration
  1684. #
  1685. # -------------------------------------------------------------
  1686. body iwidgets::Menubar::_configureMenu { path tkPath {option {}} args } {
  1687.  
  1688.     set class [winfo class $tkPath]
  1689.  
  1690.     if { $option == "" } {
  1691.     # No arguments: return all options
  1692.     set configList [$tkPath configure]
  1693.  
  1694.     if { [info exists _menuOption($path)] } {
  1695.         lappend configList [list -menu menu Menu {} $_menuOption($path)]
  1696.     } else {
  1697.         lappend configList [list -menu menu Menu {} {}]
  1698.     }
  1699.     if { [info exists _helpString($path)] } {
  1700.         lappend configList [list -helpstr helpStr HelpStr {} \
  1701.             $_helpString($path)]
  1702.     } else {
  1703.         lappend configList [list -helpstr helpStr HelpStr {} {}]
  1704.     }
  1705.     return $configList
  1706.  
  1707.     } elseif {$args == "" } {
  1708.     if { $option == "-menu" } {
  1709.         if { [info exists _menuOption($path)] } {
  1710.         return [list -menu menu Menu {} $_menuOption($path)]
  1711.         } else {
  1712.         return [list -menu menu Menu {} {}]
  1713.         }
  1714.     } elseif { $option == "-helpstr" } {
  1715.         if { [info exists _helpString($path)] } {
  1716.         return [list -helpstr helpStr HelpStr {} $_helpString($path)]
  1717.         } else {
  1718.         return [list -helpstr helpStr HelpStr {} {}]
  1719.         }
  1720.     } else {
  1721.         # ... OTHERWISE, let Tk get it.
  1722.         return [$tkPath configure $option]
  1723.     }
  1724.     } else {
  1725.     set args [concat $option $args]
  1726.  
  1727.     # If this is a menubutton, and has -menu option, process it
  1728.     if { $class == "Menubutton" && [regexp -- {-menu} $args] } {
  1729.         eval _configureMenuOption menubutton $path $args
  1730.     } else {
  1731.         eval $tkPath configure $args
  1732.     }
  1733.     return ""
  1734.     }
  1735. }
  1736.  
  1737. # -------------------------------------------------------------
  1738. #
  1739. # PRIVATE METHOD: _configureMenuOption
  1740. #
  1741. # Allows for configuration of the -menu option on
  1742. # menubuttons and cascades
  1743. #
  1744. # find out if we are the last menu, or are before one.
  1745. # delete the old menu.
  1746. # if we are the last, then add us back at the end
  1747. # if we are before another menu, get the beforePath
  1748. #
  1749. # -------------------------------------------------------------
  1750. body iwidgets::Menubar::_configureMenuOption { type path args } {
  1751.  
  1752.     regsub {[.][^.]*$} $path "" pathPrefix
  1753.  
  1754.     if { $type == "menubutton" } {
  1755.     set menuList [_getMenuList]
  1756.     set pos [lsearch $menuList $path]
  1757.     if { $pos == [expr [llength $menuList] - 1] } {
  1758.         set insert false
  1759.     } else {
  1760.         set insert true
  1761.     }
  1762.     } elseif { $type == "cascade" } {
  1763.     set lastEntryPath [_parsePath $pathPrefix.last]
  1764.     if { $lastEntryPath == $path } {
  1765.         set insert false
  1766.     } else {
  1767.         set insert true
  1768.     }
  1769.     set pos [index $path]
  1770.  
  1771.     }
  1772.  
  1773.  
  1774.     eval "delete $pathPrefix.$pos"
  1775.     if { $insert } {
  1776.     # get name from path...
  1777.     regsub {.*[.]} $path "" name
  1778.  
  1779.     eval insert $pathPrefix.$pos $type \
  1780.         $name $args
  1781.     } else {
  1782.     eval add $type $path $args
  1783.     }
  1784. }
  1785.  
  1786. # -------------------------------------------------------------
  1787. #
  1788. # PRIVATE METHOD: _configureMenuEntry
  1789. #
  1790. # This configures a menu entry. A menu entry is either a command,
  1791. # radiobutton, separator, checkbutton, or a cascade. These have
  1792. # a corresponding Tk index value for the corresponding tk menu
  1793. # path.
  1794. #
  1795. # path   : our Menubar path name to this menu entry.
  1796. # index  : the t
  1797. # args   : the args for configuration
  1798. #
  1799. # -------------------------------------------------------------
  1800. body iwidgets::Menubar::_configureMenuEntry { path index {option {}} args } {
  1801.  
  1802.     set type [type $path]
  1803.  
  1804.     # set len [llength $args]
  1805.  
  1806.     # get the menu path from the entry path name
  1807.     set tkMenuPath [_entryPathToTkMenuPath $path]
  1808.  
  1809.     if { $option == "" } {
  1810.     set configList [$tkMenuPath entryconfigure \
  1811.         [_getTkIndex $tkMenuPath $index]]
  1812.  
  1813.     if { $type == "cascade" } {
  1814.         if { [info exists _menuOption($path)] } {
  1815.         lappend configList [list -menu menu Menu {} \
  1816.             $_menuOption($path)]
  1817.         } else {
  1818.         lappend configList [list -menu menu Menu {} {}]
  1819.         }
  1820.     }
  1821.     if { [info exists _helpString($path)] } {
  1822.         lappend configList [list -helpstr helpStr HelpStr {} \
  1823.             $_helpString($path)]
  1824.     } else {
  1825.         lappend configList [list -helpstr helpStr HelpStr {} {}]
  1826.     }
  1827.     return $configList
  1828.  
  1829.     } elseif { $args == "" } {
  1830.     if { $option == "-menu" } {
  1831.         if { [info exists _menuOption($path)] } {
  1832.         return [list -menu menu Menu {} $_menuOption($path)]
  1833.         } else {
  1834.         return [list -menu menu Menu {} {}]
  1835.         }
  1836.     } elseif { $option == "-helpstr" } {
  1837.         if { [info exists _helpString($path)] } {
  1838.         return [list -helpstr helpStr HelpStr {} \
  1839.             $_helpString($path)]
  1840.         } else {
  1841.         return [list -helpstr helpStr HelpStr {} {}]
  1842.         }
  1843.     } else {
  1844.         # ... OTHERWISE, let Tk get it.
  1845.         return [$tkMenuPath entryconfigure \
  1846.             [_getTkIndex $tkMenuPath $index] $option]
  1847.     }
  1848.     } else {
  1849.     array set temp [concat $option $args]
  1850.  
  1851.     # ... Store -helpstr val,strip out -helpstr val from args
  1852.     if { [::info exists temp(-helpstr)] } {
  1853.         set _helpString($path) $temp(-helpstr)
  1854.         unset temp(-helpstr)
  1855.     }
  1856.  
  1857.     set args [array get temp]
  1858.     if { $type == "cascade" && [::info exists temp(-menu)] } {
  1859.         eval "_configureMenuOption cascade $path $args"
  1860.     } else {
  1861.         # invoke the menu's entryconfigure command
  1862.         # being careful to ajust the INDEX to be 0 or 1 based 
  1863.         # depending on the tearoff status
  1864.         # if the stripping process brought us down to no options
  1865.         # to set, then forget the configure of widget.
  1866.         if { [llength $args] != 0 } {
  1867.         eval $tkMenuPath entryconfigure \
  1868.             [_getTkIndex $tkMenuPath $index] $args
  1869.         }
  1870.     }
  1871.     return ""
  1872.     }
  1873. }
  1874.  
  1875. # -------------------------------------------------------------
  1876. #
  1877. # PRIVATE METHOD: _unsetPaths
  1878. #
  1879. # comment
  1880. #
  1881. # -------------------------------------------------------------
  1882. body iwidgets::Menubar::_unsetPaths { parent } {
  1883.  
  1884.     # first get the complete list of all menu paths
  1885.     set pathList [array names _pathMap]
  1886.  
  1887.     # for each path that matches parent prefix, unset it.
  1888.     foreach path $pathList {
  1889.     if { [regexp [subst -nocommands {^$parent}] $path] } {
  1890.         unset _pathMap($path)
  1891.     }
  1892.     }
  1893. }
  1894.  
  1895. # -------------------------------------------------------------
  1896. #
  1897. # PRIVATE METHOD: _entryPathToTkMenuPath
  1898. #
  1899. # Takes an entry path like .mbar.file.new and changes it to
  1900. # .mbar.file.menu and performs a lookup in the pathMap to
  1901. # get the corresponding menu widget name for tk
  1902. #
  1903. # -------------------------------------------------------------
  1904. body iwidgets::Menubar::_entryPathToTkMenuPath {entryPath} {
  1905.  
  1906.     # get the menu path from the entry path name
  1907.     # by stripping off the entry component of the path
  1908.     regsub {[.][^.]*$} $entryPath "" menuPath
  1909.  
  1910.     # the tkMenuPath is looked up with the .menu added to lookup
  1911.     if { [catch {set tkMenuPath $_pathMap($menuPath.menu)}] } {
  1912.     return ""
  1913.     } else {
  1914.     return $_pathMap($menuPath.menu)
  1915.     }
  1916. }
  1917.  
  1918.  
  1919. # -------------------------------------------------------------
  1920. #
  1921. # These two methods address the issue of menu entry indices being
  1922. # zero-based when the menu is not a tearoff menu and 1-based when
  1923. # it is a tearoff menu. Our strategy is to hide this difference.
  1924. # _getTkIndex returns the index as tk likes it: 0 based for non-tearoff
  1925. # and 1 based for tearoff menus.
  1926. # _getPdIndex (get pulldown index) always returns it as 0 based.
  1927. # -------------------------------------------------------------
  1928.  
  1929. # -------------------------------------------------------------
  1930. # PRIVATE METHOD: _getTkIndex
  1931. #
  1932. # give us a zero or 1-based answer depending on the tearoff
  1933. # status of the menu. If the menu denoted by tkMenuPath is a
  1934. # tearoff menu it returns a 1-based result, otherwise a 
  1935. # zero-based result.
  1936. # -------------------------------------------------------------
  1937. body iwidgets::Menubar::_getTkIndex { tkMenuPath tkIndex} {
  1938.  
  1939.     # if there is a tear off make it 1-based index
  1940.     if { [$tkMenuPath cget -tearoff] } {
  1941.     incr tkIndex
  1942.     }
  1943.  
  1944.     return $tkIndex
  1945. }
  1946.  
  1947. # -------------------------------------------------------------
  1948. # PRIVATE METHOD: _getPdIndex
  1949. #
  1950. # Take a tk index and give me a zero based numerical index
  1951. #
  1952. # Ask the menu widget for the index of the entry denoted by
  1953. # 'tkIndex'. Then if the menu is a tearoff adjust the value
  1954. # to be zero based.
  1955. #
  1956. # This method returns the index as if tearoffs did not exist.
  1957. # Always returns a zero-based index.
  1958. #
  1959. # -------------------------------------------------------------
  1960. body iwidgets::Menubar::_getPdIndex { tkMenuPath tkIndex } {
  1961.  
  1962.     # get the index from the tk menu
  1963.     # this 0 based for non-tearoff and 1-based for tearoffs
  1964.     set pdIndex [$tkMenuPath index $tkIndex]
  1965.  
  1966.     # if there is a tear off make it 0-based index
  1967.     if { [$tkMenuPath cget -tearoff] } {
  1968.     incr pdIndex -1
  1969.     }
  1970.  
  1971.     return $pdIndex
  1972. }
  1973.  
  1974. # -------------------------------------------------------------
  1975. # PRIVATE METHOD: _getMenuList
  1976. #
  1977. # Returns the list of menus in the order they are on the interface
  1978. # returned list is a list of our menu paths
  1979. #
  1980. # -------------------------------------------------------------
  1981. body iwidgets::Menubar::_getMenuList { } {
  1982.     # get the menus that are packed
  1983.     set tkPathList [pack slaves $itk_component(menubar)]
  1984.  
  1985.     regsub -- {[.]} $itk_component(hull) "" mbName
  1986.     regsub -all -- "\[.\]$mbName\[.\]menubar\[.\]" $tkPathList "." menuPathList
  1987.  
  1988.     return $menuPathList
  1989. }
  1990.  
  1991. # -------------------------------------------------------------
  1992. # PRIVATE METHOD: _getEntryList
  1993. #
  1994. #
  1995. # This method looks at a menupath and gets all the entries and
  1996. # returns a list of all the entry path names in numerical order
  1997. # based on their index values.
  1998. #
  1999. # MENU is the path to a menu, like .mbar.file.menu or .mbar.file
  2000. # we will calculate a menuPath from this: .mbar.file
  2001. # then we will build a list of entries in this menu excluding the
  2002. # path .mbar.file.menu
  2003. #
  2004. # -------------------------------------------------------------
  2005. body iwidgets::Menubar::_getEntryList { menu } {
  2006.  
  2007.     # if it ends with menu, clip it off
  2008.     regsub {[.]menu$} $menu "" menuPath
  2009.  
  2010.     # first get the complete list of all menu paths
  2011.     set pathList [array names _pathMap]
  2012.  
  2013.     set numEntries 0
  2014.     # iterate over the pathList and put on menuPathList those
  2015.     # that match the menuPattern
  2016.     foreach path $pathList {
  2017.     # if this path is on the menuPath's branch
  2018.     if { [regexp [subst -nocommands {$menuPath[.][^.]*$}] $path] } {
  2019.         # if not a menu itself
  2020.         if { ! [regexp {[.]menu$} $path] } {
  2021.         set orderedList($_pathMap($path)) $path
  2022.         incr numEntries
  2023.         }
  2024.     }
  2025.     }
  2026.     set entryList {}
  2027.  
  2028.     for {set i 0} {$i < $numEntries} {incr i} {
  2029.     lappend entryList $orderedList($i)
  2030.     }
  2031.  
  2032.     return $entryList
  2033.  
  2034. }
  2035.  
  2036. # -------------------------------------------------------------
  2037. # PRIVATE METHOD: _parsePath
  2038. #
  2039. # given path, PATH, _parsePath splits the path name into its
  2040. # component segments. It then puts the name back together one
  2041. # segment at a time and calls _getSymbolicPath to replace the
  2042. # keywords 'last' and 'end' as well as numeric digits.
  2043. #
  2044. # -------------------------------------------------------------
  2045. body iwidgets::Menubar::_parsePath { path } {
  2046.     set segments [split [string trimleft $path .] .]
  2047.  
  2048.     set concatPath ""
  2049.     foreach seg $segments {
  2050.  
  2051.     set concatPath [_getSymbolicPath $concatPath $seg]
  2052.  
  2053.     if { [catch {set _pathMap($concatPath)} ] } {
  2054.         error "bad path: \"$path\" does not exist. \"$seg\" not valid"
  2055.     }
  2056.     }
  2057.     return $concatPath
  2058. }
  2059.  
  2060. # -------------------------------------------------------------
  2061. # PRIVATE METHOD: _getSymbolicPath
  2062. #
  2063. # Given a PATH, _getSymbolicPath looks for the last segment of
  2064. # PATH to contain: a number, the keywords last or end. If one
  2065. # of these it figures out how to get us the actual pathname
  2066. # to the searched widget
  2067. #
  2068. # Implementor's notes:
  2069. #    Surely there is a shorter way to do this. The only diff
  2070. #    for non-numeric is getting the llength of the correct list
  2071. #    It is hard to know this upfront so it seems harder to generalize.
  2072. #
  2073. # -------------------------------------------------------------
  2074. body iwidgets::Menubar::_getSymbolicPath { parent segment } {
  2075.  
  2076.     # if the segment is a number, then look it up positionally
  2077.     # MATCH numeric index
  2078.     if { [regexp {^[0-9]+$} $segment] } {
  2079.  
  2080.     # if we have no parent, then we area menubutton
  2081.     if { $parent == {} } {
  2082.         set returnPath [lindex [_getMenuList] $segment]
  2083.     } else {
  2084.         set returnPath [lindex [_getEntryList $parent.menu] $segment]
  2085.     }
  2086.  
  2087.     # MATCH 'end' or 'last' keywords.
  2088.     } elseif { $segment == "end" || $segment == "last" } {
  2089.  
  2090.     # if we have no parent, then we are a menubutton
  2091.     if { $parent == {} } {
  2092.         set returnPath [lindex [_getMenuList] end]
  2093.     } else {
  2094.         set returnPath [lindex [_getEntryList $parent.menu] end]
  2095.     }
  2096.     } else {
  2097.     set returnPath $parent.$segment
  2098.     }
  2099.  
  2100.     return $returnPath
  2101. }
  2102.  
  2103. # -------------------------------------------------------------
  2104. # PROTECTED METHOD: _helpHandler
  2105. #
  2106. # Bound to the <Motion> event on a menu pane. This puts the
  2107. # help string associated with the menu entry into the 
  2108. # status widget help area. If no help exists for the current
  2109. # entry, the status widget is cleared.
  2110. #
  2111. # -------------------------------------------------------------
  2112. body iwidgets::Menubar::_helpHandler { menuPath } {
  2113.  
  2114.     if { $itk_option(-helpvariable) == {} } {
  2115.     return
  2116.     }
  2117.  
  2118.     set tkMenuWidget $_pathMap($menuPath)
  2119.  
  2120.     set entryIndex [$tkMenuWidget index active]
  2121.  
  2122.     # already on this item?
  2123.     if { $entryIndex == $_entryIndex } {
  2124.     return
  2125.     }
  2126.  
  2127.     set _entryIndex $entryIndex
  2128.  
  2129.     if {"none" != $entryIndex} {
  2130.         set entries [_getEntryList $menuPath]
  2131.  
  2132.         set menuEntryHit \
  2133.         [lindex $entries [_getPdIndex $tkMenuWidget $entryIndex]]
  2134.  
  2135.         # blank out the old one
  2136.         set $itk_option(-helpvariable) {}
  2137.  
  2138.         # if there is a help string for this entry
  2139.         if { [::info exists _helpString($menuEntryHit)] } {
  2140.         set $itk_option(-helpvariable) $_helpString($menuEntryHit)
  2141.         }
  2142.     } else {
  2143.     set $itk_option(-helpvariable) {}
  2144.     set _entryIndex -1
  2145.     }
  2146. }
  2147.  
  2148. # -------------------------------------------------------------
  2149. #
  2150. # PRIVATE METHOD: _getCallerLevel
  2151. #
  2152. # Starts at stack frame #0 and works down till we either hit
  2153. # a ::Menubar stack frame or an ::itk::Archetype stack frame 
  2154. # (the latter happens when a configure is called via the 'component'
  2155. # method
  2156. #
  2157. # Returns the level of the actual caller of the menubar command
  2158. # in the form of #num where num is the level number caller stack frame.
  2159. #
  2160. # -------------------------------------------------------------
  2161. body iwidgets::Menubar::_getCallerLevel { } {
  2162.  
  2163.     set levelName {}
  2164.     set levelsAreValid true
  2165.     set level 0
  2166.     set callerLevel #$level
  2167.  
  2168.     while { $levelsAreValid } {
  2169.     # Hit the end of the stack frame
  2170.     if [catch {uplevel #$level {namespace current}}] {
  2171.         set levelsAreValid false
  2172.         set callerLevel #[expr $level - 1]
  2173.         # still going
  2174.     } else {
  2175.         set newLevelName [uplevel #$level {namespace current}]
  2176.         # See if we have run into the first ::Menubar level
  2177.         if { $newLevelName == "::itk::Archetype" || \
  2178.             $newLevelName == "::iwidgets::Menubar" } {
  2179.         # If so, we are done-- set the callerLevel
  2180.         set levelsAreValid false
  2181.         set callerLevel #[expr $level - 1]
  2182.         } else {
  2183.         set levelName $newLevelName
  2184.         }
  2185.     }
  2186.     incr level
  2187.     }
  2188.     return $callerLevel
  2189. }
  2190.  
  2191.  
  2192. #
  2193. # The default tkMenuFind proc in menu.tcl only looks for menubuttons
  2194. # in frames.  Since our menubuttons are within the Menubar class, the
  2195. # default proc won't find them during menu traversal.  This proc
  2196. # redefines the default proc to remedy the problem.
  2197. #
  2198. proc tkMenuFind {w char} {
  2199.     global tkPriv
  2200.     set char [string tolower $char]
  2201.  
  2202.     foreach child [winfo child $w] {
  2203.     switch [winfo class $child] {
  2204.         Menubutton {
  2205.         set qchild [winfo command $child]
  2206.         set char2 [string index [$qchild cget -text] \
  2207.             [$qchild cget -underline]]
  2208.         if {([string compare $char [string tolower $char2]] == 0)
  2209.         || ($char == "")} {
  2210.             if {[$qchild cget -state] != "disabled"} {
  2211.             return $child
  2212.             }
  2213.         }
  2214.         }
  2215.         Frame -
  2216.         Menubar {
  2217.         set match [tkMenuFind $child $char]
  2218.         if {$match != ""} {
  2219.             return $match
  2220.         }
  2221.         }
  2222.     }
  2223.     }
  2224.     return {}
  2225. }
  2226.  
  2227.  
  2228.