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 / hierarchy.itk < prev    next >
Text File  |  1999-02-24  |  56KB  |  1,655 lines

  1. # Hierarchy
  2. # ----------------------------------------------------------------------
  3. # Hierarchical data viewer.  Manages a list of nodes that can be
  4. # expanded or collapsed.  Individual nodes can be highlighted.
  5. # Clicking with the right mouse button on any item brings up a
  6. # special item menu.  Clicking on the background area brings up
  7. # a different popup menu.
  8. # ----------------------------------------------------------------------
  9. #   AUTHOR:  Michael J. McLennan
  10. #            Bell Labs Innovations for Lucent Technologies
  11. #            mmclennan@lucent.com
  12. #
  13. #            Mark L. Ulferts
  14. #            DSC Communications
  15. #            mulferts@austin.dsccc.com
  16. #
  17. #      RCS:  $Id: hierarchy.itk,v 1.2 1998/08/11 14:42:07 welch Exp $
  18. # ----------------------------------------------------------------------
  19. #                Copyright (c) 1996  Lucent Technologies
  20. # ======================================================================
  21. # Permission to use, copy, modify, and distribute this software and its
  22. # documentation for any purpose and without fee is hereby granted,
  23. # provided that the above copyright notice appear in all copies and that
  24. # both that the copyright notice and warranty disclaimer appear in
  25. # supporting documentation, and that the names of Lucent Technologies
  26. # any of their entities not be used in advertising or publicity
  27. # pertaining to distribution of the software without specific, written
  28. # prior permission.
  29. #
  30. # Lucent Technologies disclaims all warranties with regard to this
  31. # software, including all implied warranties of merchantability and
  32. # fitness.  In no event shall Lucent Technologies be liable for any
  33. # special, indirect or consequential damages or any damages whatsoever
  34. # resulting from loss of use, data or profits, whether in an action of
  35. # contract, negligence or other tortuous action, arising out of or in
  36. # connection with the use or performance of this software.
  37. #
  38. # ----------------------------------------------------------------------
  39. #            Copyright (c) 1996 DSC Technologies Corporation
  40. # ======================================================================
  41. # Permission to use, copy, modify, distribute and license this software 
  42. # and its documentation for any purpose, and without fee or written 
  43. # agreement with DSC, is hereby granted, provided that the above copyright 
  44. # notice appears in all copies and that both the copyright notice and 
  45. # warranty disclaimer below appear in supporting documentation, and that 
  46. # the names of DSC Technologies Corporation or DSC Communications 
  47. # Corporation not be used in advertising or publicity pertaining to the 
  48. # software without specific, written prior permission.
  49. # DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 
  50. # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
  51. # INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
  52. # AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, 
  53. # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL 
  54. # DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 
  55. # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
  56. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
  57. # ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
  58. # SOFTWARE.
  59. # ======================================================================
  60.  
  61. #
  62. # Usual options.
  63. #
  64. itk::usual Hierarchy {
  65.     keep -cursor -textfont -font
  66.     keep -background -foreground -textbackground 
  67.     keep -selectbackground -selectforeground 
  68. }
  69.  
  70. # ------------------------------------------------------------------
  71. #                            HIERARCHY
  72. # ------------------------------------------------------------------
  73. class iwidgets::Hierarchy {
  74.     inherit iwidgets::Scrolledwidget
  75.  
  76.     constructor {args} {}
  77.  
  78.     destructor {}
  79.  
  80.     itk_option define -alwaysquery alwaysQuery AlwaysQuery 0
  81.     itk_option define -closedicon closedIcon Icon {}
  82.     itk_option define -expanded expanded Expanded 0 
  83.     itk_option define -filter filter Filter 0 
  84.     itk_option define -font font Font \
  85.     -*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-* 
  86.     itk_option define -height height Height 0
  87.     itk_option define -iconcommand iconCommand Command {}
  88.     itk_option define -markbackground markBackground Foreground #a0a0a0 
  89.     itk_option define -markforeground markForeground Background Black 
  90.     itk_option define -nodeicon nodeIcon Icon {}
  91.     itk_option define -openicon openIcon Icon {}
  92.     itk_option define -querycommand queryCommand Command {}
  93.     itk_option define -selectcommand selectCommand Command {}
  94.     itk_option define -selectbackground selectBackground Foreground #c3c3c3 
  95.     itk_option define -selectforeground selectForeground Background Black 
  96.     itk_option define -visibleitems visibleItems VisibleItems 80x24
  97.     itk_option define -width width Width 0
  98.  
  99.     public method clear {}
  100.     public method collapse {node}
  101.     public method current {}
  102.     public method draw {{when -now}}
  103.     public method expand {node}
  104.     public method mark {op args}
  105.     public method prune {node}
  106.     public method refresh {node}
  107.     public method selection {op args}
  108.     public method toggle {node}
  109.  
  110.     public method bbox {index} 
  111.     public method compare {index1 op index2} 
  112.     public method debug {args} {eval $args}
  113.     public method delete {first {last {}}} 
  114.     public method dlineinfo {index} 
  115.     public method dump {args}
  116.     public method get {index1 {index2 {}}} 
  117.     public method index {index} 
  118.     public method insert {args} 
  119.     public method scan {option args} 
  120.     public method search {args} 
  121.     public method see {index} 
  122.     public method tag {op args} 
  123.     public method window {option args} 
  124.     public method xview {args}
  125.     public method yview {args}
  126.  
  127.     protected method _contents {uid}
  128.     protected method _iconSelect {node icon}
  129.     protected method _post {x y}
  130.     protected method _drawLevel {node indent}
  131.     protected method _select {x y}
  132.     protected method _deselectSubNodes {uid}
  133.     protected method _deleteNodeInfo {uid}
  134.     protected method _getParent {uid}
  135.     protected method _getHeritage {uid}
  136.     protected method _isInternalTag {tag}
  137.  
  138.     private variable _filterCode ""  ;# Compact view flag.
  139.     private variable _hcounter 0     ;# Counter for hierarchy icons
  140.     private variable _icons          ;# Array of user icons by uid
  141.     private variable _images         ;# Array of our icons by uid
  142.     private variable _indents        ;# Array of indentation by uid
  143.     private variable _marked         ;# Array of marked nodes by uid
  144.     private variable _markers ""     ;# List of markers for level being drawn
  145.     private variable _nodes          ;# List of subnodes by uid
  146.     private variable _pending ""     ;# Pending draw flag
  147.     private variable _posted ""      ;# List of tags at posted menu position
  148.     private variable _selected       ;# Array of selected nodes by uid
  149.     private variable _tags           ;# Array of user tags by uid
  150.     private variable _text           ;# Array of displayed text by uid
  151.     private variable _states         ;# Array of selection state by uid
  152.     private variable _ucounter 0     ;# Counter for user icons
  153. }
  154.  
  155. #
  156. # Provide a lowercased access method for the Hierarchy class.
  157. proc ::iwidgets::hierarchy {pathName args} {
  158.     uplevel ::iwidgets::Hierarchy $pathName $args
  159. }
  160.  
  161. #
  162. # Use option database to override default resources of base classes.
  163. #
  164. option add *Hierarchy.menuCursor arrow widgetDefault
  165. option add *Hierarchy.labelPos n widgetDefault
  166. option add *Hierarchy.tabs 30 widgetDefault
  167.  
  168. # ------------------------------------------------------------------
  169. #                        CONSTRUCTOR
  170. # ------------------------------------------------------------------
  171. body iwidgets::Hierarchy::constructor {args} {
  172.     itk_option remove iwidgets::Labeledwidget::state
  173.  
  174.     #
  175.     # Our -width and -height options are slightly different than
  176.     # those implemented by our base class, so we're going to
  177.     # remove them and redefine our own.
  178.     #
  179.     itk_option remove iwidgets::Scrolledwidget::width
  180.     itk_option remove iwidgets::Scrolledwidget::height
  181.  
  182.     #
  183.     # Create a clipping frame which will provide the border for
  184.     # relief display.
  185.     #
  186.     itk_component add clipper {
  187.     frame $itk_interior.clipper
  188.     } {
  189.     usual
  190.  
  191.     keep -borderwidth -relief -highlightthickness -highlightcolor
  192.     rename -highlightbackground -background background Background
  193.     }    
  194.     grid $itk_component(clipper) -row 0 -column 0 -sticky nsew
  195.     grid rowconfigure $_interior 0 -weight 1
  196.     grid columnconfigure $_interior 0 -weight 1
  197.  
  198.     #
  199.     # Create a text widget for displaying our hierarchy.
  200.     #
  201.     itk_component add list {
  202.     text $itk_component(clipper).list -wrap none -cursor center_ptr \
  203.                 -state disabled -width 1 -height 1 \
  204.             -xscrollcommand \
  205.         [code $this _scrollWidget $itk_interior.horizsb] \
  206.         -yscrollcommand \
  207.         [code $this _scrollWidget $itk_interior.vertsb] \
  208.             -borderwidth 0 -highlightthickness 0
  209.     } {
  210.     usual
  211.  
  212.     keep -spacing1 -spacing2 -spacing3 -tabs
  213.     rename -font -textfont textFont Font
  214.     rename -background -textbackground textBackground Background
  215.     ignore -highlightthickness -highlightcolor
  216.     ignore -insertbackground -insertborderwidth
  217.     ignore -insertontime -insertofftime -insertwidth
  218.     ignore -selectborderwidth
  219.     ignore -borderwidth
  220.     }
  221.     grid $itk_component(list) -row 0 -column 0 -sticky nsew
  222.     grid rowconfigure $itk_component(clipper) 0 -weight 1
  223.     grid columnconfigure $itk_component(clipper) 0 -weight 1
  224.     
  225.     # 
  226.     # Configure the command on the vertical scroll bar in the base class.
  227.     #
  228.     $itk_component(vertsb) configure \
  229.     -command [code $itk_component(list) yview]
  230.  
  231.     #
  232.     # Configure the command on the horizontal scroll bar in the base class.
  233.     #
  234.     $itk_component(horizsb) configure \
  235.         -command [code $itk_component(list) xview]
  236.     
  237.     #
  238.     # Configure our text component's tab settings for twenty levels.
  239.     #
  240.     set tabs ""
  241.     for {set i 1} {$i < 20} {incr i} {
  242.     lappend tabs [expr $i*12+4]
  243.     }
  244.     $itk_component(list) configure -tabs $tabs
  245.  
  246.     #
  247.     # Add popup menus that can be configured by the user to add
  248.     # new functionality.
  249.     #
  250.     itk_component add itemMenu {
  251.     menu $itk_component(list).itemmenu -tearoff 0
  252.     } {
  253.     usual
  254.     ignore -tearoff
  255.     rename -cursor -menucursor menuCursor Cursor
  256.     }
  257.  
  258.     itk_component add bgMenu {
  259.     menu $itk_component(list).bgmenu -tearoff 0
  260.     } {
  261.     usual
  262.     ignore -tearoff
  263.     rename -cursor -menucursor menuCursor Cursor
  264.     }
  265.  
  266.     #
  267.     # Adjust the bind tags to remove the class bindings.  Also, add
  268.     # bindings for mouse button 1 to do selection and button 3 to 
  269.     # display a popup.
  270.     #
  271.     bindtags $itk_component(list) [list $itk_component(list) . all]
  272.     
  273.     bind $itk_component(list) <ButtonPress-1> \
  274.             [code $this _select %x %y]
  275.  
  276.     bind $itk_component(list) <ButtonPress-3> \
  277.             [code $this _post %x %y]
  278.     
  279.     #
  280.     # Initialize the widget based on the command line options.
  281.     #
  282.     eval itk_initialize $args
  283. }
  284.  
  285. # ------------------------------------------------------------------
  286. #                           DESTRUCTOR
  287. # ------------------------------------------------------------------
  288. body iwidgets::Hierarchy::destructor {} {
  289.     if {$_pending != ""} {
  290.     after cancel $_pending
  291.     }
  292. }
  293.  
  294. # ------------------------------------------------------------------
  295. #                             OPTIONS
  296. # ------------------------------------------------------------------
  297.  
  298. # ------------------------------------------------------------------
  299. # OPTION: -font
  300. #
  301. # Font used for text in the list.
  302. # ------------------------------------------------------------------
  303. configbody iwidgets::Hierarchy::font {
  304.     $itk_component(list) tag configure info \
  305.             -font $itk_option(-font) -spacing1 6
  306. }
  307.  
  308. # ------------------------------------------------------------------
  309. # OPTION: -selectbackground
  310. #
  311. # Background color scheme for selected nodes.
  312. # ------------------------------------------------------------------
  313. configbody iwidgets::Hierarchy::selectbackground {
  314.     $itk_component(list) tag configure hilite \
  315.             -background $itk_option(-selectbackground)
  316. }
  317.  
  318. # ------------------------------------------------------------------
  319. # OPTION: -selectforeground
  320. #
  321. # Foreground color scheme for selected nodes.
  322. # ------------------------------------------------------------------
  323. configbody iwidgets::Hierarchy::selectforeground {
  324.     $itk_component(list) tag configure hilite \
  325.             -foreground $itk_option(-selectforeground)
  326. }
  327.  
  328. # ------------------------------------------------------------------
  329. # OPTION: -markbackground
  330. #
  331. # Background color scheme for marked nodes.
  332. # ------------------------------------------------------------------
  333. configbody iwidgets::Hierarchy::markbackground {
  334.     $itk_component(list) tag configure lowlite \
  335.             -background $itk_option(-markbackground)
  336. }
  337.  
  338. # ------------------------------------------------------------------
  339. # OPTION: -markforeground
  340. #
  341. # Foreground color scheme for marked nodes.
  342. # ------------------------------------------------------------------
  343. configbody iwidgets::Hierarchy::markforeground {
  344.     $itk_component(list) tag configure lowlite \
  345.             -foreground $itk_option(-markforeground)
  346. }
  347.  
  348. # ------------------------------------------------------------------
  349. # OPTION: -querycommand
  350. #
  351. # Command executed to query the contents of each node.  If this 
  352. # command contains "%n", it is replaced with the name of the desired 
  353. # node.  In its simpilest form it should return the children of the 
  354. # given node as a list which will be depicted in the display.
  355. #
  356. # Since the names of the children are used as tags in the underlying 
  357. # text widget, each child must be unique in the hierarchy.  Due to
  358. # the unique requirement, the nodes shall be reffered to as uids 
  359. # or uid in the singular sense.
  360. #   {uid [uid ...]}
  361. #
  362. #   where uid is a unique id and primary key for the hierarchy entry
  363. #
  364. # Should the unique requirement pose a problem, the list returned
  365. # can take on another more extended form which enables the 
  366. # association of text to be displayed with the uids.  The uid must
  367. # still be unique, but the text does not have to obey the unique
  368. # rule.  In addition, the format also allows the specification of
  369. # additional tags to be used on the same entry in the hierarchy
  370. # as the uid and additional icons to be displayed just before
  371. # the node.  The tags and icons are considered to be the property of
  372. # the user in that the hierarchy widget will not depend on any of 
  373. # their values.
  374. #
  375. #   {{uid [text [tags [icons]]]} {uid [text [tags [icons]]]} ...}
  376. #
  377. #   where uid is a unique id and primary key for the hierarchy entry
  378. #         text is the text to be displayed for this uid
  379. #         tags is a list of user tags to be applied to the entry
  380. #         icons is a list of icons to be displayed in front of the text
  381. #
  382. # The hierarchy widget does a look ahead from each node to determine
  383. # if the node has a children.  This can be cost some performace with
  384. # large hierarchies.  User's can avoid this by providing a hint in
  385. # the user tags.  A tag of "leaf" or "branch" tells the hierarchy
  386. # widget the information it needs to know thereby avoiding the look
  387. # ahead operation.
  388. # ------------------------------------------------------------------
  389. configbody iwidgets::Hierarchy::querycommand {
  390.     clear
  391.     draw -eventually
  392. }
  393.  
  394. # ------------------------------------------------------------------
  395. # OPTION: -selectcommand
  396. #
  397. # Command executed to select an item in the list.  If this command
  398. # contains "%n", it is replaced with the name of the selected node.  
  399. # If it contains a "%s", it is replaced with a boolean indicator of 
  400. # the node's current selection status, where a value of 1 denotes
  401. # that the node is currently selected and 0 that it is not.
  402. # ------------------------------------------------------------------
  403. configbody iwidgets::Hierarchy::selectcommand {
  404. }
  405.  
  406. # ------------------------------------------------------------------
  407. # OPTION: -iconcommand
  408. #
  409. # Command executed upon selection of user icons.  If this command 
  410. # contains "%n", it is replaced with the name of the node the icon
  411. # belongs to.  Should it contain "%i" then the icon name is 
  412. # substituted.
  413. # ------------------------------------------------------------------
  414. configbody iwidgets::Hierarchy::iconcommand {
  415. }
  416.  
  417. # ------------------------------------------------------------------
  418. # OPTION: -alwaysquery
  419. #
  420. # Boolean flag which tells the hierarchy widget weather or not
  421. # each refresh of the display should be via a new query using
  422. # the -querycommand option or use the values previous found the
  423. # last time the query was made.
  424. # ------------------------------------------------------------------
  425. configbody iwidgets::Hierarchy::alwaysquery {
  426. }
  427.  
  428. # ------------------------------------------------------------------
  429. # OPTION: -filter
  430. #
  431. # When true only the branch nodes and selected items are displayed.
  432. # This gives a compact view of important items.
  433. # ------------------------------------------------------------------
  434. configbody iwidgets::Hierarchy::filter {
  435.     switch -- $itk_option(-filter) {
  436.     1 - true - yes - on {
  437.         set newCode {set display [info exists _selected($child)]}
  438.     }
  439.     0 - false - no - off {
  440.         set newCode {set display 1}
  441.     }
  442.     default {
  443.         error "bad filter option \"$itk_option(-filter)\":\
  444.                    should be boolean"
  445.     }
  446.     }
  447.     if {$newCode != $_filterCode} {
  448.     set _filterCode $newCode
  449.     draw -eventually
  450.     }
  451. }
  452.  
  453. # ------------------------------------------------------------------
  454. # OPTION: -expanded
  455. #
  456. # When true, the hierarchy will be completely expanded when it
  457. # is first displayed.  A fresh display can be triggered by
  458. # resetting the -querycommand option.
  459. # ------------------------------------------------------------------
  460. configbody iwidgets::Hierarchy::expanded {
  461.     switch -- $itk_option(-expanded) {
  462.     1 - true - yes - on {
  463.         ;# okay
  464.     }
  465.     0 - false - no - off {
  466.         ;# okay
  467.     }
  468.     default {
  469.         error "bad expanded option \"$itk_option(-expanded)\":\
  470.                    should be boolean"
  471.     }
  472.     }
  473. }
  474.     
  475. # ------------------------------------------------------------------
  476. # OPTION: -openicon
  477. #
  478. # Specifies the open icon image to be used in the hierarchy.  Should
  479. # one not be provided, then one will be generated, pixmap if 
  480. # possible, bitmap otherwise.
  481. # ------------------------------------------------------------------
  482. configbody iwidgets::Hierarchy::openicon {
  483.     if {$itk_option(-openicon) == {}} {
  484.     if {[lsearch [image names] openFolder] == -1} {
  485.         if {[lsearch [image types] pixmap] != -1} {
  486.         image create pixmap openFolder -data {
  487.             /* XPM */
  488.             static char * dir_opened [] = {
  489.             "16 16 4 1",
  490.             /* colors */
  491.             ". c grey85 m white g4 grey90",
  492.             "b c black  m black g4 black",
  493.             "y c yellow m white g4 grey80",
  494.             "g c grey70 m white g4 grey70",
  495.             /* pixels */
  496.             "................",
  497.             "................",
  498.             "................",
  499.             "..bbbb..........",
  500.             ".bggggb.........",
  501.             "bggggggbbbbbbb..",
  502.             "bggggggggggggb..",
  503.             "bgbbbbbbbbbbbbbb",
  504.             "bgbyyyyyyyyyyybb",
  505.             "bbyyyyyyyyyyyyb.",
  506.             "bbyyyyyyyyyyybb.",
  507.             "byyyyyyyyyyyyb..",
  508.             "bbbbbbbbbbbbbb..",
  509.             "................",
  510.             "................",
  511.             "................"};
  512.         }
  513.         } else {
  514.         image create bitmap openFolder -data {
  515.             #define open_width 16
  516.             #define open_height 16
  517.             static char open_bits[] = {
  518.             0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x42, 0x00, 
  519.             0x81, 0x3f, 0x01, 0x20, 0xf9, 0xff, 0x0d, 0xc0, 
  520.             0x07, 0x40, 0x03, 0x60, 0x01, 0x20, 0x01, 0x30,
  521.             0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  522.         }
  523.         }
  524.  
  525.         set itk_option(-openicon) openFolder
  526.     }
  527.  
  528.     } else {
  529.     if {[lsearch [image names] $itk_option(-openicon)] == -1} {
  530.         error "bad openicon option \"$itk_option(-openicon)\":\
  531.                    should be an existing image"
  532.     }
  533.     }
  534. }
  535.  
  536. # ------------------------------------------------------------------
  537. # OPTION: -closedicon
  538. #
  539. # Specifies the closed icon image to be used in the hierarchy.  
  540. # Should one not be provided, then one will be generated, pixmap if 
  541. # possible, bitmap otherwise.
  542. # ------------------------------------------------------------------
  543. configbody iwidgets::Hierarchy::closedicon {
  544.     if {$itk_option(-closedicon) == {}} {
  545.     if {[lsearch [image names] closedFolder] == -1} {
  546.         if {[lsearch [image types] pixmap] != -1} {
  547.         image create pixmap closedFolder -data {
  548.             /* XPM */
  549.             static char *dir_closed[] = {
  550.             "16 16 3 1",
  551.             ". c grey85 m white g4 grey90",
  552.             "b c black  m black g4 black",
  553.             "y c yellow m white g4 grey80",
  554.             "................",
  555.             "................",
  556.             "................",
  557.             "..bbbb..........",
  558.             ".byyyyb.........",
  559.             "bbbbbbbbbbbbbb..",
  560.             "byyyyyyyyyyyyb..",
  561.             "byyyyyyyyyyyyb..",
  562.             "byyyyyyyyyyyyb..",
  563.             "byyyyyyyyyyyyb..",
  564.             "byyyyyyyyyyyyb..",
  565.             "byyyyyyyyyyyyb..",
  566.             "bbbbbbbbbbbbbb..",
  567.             "................",
  568.             "................",
  569.             "................"};    
  570.         }
  571.         } else {
  572.         image create bitmap closedFolder -data {
  573.             #define closed_width 16
  574.             #define closed_height 16
  575.             static char closed_bits[] = {
  576.             0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x84, 0x00, 
  577.             0xfe, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 
  578.             0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
  579.             0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  580.         }
  581.         }
  582.  
  583.         set itk_option(-closedicon) closedFolder
  584.     }
  585.  
  586.     } else {
  587.     if {[lsearch [image names] $itk_option(-closedicon)] == -1} {
  588.         error "bad closedicon option \"$itk_option(-closedicon)\":\
  589.                    should be an existing image"
  590.     }
  591.     }
  592. }
  593.  
  594. # ------------------------------------------------------------------
  595. # OPTION: -nodeicon
  596. #
  597. # Specifies the node icon image to be used in the hierarchy.  Should 
  598. # one not be provided, then one will be generated, pixmap if 
  599. # possible, bitmap otherwise.
  600. # ------------------------------------------------------------------
  601. configbody iwidgets::Hierarchy::nodeicon {
  602.     if {$itk_option(-nodeicon) == {}} {
  603.     if {[lsearch [image names] nodeFolder] == -1} {
  604.         if {[lsearch [image types] pixmap] != -1} {
  605.         image create pixmap nodeFolder -data {
  606.             /* XPM */
  607.             static char *dir_node[] = {
  608.             "16 16 3 1",
  609.             ". c grey85 m white g4 grey90",
  610.             "b c black  m black g4 black",
  611.             "y c yellow m white g4 grey80",
  612.             "................",
  613.             "................",
  614.             "................",
  615.             "...bbbbbbbbbbb..",
  616.             "..bybyyyyyyyyb..",
  617.             ".byybyyyyyyyyb..",
  618.             "byyybyyyyyyyyb..",
  619.             "bbbbbyyyyyyyyb..",
  620.             "byyyyyyyyyyyyb..",
  621.             "byyyyyyyyyyyyb..",
  622.             "byyyyyyyyyyyyb..",
  623.             "byyyyyyyyyyyyb..",
  624.             "bbbbbbbbbbbbbb..",
  625.             "................",
  626.             "................",
  627.             "................"};    
  628.         }
  629.         } else {
  630.         image create bitmap nodeFolder -data {
  631.             #define node_width 16
  632.             #define node_height 16
  633.             static char node_bits[] = {
  634.             0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x50, 0x40, 
  635.             0x48, 0x40, 0x44, 0x40, 0x42, 0x40, 0x7e, 0x40, 
  636.             0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
  637.             0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  638.         }
  639.         }
  640.  
  641.         set itk_option(-nodeicon) nodeFolder
  642.     }
  643.  
  644.     } else {
  645.     if {[lsearch [image names] $itk_option(-nodeicon)] == -1} {
  646.         error "bad nodeicon option \"$itk_option(-nodeicon)\":\
  647.                    should be an existing image"
  648.     }
  649.     }
  650. }
  651.  
  652. # ------------------------------------------------------------------
  653. # OPTION: -width
  654. #
  655. # Specifies the width of the hierarchy widget as an entire unit.
  656. # The value may be specified in any of the forms acceptable to 
  657. # Tk_GetPixels.  Any additional space needed to display the other
  658. # components such as labels, margins, and scrollbars force the text
  659. # to be compressed.  A value of zero along with the same value for 
  660. # the height causes the value given for the visibleitems option 
  661. # to be applied which administers geometry constraints in a different
  662. # manner.
  663. # ------------------------------------------------------------------
  664. configbody iwidgets::Hierarchy::width {
  665.     if {$itk_option(-width) != 0} {
  666.     set shell [lindex [grid info $itk_component(clipper)] 1]
  667.  
  668.     #
  669.     # Due to a bug in the tk4.2 grid, we have to check the 
  670.     # propagation before setting it.  Setting it to the same
  671.     # value it already is will cause it to toggle.
  672.     #
  673.     if {[grid propagate $shell]} {
  674.         grid propagate $shell no
  675.     }
  676.     
  677.     $itk_component(list) configure -width 1
  678.     $shell configure \
  679.         -width [winfo pixels $shell $itk_option(-width)] 
  680.     } else {
  681.     configure -visibleitems $itk_option(-visibleitems)
  682.     }
  683. }
  684.  
  685. # ------------------------------------------------------------------
  686. # OPTION: -height
  687. #
  688. # Specifies the height of the hierarchy widget as an entire unit.
  689. # The value may be specified in any of the forms acceptable to 
  690. # Tk_GetPixels.  Any additional space needed to display the other
  691. # components such as labels, margins, and scrollbars force the text
  692. # to be compressed.  A value of zero along with the same value for 
  693. # the width causes the value given for the visibleitems option 
  694. # to be applied which administers geometry constraints in a different
  695. # manner.
  696. # ------------------------------------------------------------------
  697. configbody iwidgets::Hierarchy::height {
  698.     if {$itk_option(-height) != 0} {
  699.     set shell [lindex [grid info $itk_component(clipper)] 1]
  700.  
  701.     #
  702.     # Due to a bug in the tk4.2 grid, we have to check the 
  703.     # propagation before setting it.  Setting it to the same
  704.     # value it already is will cause it to toggle.
  705.     #
  706.     if {[grid propagate $shell]} {
  707.         grid propagate $shell no
  708.     }
  709.     
  710.     $itk_component(list) configure -height 1
  711.     $shell configure \
  712.         -height [winfo pixels $shell $itk_option(-height)] 
  713.     } else {
  714.     configure -visibleitems $itk_option(-visibleitems)
  715.     }
  716. }
  717.  
  718. # ------------------------------------------------------------------
  719. # OPTION: -visibleitems
  720. #
  721. # Specified the widthxheight in characters and lines for the text.
  722. # This option is only administered if the width and height options
  723. # are both set to zero, otherwise they take precedence.  With the
  724. # visibleitems option engaged, geometry constraints are maintained
  725. # only on the text.  The size of the other components such as 
  726. # labels, margins, and scroll bars, are additive and independent, 
  727. # effecting the overall size of the scrolled text.  In contrast,
  728. # should the width and height options have non zero values, they
  729. # are applied to the scrolled text as a whole.  The text is 
  730. # compressed or expanded to maintain the geometry constraints.
  731. # ------------------------------------------------------------------
  732. configbody iwidgets::Hierarchy::visibleitems {
  733.     if {[regexp {^[0-9]+x[0-9]+$} $itk_option(-visibleitems)]} {
  734.     if {($itk_option(-width) == 0) && \
  735.         ($itk_option(-height) == 0)} {
  736.         set chars [lindex [split $itk_option(-visibleitems) x] 0]
  737.         set lines [lindex [split $itk_option(-visibleitems) x] 1]
  738.         
  739.         set shell [lindex [grid info $itk_component(clipper)] 1]
  740.  
  741.         #
  742.         # Due to a bug in the tk4.2 grid, we have to check the 
  743.         # propagation before setting it.  Setting it to the same
  744.         # value it already is will cause it to toggle.
  745.         #
  746.         if {! [grid propagate $shell]} {
  747.         grid propagate $shell yes
  748.         }
  749.         
  750.         $itk_component(list) configure -width $chars -height $lines
  751.     }
  752.     
  753.     } else {
  754.     error "bad visibleitems option\
  755.         \"$itk_option(-visibleitems)\": should be\
  756.         widthxheight"
  757.     }
  758. }
  759.  
  760. # ------------------------------------------------------------------
  761. #                         PUBLIC METHODS
  762. # ------------------------------------------------------------------
  763.  
  764. # ----------------------------------------------------------------------
  765. # PUBLIC METHOD: clear
  766. #
  767. # Removes all items from the display including all tags and icons.  
  768. # The display will remain empty until the -filter or -querycommand 
  769. # options are set.
  770. # ----------------------------------------------------------------------
  771. body iwidgets::Hierarchy::clear {} {
  772.     $itk_component(list) configure -state normal -cursor watch
  773.     $itk_component(list) delete 1.0 end
  774.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  775.     
  776.     catch {unset _nodes}
  777.     catch {unset _text}
  778.     catch {unset _tags}
  779.     catch {unset _icons}
  780.     catch {unset _states}
  781.     catch {unset _images}
  782.     catch {unset _indents}
  783.  
  784.     return
  785. }
  786.  
  787. # ----------------------------------------------------------------------
  788. # PUBLIC METHOD: selection option ?uid uid...?
  789. #
  790. # Handles all operations controlling selections in the hierarchy.
  791. # Selections may be cleared, added, removed, or queried.  The add and
  792. # remove options accept a series of unique ids.
  793. # ----------------------------------------------------------------------
  794. body iwidgets::Hierarchy::selection {op args} {
  795.     switch -- $op {
  796.         clear {
  797.             $itk_component(list) tag remove hilite 1.0 end
  798.             catch {unset _selected}
  799.         return
  800.         }
  801.         add {
  802.             foreach node $args {
  803.                 set _selected($node) 1
  804.                 catch {
  805.                     $itk_component(list) tag add hilite \
  806.                 "$node.first" "$node.last"
  807.                 }
  808.             }
  809.         }
  810.         remove {
  811.             foreach node $args {
  812.                 catch {
  813.                     unset _selected($node)
  814.                     $itk_component(list) tag remove hilite \
  815.                 "$node.first" "$node.last"
  816.                 }
  817.             }
  818.         }
  819.     get {
  820.         return [array names _selected]
  821.     }
  822.         default {
  823.             error "bad selection operation \"$op\":\
  824.                    should be add, remove, clear or get"
  825.         }
  826.     }
  827. }
  828.  
  829. # ----------------------------------------------------------------------
  830. # PUBLIC METHOD: mark option ?arg arg...?
  831. #
  832. # Handles all operations controlling marks in the hierarchy.  Marks may 
  833. # be cleared, added, removed, or queried.  The add and remove options 
  834. # accept a series of unique ids.
  835. # ----------------------------------------------------------------------
  836. body iwidgets::Hierarchy::mark {op args} {
  837.     switch -- $op {
  838.         clear {
  839.             $itk_component(list) tag remove lowlite 1.0 end
  840.             catch {unset _marked}
  841.         return
  842.         }
  843.         add {
  844.             foreach node $args {
  845.                 set _marked($node) 1
  846.                 catch {
  847.                     $itk_component(list) tag add lowlite \
  848.                 "$node.first" "$node.last"
  849.                 }
  850.             }
  851.         }
  852.         remove {
  853.             foreach node $args {
  854.                 catch {
  855.                     unset _marked($node)
  856.                     $itk_component(list) tag remove lowlite \
  857.                 "$node.first" "$node.last"
  858.                 }
  859.             }
  860.         }
  861.     get {
  862.         return [array names _marked]
  863.     }
  864.         default {
  865.             error "bad mark operation \"$op\":\
  866.                    should be add, remove, clear or get"
  867.         }
  868.     }
  869. }
  870.  
  871. # ----------------------------------------------------------------------
  872. # PUBLIC METHOD: current
  873. #
  874. # Returns the node that was most recently selected by the right mouse
  875. # button when the item menu was posted.  Usually used by the code
  876. # in the item menu to figure out what item is being manipulated.
  877. # ----------------------------------------------------------------------
  878. body iwidgets::Hierarchy::current {} {
  879.     return $_posted
  880. }
  881.  
  882. # ----------------------------------------------------------------------
  883. # PUBLIC METHOD: expand node
  884. #
  885. # Expands the hierarchy beneath the specified node.  Since this can take
  886. # a moment for large hierarchies, the cursor will be changed to a watch
  887. # during the expansion.
  888. # ----------------------------------------------------------------------
  889. body iwidgets::Hierarchy::expand {node} {
  890.     if {! [info exists _states($node)]} {
  891.     error "bad expand node argument: \"$node\", the node doesn't exist"
  892.     }
  893.  
  894.     if {!$_states($node) && \
  895.         (([lsearch $_tags($node) branch] != -1) || \
  896.          ([llength [_contents $node]] > 0))} {
  897.         $itk_component(list) configure -state normal -cursor watch
  898.         update
  899.  
  900.     #
  901.     # Get the indentation level for the node.
  902.     #
  903.         set indent $_indents($node)
  904.  
  905.         set _markers ""
  906.         $itk_component(list) mark set insert "$node:start"
  907.         _drawLevel $node $indent
  908.  
  909.     #
  910.     # Following the draw, all our markers need adjusting.
  911.     #
  912.         foreach {name index} $_markers {
  913.             $itk_component(list) mark set $name $index
  914.         }
  915.  
  916.     #
  917.     # Set the image to be the open icon, denote the new state,
  918.     # and set the cursor back to normal along with the state.
  919.     #
  920.     $_images($node) configure -image $itk_option(-openicon)
  921.  
  922.         set _states($node) 1
  923.  
  924.         $itk_component(list) configure -state disabled \
  925.         -cursor $itk_option(-cursor)
  926.     }
  927. }
  928.  
  929. # ----------------------------------------------------------------------
  930. # PUBLIC METHOD: collapse node
  931. #
  932. # Collapses the hierarchy beneath the specified node.  Since this can 
  933. # take a moment for large hierarchies, the cursor will be changed to a 
  934. # watch during the expansion.
  935. # ----------------------------------------------------------------------
  936. body iwidgets::Hierarchy::collapse {node} {
  937.     if {! [info exists _states($node)]} {
  938.     error "bad collapse node argument: \"$node\", the node doesn't exist"
  939.     }
  940.  
  941.     if {[info exists _states($node)] && $_states($node) && \
  942.         (([lsearch $_tags($node) branch] != -1) || \
  943.          ([llength [_contents $node]] > 0))} {
  944.         $itk_component(list) configure -state normal -cursor watch
  945.     update
  946.  
  947.     _deselectSubNodes $node
  948.  
  949.         $itk_component(list) delete "$node:start" "$node:end"
  950.  
  951.     catch {$_images($node) configure -image $itk_option(-closedicon)}
  952.  
  953.         set _states($node) 0
  954.  
  955.         $itk_component(list) configure -state disabled \
  956.         -cursor $itk_option(-cursor)
  957.     }
  958. }
  959.  
  960. # ----------------------------------------------------------------------
  961. # PUBLIC METHOD: toggle node
  962. #
  963. # Toggles the hierarchy beneath the specified node.  If the hierarchy
  964. # is currently expanded, then it is collapsed, and vice-versa.
  965. # ----------------------------------------------------------------------
  966. body iwidgets::Hierarchy::toggle {node} {
  967.     if {! [info exists _states($node)]} {
  968.     error "bad toggle node argument: \"$node\", the node doesn't exist"
  969.     }
  970.  
  971.     if {$_states($node)} {
  972.         collapse $node
  973.     } else {
  974.         expand $node
  975.     }
  976. }
  977.  
  978. # ----------------------------------------------------------------------
  979. # PUBLIC METHOD: prune node
  980. #
  981. # Removes a particular node from the hierarchy.
  982. # ----------------------------------------------------------------------
  983. body iwidgets::Hierarchy::prune {node} {
  984.     #
  985.     # While we're working, change the state and cursor so we can
  986.     # edit the text and give a busy visual clue.
  987.     #
  988.     $itk_component(list) configure -state normal -cursor watch
  989.  
  990.     #
  991.     # Recursively delete all the subnode information from our internal
  992.     # arrays and remove all the tags.  
  993.     #
  994.     _deleteNodeInfo $node
  995.  
  996.     #
  997.     # If the mark $node:end exists then the node has decendents so
  998.     # so we'll remove from the mark $node:start to $node:end in order 
  999.     # to delete all the subnodes below it in the text.  
  1000.     # 
  1001.     if {[lsearch [$itk_component(list) mark names] $node:end] != -1} {
  1002.     $itk_component(list) delete $node:start $node:end
  1003.     $itk_component(list) mark unset $node:end
  1004.     } 
  1005.  
  1006.     #
  1007.     # Next we need to remove the node itself.  Using the ranges for
  1008.     # its tag we'll remove it from line start to the end plus one
  1009.     # character which takes us to the start of the next node.
  1010.     #
  1011.     foreach {start end} [$itk_component(list) tag ranges $node] {
  1012.     $itk_component(list) delete "$start linestart" "$end + 1 char"
  1013.     }
  1014.  
  1015.     #
  1016.     # Delete the tag for this node.
  1017.     #
  1018.     $itk_component(list) tag delete $node
  1019.  
  1020.     #
  1021.     # The node must be removed from the list of subnodes for its parent.
  1022.     # We don't really have a clean way to do upwards referencing, so
  1023.     # the dirty way will have to do.  We'll cycle through each node
  1024.     # and if this node is in its list of subnodes, we'll remove it.
  1025.     #
  1026.     foreach uid [array names _nodes] {
  1027.     if {[set index [lsearch $_nodes($uid) $node]] != -1} {
  1028.         set _nodes($uid) [lreplace $_nodes($uid) $index $index]
  1029.     }
  1030.     }
  1031.  
  1032.     #
  1033.     # We're done, so change the state and cursor back to their 
  1034.     # original values.
  1035.     #
  1036.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  1037. }
  1038.  
  1039. # ----------------------------------------------------------------------
  1040. # PUBLIC METHOD: draw ?when?
  1041. #
  1042. # Performs a complete draw of the entire hierarchy.
  1043. # ----------------------------------------------------------------------
  1044. body iwidgets::Hierarchy::draw {{when -now}} {
  1045.     if {$when == "-eventually"} {
  1046.         if {$_pending == ""} {
  1047.             set _pending [after idle [code $this draw -now]]
  1048.         }
  1049.         return
  1050.     } elseif {$when != "-now"} {
  1051.         error "bad when option \"$when\": should be -eventually or -now"
  1052.     }
  1053.     $itk_component(list) configure -state normal -cursor watch
  1054.     update
  1055.  
  1056.     $itk_component(list) delete 1.0 end
  1057.     catch {unset _images}
  1058.     set _markers ""
  1059.  
  1060.     _drawLevel "" ""
  1061.  
  1062.     foreach {name index} $_markers {
  1063.         $itk_component(list) mark set $name $index
  1064.     }
  1065.  
  1066.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  1067.     set _pending ""
  1068. }
  1069.  
  1070. # ----------------------------------------------------------------------
  1071. # PUBLIC METHOD: refresh node
  1072. #
  1073. # Performs a redraw of a specific node.  If that node is currently 
  1074. # not visible, then no action is taken.
  1075. # ----------------------------------------------------------------------
  1076. body iwidgets::Hierarchy::refresh {node} {
  1077.     if {! [info exists _nodes($node)]} {
  1078.     error "bad refresh node argument: \"$node\", the node doesn't exist"
  1079.     }
  1080.  
  1081.     
  1082.     if {! $_states($node)} {return}
  1083.  
  1084.     foreach parent [_getHeritage $node] {
  1085.     if {! $_states($parent)} {return}
  1086.     }
  1087.  
  1088.     $itk_component(list) configure -state normal -cursor watch
  1089.     $itk_component(list) delete $node:start $node:end
  1090.  
  1091.     set _markers ""
  1092.     $itk_component(list) mark set insert "$node:start"
  1093.     set indent $_indents($node)
  1094.  
  1095.     _drawLevel $node $indent
  1096.  
  1097.     foreach {name index} $_markers {
  1098.         $itk_component(list) mark set $name $index
  1099.     }
  1100.  
  1101.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  1102. }
  1103.  
  1104. # ------------------------------------------------------------------
  1105. # THIN WRAPPED TEXT METHODS:
  1106. #
  1107. # The following methods are thin wraps of standard text methods.
  1108. # Consult the Tk text man pages for functionallity and argument
  1109. # documentation.
  1110. # ------------------------------------------------------------------
  1111.  
  1112. # ------------------------------------------------------------------
  1113. # PUBLIC METHOD: bbox index
  1114. #
  1115. # Returns four element list describing the bounding box for the list
  1116. # item at index
  1117. # ------------------------------------------------------------------
  1118. body iwidgets::Hierarchy::bbox {index} {
  1119.     return [$itk_component(list) bbox $index]
  1120. }
  1121.  
  1122. # ------------------------------------------------------------------
  1123. # PUBLIC METHOD compare index1 op index2
  1124. #
  1125. # Compare indices according to relational operator.
  1126. # ------------------------------------------------------------------
  1127. body iwidgets::Hierarchy::compare {index1 op index2} {
  1128.     return [$itk_component(list) compare $index1 $op $index2]
  1129. }
  1130.  
  1131. # ------------------------------------------------------------------
  1132. # PUBLIC METHOD delete first ?last?
  1133. #
  1134. # Delete a range of characters from the text.
  1135. # ------------------------------------------------------------------
  1136. body iwidgets::Hierarchy::delete {first {last {}}} {
  1137.     $itk_component(list) configure -state normal -cursor watch
  1138.     $itk_component(list) delete $first $last
  1139.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  1140. }
  1141.  
  1142. # ------------------------------------------------------------------
  1143. # PUBLIC METHOD dump ?switches? index1 ?index2?
  1144. #
  1145. # Returns information about the contents of the text widget from 
  1146. # index1 to index2.
  1147. # ------------------------------------------------------------------
  1148. body iwidgets::Hierarchy::dump {args} {
  1149.     return [eval $itk_component(list) dump $args]
  1150. }
  1151.  
  1152. # ------------------------------------------------------------------
  1153. # PUBLIC METHOD dlineinfo index
  1154. #
  1155. # Returns a five element list describing the area occupied by the
  1156. # display line containing index.
  1157. # ------------------------------------------------------------------
  1158. body iwidgets::Hierarchy::dlineinfo {index} {
  1159.     return [$itk_component(list) dlineinfo $index]
  1160. }
  1161.  
  1162. # ------------------------------------------------------------------
  1163. # PUBLIC METHOD get index1 ?index2?
  1164. #
  1165. # Return text from start index to end index.
  1166. # ------------------------------------------------------------------
  1167. body iwidgets::Hierarchy::get {index1 {index2 {}}} {
  1168.     return [$itk_component(list) get $index1 $index2]
  1169. }
  1170.  
  1171. # ------------------------------------------------------------------
  1172. # PUBLIC METHOD index index
  1173. #
  1174. # Return position corresponding to index.
  1175. # ------------------------------------------------------------------
  1176. body iwidgets::Hierarchy::index {index} {
  1177.     return [$itk_component(list) index $index]
  1178. }
  1179.  
  1180. # ------------------------------------------------------------------
  1181. # PUBLIC METHOD insert index chars ?tagList?
  1182. #
  1183. # Insert text at index.
  1184. # ------------------------------------------------------------------
  1185. body iwidgets::Hierarchy::insert {args} {
  1186.     $itk_component(list) configure -state normal -cursor watch
  1187.     eval $itk_component(list) insert $args
  1188.     $itk_component(list) configure -state disabled -cursor $itk_option(-cursor)
  1189. }
  1190.  
  1191. # ------------------------------------------------------------------
  1192. # PUBLIC METHOD scan option args
  1193. #
  1194. # Implements scanning on texts.
  1195. # ------------------------------------------------------------------
  1196. body iwidgets::Hierarchy::scan {option args} {
  1197.     eval $itk_component(list) scan $option $args
  1198. }
  1199.  
  1200. # ------------------------------------------------------------------
  1201. # PUBLIC METHOD search ?switches? pattern index ?varName?
  1202. #
  1203. # Searches the text for characters matching a pattern.
  1204. # ------------------------------------------------------------------
  1205. body iwidgets::Hierarchy::search {args} {
  1206.     return [eval $itk_component(list) search $args]
  1207. }
  1208.  
  1209. # ------------------------------------------------------------------
  1210. # PUBLIC METHOD see index
  1211. #
  1212. # Adjusts the view in the window so the character at index is 
  1213. # visible.
  1214. # ------------------------------------------------------------------
  1215. body iwidgets::Hierarchy::see {index} {
  1216.     $itk_component(list) see $index
  1217. }
  1218.  
  1219. # ------------------------------------------------------------------
  1220. # PUBLIC METHOD tag option ?arg arg ...?
  1221. #
  1222. # Manipulate tags dependent on options.
  1223. # ------------------------------------------------------------------
  1224. body iwidgets::Hierarchy::tag {op args} {
  1225.     return [eval $itk_component(list) tag $op $args]
  1226. }
  1227.  
  1228. # ------------------------------------------------------------------
  1229. # PUBLIC METHOD window option ?arg arg ...?
  1230. #
  1231. # Manipulate embedded windows.
  1232. # ------------------------------------------------------------------
  1233. body iwidgets::Hierarchy::window {option args} {
  1234.     return [eval $itk_component(list) window $option $args]
  1235. }
  1236.  
  1237. # ----------------------------------------------------------------------
  1238. # PUBLIC METHOD: xview args
  1239. #
  1240. # Thin wrap of the text widget's xview command.
  1241. # ----------------------------------------------------------------------
  1242. body iwidgets::Hierarchy::xview {args} {
  1243.     return [eval itk_component(list) xview $args]
  1244. }
  1245.  
  1246. # ----------------------------------------------------------------------
  1247. # PUBLIC METHOD: yview args
  1248. #
  1249. # Thin wrap of the text widget's yview command.
  1250. # ----------------------------------------------------------------------
  1251. body iwidgets::Hierarchy::yview {args} {
  1252.     return [eval $itk_component(list) yview $args]
  1253. }
  1254.  
  1255. # ------------------------------------------------------------------
  1256. #                       PROTECTED METHODS
  1257. # ------------------------------------------------------------------
  1258.  
  1259. # ----------------------------------------------------------------------
  1260. # PROTECTED METHOD: _drawLevel node indent
  1261. #
  1262. # Used internally by draw to draw one level of the hierarchy.
  1263. # Draws all of the nodes under node, using the indent string to
  1264. # indent nodes.
  1265. # ----------------------------------------------------------------------
  1266. body iwidgets::Hierarchy::_drawLevel {node indent} {
  1267.     lappend _markers "$node:start" [$itk_component(list) index insert]
  1268.     set bg [$itk_component(list) cget -background]
  1269.  
  1270.     #
  1271.     # Obtain the list of subnodes for this node and cycle through
  1272.     # each one displaying it in the hierarchy.
  1273.     #
  1274.     foreach child [_contents $node] {
  1275.     set _images($child) "$itk_component(list).hicon[incr _hcounter]"
  1276.  
  1277.         if {![info exists _states($child)]} {
  1278.             set _states($child) $itk_option(-expanded)
  1279.         }
  1280.  
  1281.     #
  1282.     # Check the user tags to see if they have been kind enough
  1283.     # to tell us ahead of time what type of node we are dealing
  1284.     # with branch or leaf.  If they neglected to do so, then
  1285.     # get the contents of the child node to see if it has children
  1286.     # itself.
  1287.     #
  1288.     set display 0
  1289.  
  1290.     if {[lsearch $_tags($child) leaf] != -1} {
  1291.         set type leaf
  1292.     } elseif {[lsearch $_tags($child) branch] != -1} {
  1293.         set type branch
  1294.     } else {
  1295.         if {[llength [_contents $child]] == 0} {
  1296.         set type leaf
  1297.         } else {
  1298.         set type branch
  1299.         }
  1300.     }
  1301.  
  1302.     #
  1303.     # Now that we know the type of node, branch or leaf, we know
  1304.     # the type of icon to use.
  1305.     #
  1306.     if {$type == "leaf"} {
  1307.             set icon $itk_option(-nodeicon)
  1308.             eval $_filterCode
  1309.     } else {
  1310.             if {$_states($child)} {
  1311.                 set icon $itk_option(-openicon)
  1312.             } else {
  1313.                 set icon $itk_option(-closedicon)
  1314.             }
  1315.             set display 1
  1316.     }
  1317.  
  1318.     #
  1319.     # If display is set then we're going to be drawing this node.
  1320.     # Save off the indentation level for this node and do the indent.
  1321.     #
  1322.     if {$display} {
  1323.         set _indents($child) "$indent\t"
  1324.         $itk_component(list) insert insert $indent
  1325.  
  1326.         #
  1327.         # Add the branch or leaf icon and setup a binding to toggle
  1328.         # its expanded/collapsed state.
  1329.         #
  1330.         label $_images($child) -image $icon -background $bg 
  1331.         bind $_images($child) <ButtonPress-1> [code $this toggle $child]
  1332.         $itk_component(list) window create insert -window $_images($child)
  1333.  
  1334.         #
  1335.         # If any user icons exist then draw them as well.  The little
  1336.         # regexp is just to check and see if they've passed in a
  1337.         # command which needs to be evaluated as opposed to just
  1338.         # a variable.  Also, attach a binding to call them if their
  1339.         # icon is selected.
  1340.         #
  1341.         if {[info exists _icons($child)]} {
  1342.         foreach image $_icons($child) {
  1343.             set wid "$itk_component(list).uicon[incr _ucounter]"
  1344.  
  1345.             if {[regexp {\[.*\]} $image]} {
  1346.             eval label $wid -image $image -background $bg 
  1347.             } else {
  1348.             label $wid -image $image -background $bg 
  1349.             }
  1350.  
  1351.             bind $wid <ButtonPress-1> \
  1352.             [code $this _iconSelect $child $image]
  1353.             $itk_component(list) window create insert -window $wid
  1354.         }
  1355.         }
  1356.  
  1357.         #
  1358.         # Create the list of tags to be applied to the text.  Start
  1359.         # out with a tag of "info" and append "hilite" if the node
  1360.         # is currently selected, finally add the tags given by the
  1361.         # user.
  1362.         #
  1363.         set texttags [list "info" $child]
  1364.  
  1365.         if {[info exists _selected($child)]} {
  1366.         lappend texttags hilite
  1367.         } 
  1368.  
  1369.         foreach tag $_tags($child) {
  1370.         lappend texttags $tag
  1371.         }
  1372.  
  1373.         #
  1374.         # Insert the text for the node along with the tags and 
  1375.         # append to the markers the start of this node.  The text
  1376.         # has been broken at newlines into a list.  We'll make sure
  1377.         # that each line is at the same indentation position.
  1378.         #
  1379.         set firstline 1
  1380.         foreach line $_text($child) {
  1381.         if {$firstline} {
  1382.             $itk_component(list) insert insert " "
  1383.         } else {
  1384.             $itk_component(list) insert insert "$indent\t"
  1385.         }
  1386.  
  1387.         $itk_component(list) insert insert $line $texttags "\n"
  1388.         set firstline 0
  1389.         }
  1390.  
  1391.         lappend _markers "$child:start" [$itk_component(list) index insert]
  1392.  
  1393.         #
  1394.         # If the state of the node is open, proceed to draw the next 
  1395.         # node below it in the hierarchy.
  1396.         #
  1397.         if {$_states($child)} {
  1398.         _drawLevel $child "$indent\t"
  1399.         }
  1400.     }
  1401.     }
  1402.  
  1403.     lappend _markers "$node:end" [$itk_component(list) index insert]
  1404. }
  1405.  
  1406. # ----------------------------------------------------------------------
  1407. # PROTECTED METHOD: _contents uid
  1408. #
  1409. # Used internally to get the contents of a particular node.  If this
  1410. # is the first time the node has been seen or the -alwaysquery
  1411. # option is set, the -querycommand code is executed to query the node 
  1412. # list, and the list is stored until the next time it is needed.
  1413. #
  1414. # The querycommand may return not only the list of subnodes for the 
  1415. # node but additional information on the tags and icons to be used.  
  1416. # The return value must be parsed based on the number of elements in 
  1417. # the list where the format is a list of lists:
  1418. #
  1419. # {{uid [text [tags [icons]]]} {uid [text [tags [icons]]]} ...}
  1420. # ----------------------------------------------------------------------
  1421. body iwidgets::Hierarchy::_contents {uid} {
  1422.     if {! $itk_option(-alwaysquery) && [info exists _nodes($uid)]} {
  1423.         return $_nodes($uid)
  1424.     }
  1425.  
  1426.     # 
  1427.     # Substitute any %n's for the node name whose children we're
  1428.     # interested in obtaining.
  1429.     #
  1430.     set cmd $itk_option(-querycommand)
  1431.     regsub -all {%n} $cmd [list $uid] cmd
  1432.  
  1433.     set nodeinfolist [uplevel \#0 $cmd]
  1434.  
  1435.     #
  1436.     # Cycle through the node information returned by the query
  1437.     # command determining if additional information such as text,
  1438.     # user tags, or user icons have been provided.  For text,
  1439.     # break it into a list at any newline characters.
  1440.     #
  1441.     set _nodes($uid) {}
  1442.  
  1443.     foreach nodeinfo $nodeinfolist {
  1444.     set subnodeuid [lindex $nodeinfo 0]
  1445.     lappend _nodes($uid) $subnodeuid
  1446.  
  1447.     set llen [llength $nodeinfo] 
  1448.  
  1449.     if {$llen == 0 || $llen > 4} {
  1450.         error "invalid number of elements returned by query\
  1451.                        command for node: \"$uid\",\
  1452.                        should be uid \[text \[tags \[icons\]\]\]"
  1453.     }
  1454.  
  1455.     if {$llen == 1} {
  1456.         set _text($subnodeuid) [split $subnodeuid \n]
  1457.     } 
  1458.     if {$llen > 1} {
  1459.         set _text($subnodeuid) [split [lindex $nodeinfo 1] \n]
  1460.     }
  1461.     if {$llen > 2} {
  1462.         set _tags($subnodeuid) [lindex $nodeinfo 2]
  1463.     } else {
  1464.         set _tags($subnodeuid) unknown
  1465.     }
  1466.     if {$llen > 3} {
  1467.         set _icons($subnodeuid) [lindex $nodeinfo 3]
  1468.     }
  1469.     }
  1470.           
  1471.     #
  1472.     # Return the list of nodes.
  1473.     #
  1474.     return $_nodes($uid)
  1475. }
  1476.  
  1477. # ----------------------------------------------------------------------
  1478. # PROTECTED METHOD: _post x y
  1479. #
  1480. # Used internally to post the popup menu at the coordinate (x,y)
  1481. # relative to the widget.  If (x,y) is on an item, then the itemMenu
  1482. # component is posted.  Otherwise, the bgMenu is posted.
  1483. # ----------------------------------------------------------------------
  1484. body iwidgets::Hierarchy::_post {x y} {
  1485.     set rx [expr [winfo rootx $itk_component(list)]+$x]
  1486.     set ry [expr [winfo rooty $itk_component(list)]+$y]
  1487.  
  1488.     set index [$itk_component(list) index @$x,$y]
  1489.  
  1490.     #
  1491.     # The posted variable will hold the list of tags which exist at
  1492.     # this x,y position that will be passed back to the user.  They
  1493.     # don't need to know about our internal tags, info, hilite, and
  1494.     # lowlite, so remove them from the list.
  1495.     # 
  1496.     set _posted {}
  1497.  
  1498.     foreach tag [$itk_component(list) tag names $index] {
  1499.         if {![_isInternalTag $tag]} {
  1500.             lappend _posted $tag
  1501.         }
  1502.     }
  1503.  
  1504.     #
  1505.     # If we have tags then do the popup at this position.
  1506.     #
  1507.     if {$_posted != {}} {
  1508.     tk_popup $itk_component(itemMenu) $rx $ry
  1509.     } else {
  1510.     tk_popup $itk_component(bgMenu) $rx $ry
  1511.     }
  1512. }
  1513.  
  1514. # ----------------------------------------------------------------------
  1515. # PROTECTED METHOD: _select x y
  1516. #
  1517. # Used internally to select an item at the coordinate (x,y) relative 
  1518. # to the widget.  The command associated with the -selectcommand
  1519. # option is execute following % character substitutions.  If %n
  1520. # appears in the command, the selected node is substituted.  If %s
  1521. # appears, a boolean value representing the current selection state
  1522. # will be substituted.
  1523. # ----------------------------------------------------------------------
  1524. body iwidgets::Hierarchy::_select {x y} {
  1525.     if {$itk_option(-selectcommand) != {}} {
  1526.     if {[set seltags [$itk_component(list) tag names @$x,$y]] != {}} {
  1527.         foreach tag $seltags {
  1528.         if {![_isInternalTag $tag]} {
  1529.             lappend node $tag
  1530.         }
  1531.         }
  1532.  
  1533.         if {[lsearch $seltags "hilite"] == -1} {
  1534.         set selectstatus 0
  1535.         } else {
  1536.         set selectstatus 1
  1537.         }
  1538.  
  1539.         set cmd $itk_option(-selectcommand)
  1540.         regsub -all {%n} $cmd [list $node] cmd
  1541.         regsub -all {%s} $cmd [list $selectstatus] cmd
  1542.  
  1543.         uplevel #0 $cmd
  1544.     }
  1545.     }
  1546.  
  1547.     return
  1548. }
  1549.  
  1550. # ----------------------------------------------------------------------
  1551. # PROTECTED METHOD: _iconSelect node icon
  1552. #
  1553. # Used internally to upon selection of user icons.  The -iconcommand
  1554. # is executed after substitution of the node for %n and icon for %i.
  1555. # ----------------------------------------------------------------------
  1556. body iwidgets::Hierarchy::_iconSelect {node icon} {
  1557.     set cmd $itk_option(-iconcommand)
  1558.     regsub -all {%n} $cmd [list $node] cmd
  1559.     regsub -all {%i} $cmd [list $icon] cmd
  1560.  
  1561.     uplevel \#0 $cmd
  1562.  
  1563.     return {}
  1564. }
  1565.  
  1566. # ----------------------------------------------------------------------
  1567. # PROTECTED METHOD: _deselectSubNodes uid
  1568. #
  1569. # Used internally to recursively deselect all the nodes beneath a 
  1570. # particular node.
  1571. # ----------------------------------------------------------------------
  1572. body iwidgets::Hierarchy::_deselectSubNodes {uid} {
  1573.     foreach node $_nodes($uid) {
  1574.     if {[array names _selected $node] != {}} {
  1575.         unset _selected($node)
  1576.     }
  1577.     
  1578.     if {[array names _nodes $node] != {}} {
  1579.         _deselectSubNodes $node
  1580.     }
  1581.     }
  1582. }
  1583.  
  1584. # ----------------------------------------------------------------------
  1585. # PROTECTED METHOD: _deleteNodeInfo uid
  1586. #
  1587. # Used internally to recursively delete all the information about a
  1588. # node and its decendents.
  1589. # ----------------------------------------------------------------------
  1590. body iwidgets::Hierarchy::_deleteNodeInfo {uid} {
  1591.     #
  1592.     # Recursively call ourseleves as we go down the hierarchy beneath
  1593.     # this node.
  1594.     #
  1595.     if {[info exists _nodes($uid)]} {
  1596.     foreach node $_nodes($uid) {
  1597.         if {[array names _nodes $node] != {}} {
  1598.         _deleteNodeInfo $node
  1599.         }
  1600.     }
  1601.     }
  1602.  
  1603.     #
  1604.     # Unset any entries in our arrays for the node.
  1605.     #
  1606.     catch {unset _nodes($uid)}
  1607.     catch {unset _text($uid)}
  1608.     catch {unset _tags($uid)}
  1609.     catch {unset _icons($uid)}
  1610.     catch {unset _states($uid)}
  1611.     catch {unset _images($uid)}
  1612.     catch {unset _indents($uid)}
  1613. }
  1614.  
  1615. # ----------------------------------------------------------------------
  1616. # PROTECTED METHOD: _getParent uid
  1617. #
  1618. # Used internally to determine the parent for a node.
  1619. # ----------------------------------------------------------------------
  1620. body iwidgets::Hierarchy::_getParent {uid} {
  1621.     foreach node [array names _nodes] {
  1622.     if {[set index [lsearch $_nodes($node) $uid]] != -1} {
  1623.         return $node
  1624.     }
  1625.     }
  1626. }
  1627.  
  1628. # ----------------------------------------------------------------------
  1629. # PROTECTED METHOD: _getHeritage uid
  1630. #
  1631. # Used internally to determine the list of parents for a node.
  1632. # ----------------------------------------------------------------------
  1633. body iwidgets::Hierarchy::_getHeritage {uid} {
  1634.     set parents {}
  1635.  
  1636.     if {[set parent [_getParent $uid]] != {}} {
  1637.     lappend parents $parent
  1638.     }
  1639.  
  1640.     return $parents
  1641. }
  1642.  
  1643. # ----------------------------------------------------------------------
  1644. # PROTECTED METHOD (could be proc?): _isInternalTag tag
  1645. #
  1646. # Used internally to tags not to used for user callback commands
  1647. # ----------------------------------------------------------------------
  1648. body iwidgets::Hierarchy::_isInternalTag {tag} {
  1649.    set ii [expr [lsearch -exact {info hilite lowlite unknown} $tag] != -1];
  1650.    return $ii;
  1651. }
  1652.