home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 December / PCWorld_2000-12_cd.bin / Komunikace / Comanche / comanche.exe / lib / iwidgets2.2.0 / scripts / optionmenu.itk < prev    next >
Text File  |  1999-02-24  |  21KB  |  635 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:46:40 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 -items items Items ""
  72.     itk_option define -borderwidth borderWidth BorderWidth 2
  73.     itk_option define -highlightthickness highlightThickness HighlightThickness 1
  74.     itk_option define -state state State normal
  75.  
  76.     public {
  77.       method index {index} 
  78.       method delete {first {last {}}} 
  79.       method disable {index} 
  80.       method enable {args} 
  81.       method get {} 
  82.       method insert {index string args} 
  83.       method popupMenu {args} 
  84.       method select {index} 
  85.       method sort {{mode "increasing"}} 
  86.     }
  87.  
  88.     protected {
  89.       variable _calcSize ""  ;# non-null => _calcSize pending
  90.     }
  91.  
  92.     private {
  93.       method _buttonRelease {time} 
  94.       method _getNextItem {index} 
  95.       method _next {} 
  96.       method _postMenu {time} 
  97.       method _previous {} 
  98.       method _setItem {item} 
  99.       method _setSize {{when later}} 
  100.       method _setitems {items} ;# Set the list of menu entries
  101.  
  102.       variable _postTime 0
  103.       variable _items {}       ;# List of popup menu entries
  104.       variable _numitems 0     ;# List of popup menu entries
  105.  
  106.       variable _currentItem "" ;# Active menu selection
  107.     }
  108. }
  109.  
  110. #
  111. # Provide a lowercased access method for the Optionmenu class.
  112. proc ::iwidgets::optionmenu {pathName args} {
  113.     uplevel ::iwidgets::Optionmenu $pathName $args
  114. }
  115.  
  116. # ------------------------------------------------------------------
  117. #                        CONSTRUCTOR
  118. # ------------------------------------------------------------------
  119. body iwidgets::Optionmenu::constructor {args} {
  120.     global tcl_platform
  121.  
  122.     component hull configure -highlightthickness 0
  123.  
  124.     itk_component add menuBtn {
  125.     menubutton $itk_interior.menuBtn -relief raised -indicator on \
  126.             -textvariable [scope _currentItem] -takefocus 1 \
  127.             -menu $itk_interior.menuBtn.menu
  128.     } {
  129.         usual
  130.     keep -borderwidth
  131.         if {$tcl_platform(platform) != "unix"} {
  132.             ignore -activebackground -activeforeground
  133.         }
  134.     }
  135.     pack $itk_interior.menuBtn -fill x
  136.     pack propagate $itk_interior no
  137.  
  138.     itk_component add popupMenu {
  139.     menu $itk_interior.menuBtn.menu -tearoff no
  140.     } {
  141.     usual
  142.     ignore -tearoff
  143.     keep -activeborderwidth -borderwidth
  144.     rename -cursor -popupcursor popupCursor Cursor
  145.     }
  146.  
  147.     #
  148.     # Bind to button release for all components.
  149.     #
  150.     bind $itk_component(menuBtn) <ButtonPress-1> \
  151.         "[code $this _postMenu %t]; break"
  152.     bind $itk_component(menuBtn) <KeyPress-space> \
  153.         "[code $this _postMenu %t]; break"
  154.     bind $itk_component(popupMenu) <ButtonRelease-1> \
  155.         [code $this _buttonRelease %t]
  156.  
  157.     #
  158.     # Initialize the widget based on the command line options.
  159.     #
  160.     eval itk_initialize $args
  161. }
  162.  
  163. # ------------------------------------------------------------------
  164. #                           DESTRUCTOR
  165. # ------------------------------------------------------------------
  166. body iwidgets::Optionmenu::destructor {} {
  167.     if {$_calcSize != ""} {after cancel $_calcSize}
  168. }
  169.  
  170. # ------------------------------------------------------------------
  171. #                             OPTIONS
  172. # ------------------------------------------------------------------
  173.  
  174. # ------------------------------------------------------------------
  175. # OPTION -clicktime
  176. #
  177. # Interval time (in msec) used to determine that a single mouse 
  178. # click has occurred. Used to post menu on a quick mouse click.
  179. # **WARNING** changing this value may cause the sigle-click 
  180. # functionality to not work properly!
  181. # ------------------------------------------------------------------
  182. configbody iwidgets::Optionmenu::clicktime {}
  183.  
  184. # ------------------------------------------------------------------
  185. # OPTION -command
  186. #
  187. # Specifies a command to be evaluated upon change in option menu.
  188. # ------------------------------------------------------------------
  189. configbody iwidgets::Optionmenu::command {}
  190.  
  191. # ------------------------------------------------------------------
  192. # OPTION -cyclicon
  193. #
  194. # Turns on/off the 3rd mouse button capability. This feature
  195. # allows the right mouse button to cycle through the popup 
  196. # menu list without poping it up. <shift>M3 cycles through
  197. # the menu in reverse order.
  198. # ------------------------------------------------------------------
  199. configbody iwidgets::Optionmenu::cyclicon {
  200.     if {$itk_option(-cyclicon)} {
  201.         bind $itk_component(menuBtn) <3> [code $this _next]
  202.         bind $itk_component(menuBtn) <Shift-3> [code $this _previous]
  203.         bind $itk_component(menuBtn) <KeyPress-Down> [code $this _next]
  204.         bind $itk_component(menuBtn) <KeyPress-Up> [code $this _previous]
  205.     } else {
  206.         bind $itk_component(menuBtn) <3> break
  207.         bind $itk_component(menuBtn) <Shift-3> break
  208.         bind $itk_component(menuBtn) <KeyPress-Down> break
  209.         bind $itk_component(menuBtn) <KeyPress-Up> break
  210.     }
  211. }
  212.  
  213. # ------------------------------------------------------------------
  214. # OPTION -width
  215. #
  216. # Allows the menu label width to be set to a fixed size
  217. # ------------------------------------------------------------------
  218. configbody iwidgets::Optionmenu::width {
  219.     _setSize
  220. }
  221.  
  222. # ------------------------------------------------------------------
  223. # OPTION -font
  224. #
  225. # Change all fonts for this widget. Also re-calculate height based
  226. # on font size (used to line up menu items over menu button label).
  227. # ------------------------------------------------------------------
  228. configbody iwidgets::Optionmenu::font {
  229.     _setSize
  230. }
  231.  
  232. # ------------------------------------------------------------------
  233. # OPTION -items
  234. #
  235. # Create a list of items available on the menu. Used to create the
  236. # popup menu.
  237. # ------------------------------------------------------------------
  238. configbody iwidgets::Optionmenu::items {
  239.     _setitems $itk_option(-items)
  240. }
  241.  
  242. # ------------------------------------------------------------------
  243. # OPTION -borderwidth
  244. #
  245. # Change borderwidth for this widget. Also re-calculate height based
  246. # on font size (used to line up menu items over menu button label).
  247. # ------------------------------------------------------------------
  248. configbody iwidgets::Optionmenu::borderwidth {
  249.     _setSize
  250. }
  251.  
  252. # ------------------------------------------------------------------
  253. # OPTION -highlightthickness
  254. #
  255. # Change highlightthickness for this widget. Also re-calculate
  256. # height based on font size (used to line up menu items over
  257. # menu button label).
  258. # ------------------------------------------------------------------
  259. configbody iwidgets::Optionmenu::highlightthickness {
  260.     _setSize
  261. }
  262.  
  263. # ------------------------------------------------------------------
  264. # OPTION -state
  265. #
  266. # Specified one of two states for the Optionmenu: normal, or
  267. # disabled.  If the Optionmenu is disabled, then option menu
  268. # selection is ignored.
  269. # ------------------------------------------------------------------
  270. configbody iwidgets::Optionmenu::state {
  271.     switch $itk_option(-state) {
  272.         normal {
  273.             $itk_component(menuBtn) config -state normal
  274.             $itk_component(label) config -fg $itk_option(-foreground)
  275.         } 
  276.         disabled {
  277.             $itk_component(menuBtn) config -state disabled
  278.             $itk_component(label) config -fg $itk_option(-disabledforeground)
  279.         }
  280.         default {
  281.             error "bad state option \"$itk_option(-state)\":\
  282.             should be disabled or normal"
  283.         }
  284.     }
  285. }
  286.  
  287. # ------------------------------------------------------------------
  288. #                            METHODS
  289. # ------------------------------------------------------------------
  290.  
  291. # ------------------------------------------------------------------
  292. # METHOD: index index
  293. #
  294. # Return the numerical index corresponding to index.
  295. # ------------------------------------------------------------------
  296. body iwidgets::Optionmenu::index {index} {
  297.  
  298.     if {[regexp {(^[0-9]+$)} $index]} {
  299.     set idx [$itk_component(popupMenu) index $index]
  300.  
  301.     if {$idx == "none"} {
  302.         return 0
  303.     }
  304.     return [expr {$index > $idx ? $_numitems : $idx}]
  305.     
  306.     } elseif {$index == "end"} {
  307.     return $_numitems
  308.     
  309.     } elseif {$index == "select"} {
  310.     return [lsearch $_items $_currentItem]
  311.     
  312.     }
  313.  
  314.     set numValue [lsearch -glob $_items $index]
  315.  
  316.     if {$numValue == -1} {
  317.         error "bad Optionmenu index \"$index\""
  318.     }
  319.     return $numValue
  320. }
  321.  
  322. # ------------------------------------------------------------------
  323. # METHOD: delete first ?last?
  324. #
  325. # Remove an item (or range of items) from the popup menu. 
  326. # ------------------------------------------------------------------
  327. body iwidgets::Optionmenu::delete {first {last {}}} {
  328.  
  329.     set first [index $first]
  330.     set last [expr {$last != {} ? [index $last] : $first}]    
  331.     set nextAvail $_currentItem
  332.     
  333.     #
  334.     # If current item is in delete range point to next available.
  335.     #
  336.     if {$_numitems > 1 &&
  337.     ([lsearch -exact [lrange $_items $first $last] [get]] != -1)} {
  338.     set nextAvail [_getNextItem $last]
  339.     }
  340.     
  341.     _setitems [lreplace $_items $first $last]
  342.     
  343.     #
  344.     # Make sure "nextAvail" is still in the list.
  345.     #
  346.     set index [lsearch -exact $_items $nextAvail]
  347.     _setItem [expr {$index != -1 ? $nextAvail : ""}]
  348. }
  349.  
  350. # ------------------------------------------------------------------
  351. # METHOD: disable index
  352. #
  353. # Disable a menu item in the option menu.  This will prevent the user
  354. # from being able to select this item from the menu.  This only effects
  355. # the state of the item in the menu, in other words, should the item
  356. # be the currently selected item, the user is responsible for 
  357. # determining this condition and taking appropriate action.
  358. # ------------------------------------------------------------------
  359. body iwidgets::Optionmenu::disable {index} {
  360.     set index [index $index]
  361.     $itk_component(popupMenu) entryconfigure $index -state disabled
  362. }
  363.  
  364. # ------------------------------------------------------------------
  365. # METHOD: enable index
  366. #
  367. # Enable a menu item in the option menu.  This will allow the user
  368. # to select this item from the menu.  
  369. # ------------------------------------------------------------------
  370. body iwidgets::Optionmenu::enable {index} {
  371.     set index [index $index]
  372.     $itk_component(popupMenu) entryconfigure $index -state normal
  373. }
  374.  
  375. # ------------------------------------------------------------------
  376. # METHOD: get
  377. #
  378. # Returns the current menu item.
  379. # ------------------------------------------------------------------
  380. body iwidgets::Optionmenu::get {} {
  381.     return $_currentItem
  382. }
  383.  
  384. # ------------------------------------------------------------------
  385. # METHOD: insert index string ?string?
  386. #
  387. # Insert an item in the popup menu.
  388. # ------------------------------------------------------------------
  389. body iwidgets::Optionmenu::insert {index string args} {
  390.     set index [index $index]
  391.     set args [linsert $args 0 $string]
  392.     _setitems [eval linsert {$_items} $index $args]
  393.     return ""
  394. }
  395.  
  396. # ------------------------------------------------------------------
  397. # METHOD: select index
  398. #
  399. # Select an item from the popup menu to display on the menu label
  400. # button. 
  401. # ------------------------------------------------------------------
  402. body iwidgets::Optionmenu::select {index} {
  403.     set index [index $index]
  404.     _setItem [lindex $_items $index]
  405. }
  406.  
  407. # ------------------------------------------------------------------
  408. # METHOD: popupMenu
  409. #
  410. # Evaluates the specified args against the popup menu component
  411. # and returns the result.
  412. # ------------------------------------------------------------------
  413. body iwidgets::Optionmenu::popupMenu {args} {
  414.     return [eval $itk_component(popupMenu) $args]    
  415. }
  416.  
  417. # ------------------------------------------------------------------
  418. # METHOD: sort mode
  419. #
  420. # Sort the current menu in either "ascending" or "descending" order.
  421. # ------------------------------------------------------------------
  422. body iwidgets::Optionmenu::sort {{mode "increasing"}} {
  423.     switch $mode {
  424.     ascending -
  425.     increasing {
  426.         _setitems [lsort -increasing $_items]
  427.     }
  428.     descending -
  429.     decreasing {
  430.         _setitems [lsort -decreasing $_items]
  431.     }
  432.     default {
  433.         error "bad sort argument \"$mode\": should be ascending,\
  434.             descending, increasing, or decreasing"
  435.     }
  436.     }
  437.     return ""
  438. }
  439.  
  440. # ------------------------------------------------------------------
  441. # PRIVATE METHOD: _buttonRelease
  442. #
  443. # Display the popup menu. Menu position is calculated.
  444. # ------------------------------------------------------------------
  445. body iwidgets::Optionmenu::_buttonRelease {time} {
  446.     if {[expr abs([expr $_postTime - $time])] <= $itk_option(-clicktime)} {
  447.         return -code break
  448.     }
  449. }
  450.  
  451. # ------------------------------------------------------------------
  452. # PRIVATE METHOD: _getNextItem index
  453. #
  454. # Allows either a string or index number to be passed in, and returns
  455. # the next item in the list in string format. Wrap around is automatic.
  456. # ------------------------------------------------------------------
  457. body iwidgets::Optionmenu::_getNextItem {index} {
  458.  
  459.     if {[incr index] >= $_numitems} {
  460.     set index 0   ;# wrap around
  461.     }
  462.     return [lindex $_items $index]
  463. }
  464.  
  465. # ------------------------------------------------------------------
  466. # PRIVATE METHOD: _next
  467. #
  468. # Sets the current option label to next item in list if that item is
  469. # not disbaled.
  470. # ------------------------------------------------------------------
  471. body iwidgets::Optionmenu::_next {} {
  472.     if {$itk_option(-state) != "normal"} {
  473.         return
  474.     }
  475.     set i [lsearch -exact $_items $_currentItem]
  476.     
  477.     for {set cnt 0} {$cnt < $_numitems} {incr cnt} {
  478.  
  479.         if {[incr i] >= $_numitems} {
  480.             set i 0
  481.         }
  482.         
  483.         if {[$itk_component(popupMenu) entrycget $i -state] != "disabled"} {
  484.             _setItem [lindex $_items $i]
  485.             break
  486.         }
  487.     }
  488. }
  489.  
  490. # ------------------------------------------------------------------
  491. # PRIVATE METHOD: _previous
  492. #
  493. # Sets the current option label to previous item in list if that 
  494. # item is not disbaled.
  495. # ------------------------------------------------------------------
  496. body iwidgets::Optionmenu::_previous {} {
  497.     if {$itk_option(-state) != "normal"} {
  498.         return
  499.     }
  500.  
  501.     set i [lsearch -exact $_items $_currentItem]
  502.     
  503.     for {set cnt 0} {$cnt < $_numitems} {incr cnt} {
  504.     set i [expr $i - 1]
  505.     
  506.     if {$i < 0} {
  507.         set i [expr $_numitems - 1]
  508.     }
  509.  
  510.     if {[$itk_component(popupMenu) entrycget $i -state] != "disabled"} {
  511.         _setItem [lindex $_items $i]
  512.         break
  513.     }
  514.     }
  515. }
  516.  
  517. # ------------------------------------------------------------------
  518. # PRIVATE METHOD: _postMenu time
  519. #
  520. # Display the popup menu. Menu position is calculated.
  521. # ------------------------------------------------------------------
  522. body iwidgets::Optionmenu::_postMenu {time} {
  523.     #
  524.     # Don't bother to post if menu is empty.
  525.     #
  526.     if {[llength $_items] > 0 && $itk_option(-state) == "normal"} {
  527.         set _postTime $time
  528.         set itemIndex [lsearch -exact $_items $_currentItem]
  529.  
  530.         set margin [expr $itk_option(-borderwidth) \
  531.             + $itk_option(-highlightthickness)]
  532.  
  533.         set x [expr [winfo rootx $itk_component(menuBtn)] + $margin]
  534.         set y [expr [winfo rooty $itk_component(menuBtn)] \
  535.             - [$itk_component(popupMenu) yposition $itemIndex] + $margin]
  536.  
  537.         tk_popup $itk_component(popupMenu) $x $y
  538.     }
  539. }
  540.  
  541. # ------------------------------------------------------------------
  542. # PRIVATE METHOD: _setItem
  543. #
  544. # Set the menu button label to item, then dismiss the popup menu.
  545. # Also check if item has been changed. If so, also call user-supplied
  546. # command.
  547. # ------------------------------------------------------------------
  548. body iwidgets::Optionmenu::_setItem {item} {
  549.     if {$_currentItem != $item} {
  550.         set _currentItem $item
  551.     if {[winfo ismapped $itk_component(hull)]} {
  552.         uplevel #0 $itk_option(-command)
  553.     }
  554.     }
  555. }
  556.  
  557. # ------------------------------------------------------------------
  558. # PRIVATE METHOD: _setitems items
  559. #
  560. # Create a list of items available on the menu. Used to create the
  561. # popup menu.
  562. # ------------------------------------------------------------------
  563. body iwidgets::Optionmenu::_setitems {items_} {
  564.  
  565.     #
  566.     # Delete the old menu entries, and set the new list of
  567.     # menu entries to those specified in "items_".
  568.     #
  569.     $itk_component(popupMenu) delete 0 last
  570.     set _items ""
  571.     set _numitems [llength $items_]
  572.  
  573.     #
  574.     # Clear the menu button label.
  575.     #
  576.     if {$_numitems == 0} {
  577.     _setItem ""
  578.     return
  579.     }
  580.  
  581.     set savedCurrentItem $_currentItem
  582.     
  583.     foreach opt $items_ {
  584.         lappend _items $opt
  585.         $itk_component(popupMenu) add command -label " $opt " \
  586.             -command [code $this _setItem $opt]
  587.     }
  588.     set first [lindex $_items 0]
  589.     
  590.     #
  591.     # Make sure "savedCurrentItem" is still in the list.
  592.     #
  593.     if {$first != ""} {
  594.         set i [lsearch -exact $_items $savedCurrentItem]
  595.         select [expr {$i != -1 ? $savedCurrentItem : $first}]
  596.     } else {
  597.     _setItem ""
  598.     }
  599.  
  600.     _setSize
  601.     set itk_option(-items) $_items
  602. }
  603.  
  604. # ------------------------------------------------------------------
  605. # PRIVATE METHOD: _setSize ?when?
  606. #
  607. # Set the size of the option menu.  If "when" is "now", the change 
  608. # is applied immediately.  If it is "later" or it is not specified, 
  609. # then the change is applied later, when the application is idle.
  610. # ------------------------------------------------------------------
  611. body iwidgets::Optionmenu::_setSize {{when later}} {
  612.  
  613.     if {$when == "later"} {
  614.     if {$_calcSize == ""} {
  615.         set _calcSize [after idle [code $this _setSize now]]
  616.     }
  617.     return
  618.     }
  619.  
  620.     set margin [expr 2*($itk_option(-borderwidth) \
  621.         + $itk_option(-highlightthickness))]
  622.  
  623.     if {"0" != $itk_option(-width)} {
  624.         set width $itk_option(-width)
  625.     } else {
  626.     set width [expr [winfo reqwidth $itk_component(popupMenu)]+$margin+20]
  627.     }
  628.     set height [winfo reqheight $itk_component(menuBtn)]
  629.     $itk_component(lwchildsite) configure -width $width -height $height
  630.  
  631.     set _calcSize ""
  632. }
  633.