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 / optionmenu.itk < prev    next >
Text File  |  1999-02-24  |  21KB  |  641 lines

  1. #
  2. # Optionmenu
  3. # ----------------------------------------------------------------------
  4. # Implements an option menu widget with options to manage it. 
  5. # An option menu displays a frame containing a label and a button.
  6. # A pop-up menu will allow for the value of the button to change. 
  7. #
  8. # ----------------------------------------------------------------------
  9. #  AUTHOR:  Alfredo Jahn             Phone: (214) 519-3545
  10. #                                    Email: ajahn@spd.dsccc.com
  11. #                                           alfredo@wn.com
  12. #
  13. #  @(#) $Id: optionmenu.itk,v 1.2 1998/08/11 14:42:11 welch Exp $
  14. # ----------------------------------------------------------------------
  15. #            Copyright (c) 1995 DSC Technologies Corporation
  16. # ======================================================================
  17. # Permission to use, copy, modify, distribute and license this software 
  18. # and its documentation for any purpose, and without fee or written 
  19. # agreement with DSC, is hereby granted, provided that the above copyright 
  20. # notice appears in all copies and that both the copyright notice and 
  21. # warranty disclaimer below appear in supporting documentation, and that 
  22. # the names of DSC Technologies Corporation or DSC Communications 
  23. # Corporation not be used in advertising or publicity pertaining to the 
  24. # software without specific, written prior permission.
  25. # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
  26. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
  27. # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
  28. # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
  29. # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
  30. # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
  31. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  32. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
  33. # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
  34. # SOFTWARE.
  35. # ======================================================================
  36.  
  37. #
  38. # Default resources.
  39. #
  40.  
  41. option add *Optionmenu.highlightThickness    1    widgetDefault
  42. option add *Optionmenu.borderWidth        2    widgetDefault
  43. option add *Optionmenu.labelPos            w    widgetDefault
  44. option add *Optionmenu.labelMargin        2    widgetDefault
  45. option add *Optionmenu.popupCursor        arrow    widgetDefault
  46.  
  47. #
  48. # Usual options.
  49. #
  50. itk::usual Optionmenu {
  51.     keep -activebackground -activeborderwidth -activeforeground \
  52.      -background -borderwidth -cursor -disabledforeground -font \
  53.      -foreground -highlightcolor -highlightthickness -labelfont \
  54.      -popupcursor
  55. }
  56.  
  57. # ------------------------------------------------------------------
  58. #                            OPTONMENU
  59. # ------------------------------------------------------------------
  60. class iwidgets::Optionmenu {
  61.     inherit iwidgets::Labeledwidget
  62.     
  63.     constructor {args} {}
  64.     destructor {}
  65.  
  66.     itk_option define -clicktime clickTime ClickTime 150
  67.     itk_option define -command command Command {}
  68.     itk_option define -cyclicon cyclicOn CyclicOn true
  69.     itk_option define -width width Width 0
  70.     itk_option define -font font Font -Adobe-Helvetica-Bold-R-Normal--*-120-*
  71.     itk_option define -borderwidth borderWidth BorderWidth 2
  72.     itk_option define -highlightthickness highlightThickness HighlightThickness 1
  73.     itk_option define -state state State normal
  74.  
  75.     public {
  76.       method index {index} 
  77.       method delete {first {last {}}} 
  78.       method disable {index} 
  79.       method enable {args} 
  80.       method get {{first "current"} {last ""}} 
  81.       method insert {index string args} 
  82.       method popupMenu {args} 
  83.       method select {index} 
  84.       method sort {{mode "increasing"}} 
  85.     }
  86.  
  87.     protected {
  88.       variable _calcSize ""  ;# non-null => _calcSize pending
  89.     }
  90.  
  91.     private {
  92.       method _buttonRelease {time} 
  93.       method _getNextItem {index} 
  94.       method _next {} 
  95.       method _postMenu {time} 
  96.       method _previous {} 
  97.       method _setItem {item} 
  98.       method _setSize {{when later}} 
  99.       method _setitems {items} ;# Set the list of menu entries
  100.  
  101.       variable _postTime 0
  102.       variable _items {}       ;# List of popup menu entries
  103.       variable _numitems 0     ;# List of popup menu entries
  104.  
  105.       variable _currentItem "" ;# Active menu selection
  106.     }
  107. }
  108.  
  109. #
  110. # Provide a lowercased access method for the Optionmenu class.
  111. proc ::iwidgets::optionmenu {pathName args} {
  112.     uplevel ::iwidgets::Optionmenu $pathName $args
  113. }
  114.  
  115. # ------------------------------------------------------------------
  116. #                        CONSTRUCTOR
  117. # ------------------------------------------------------------------
  118. body iwidgets::Optionmenu::constructor {args} {
  119.     global tcl_platform
  120.  
  121.     component hull configure -highlightthickness 0
  122.  
  123.     itk_component add menuBtn {
  124.     menubutton $itk_interior.menuBtn -relief raised -indicator on \
  125.             -textvariable [scope _currentItem] -takefocus 1 \
  126.             -menu $itk_interior.menuBtn.menu
  127.     } {
  128.         usual
  129.     keep -borderwidth
  130.         if {$tcl_platform(platform) != "unix"} {
  131.             ignore -activebackground -activeforeground
  132.         }
  133.     }
  134.     pack $itk_interior.menuBtn -fill x
  135.     pack propagate $itk_interior no
  136.  
  137.     itk_component add popupMenu {
  138.     menu $itk_interior.menuBtn.menu -tearoff no
  139.     } {
  140.     usual
  141.     ignore -tearoff
  142.     keep -activeborderwidth -borderwidth
  143.     rename -cursor -popupcursor popupCursor Cursor
  144.     }
  145.  
  146.     #
  147.     # Bind to button release for all components.
  148.     #
  149.     bind $itk_component(menuBtn) <ButtonPress-1> \
  150.         "[code $this _postMenu %t]; break"
  151.     bind $itk_component(menuBtn) <KeyPress-space> \
  152.         "[code $this _postMenu %t]; break"
  153.     bind $itk_component(popupMenu) <ButtonRelease-1> \
  154.         [code $this _buttonRelease %t]
  155.  
  156.     #
  157.     # Initialize the widget based on the command line options.
  158.     #
  159.     eval itk_initialize $args
  160. }
  161.  
  162. # ------------------------------------------------------------------
  163. #                           DESTRUCTOR
  164. # ------------------------------------------------------------------
  165. body iwidgets::Optionmenu::destructor {} {
  166.     if {$_calcSize != ""} {after cancel $_calcSize}
  167. }
  168.  
  169. # ------------------------------------------------------------------
  170. #                             OPTIONS
  171. # ------------------------------------------------------------------
  172.  
  173. # ------------------------------------------------------------------
  174. # OPTION -clicktime
  175. #
  176. # Interval time (in msec) used to determine that a single mouse 
  177. # click has occurred. Used to post menu on a quick mouse click.
  178. # **WARNING** changing this value may cause the sigle-click 
  179. # functionality to not work properly!
  180. # ------------------------------------------------------------------
  181. configbody iwidgets::Optionmenu::clicktime {}
  182.  
  183. # ------------------------------------------------------------------
  184. # OPTION -command
  185. #
  186. # Specifies a command to be evaluated upon change in option menu.
  187. # ------------------------------------------------------------------
  188. configbody iwidgets::Optionmenu::command {}
  189.  
  190. # ------------------------------------------------------------------
  191. # OPTION -cyclicon
  192. #
  193. # Turns on/off the 3rd mouse button capability. This feature
  194. # allows the right mouse button to cycle through the popup 
  195. # menu list without poping it up. <shift>M3 cycles through
  196. # the menu in reverse order.
  197. # ------------------------------------------------------------------
  198. configbody iwidgets::Optionmenu::cyclicon {
  199.     if {$itk_option(-cyclicon)} {
  200.         bind $itk_component(menuBtn) <3> [code $this _next]
  201.         bind $itk_component(menuBtn) <Shift-3> [code $this _previous]
  202.         bind $itk_component(menuBtn) <KeyPress-Down> [code $this _next]
  203.         bind $itk_component(menuBtn) <KeyPress-Up> [code $this _previous]
  204.     } else {
  205.         bind $itk_component(menuBtn) <3> break
  206.         bind $itk_component(menuBtn) <Shift-3> break
  207.         bind $itk_component(menuBtn) <KeyPress-Down> break
  208.         bind $itk_component(menuBtn) <KeyPress-Up> break
  209.     }
  210. }
  211.  
  212. # ------------------------------------------------------------------
  213. # OPTION -width
  214. #
  215. # Allows the menu label width to be set to a fixed size
  216. # ------------------------------------------------------------------
  217. configbody iwidgets::Optionmenu::width {
  218.     _setSize
  219. }
  220.  
  221. # ------------------------------------------------------------------
  222. # OPTION -font
  223. #
  224. # Change all fonts for this widget. Also re-calculate height based
  225. # on font size (used to line up menu items over menu button label).
  226. # ------------------------------------------------------------------
  227. configbody iwidgets::Optionmenu::font {
  228.     _setSize
  229. }
  230.  
  231. # ------------------------------------------------------------------
  232. # OPTION -borderwidth
  233. #
  234. # Change borderwidth for this widget. Also re-calculate height based
  235. # on font size (used to line up menu items over menu button label).
  236. # ------------------------------------------------------------------
  237. configbody iwidgets::Optionmenu::borderwidth {
  238.     _setSize
  239. }
  240.  
  241. # ------------------------------------------------------------------
  242. # OPTION -highlightthickness
  243. #
  244. # Change highlightthickness for this widget. Also re-calculate
  245. # height based on font size (used to line up menu items over
  246. # menu button label).
  247. # ------------------------------------------------------------------
  248. configbody iwidgets::Optionmenu::highlightthickness {
  249.     _setSize
  250. }
  251.  
  252. # ------------------------------------------------------------------
  253. # OPTION -state
  254. #
  255. # Specified one of two states for the Optionmenu: normal, or
  256. # disabled.  If the Optionmenu is disabled, then option menu
  257. # selection is ignored.
  258. # ------------------------------------------------------------------
  259. configbody iwidgets::Optionmenu::state {
  260.     switch $itk_option(-state) {
  261.         normal {
  262.             $itk_component(menuBtn) config -state normal
  263.             $itk_component(label) config -fg $itk_option(-foreground)
  264.         } 
  265.         disabled {
  266.             $itk_component(menuBtn) config -state disabled
  267.             $itk_component(label) config -fg $itk_option(-disabledforeground)
  268.         }
  269.         default {
  270.             error "bad state option \"$itk_option(-state)\":\
  271.             should be disabled or normal"
  272.         }
  273.     }
  274. }
  275.  
  276. # ------------------------------------------------------------------
  277. #                            METHODS
  278. # ------------------------------------------------------------------
  279.  
  280. # ------------------------------------------------------------------
  281. # METHOD: index index
  282. #
  283. # Return the numerical index corresponding to index.
  284. # ------------------------------------------------------------------
  285. body iwidgets::Optionmenu::index {index} {
  286.  
  287.     if {[regexp {(^[0-9]+$)} $index]} {
  288.     set idx [$itk_component(popupMenu) index $index]
  289.  
  290.     if {$idx == "none"} {
  291.         return 0
  292.     }
  293.     return [expr {$index > $idx ? $_numitems : $idx}]
  294.     
  295.     } elseif {$index == "end"} {
  296.     return $_numitems
  297.     
  298.     } elseif {$index == "select"} {
  299.     return [lsearch $_items $_currentItem]
  300.     
  301.     }
  302.  
  303.     set numValue [lsearch -glob $_items $index]
  304.  
  305.     if {$numValue == -1} {
  306.         error "bad Optionmenu index \"$index\""
  307.     }
  308.     return $numValue
  309. }
  310.  
  311. # ------------------------------------------------------------------
  312. # METHOD: delete first ?last?
  313. #
  314. # Remove an item (or range of items) from the popup menu. 
  315. # ------------------------------------------------------------------
  316. body iwidgets::Optionmenu::delete {first {last {}}} {
  317.  
  318.     set first [index $first]
  319.     set last [expr {$last != {} ? [index $last] : $first}]    
  320.     set nextAvail $_currentItem
  321.     
  322.     #
  323.     # If current item is in delete range point to next available.
  324.     #
  325.     if {$_numitems > 1 &&
  326.     ([lsearch -exact [lrange $_items $first $last] [get]] != -1)} {
  327.     set nextAvail [_getNextItem $last]
  328.     }
  329.     
  330.     _setitems [lreplace $_items $first $last]
  331.     
  332.     #
  333.     # Make sure "nextAvail" is still in the list.
  334.     #
  335.     set index [lsearch -exact $_items $nextAvail]
  336.     _setItem [expr {$index != -1 ? $nextAvail : ""}]
  337. }
  338.  
  339. # ------------------------------------------------------------------
  340. # METHOD: disable index
  341. #
  342. # Disable a menu item in the option menu.  This will prevent the user
  343. # from being able to select this item from the menu.  This only effects
  344. # the state of the item in the menu, in other words, should the item
  345. # be the currently selected item, the user is responsible for 
  346. # determining this condition and taking appropriate action.
  347. # ------------------------------------------------------------------
  348. body iwidgets::Optionmenu::disable {index} {
  349.     set index [index $index]
  350.     $itk_component(popupMenu) entryconfigure $index -state disabled
  351. }
  352.  
  353. # ------------------------------------------------------------------
  354. # METHOD: enable index
  355. #
  356. # Enable a menu item in the option menu.  This will allow the user
  357. # to select this item from the menu.  
  358. # ------------------------------------------------------------------
  359. body iwidgets::Optionmenu::enable {index} {
  360.     set index [index $index]
  361.     $itk_component(popupMenu) entryconfigure $index -state normal
  362. }
  363.  
  364. # ------------------------------------------------------------------
  365. # METHOD: get
  366. #
  367. # Returns the current menu item.
  368. # ------------------------------------------------------------------
  369. body iwidgets::Optionmenu::get {{first "current"} {last ""}} {
  370.     if {"current" == $first} {
  371.         return $_currentItem
  372.     }
  373.  
  374.     set first [index $first]
  375.     if {"" == $last} {
  376.         return [$itk_component(popupMenu) entrycget $first -label]
  377.     }
  378.  
  379.     if {"end" == $last} {
  380.         set last [$itk_component(popupMenu) index end]
  381.     } else {
  382.         set last [index $last]
  383.     }
  384.     set rval ""
  385.     while {$first <= $last} {
  386.         lappend rval [$itk_component(popupMenu) entrycget $first -label]
  387.         incr first
  388.     }
  389.     return $rval
  390. }
  391.  
  392. # ------------------------------------------------------------------
  393. # METHOD: insert index string ?string?
  394. #
  395. # Insert an item in the popup menu.
  396. # ------------------------------------------------------------------
  397. body iwidgets::Optionmenu::insert {index string args} {
  398.     set index [index $index]
  399.     set args [linsert $args 0 $string]
  400.     _setitems [eval linsert {$_items} $index $args]
  401.     return ""
  402. }
  403.  
  404. # ------------------------------------------------------------------
  405. # METHOD: select index
  406. #
  407. # Select an item from the popup menu to display on the menu label
  408. # button. 
  409. # ------------------------------------------------------------------
  410. body iwidgets::Optionmenu::select {index} {
  411.     set index [index $index]
  412.     _setItem [lindex $_items $index]
  413. }
  414.  
  415. # ------------------------------------------------------------------
  416. # METHOD: popupMenu
  417. #
  418. # Evaluates the specified args against the popup menu component
  419. # and returns the result.
  420. # ------------------------------------------------------------------
  421. body iwidgets::Optionmenu::popupMenu {args} {
  422.     return [eval $itk_component(popupMenu) $args]    
  423. }
  424.  
  425. # ------------------------------------------------------------------
  426. # METHOD: sort mode
  427. #
  428. # Sort the current menu in either "ascending" or "descending" order.
  429. # ------------------------------------------------------------------
  430. body iwidgets::Optionmenu::sort {{mode "increasing"}} {
  431.     switch $mode {
  432.     ascending -
  433.     increasing {
  434.         _setitems [lsort -increasing $_items]
  435.     }
  436.     descending -
  437.     decreasing {
  438.         _setitems [lsort -decreasing $_items]
  439.     }
  440.     default {
  441.         error "bad sort argument \"$mode\": should be ascending,\
  442.             descending, increasing, or decreasing"
  443.     }
  444.     }
  445. }
  446.  
  447. # ------------------------------------------------------------------
  448. # PRIVATE METHOD: _buttonRelease
  449. #
  450. # Display the popup menu. Menu position is calculated.
  451. # ------------------------------------------------------------------
  452. body iwidgets::Optionmenu::_buttonRelease {time} {
  453.     if {[expr abs([expr $_postTime - $time])] <= $itk_option(-clicktime)} {
  454.         return -code break
  455.     }
  456. }
  457.  
  458. # ------------------------------------------------------------------
  459. # PRIVATE METHOD: _getNextItem index
  460. #
  461. # Allows either a string or index number to be passed in, and returns
  462. # the next item in the list in string format. Wrap around is automatic.
  463. # ------------------------------------------------------------------
  464. body iwidgets::Optionmenu::_getNextItem {index} {
  465.  
  466.     if {[incr index] >= $_numitems} {
  467.     set index 0   ;# wrap around
  468.     }
  469.     return [lindex $_items $index]
  470. }
  471.  
  472. # ------------------------------------------------------------------
  473. # PRIVATE METHOD: _next
  474. #
  475. # Sets the current option label to next item in list if that item is
  476. # not disbaled.
  477. # ------------------------------------------------------------------
  478. body iwidgets::Optionmenu::_next {} {
  479.     if {$itk_option(-state) != "normal"} {
  480.         return
  481.     }
  482.     set i [lsearch -exact $_items $_currentItem]
  483.     
  484.     for {set cnt 0} {$cnt < $_numitems} {incr cnt} {
  485.  
  486.         if {[incr i] >= $_numitems} {
  487.             set i 0
  488.         }
  489.         
  490.         if {[$itk_component(popupMenu) entrycget $i -state] != "disabled"} {
  491.             _setItem [lindex $_items $i]
  492.             break
  493.         }
  494.     }
  495. }
  496.  
  497. # ------------------------------------------------------------------
  498. # PRIVATE METHOD: _previous
  499. #
  500. # Sets the current option label to previous item in list if that 
  501. # item is not disbaled.
  502. # ------------------------------------------------------------------
  503. body iwidgets::Optionmenu::_previous {} {
  504.     if {$itk_option(-state) != "normal"} {
  505.         return
  506.     }
  507.  
  508.     set i [lsearch -exact $_items $_currentItem]
  509.     
  510.     for {set cnt 0} {$cnt < $_numitems} {incr cnt} {
  511.     set i [expr $i - 1]
  512.     
  513.     if {$i < 0} {
  514.         set i [expr $_numitems - 1]
  515.     }
  516.  
  517.     if {[$itk_component(popupMenu) entrycget $i -state] != "disabled"} {
  518.         _setItem [lindex $_items $i]
  519.         break
  520.     }
  521.     }
  522. }
  523.  
  524. # ------------------------------------------------------------------
  525. # PRIVATE METHOD: _postMenu time
  526. #
  527. # Display the popup menu. Menu position is calculated.
  528. # ------------------------------------------------------------------
  529. body iwidgets::Optionmenu::_postMenu {time} {
  530.     #
  531.     # Don't bother to post if menu is empty.
  532.     #
  533.     if {[llength $_items] > 0 && $itk_option(-state) == "normal"} {
  534.         set _postTime $time
  535.         set itemIndex [lsearch -exact $_items $_currentItem]
  536.  
  537.         set margin [expr $itk_option(-borderwidth) \
  538.             + $itk_option(-highlightthickness)]
  539.  
  540.         set x [expr [winfo rootx $itk_component(menuBtn)] + $margin]
  541.         set y [expr [winfo rooty $itk_component(menuBtn)] \
  542.             - [$itk_component(popupMenu) yposition $itemIndex] + $margin]
  543.  
  544.         tk_popup $itk_component(popupMenu) $x $y
  545.     }
  546. }
  547.  
  548. # ------------------------------------------------------------------
  549. # PRIVATE METHOD: _setItem
  550. #
  551. # Set the menu button label to item, then dismiss the popup menu.
  552. # Also check if item has been changed. If so, also call user-supplied
  553. # command.
  554. # ------------------------------------------------------------------
  555. body iwidgets::Optionmenu::_setItem {item} {
  556.     if {$_currentItem != $item} {
  557.         set _currentItem $item
  558.     if {[winfo ismapped $itk_component(hull)]} {
  559.         uplevel #0 $itk_option(-command)
  560.     }
  561.     }
  562. }
  563.  
  564. # ------------------------------------------------------------------
  565. # PRIVATE METHOD: _setitems items
  566. #
  567. # Create a list of items available on the menu. Used to create the
  568. # popup menu.
  569. # ------------------------------------------------------------------
  570. body iwidgets::Optionmenu::_setitems {items_} {
  571.  
  572.     #
  573.     # Delete the old menu entries, and set the new list of
  574.     # menu entries to those specified in "items_".
  575.     #
  576.     $itk_component(popupMenu) delete 0 last
  577.     set _items ""
  578.     set _numitems [llength $items_]
  579.  
  580.     #
  581.     # Clear the menu button label.
  582.     #
  583.     if {$_numitems == 0} {
  584.     _setItem ""
  585.     return
  586.     }
  587.  
  588.     set savedCurrentItem $_currentItem
  589.     
  590.     foreach opt $items_ {
  591.         lappend _items $opt
  592.         $itk_component(popupMenu) add command -label $opt \
  593.             -command [code $this _setItem $opt]
  594.     }
  595.     set first [lindex $_items 0]
  596.     
  597.     #
  598.     # Make sure "savedCurrentItem" is still in the list.
  599.     #
  600.     if {$first != ""} {
  601.         set i [lsearch -exact $_items $savedCurrentItem]
  602.         select [expr {$i != -1 ? $savedCurrentItem : $first}]
  603.     } else {
  604.     _setItem ""
  605.     }
  606.  
  607.     _setSize
  608. }
  609.  
  610. # ------------------------------------------------------------------
  611. # PRIVATE METHOD: _setSize ?when?
  612. #
  613. # Set the size of the option menu.  If "when" is "now", the change 
  614. # is applied immediately.  If it is "later" or it is not specified, 
  615. # then the change is applied later, when the application is idle.
  616. # ------------------------------------------------------------------
  617. body iwidgets::Optionmenu::_setSize {{when later}} {
  618.  
  619.     if {$when == "later"} {
  620.     if {$_calcSize == ""} {
  621.         set _calcSize [after idle [code $this _setSize now]]
  622.     }
  623.     return
  624.     }
  625.  
  626.     set margin [expr 2*($itk_option(-borderwidth) \
  627.         + $itk_option(-highlightthickness))]
  628.  
  629.     if {"0" != $itk_option(-width)} {
  630.         set width $itk_option(-width)
  631.     } else {
  632.     set width [expr [winfo reqwidth $itk_component(popupMenu)]+$margin+20]
  633.     }
  634.     set height [winfo reqheight $itk_component(menuBtn)]
  635.     $itk_component(lwchildsite) configure -width $width -height $height
  636.  
  637.     set _calcSize ""
  638. }
  639.