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