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 / combobox.itk < prev    next >
Text File  |  1999-02-24  |  37KB  |  1,138 lines

  1. #
  2. # Combobox
  3. # ----------------------------------------------------------------------
  4. # Implements a Combobox widget. A Combobox has 2 basic styles: simple and
  5. # dropdown. Dropdowns display an entry field with an arrow button to the 
  6. # right of it. When the arrow button is pressed a selectable list of items 
  7. # is popped up. A simple Combobox displays an entry field and a listbox 
  8. # just beneath it which is always displayed. In both types, if the user 
  9. # selects an item in the listbox, the contents of the entry field are 
  10. # replaced with the text from the selected item. If the Combobox is 
  11. # editable, the user can type in the entry field and when <Return> is pressed
  12. # the item will be inserted into the list.
  13. #
  14. # WISH LIST:
  15. #   This section lists possible future enhancements.  
  16. #
  17. #     Combobox 1.x:
  18. #         - convert bindings to bindtags.
  19. #         - determine clean way to have a state for listbox/canvas.
  20. #         - add completion functionality when typing in the entry.
  21. #         - add grab option: global, local, none.
  22. #
  23. # ----------------------------------------------------------------------
  24. #  AUTHOR: John S. Sigler               EMAIL: jsigler@spd.dsccc.com
  25. #                                              sigler@onramp.net
  26. #  @(#) $Id: combobox.itk,v 1.1 1998/07/27 18:49:23 stanton Exp $
  27. # ----------------------------------------------------------------------
  28. #                   Copyright (c) 1995  John S. Sigler
  29. # ======================================================================
  30. # Permission is hereby granted, without written agreement and without
  31. # license or royalty fees, to use, copy, modify, and distribute this
  32. # software and its documentation for any purpose, provided that the
  33. # above copyright notice and the following two paragraphs appear in
  34. # all copies of this software.
  35. # IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  36. # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 
  37. # ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 
  38. # IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
  39. # DAMAGE.
  40. #
  41. # THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 
  42. # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
  43. # FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  44. # ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  45. # PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  46. # ======================================================================
  47.  
  48. #
  49. # Default resources.
  50. #
  51. option add *Combobox.borderWidth 2 widgetDefault
  52. option add *Combobox.labelPos w widgetDefault
  53. option add *Combobox.listHeight 150 widgetDefault
  54. option add *Combobox.hscrollMode dynamic widgetDefault
  55. option add *Combobox.vscrollMode dynamic widgetDefault
  56.  
  57. #
  58. # Usual options.
  59. #
  60. itk::usual Combobox {
  61.     keep -background -borderwidth -cursor -foreground -highlightcolor \
  62.      -highlightthickness -insertbackground -insertborderwidth \
  63.      -insertofftime -insertontime -insertwidth -labelfont -popupcursor \
  64.      -selectbackground -selectborderwidth -selectforeground \
  65.      -textbackground -textfont
  66. }
  67.  
  68. # ------------------------------------------------------------------
  69. #                            COMBOBOX
  70. # ------------------------------------------------------------------
  71. class iwidgets::Combobox {
  72.     inherit iwidgets::Entryfield
  73.     
  74.     constructor {args} {}
  75.     destructor {}
  76.  
  77.     itk_option define -autoclear autoClear AutoClear true
  78.     itk_option define -arrowrelief arrowRelief Relief raised
  79.     itk_option define -dropdown dropdown Dropdown true
  80.     itk_option define -editable editable Editable true
  81.     itk_option define -fliparrow flipArrow FlipArrow false
  82.     itk_option define -items items Items ""
  83.     itk_option define -labelpos labelPos Position w
  84.     itk_option define -listheight listHeight Height 150
  85.     itk_option define -margin margin Margin 1
  86.     itk_option define -popupcursor popupCursor Cursor arrow
  87.     itk_option define -selectioncommand selectionCommand SelectionCommand {}
  88.     itk_option define -unique unique Unique true
  89.  
  90.     public method clear {{component all}}
  91.     public method curselection {}
  92.     public method delete {component first {last {}}}
  93.     public method get {{index {}}}
  94.     public method getcurselection {}
  95.     public method insert {component index args}
  96.     public method invoke {}
  97.     public method justify {direction}
  98.     public method see {index}
  99.     public method selection {option first {last {}}}
  100.     public method size {}
  101.     public method sort {{mode ascending}}
  102.     public method xview {args}
  103.     public method yview {args}
  104.  
  105.     protected method _addToList {}
  106.     protected method _createComponents {}
  107.     protected method _deleteList {first {last {}}}
  108.     protected method _deleteText {first {last {}}}
  109.     protected method _doLayout {{when later}}
  110.     protected method _drawArrow {{mode normal}}
  111.     protected method _dropdownBtnRelease {{window {}} {x 1} {y 1}}
  112.     protected method _ignoreNextBtnRelease {ignore}
  113.     protected method _insertText {index string}
  114.     protected method _next {}
  115.     protected method _packArrow {labelpos}
  116.     protected method _packComponents {{when later}}
  117.     protected method _positionList {}
  118.     protected method _postList {}
  119.     protected method _previous {}
  120.     protected method _resizeArrow {}
  121.     protected method _selectCmd {}
  122.     protected method _toggleList {}
  123.     protected method _unpostList {}
  124.     protected method _commonBindings {}
  125.     protected method _dropdownBindings {}
  126.     protected method _simpleBindings {}
  127.     protected method _listShowing {{val ""}}
  128.  
  129.     private method _slbListbox {}
  130.  
  131.     private   variable _currItem {};           ;# current selected item.
  132.     private   variable _ignoreRelease true     ;# next button release ignored.
  133.     private   variable _isPosted false;        ;# is the dropdown popped up.
  134.     private   variable _repacking {}    ;# non-null => _packComponents pending.
  135.     private   common _listShowing
  136. }    
  137.  
  138. #
  139. # Provide a lowercase access method for the Combobox class.
  140. proc ::iwidgets::combobox {pathName args} {
  141.     uplevel ::iwidgets::Combobox $pathName $args
  142. }
  143.  
  144. # ------------------------------------------------------------------
  145. #                       CONSTRUCTOR
  146. # ------------------------------------------------------------------
  147. body iwidgets::Combobox::constructor {args} {
  148.     set _listShowing($this) 0
  149.  
  150.     # combobox is different as all components are created 
  151.     # after determining what the dropdown style is...
  152.  
  153.     # configure args
  154.     eval itk_initialize $args
  155.     
  156.     # create components that are dependent on options 
  157.     # (Scrolledlistbox, arrow button) and pack them.
  158.     _doLayout
  159. }
  160.     
  161. # ------------------------------------------------------------------
  162. #                          DESTRUCTOR
  163. # ------------------------------------------------------------------
  164. body iwidgets::Combobox::destructor {} {
  165.     # catch any repacking that may be waiting for idle time
  166.     if {$_repacking != ""} {
  167.     after cancel $_repacking
  168.     }
  169. }
  170.     
  171. # ================================================================
  172. #                           OPTIONS
  173. # ================================================================
  174.  
  175. # --------------------------------------------------------------------
  176. # OPTION:  -autoclear
  177. #
  178. # Specifies wheather or not to clear the entry field as items are
  179. # added to the list.  The default is true.
  180. # --------------------------------------------------------------------
  181. configbody iwidgets::Combobox::autoclear {}
  182.  
  183. # --------------------------------------------------------------------
  184. # OPTION:  -arrowrelief
  185. #
  186. # Relief style used on the arrow button.
  187. # --------------------------------------------------------------------
  188. configbody iwidgets::Combobox::arrowrelief {}
  189.  
  190. # --------------------------------------------------------------------
  191. # OPTION:  -dropdown  
  192. #
  193. # Boolean which determines the Combobox style: dropdown or simple.
  194. # Because the two style's lists reside in different toplevel widgets
  195. # this is more complicated than it should be.
  196. # --------------------------------------------------------------------
  197. configbody iwidgets::Combobox::dropdown {
  198.     if {$itk_option(-dropdown)} {
  199.     if {[winfo exists $itk_interior.list]} {
  200.         destroy $itk_component(list)
  201.         _doLayout
  202.     }
  203.     } else {
  204.     if {[winfo exists $itk_interior.popup.list]} {
  205.         destroy $itk_component(margin)
  206.         destroy $itk_component(arrowBtn)
  207.         destroy $itk_component(popup)  ;# this deletes the list too
  208.         _doLayout
  209.     }
  210.     }
  211. }
  212.  
  213. # --------------------------------------------------------------------
  214. # OPTION: -editable  
  215. #
  216. # Boolean which allows/disallows user input to the entry field area.
  217. # --------------------------------------------------------------------
  218. configbody iwidgets::Combobox::editable {
  219.     if {$itk_option(-editable)} {
  220.     configure -state normal -command [code $this _addToList]
  221.     bind $itk_component(entry) <1> ""
  222.     } else {
  223.     configure -state disabled
  224.     bind $itk_component(entry) <1> [code $this _toggleList]
  225.     }
  226. }
  227.  
  228. # --------------------------------------------------------------------
  229. # OPTION: -fliparrow
  230. #
  231. # Boolean which causes dropdown comboboxes to have their arrow point
  232. # up when the listbox is displayed.
  233. # --------------------------------------------------------------------
  234. configbody iwidgets::Combobox::fliparrow {
  235.     # boolean error check
  236.     if {$itk_option(-fliparrow)} {
  237.     }
  238. }
  239.  
  240. # --------------------------------------------------------------------
  241. # OPTION: -items  
  242. #
  243. # Set/get the current list of items in the listbox.
  244. # --------------------------------------------------------------------
  245. configbody iwidgets::Combobox::items {
  246.     # clear out what was in entry field before
  247.     if {$itk_option(-autoclear)} {
  248.     delete entry 0 end
  249.     }
  250.  
  251.     if {[winfo exists $itk_interior.popup.list]} {
  252.     _drawArrow normal
  253.     }
  254. }
  255.  
  256. # --------------------------------------------------------------------
  257. # OPTION: -labelpos
  258. #
  259. # Labelpos. Overrides the labelpos of the labeledwidget. This doesn't
  260. # work as positionalLabel seems to get called after this routine is called!
  261. # --------------------------------------------------------------------
  262. configbody iwidgets::Combobox::labelpos {
  263.     iwidgets::Labeledwidget::_positionLabel 
  264.  
  265.     switch -- $itk_option(-labelpos) {
  266.     e -
  267.     w {
  268.         pack configure $itk_component(label) -anchor n
  269.     }
  270.     }
  271. }
  272.  
  273. # --------------------------------------------------------------------
  274. # OPTION: -listheight  
  275. #
  276. # Listbox height in pixels. (Need to integrate the scrolledlistbox
  277. # -visibleitems option here - at least for simple listbox.)
  278. # --------------------------------------------------------------------
  279. configbody iwidgets::Combobox::listheight {}
  280.  
  281. # --------------------------------------------------------------------
  282. # OPTION:  -margin
  283. #
  284. # Spacer between the entry field and arrow button of dropdown style
  285. # Comboboxes.
  286. # --------------------------------------------------------------------
  287. configbody iwidgets::Combobox::margin {}
  288.  
  289. # --------------------------------------------------------------------
  290. # OPTION:  -popupcursor
  291. #
  292. # Set the cursor for the popup list.
  293. # --------------------------------------------------------------------
  294. configbody iwidgets::Combobox::popupcursor {}
  295.  
  296. # --------------------------------------------------------------------
  297. # OPTION:  -selectioncommand
  298. #
  299. # Defines the proc to be called when an item is selected in the list.
  300. # --------------------------------------------------------------------
  301. configbody iwidgets::Combobox::selectioncommand {}
  302.  
  303. # --------------------------------------------------------------------
  304. # OPTION: -unique  
  305. #
  306. # Boolean which disallows/allows adding duplicate items to the listbox.
  307. # --------------------------------------------------------------------
  308. configbody iwidgets::Combobox::unique {
  309.     # boolean error check
  310.     if {$itk_option(-unique)} {
  311.     }
  312. }
  313.  
  314. # =================================================================
  315. #                            METHODS
  316. # =================================================================
  317.  
  318. # ------------------------------------------------------
  319. #  PUBLIC METHOD: clear ?component?
  320. #
  321. #  Remove all elements from the listbox, all contents
  322. #  from the entry component, or both (if all).
  323. #
  324. # ------------------------------------------------------
  325. body iwidgets::Combobox::clear {{component all}} {
  326.     switch -- $component {
  327.     entry {
  328.         iwidgets::Entryfield::clear
  329.     }
  330.     list {
  331.         delete list 0 end
  332.     }
  333.     all {
  334.         delete list 0 end
  335.         iwidgets::Entryfield::clear
  336.     }
  337.     default {
  338.         error "bad Combobox component \"$component\": must be entry, list,\
  339. or all."
  340.     }
  341.     }
  342.     return
  343. }
  344.  
  345. # ------------------------------------------------------
  346. # PUBLIC METHOD: curselection
  347. #
  348. # Return the current selection index.
  349. #
  350. # ------------------------------------------------------
  351. body iwidgets::Combobox::curselection {} {
  352.     return [$itk_component(list) curselection]
  353. }
  354.  
  355. # ------------------------------------------------------
  356. # PUBLIC METHOD: delete component first ?last?
  357. #
  358. # Delete an item or items from the listbox OR delete
  359. # text from the entry field. First argument determines
  360. # which component deletion occurs in - valid values are
  361. # entry or list.
  362. #
  363. # ------------------------------------------------------
  364. body iwidgets::Combobox::delete {component first {last {}}} {
  365.     switch -- $component {
  366.     entry {
  367.         iwidgets::Entryfield::delete $first $last
  368.     }
  369.     list {
  370.         _deleteList $first $last
  371.     }
  372.     default {
  373.            error "bad Combobox component \"$component\": must be entry or list."
  374.     }
  375.     }
  376. }
  377.  
  378. # ------------------------------------------------------
  379. # PUBLIC METHOD: get ?index?
  380. #
  381. #
  382. # Retrieve entry contents if no args OR use args as list 
  383. # index and retrieve list item at index .
  384. #
  385. # ------------------------------------------------------
  386. body iwidgets::Combobox::get {{index {}}} {
  387.     # no args means to get the current text in the entry field area
  388.     if {$index == {}} {
  389.     iwidgets::Entryfield::get
  390.     } else {
  391.     eval $itk_component(list) get $index
  392.     }
  393. }
  394.  
  395. # ------------------------------------------------------
  396. # PUBLIC METHOD: getcurselection
  397. #
  398. # Return currently selected item in the listbox. Shortcut
  399. # version of get curselection command combination.
  400. #
  401. # ------------------------------------------------------
  402. body iwidgets::Combobox::getcurselection {} {
  403.     return [$itk_component(list) getcurselection]
  404. }
  405.  
  406. # ------------------------------------------------------------------
  407. # PUBLIC METHOD: ivoke
  408. #
  409. # Pops up or down a dropdown combobox.
  410. # ------------------------------------------------------------------
  411. body iwidgets::Combobox::invoke {} {
  412.     if {$itk_option(-dropdown)} {
  413.     return [_toggleList]
  414.     }
  415.     return 
  416. }
  417.  
  418. # ------------------------------------------------------------
  419. # PUBLIC METHOD: insert comonent index string ?string ...?
  420. #
  421. # Insert an item into the listbox OR text into the entry area.
  422. # Valid component names are entry or list.
  423. #
  424. # ------------------------------------------------------------
  425. body iwidgets::Combobox::insert {component index args} {
  426.     if {$args == {}} {
  427.     error "no value given for parameter \"string\" in function\
  428. \"Combobox::insert\""
  429.     } 
  430.  
  431.     switch -- $component {
  432.     entry {
  433.         if { [llength $args] > 1} {
  434.         error "called function \"Combobox::insert entry\" with too\
  435. many arguments"
  436.         } else {
  437.         eval iwidgets::Entryfield::insert $index $args
  438.         }
  439.     }
  440.     list {
  441.         configure -items [eval linsert {$itk_option(-items)} $index $args]
  442.     }
  443.     default {
  444.            error "bad Combobox component \"$component\": must be entry or list."
  445.     }
  446.     }
  447. }
  448.  
  449. # ------------------------------------------------------
  450. # PUBLIC METHOD: justify direction
  451. #
  452. # Wrapper for justifying the listbox items in one of
  453. # 4 directions:  top, bottom, left, or right.
  454. #
  455. # ------------------------------------------------------
  456. body iwidgets::Combobox::justify {direction} {
  457.     return [$itk_component(list) justify $direction]
  458. }
  459.  
  460. # ------------------------------------------------------------------
  461. # PUBLIC METHOD: see index
  462. #
  463. # Adjusts the view such that the element given by index is visible.
  464. # ------------------------------------------------------------------
  465. body iwidgets::Combobox::see {index} {
  466.     return [$itk_component(list) see $index]
  467. }
  468.  
  469. # ------------------------------------------------------------------
  470. # PUBLIC METHOD: selection option first ?last?
  471. #
  472. # Adjusts the selection within the listbox and changes the contents
  473. # of the entry component to be the value of the selected list item.
  474. # ------------------------------------------------------------------
  475. body iwidgets::Combobox::selection {option first {last {}}} {
  476.     # thin wrap
  477.     set rtn [eval $itk_component(list) selection $option $first $last]
  478.     set _currItem $first
  479.  
  480.     # combobox additions
  481.     set theText [getcurselection]
  482.     if {$theText != [$itk_component(entry) get]} {
  483.     clear entry
  484.     if {$theText != ""} {
  485.         insert entry 0 $theText
  486.     }
  487.     }
  488.     return $rtn
  489. }
  490.  
  491. # ------------------------------------------------------------------
  492. # PUBLIC METHOD: size 
  493. #
  494. # Returns a decimal string indicating the total number of elements 
  495. # in the listbox.
  496. # ------------------------------------------------------------------
  497. body iwidgets::Combobox::size {} {
  498.     return [$itk_component(list) size]
  499. }
  500.  
  501. # ------------------------------------------------------
  502. # PUBLIC METHOD: sort ?mode?
  503. #
  504. # Sort the current list in either "ascending" or "descending" order.
  505. #
  506. #   jss: how should i handle selected items?
  507. #
  508. # ------------------------------------------------------
  509. body iwidgets::Combobox::sort {{mode ascending}} {
  510.     return [$itk_component(list) sort $mode]
  511. }
  512.  
  513.  
  514. # ------------------------------------------------------------------
  515. # PUBLIC METHOD: xview ?arg arg ...?
  516. #
  517. # Change or query the vertical position of the text in the list box.
  518. # ------------------------------------------------------------------
  519. body iwidgets::Combobox::xview {args} {
  520.     return [eval $itk_component(list) xview $args]
  521. }
  522.  
  523. # ------------------------------------------------------------------
  524. # PUBLIC METHOD: yview ?arg arg ...?
  525. #
  526. # Change or query the horizontal position of the text in the list box.
  527. # ------------------------------------------------------------------
  528. body iwidgets::Combobox::yview {args} {
  529.     return [eval $itk_component(list) yview $args]
  530. }
  531.  
  532. # ------------------------------------------------------
  533. # PROTECTED METHOD: _addToList
  534. #
  535. # Add the current item in the entry to the listbox.
  536. #
  537. # ------------------------------------------------------
  538. body iwidgets::Combobox::_addToList {} {
  539.     set input [get]
  540.     if {$input != ""} {
  541.     if {$itk_option(-unique)} {
  542.         # if item is already in list, select it and exit
  543.         set item [lsearch -exact $itk_option(-items) $input]
  544.         if {$item != -1} {
  545.         selection clear 0 end
  546.         if {$item != {}} {
  547.             selection set $item $item
  548.             set _currItem $item
  549.         }
  550.         return
  551.         }
  552.     }
  553.     # add the item to end of list and clear the text area
  554.     insert list end $input
  555.  
  556.     if {$itk_option(-autoclear)} {
  557.         delete entry 0 end
  558.     }
  559.     }
  560. }
  561.  
  562. # ------------------------------------------------------
  563. # PROTECTED METHOD:   _createComponents
  564. #
  565. # Create deferred combobox components and add bindings.
  566. #
  567. # ------------------------------------------------------
  568. body iwidgets::Combobox::_createComponents {} {
  569.     if {$itk_option(-dropdown)} {
  570.     # --- build a dropdown combobox ---
  571.  
  572.     # make the arrow and margin childsite's be on the right hand side
  573.     configure -childsitepos e
  574.  
  575.     # add spacer between arrow and the entry component
  576.     itk_component add margin {
  577.         frame $itk_interior.margin -width $itk_option(-margin)
  578.     } {
  579.         rename -width -margin margin Margin
  580.     }
  581.     
  582.     # arrow button to popup the list
  583.     itk_component add arrowBtn {
  584.         canvas $itk_interior.arrowBtn -borderwidth 2 \
  585.         -width 16 -height 16
  586.     } {
  587.         keep -background -borderwidth -cursor  \
  588.         -highlightcolor -highlightthickness
  589.         rename -relief -arrowrelief arrowRelief Relief
  590.         rename -highlightbackground -background background Background
  591.     }
  592.     
  593.     # popup list container
  594.     itk_component add popup {
  595.         toplevel $itk_interior.popup
  596.     } {
  597.         keep -background -cursor
  598.     }
  599.     wm withdraw $itk_interior.popup
  600.     wm overrideredirect $itk_interior.popup 1
  601.     
  602.     # the listbox
  603.     itk_component add list {
  604.         iwidgets::Scrolledlistbox $itk_interior.popup.list -exportselection no \
  605.         -vscrollmode dynamic -hscrollmode dynamic 
  606.     } {
  607.         keep -background -borderwidth -cursor -foreground \
  608.         -highlightcolor -highlightthickness \
  609.         -hscrollmode -items -selectbackground \
  610.         -selectborderwidth -selectforeground -textbackground \
  611.         -textfont -vscrollmode
  612.         rename -height -listheight listHeight Height
  613.         rename -cursor -popupcursor popupCursor Cursor
  614.     }
  615.     # mode specific bindings
  616.     _dropdownBindings
  617.  
  618.     # Ugly hack to avoid tk buglet revealed in _dropdownBtnRelease where 
  619.     # relief is used but not set in scrollbar.tcl. 
  620.     global tkPriv
  621.     set tkPriv(relief) raise
  622.  
  623.     } else {
  624.     # --- build a simple combobox ---
  625.     configure -childsitepos s
  626.     itk_component add list {
  627.         iwidgets::Scrolledlistbox $itk_interior.list -exportselection no \
  628.         -vscrollmode dynamic -hscrollmode dynamic 
  629.     } {
  630.         keep -background -borderwidth -cursor -foreground \
  631.         -highlightcolor -highlightthickness \
  632.         -hscrollmode -items -selectbackground \
  633.         -selectborderwidth -selectforeground -textbackground \
  634.         -textfont -visibleitems -vscrollmode 
  635.         rename -height -listheight listHeight Height
  636.     }
  637.     # add mode specific bindings
  638.     _simpleBindings
  639.     }
  640.  
  641.     # popup cursor applies only to the list within the combobox
  642.     configure -popupcursor $itk_option(-popupcursor)
  643.  
  644.     # add mode independent bindings
  645.     _commonBindings
  646. }
  647.  
  648. # ------------------------------------------------------
  649. # PROTECTED METHOD: _deleteList first ?last?
  650. #
  651. # Delete an item or items from the listbox. Called via 
  652. # "delete list args".
  653. #
  654. # ------------------------------------------------------
  655. body iwidgets::Combobox::_deleteList {first {last {}}} {
  656.  
  657.     if {$last == {}} {
  658.     set last $first
  659.     }
  660.     configure -items [eval lreplace {$itk_option(-items)} $first $last]
  661.  
  662.     # remove the item if it is no longer in the list
  663.     set text [$this get]
  664.     if {$text != ""} {
  665.     set index [lsearch -exact $itk_option(-items) $text ]
  666.     if {$index == -1} {
  667.         clear entry
  668.     }
  669.     }
  670.     return
  671. }
  672.  
  673. # ------------------------------------------------------
  674. # PROTECTED METHOD: _deleteText first ?last?
  675. #
  676. # Renamed Entryfield delete method. Called via "delete entry args".
  677. #
  678. # ------------------------------------------------------
  679. body iwidgets::Combobox::_deleteText {first {last {}}} {
  680.     $itk_component(entry) configure -state normal 
  681.     set rtrn [delete $first $last]
  682.     if {$itk_option(-editable)} {
  683.     } else {
  684.     $itk_component(entry) configure -state disabled
  685.     }
  686.     return $rtrn
  687. }
  688.  
  689. # ------------------------------------------------------
  690. # PROTECTED METHOD:   _doLayout ?when?
  691. #
  692. # Call methods to create and pack the Combobox components.
  693. #
  694. # ------------------------------------------------------
  695. body iwidgets::Combobox::_doLayout {{when later}} {
  696.     _createComponents
  697.     _packComponents $when
  698. }
  699.  
  700.  
  701. # ------------------------------------------------------
  702. # PROTECTED METHOD:   _drawArrow ?mode?
  703. #
  704. # Draw the arrow button. Determines packing according to
  705. # -labelpos.
  706. #
  707. # ------------------------------------------------------
  708. body iwidgets::Combobox::_drawArrow {{mode normal}} {
  709.     set flip false
  710.     switch -- $mode {
  711.     normal {
  712.         set fg [cget -foreground]
  713.         $itk_component(arrowBtn) configure -relief $itk_option(-arrowrelief)
  714.         bind $itk_component(arrowBtn) <1> [code $this _toggleList]
  715.         if {$_isPosted} {
  716.         if {$itk_option(-fliparrow)} {
  717.             set flip true
  718.         }
  719.         }
  720.     }
  721.     disabled {
  722.         set fg grey65
  723.         $itk_component(arrowBtn) configure -relief $itk_option(-arrowrelief)
  724.         bind $itk_component(arrowBtn) <1> ""
  725.     }
  726.     depressed {
  727.         set fg [cget -foreground]
  728.         $itk_component(arrowBtn) configure -relief sunken
  729. #        update
  730.     }
  731.     }
  732.  
  733.     # undraw the old arrow polygon
  734.     $itk_component(arrowBtn) delete arrow
  735.  
  736.     # draw new arrow
  737.     set bw [expr ([$itk_component(arrowBtn) cget -borderwidth] + \
  738.               [$itk_component(arrowBtn) cget -highlightthickness]) / 2]
  739.     set h [expr [$itk_component(arrowBtn) cget -height] + (2*$bw)]
  740.     set w [expr [$itk_component(arrowBtn) cget -width] + (2*$bw)]
  741.  
  742.     if {$flip} {
  743.     $itk_component(arrowBtn) create polygon \
  744.         [expr .25*$w+$bw] [expr .75*$h+$bw] \
  745.         [expr .75*$w+$bw] [expr .75*$h+$bw] \
  746.         [expr .5*$w+$bw] [expr .25*$h+$bw-1] -fill $fg -tag arrow
  747.     } else {
  748.     $itk_component(arrowBtn) create polygon \
  749.         [expr .25*$w+$bw] [expr .25*$h+$bw] \
  750.         [expr .75*$w+$bw] [expr .25*$h+$bw] \
  751.         [expr .5*$w+$bw] [expr .75*$h+$bw] \
  752.         -fill $fg -tag arrow
  753.     }
  754. }
  755.  
  756. # ------------------------------------------------------
  757. # PROTECTED METHOD: _dropdownBtnRelease window x y
  758. #
  759. # Event handler for button releases while a dropdown list
  760. # is posted.
  761. #
  762. # ------------------------------------------------------
  763. body iwidgets::Combobox::_dropdownBtnRelease {{window {}} {x 1} {y 1}} {
  764.  
  765.     # if it's a scrollbar then ignore the release
  766.     if {($window == [$itk_component(list) component vertsb]) ||
  767.     ($window == [$itk_component(list) component horizsb])} {
  768.     return
  769.     }
  770.  
  771.     # 1st release allows list to stay up unless we are in listbox
  772.     if {$_ignoreRelease} {
  773.     _ignoreNextBtnRelease false
  774.     return
  775.     }
  776.     
  777.     # should I use just the listbox or also include the scrollbars
  778.     if {($x >= 0) && ($x < [winfo width [_slbListbox]]) && 
  779.     ($y >= 0) && ($y < [winfo height [_slbListbox]])} {
  780.     _selectCmd
  781.     }
  782.     
  783.     _unpostList
  784. }
  785.  
  786. # ------------------------------------------------------
  787. # PROTECTED METHOD: _ignoreNextBtnRelease ignore
  788. #
  789. # Set private variable _ignoreRelease. If this variable
  790. # is true then the next button release will not remove
  791. # a dropdown list.
  792. #
  793. # ------------------------------------------------------
  794. body iwidgets::Combobox::_ignoreNextBtnRelease {ignore} {
  795.     set _ignoreRelease $ignore
  796. }
  797.  
  798. # ------------------------------------------------------
  799. # PROTECTED METHOD:   _next
  800. #
  801. # Select the next item in the list.
  802. #
  803. # ------------------------------------------------------
  804. body iwidgets::Combobox::_next {} {
  805.     if {[$this size] <= 1} {
  806.     return
  807.     }
  808.     set i [$this curselection]
  809.     if {($i == {}) || ($i == [expr [$this size]-1]) } {
  810.     set i 0
  811.     } else {
  812.     incr i
  813.     }
  814.     $this selection clear 0 end
  815.     $this selection set $i $i
  816.     $this see $i
  817.     set _currItem $i
  818. }
  819.  
  820. # ------------------------------------------------------
  821. # PROTECTED METHOD:   _packArrow labelpos
  822. #
  823. # Pack the arrowbutton according to the label position.
  824. #
  825. # ------------------------------------------------------
  826. body iwidgets::Combobox::_packArrow {labelpos} {
  827.     pack $itk_component(arrowBtn) -side right -anchor center
  828. }
  829.  
  830. # ------------------------------------------------------
  831. # PROTECTED METHOD:   _packComponents ?when?
  832. #
  833. # Pack the components of the combobox and add bindings.
  834. #
  835. # ------------------------------------------------------
  836. body iwidgets::Combobox::_packComponents {{when later}} {
  837.     if {$when == "later"} {
  838.     if {$_repacking == ""} {
  839.         set _repacking [after idle [code $this _packComponents now]]
  840.         return
  841.     }
  842.     } elseif {$when != "now"} {
  843.     error "bad option \"$when\": should be now or later"
  844.     }
  845.  
  846.     if {$itk_option(-dropdown)} {
  847.     pack $itk_component(margin) -side left
  848.     pack configure $itk_component(list) -expand yes -fill both
  849.     _resizeArrow
  850.     pack $itk_component(arrowBtn) -side right
  851.     } else {
  852.     # size and pack list hack
  853.     set w [winfo reqwidth $itk_component(hull)]
  854.     $itk_component(list) configure -width $w
  855.     pack configure $itk_component(entry) -fill x -side top -expand no
  856.     pack configure $itk_component(efchildsite) -side top \
  857.         -after $itk_component(entry) -fill both -expand yes
  858.     pack configure $itk_component(list) -side top -fill both -expand yes
  859.     }
  860.     
  861.     set _repacking ""
  862. }
  863.  
  864. # ------------------------------------------------------
  865. # PROTECTED METHOD:   _positionList
  866. #
  867. # Determine the position (geometry) for the popped up list
  868. # and map it to the screen.
  869. #
  870. # ------------------------------------------------------
  871. body iwidgets::Combobox::_positionList {} {
  872.  
  873.     set x [winfo rootx $itk_component(entry) ]
  874.     set y [expr [winfo rooty $itk_component(entry) ] + \
  875.            [winfo height $itk_component(entry) ]]
  876.     set w [winfo width $itk_component(entry) ]
  877.     set h [winfo height [_slbListbox] ]
  878.     set sh [winfo screenheight .]
  879.  
  880.     if {([expr $y+$h] > $sh) && ($y > [expr $sh/2])} {
  881.     set y [expr [winfo rooty $itk_component(entry) ] - $h]
  882.     }
  883.     
  884.     $itk_component(list) configure -width $w
  885.     wm geometry $itk_component(popup) +$x+$y
  886. }
  887.  
  888. # ------------------------------------------------------
  889. # PROTECTED METHOD:   _postList
  890. #
  891. # Pop up the list in a dropdown style Combobox.
  892. #
  893. # ------------------------------------------------------
  894. body iwidgets::Combobox::_postList {} {
  895.  
  896.     if {[$itk_component(list) size] == ""} {
  897.     return
  898.     }
  899.  
  900.     set _isPosted true
  901.     _drawArrow depressed      ;# sad button
  902.     _positionList
  903.  
  904.     # map window and do a grab
  905.     wm deiconify $itk_component(popup)
  906.     _listShowing -wait
  907.     grab -global $itk_component(popup) 
  908.     raise $itk_component(popup)
  909.     focus $itk_component(popup)
  910.  
  911.     _drawArrow normal
  912.     set _ignoreRelease true
  913. }
  914.  
  915. # ------------------------------------------------------
  916. # PROTECTED METHOD:    _previous
  917. #
  918. # Select the previous item in the list. Wraps at front
  919. # and end of list. 
  920. #
  921. # ------------------------------------------------------
  922. body iwidgets::Combobox::_previous {} {
  923.     if {[$this size] <= 1} {
  924.     return
  925.     }
  926.     set i [$this curselection]
  927.     if {$i == "" || $i == 0} {
  928.     set i [expr [$this size] - 1]
  929.     } else {
  930.     incr i -1
  931.     }
  932.     $this selection clear 0 end
  933.     $this selection set $i $i
  934.     $this see $i
  935.     set _currItem $i
  936. }
  937.  
  938. # ------------------------------------------------------
  939. # PROTECTED METHOD:   _resizeArrow
  940. #
  941. # Recalculate the arrow button size and then redraw it.
  942. #
  943. # ------------------------------------------------------
  944. body iwidgets::Combobox::_resizeArrow {} {
  945.     set bw [expr [$itk_component(arrowBtn) cget -borderwidth]+ \
  946.         [$itk_component(arrowBtn) cget -highlightthickness]]
  947.     set newHeight [expr [winfo reqheight $itk_component(entry) ]-(2*$bw)]
  948.     $itk_component(arrowBtn) configure -width $newHeight -height $newHeight
  949.     _drawArrow
  950. }
  951.  
  952. # ------------------------------------------------------
  953. # PROTECTED METHOD:   _selectCmd
  954. #
  955. # Called when list item is selected to insert new text 
  956. # in entry, and call user -command callback if defined.
  957. #
  958. # ------------------------------------------------------
  959. body iwidgets::Combobox::_selectCmd {} {
  960.     $itk_component(entry) configure -state normal
  961.     
  962.     set _currItem [$itk_component(list) curselection]
  963.     set item [$itk_component(list) getcurselection]
  964.     clear entry
  965.     $itk_component(entry) insert 0 $item
  966.     if {$itk_option(-editable)} {
  967.     } else {
  968.     $itk_component(entry) configure -state disabled
  969.     }
  970.  
  971.     # execute user command
  972.     if {$itk_option(-selectioncommand) != ""} {
  973.     uplevel #0 $itk_option(-selectioncommand)
  974.     }
  975. }
  976.  
  977. # ------------------------------------------------------
  978. # PROTECTED METHOD:  _toggleList
  979. #
  980. # Post or unpost the dropdown listbox (toggle).
  981. #
  982. # ------------------------------------------------------
  983. body iwidgets::Combobox::_toggleList {} {
  984.     if {[winfo ismapped $itk_component(popup)] } {
  985.     _unpostList
  986.     } else {
  987.     _postList
  988.     }
  989. }
  990.  
  991. # ------------------------------------------------------
  992. # PROTECTED METHOD:   _unpostList
  993. #
  994. # Unmap the listbox (pop it down).
  995. #
  996. # ------------------------------------------------------
  997. body iwidgets::Combobox::_unpostList {} {
  998.     # Determine if event occured in the scrolledlistbox and, if it did, 
  999.     # don't unpost it. (A selection in the list unposts it correctly and 
  1000.     # in the scrollbar we don't want to unpost it.)
  1001.     set x [winfo x $itk_component(list)]
  1002.     set y [winfo y $itk_component(list)]
  1003.     set w [winfo width $itk_component(list)]
  1004.     set h [winfo height $itk_component(list)]
  1005.  
  1006.     wm withdraw $itk_component(popup)
  1007.     grab release $itk_component(popup)    
  1008.     
  1009.     set _isPosted false
  1010.     _drawArrow normal
  1011.     
  1012.     $itk_component(list) selection clear 0 end
  1013.     if {$_currItem != {}} {
  1014.     $itk_component(list) selection set $_currItem $_currItem
  1015.     $itk_component(list) activate $_currItem
  1016.     }
  1017.  
  1018.     if {$itk_option(-editable)} {
  1019.     $itk_component(entry) configure -state normal
  1020.     } else {
  1021.     $itk_component(entry) configure -state disabled
  1022.     }
  1023. }
  1024.  
  1025. # ------------------------------------------------------
  1026. # PROTECTED METHOD:   _commonBindings
  1027. #
  1028. # Bindings that are used by both simple and dropdown
  1029. # style Comboboxes.
  1030. #
  1031. # ------------------------------------------------------
  1032. body iwidgets::Combobox::_commonBindings {} {
  1033.     bind $itk_component(entry) <Down>       [code $this _next]
  1034.     bind $itk_component(entry) <Up>         [code $this _previous]
  1035.     bind $itk_component(entry) <Control-n>  [code $this _next]
  1036.     bind $itk_component(entry) <Control-p>  [code $this _previous]
  1037.     bind [_slbListbox]         <Control-n>  [code $this _next]
  1038.     bind [_slbListbox]         <Control-p>  [code $this _previous]
  1039. }
  1040.  
  1041.  
  1042. # ------------------------------------------------------
  1043. # PROTECTED METHOD: _dropdownBindings
  1044. #
  1045. # Bindings used only by the dropdown type Combobox.
  1046. #
  1047. # ------------------------------------------------------
  1048. body iwidgets::Combobox::_dropdownBindings {} {
  1049.     bind $itk_component(popup)  <Escape> [code $this _unpostList]
  1050.     bind $itk_component(popup)  <space>  \
  1051.     "[code $this _selectCmd]; [code $this _unpostList]"
  1052.     bind $itk_component(popup)  <Return> \
  1053.     "[code $this _selectCmd]; [code $this _unpostList]"
  1054.     bind $itk_component(popup)  <ButtonRelease-1> \
  1055.     [code $this _dropdownBtnRelease %W %x %y]
  1056.  
  1057.     bind $itk_component(list)  <Map> \
  1058.     [code $this _listShowing 1]
  1059.     bind $itk_component(list)  <Unmap> \
  1060.     [code $this _listShowing 0]
  1061.  
  1062.     # once in the listbox, we drop on the next release (unless in scrollbar)
  1063.     bind [_slbListbox]   <Enter>   \
  1064.     [code $this _ignoreNextBtnRelease false]
  1065.  
  1066.     bind $itk_component(arrowBtn) <Configure>  [code $this _drawArrow]
  1067.     bind $itk_component(arrowBtn) <3>          [code $this _next]
  1068.     bind $itk_component(arrowBtn) <Shift-3>    [code $this _previous]
  1069.     bind $itk_component(arrowBtn) <Down>       [code $this _next]
  1070.     bind $itk_component(arrowBtn) <Up>         [code $this _previous]
  1071.     bind $itk_component(arrowBtn) <Control-n>  [code $this _next]
  1072.     bind $itk_component(arrowBtn) <Control-p>  [code $this _previous]
  1073.     bind $itk_component(arrowBtn) <Shift-Down> [code $this _toggleList]
  1074.     bind $itk_component(arrowBtn) <Shift-Up>   [code $this _toggleList]
  1075.     bind $itk_component(arrowBtn) <Return>     [code $this _toggleList]
  1076.     bind $itk_component(arrowBtn) <space>      [code $this _toggleList]
  1077.  
  1078.     bind $itk_component(entry)    <Configure>  [code $this _resizeArrow]
  1079.     bind $itk_component(entry)    <Shift-Down> [code $this _toggleList]
  1080.     bind $itk_component(entry)    <Shift-Up>   [code $this _toggleList]
  1081. }
  1082.  
  1083. # ------------------------------------------------------
  1084. # PROTECTED METHOD: _simpleBindings
  1085. #
  1086. # Bindings used only by the simple type Comboboxes.
  1087. #
  1088. # ------------------------------------------------------
  1089. body iwidgets::Combobox::_simpleBindings {} {
  1090.     bind [_slbListbox]         <ButtonRelease-1> [code $this _selectCmd]
  1091.     bind [_slbListbox]         <space>     [code $this _selectCmd]
  1092.     bind [_slbListbox]         <Return>    [code $this _selectCmd]
  1093.     bind $itk_component(entry) <Escape>     ""
  1094.     bind $itk_component(entry) <Shift-Down> ""
  1095.     bind $itk_component(entry) <Shift-Up>   ""
  1096.     bind $itk_component(entry) <Configure>  ""
  1097. }
  1098.  
  1099. # ------------------------------------------------------
  1100. # PROTECTED METHOD: _listShowing ?val?
  1101. #
  1102. # Used instead of "tkwait visibility" to make sure that
  1103. # the dropdown list is visible.  Whenever the list gets
  1104. # mapped or unmapped, this method is called to keep
  1105. # track of it.  When it is called with the value "-wait",
  1106. # it waits for the list to be mapped.
  1107. # ------------------------------------------------------
  1108. body iwidgets::Combobox::_listShowing {{val ""}} {
  1109.     if {$val == ""} {
  1110.         return $_listShowing($this)
  1111.     } elseif {$val == "-wait"} {
  1112.         while {!$_listShowing($this)} {
  1113.             tkwait variable [scope _listShowing($this)]
  1114.         }
  1115.         return
  1116.     }
  1117.     set _listShowing($this) $val
  1118. }
  1119.  
  1120. # ------------------------------------------------------
  1121. # PRIVATE METHOD:    _slbListbox
  1122. #
  1123. # Access the tk listbox window out of the scrolledlistbox.
  1124. #
  1125. # ------------------------------------------------------
  1126. body iwidgets::Combobox::_slbListbox {} {
  1127.     return [$itk_component(list) component listbox]
  1128. }
  1129.  
  1130. # ... emacs mode recognition ...
  1131. # Local Variables:
  1132. # mode: tcl
  1133. # End:
  1134.  
  1135.