source: tol/tolbase/lib/wtree/wtree.tcl @ a8dad5c

1941-docker-image-to-testdevel
Last change on this file since a8dad5c was a8dad5c, checked in by Jorge Pérez <josp.jorge@…>, 15 years ago

refs 703, wtree is helper widget to make easy the use of tktreectrl, wtree is the widget used in MMS GUI

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@3876 be4846ae-f051-483a-831f-b64e7d08527f

  • Property mode set to 100644
File size: 30.9 KB
Line 
1if { [ string compare $::tcl_version "8.5" ] >= 0 } {
2  package require tile
3}
4package require -exact treectrl 2.2.9
5package require autoscroll
6package require snit
7
8#
9# TODO:
10#
11#  - filtrado: una entry y filtrar tipo google
12#
13#     buscar mientras se escribe puede ser lento, podemos encolar la
14#     busqueda para que sea atendida en un after idle. Para eso el
15#     loop de busqueda debe entrar en el loop de eventos de tcl
16#
17#  - especificar columnas de filtrado
18#
19#  - parametrizar el contenido de las columnas: usar -tags, sino se da
20#    tag se asume el tag column${i}
21#
22#  - propiedad "-globalcheck" para indicar si el campo check debe
23#    sincronizar entre todos los items. Esto implica que sea de tipo
24#    "tristate" para poder reflejar un nodo con hijos parcialmente
25#    seleccionados. Si un campo check no esta marcado como
26#    "globalcheck" entonces es de tipo "on/off".
27#
28#  - la columna 1 puede llevar dos imagenes para open y close que
29#    pueden estar dadas por imision.
30#
31#  - open_image, close_image: dar unas por omision. No tener en cuenta
32#    si es formato tabla.
33#
34#  - en una columna puedo tener "image", "text" o check
35#
36
37#
38# Ejemplo de especificacion:
39if { 0 } {
40  wtree .t01.t \
41      -columns [ list \
42                     { -tags ID -label "Identifier" \
43                           {-type image} {-type text } } \
44                     { -tags DESC -label "Description" {-type text} } ]
45
46 # en la definicion de las elementos de columnas se puede dar un unico
47 # elemento en cuyo caso se dice el tipo o se puede dar una lista de
48 # pares uno de los cuales espcifica el tipo del elemento y el resto
49 # define propiedades del elemento.
50
51  .t01.t insert { {"" "node1"} {"Desc. node1"} }
52  .t01.t insert [ list { -image "" -text "node 1" } { -text "Desc. node 1" } ]
53
54 # en la insercion de un item las columnas se pueden dar con un solo
55 # elemento si la columna contiene solo un elemento o una lista de
56 # pares indicando para cada tipo el valor de la propiedad
57}
58 
59snit::widget wtree {
60  typevariable SystemButtonFace
61  typevariable SystemHighlight
62  typevariable SystemHighlightText
63  typevariable ButtonImageConf
64
65  option -columns \
66      -default "" -configuremethod "_conf-columns"
67  option -buttonstyle \
68      -default "" -configuremethod "_conf-buttonstyle"
69  option -table \
70      -default no -configuremethod "_conf-table"
71  option -contextmenu  \
72      -default "" -configuremethod "_conf-contextmenu"
73
74  option -filter \
75      -default 0 -configuremethod "_conf-filter"
76
77  option -columnfilter \
78      -default "" -configuremethod "_conf-columnfilter"
79 
80  variable tree
81  variable vscrollbar
82  variable hscrollbar
83
84  variable elements
85  variable styles
86  variable column_info
87
88  variable textFilter
89  variable columnsToFilter -array {-1 0}
90  variable idIdleFilter ""
91  variable idWaitFilter ""
92  variable breakFilter 0
93  variable numItemsFiltered 0
94  variable listFilterOn  ""
95  variable listFilterOff ""
96  variable widgets
97
98  variable sortedColumn 0
99
100  method _getTrace_ { } {
101    trace info variable [ myvar textFilter ]
102  }
103
104  delegate method * to tree
105  delegate option * to tree
106  delegate option -borderwidth to hull
107  delegate option -bd to hull
108  delegate option -relief to hull
109 
110  typeconstructor {
111   
112    set thisPlatform $::tcl_platform(platform)
113    set w [ listbox .___lb___ ]
114    set SystemButtonFace [$w cget -highlightbackground]
115    set SystemHighlight [$w cget -selectbackground]
116    set SystemHighlightText [$w cget -selectforeground]
117    destroy $w
118
119    if {$thisPlatform == "unix"} {
120      # I hate that gray selection color
121      set SystemHighlight #316ac5
122      set SystemHighlightText White
123    }
124
125    if { [ lsearch -exact [ image names ] mac-collapse ] == -1 } {
126      image create photo mac-collapse  -data {
127        R0lGODlhEAAQALIAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMyH5BAUAAAYA
128        LAAAAAAQABAAggAAAGZmzIiIiLu7u5mZ/8zM/////wAAAAMlaLrc/jDKSRm4
129        OAMHiv8EIAwcYRKBSD6AmY4S8K4xXNFVru9SAgAh/oBUaGlzIGFuaW1hdGVk
130        IEdJRiBmaWxlIHdhcyBjb25zdHJ1Y3RlZCB1c2luZyBVbGVhZCBHSUYgQW5p
131        bWF0b3IgTGl0ZSwgdmlzaXQgdXMgYXQgaHR0cDovL3d3dy51bGVhZC5jb20g
132        dG8gZmluZCBvdXQgbW9yZS4BVVNTUENNVAAh/wtQSUFOWUdJRjIuMAdJbWFn
133        ZQEBADs=
134      }
135    }
136
137    if { [ lsearch -exact [ image names ] mac-expand ] == -1 } {
138      image create photo mac-expand -data {
139        R0lGODlhEAAQALIAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMyH5BAUAAAYA
140        LAAAAAAQABAAggAAAGZmzIiIiLu7u5mZ/8zM/////wAAAAMnaLrc/lCB6MCk
141        C5SLNeGR93UFQQRgVaLCEBasG35tB9Qdjhny7vsJACH+gFRoaXMgYW5pbWF0
142        ZWQgR0lGIGZpbGUgd2FzIGNvbnN0cnVjdGVkIHVzaW5nIFVsZWFkIEdJRiBB
143        bmltYXRvciBMaXRlLCB2aXNpdCB1cyBhdCBodHRwOi8vd3d3LnVsZWFkLmNv
144        bSB0byBmaW5kIG91dCBtb3JlLgFVU1NQQ01UACH/C1BJQU5ZR0lGMi4wB0lt
145        YWdlAQEAOw==
146      }
147    }
148
149    if { [ lsearch -exact [ image names ] folder-open ] == -1 } {
150      image create photo folder-open -data {
151        R0lGODlhEAANANIAAAAAAISEhMbGxv//AP////8AAAAAAAAAACH5BAkZAAUA
152        LAAAAAAQAA0AAAM8WBrM+rAEQmmIb4qxBWnNQnCkV32ARHRlGQBgDA7vdN6v
153        UK8tC78qlrCWmvRKsJTquHkpZTKAsiCtWq0JAAA7
154      }
155    }
156   
157    if { [ lsearch -exact [ image names ] folder-closed ] == -1 } {
158      image create photo folder-closed -data {
159        R0lGODlhDwANANIAAAAAAISEhMbGxv//AP////8AAAAAAAAAACH5BAkZAAUA
160        LAAAAAAPAA0AAAMzWBXM+jCIMUWAT9JtmwtEKI5hAIBcOplgpVIs8bqxa8On
161        fNP5zsWzDctD9AAKgKRyuUwAAAA7
162      }
163    }
164
165    if { [ lsearch -exact [ image names ] actcross16 ] == -1 } {
166      image create photo actcross16 -data {
167        R0lGODlhEAAQAIIAAASC/PwCBMQCBEQCBIQCBAAAAAAAAAAAACH5BAEAAAAA
168        LAAAAAAQABAAAAMuCLrc/hCGFyYLQjQsquLDQ2ScEEJjZkYfyQKlJa2j7AQn
169        MM7NfucLze1FLD78CQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJz
170        aW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVz
171        ZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7
172      }
173    }
174
175    array set ButtonImageConf {
176      ""       ""
177      "mac"    { mac-collapse open mac-expand    !open }
178      "folder" { folder-open  open folder-closed !open }
179    }
180  }
181
182  method DoubleButton1 {w x y} {
183
184    set id [$w identify $x $y]
185    if {$id eq ""} {
186      return
187    }
188    if {[lindex $id 0] eq "item"} {
189      #lassign $id where item arg1 arg2
190      foreach  {where item arg1 arg2} $id break
191     
192      if {[llength [$w item descendants $item]] == 0} {
193        $self notify generate <<ItemSelected>> [list T $w c $item]
194      } else {
195        $w item toggle $item
196      }
197     
198    } else {
199      TreeCtrl::DoubleButton1 $w $x $y
200    }
201  }
202
203  method KeyPressReturn {w} {
204
205    if {[llength [$w item descendants active]] == 0} {
206      $self notify generate <<ItemSelected>> [list T $w c active]
207    } else {
208      $w item toggle active
209    }
210  }
211 
212  method Button3 {w x y rootX rootY} {
213
214    set cmenu [$self cget -contextmenu]
215    if {$cmenu eq ""} {
216      return
217    }
218    set id [$w identify $x $y]
219
220    #if {$id eq ""} {
221    #  return
222    #}
223    if {[lindex $id 0] == "item"} {
224      foreach {where item arg1 arg2} $id break
225     
226      if {[$w selection includes $item]==0} {
227        $w selection clear
228        $w selection add $item
229      }
230      $w activate $item
231    }
232    tk_popup $cmenu $rootX $rootY
233  }
234 
235  method KeyPressEscape {} {
236
237    $self notify generate <<Escape>>
238  }
239 
240  method SortbyColumn {t column} {
241
242        #puts "SortbyColumn: column=$column, tree=$t"
243        if {[$t column compare $column == $sortedColumn]} {
244          if {[$t column cget $sortedColumn -arrow] eq "up"} {
245                set order -decreasing
246                set arrow "down"
247          } else {
248                set order -increasing
249                set arrow "up"
250          }
251        } else {
252          if {[$t column cget $sortedColumn -arrow] eq "down"} {
253                set order -decreasing
254                set arrow "down"
255          } else {
256                set order -increasing
257                set arrow "up"
258          }
259          $t column configure $sortedColumn -arrow none
260          set sortedColumn $column
261        }
262        $t column configure $column -arrow $arrow
263       
264        $t item sort root $order -column $column -dictionary
265  }
266
267  constructor { args } {
268    # frame para el filtro de nodos
269    frame $win.frameFilter
270    label $win.frameFilter.l -text "[ msgcat::mc Search ]:"
271    menubutton $win.frameFilter.b -image mac-collapse \
272        -menu $win.frameFilter.b.m
273    menu $win.frameFilter.b.m \
274        -postcommand [ mymethod _postFilterMenu $win.frameFilter.b.m ] \
275        -tearoff no
276    frame $win.frameFilter.fentry -relief sunken -bd 1 -highlightthickness 1
277    set widgets(filter,entry) \
278        [ entry $win.frameFilter.fentry.e \
279              -bd 0 -highlightthickness 0 \
280              -textvar [ myvar textFilter ] ]
281    set widgets(filter,bcancel) \
282        [ button $win.frameFilter.fentry.b \
283              -bd 0 -highlightthickness 0 \
284              -image actcross16 \
285              -command [ mymethod _cancelFilter ] ]
286    grid $widgets(filter,entry) -row 0 -column 0 -sticky snew
287    grid $widgets(filter,bcancel) -row 0 -column 1 -sticky ew
288    grid rowconfigure $win.frameFilter.fentry 0 -weight 1
289    grid columnconfigure $win.frameFilter.fentry 0 -weight 1
290    grid remove $widgets(filter,bcancel)
291   
292    bind $widgets(filter,entry) <Key-Return> [ mymethod _applyFilter ]
293    grid $win.frameFilter.l -row 0 -column 1 -sticky "e"
294    grid $win.frameFilter.b -row 0 -column 2 -sticky "e"
295    grid $win.frameFilter.fentry -row 0 -column 3 -sticky "ew"
296    grid columnconfigure $win.frameFilter 0 -weight 1
297    grid columnconfigure $win.frameFilter 3 -weight 1
298    grid $win.frameFilter -row 0 -column 0 -sticky "snew"
299
300    trace add variable [ myvar textFilter ] write [ mymethod _onChangeFilter ]
301
302    # frame para tree widget
303    frame $win.frameTree
304    grid $win.frameTree -row 1 -column 0 -sticky "snew"
305    grid rowconfigure $win.frameTree 0 -weight 1
306    grid columnconfigure $win.frameTree 0 -weight 1
307   
308    install tree as treectrl $win.frameTree.t \
309        -highlightthickness 0 -borderwidth 0 \
310        -xscrollincrement 20 -showheader 1 -indent 19 \
311        -selectmode extended -showroot no \
312        -showrootbutton no -showbuttons yes -showlines yes \
313        -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50"
314
315
316    $tree notify install <<ItemSelected>>
317    $tree notify install <<Escape>>
318
319    $tree notify install <Header-invoke>
320
321    $tree notify install <Edit-begin>
322    $tree notify install <Edit-end>
323    $tree notify install <Edit-accept>
324   
325    $tree notify bind $tree <Edit-begin> \
326        [ mymethod EditCallback begin %I %C %E %t ]
327    $tree notify bind $tree <Edit-end> \
328        [ mymethod EditCallback end %I %C %E %t ]
329    $tree notify bind $tree <Edit-accept>  \
330        [ mymethod EditCallback accept %I %C %E %t ]
331   
332    bind $tree <ButtonPress-1> [ mymethod OnButtonPress1 %x %y ]
333    bind $tree <Control-ButtonPress-1> { }
334    bind $tree <Shift-ButtonPress-1> { }
335
336    set height [font metrics [$tree cget -font] -linespace]
337    if {$height < 18} {
338      set height 18
339    }
340    $tree configure -itemheight $height
341
342    # create the elements & styles
343
344    set elements(image) [ $tree element create eIMG image ]
345    set elements(text) [ $tree element create eTXT text \
346                           -fill [list $wtree::SystemHighlightText \
347                                      {selected focus}] ]
348    set elements(check) [ $tree element create eCHK window ]
349   
350    set elements(rect) [ $tree element create eRECT rect \
351                             -showfocus yes -open ew \
352                             -fill [ list \
353                                         $wtree::SystemHighlight \
354                                         {selected focus} \
355                                         gray {selected !focus} ] ]
356
357
358    # vertical scrollbar
359    if { [ string compare $::tcl_version "8.5" ] >= 0 } {
360      set wscroll ttk::scrollbar
361    } else {
362      set wscroll scrollbar
363    }
364    install vscrollbar as $wscroll $win.frameTree.sv \
365        -orient vertical -command [list $win.frameTree.t yview]
366
367    # horizontal scrollbar
368    install hscrollbar as $wscroll $win.frameTree.sh \
369        -orient horizontal -command [list $win.frameTree.t xview]
370
371    grid $tree       -row 0 -column 0 -sticky nsew
372    grid $vscrollbar -row 0 -column 1 -sticky ns
373    grid $hscrollbar -row 1 -column 0 -sticky ew
374    grid columnconfigure $win 0 -weight 1
375    grid rowconfigure    $win 1 -weight 1
376
377    focus $tree
378   
379    $self _configure_bindings
380    $self _conf-columns -columns $options(-columns)
381    $self _conf-contextmenu -contextmenu $options(-contextmenu)
382    $self configurelist $args
383    if { !$options(-filter) } {
384      grid remove $win.frameFilter
385    }
386    #return $win
387  }
388 
389  method getItemText { item } {
390    set itemText ""
391    # buscar las columnas que tiene elemento eTXT
392    foreach c [ $tree column list ] {
393      if { !$columnsToFilter($c) } {
394        continue
395      }
396      # extraigo el style asociado a la columna
397      set columnStyle [ $tree column cget $c -itemstyle ]
398      # extraigo los elementos del style
399      set styleElements [ $tree style elements $columnStyle ]
400      # busco si eTXT es uno de esos elementos
401      if { [ lsearch $styleElements "eTXT" ] != -1 } {
402        # el root no tiene styles
403        catch {
404          append itemText [ $tree item element cget $item $c eTXT -text ] " "
405        }
406      }
407    }
408    return $itemText
409  }
410
411  method getColumnLabels { args } {
412    array set opts {
413      -onlyvisible yes
414      -hastext no
415    }
416    array set opts $args
417    set labels [ list ]
418    foreach c [ $tree column list ] {
419      if { $opts(-hastext) } {
420        # extraigo el style asociado a la columna
421        set columnStyle [ $tree column cget $c -itemstyle ]
422        # extraigo los elementos del style
423        set styleElements [ $tree style elements $columnStyle ]
424        # busco si eTXT es uno de esos elementos
425        if { [ lsearch $styleElements "eTXT" ] == -1 } {
426          # no tiene elemento Text
427          continue
428        }
429      }
430      if { $opts(-onlyvisible) && ![ $tree column cget $c -visible ] } {
431        # no es visible
432        continue
433      }
434      lappend labels [ list -idx $c -text [ $tree column cget $c -text ] ]
435    }
436    return $labels
437  }
438
439  method _postFilterMenu { m } {
440    $m delete 0 end
441    foreach c [ $self getColumnLabels ] {
442      array set cinfo $c
443      $m add checkbutton -label $cinfo(-text) -onvalue 1 -offvalue 0 \
444          -variable [ myvar columnsToFilter($cinfo(-idx)) ]
445    }
446  }
447
448  method _setWaitCursor { } {
449    $win configure -cursor watch
450    $widgets(filter,entry) configure -cursor watch
451    update
452  }
453
454  method _setNormalCursor { } {
455    $win configure -cursor {}
456    $widgets(filter,entry) configure -cursor {}
457    update
458  }
459
460  method _cancelFilter { } {
461    set textFilter ""
462    grid remove $widgets(filter,bcancel)
463  }
464
465  method filterItems { text } {
466    #puts "filterItems $text"
467    $self _setWaitCursor
468    set negativeWords [ list ]
469    set positiveWords [ list ]
470    set negating 0
471    set wordList [regexp -inline -all -- {\S+} $text]
472    foreach word $wordList {
473      if { $negating } {
474        lappend negative $word
475        set negating 0
476      } elseif { [ string index $word 0 ] eq "-" } {
477        # se pide negar la palabra
478        set _word [ string range $word 1 end ]
479        if { $_word eq "" } {
480          set negating 1
481        } else {
482          lappend negativeWords $_word
483        }
484      } else {
485        lappend positiveWords $word
486      }
487    }
488    # regexp {^(?!(.*(foo|bar).*))} atttcokebar
489    if { [ llength $negativeWords ] } {
490      set negativeRegExp "^(?!(.*([ join $negativeWords | ]).*))"
491    } else {
492      set negativeRegExp ""
493    }
494    set breakFilter 0
495    set numItemsFiltered 0
496    set listFilterOn [ list ]
497    set listFilterOff [ list ]
498    $self _filterNodeSet [ $tree item children root ] \
499        $positiveWords $negativeRegExp
500    $self _setNormalCursor
501  }
502 
503  method _filterNodeSet { nodes positiveWords negativeRegExp } {
504    #puts "filterNodeSet '$nodes' '$positiveWords'"
505    # recorro cada uno de los nodos
506    foreach n $nodes {
507      # filtro el nodo
508     
509      #esto es para el filtrado automatico incremental
510      if { $breakFilter } {
511        return 0
512      }
513
514      if { [ $self _filterNode $n $positiveWords $negativeRegExp ] } {
515        # si el resultado es visible desciendo y filtro a los hijos
516        $self _filterNodeSet [ $tree item children $n ] \
517            $positiveWords $negativeRegExp
518      }
519
520      # esto es para el filtrado automatico incremental
521      incr numItemsFiltered
522      if { !$breakFilter && !( $numItemsFiltered % 1000 ) } {
523        # refresco la cola de eventos
524        if { [ llength $listFilterOn ] } {
525          $tree item configure [ list list $listFilterOn ] -visible 1
526          set listFilterOn [ list ]
527        }
528        if { [ llength $listFilterOff ] } {
529          $tree item configure [ list list $listFilterOff ] -visible 0
530          set listFilterOff [ list ]
531        }
532        update
533      }
534    }
535    if { [ llength $listFilterOn ] } {
536      $tree item configure [ list list $listFilterOn ] -visible 1
537      set listFilterOn [ list ]
538    }
539    if { [ llength $listFilterOff ] } {
540      $tree item configure [ list list $listFilterOff ] -visible 0
541      set listFilterOff [ list ]
542    }
543    return 1
544  }
545
546  method _filterNode { node positiveWords negativeRegExp } {
547    #puts "filterNode '$node' '$positiveWords' '$negativeRegExp'"
548    set itemText [ $self getItemText $node ]
549    #puts "itemText $itemText"
550    # hago match, luego hago algo mas sofisticado con regexp
551    # set visible [ string match "*${text}*" $itemText ]
552    if { [ string length $negativeRegExp ] &&
553         ![ regexp -nocase $negativeRegExp $itemText ] } {
554      # itemText contiene alguna de las palabra prohibidas
555      #puts "itemText contiene alguna de las palabra prohibidas"
556      #$tree item configure $node -visible 0
557      lappend listFilterOff $node
558      return 0
559    }
560    foreach word $positiveWords {
561      if { ![ string match -nocase "*${word}*" $itemText ] } {
562        # itemText no contiene una de las palabras deseadas
563        #puts "itemText no contiene una de las palabras deseadas: $word"
564        #puts "string match \"*${word}*\" $itemText"
565        #$tree item configure $node -visible 0
566        lappend listFilterOff $node
567        return 0
568      }
569    }
570    # finalmente todas las palabras deseadas y niguna de las
571    # prohibidas estan.
572    #$tree item configure $node -visible 1
573    lappend listFilterOn $node
574    return 1
575  }
576
577  method _applyFilter { } {
578    set columnFound -1
579    foreach c [ array name columnsToFilter ] {
580      if { $columnsToFilter($c) } {
581        set columnFound $c
582        break
583      }
584    }
585    if { $columnFound == -1 } {
586      return
587    }
588    if { $textFilter eq "" } {
589      $tree item configure all -visible 1
590    } else {
591      $self filterItems $textFilter
592    }
593  }
594
595  method _onChangeFilter { args } {
596    if { $textFilter eq "" } {
597      grid remove $widgets(filter,bcancel)
598      $tree item configure all -visible 1
599    } else {
600      #puts "filtering on $textFilter"
601      grid $widgets(filter,bcancel)
602      update
603      after cancel $idWaitFilter
604      set breakFilter 1
605      set idWaitFilter [ after 500 $self _scheduleFilter ]
606    }
607  }
608 
609  method _scheduleFilter { } {
610    after cancel $idIdleFilter
611    set idIdleFilter [ after idle $self _applyFilter ]
612  }
613
614  method _configure_bindings { } {
615   
616    bind $tree <Double-ButtonPress-1> "[mymethod DoubleButton1 %W %x %y] ; break"
617    bind $tree <Return> "[mymethod KeyPressReturn %W] ; break"
618
619    bind $tree <ButtonPress-3> [ mymethod Button3 %W %x %y %X %Y]
620
621    bind $tree <Escape> "[mymethod KeyPressEscape] ; break"
622   
623    $tree notify bind $tree <Header-invoke> [mymethod SortbyColumn %W %C]
624
625    $tree notify bind $vscrollbar <Scroll-y> { %W set %l %u }
626    bind $vscrollbar <ButtonPress-1> [list focus $tree]
627   
628    $tree notify bind $hscrollbar <Scroll-x> { %W set %l %u }
629    bind $hscrollbar <ButtonPress-1> [list focus $tree]
630   
631    ::autoscroll::autoscroll $vscrollbar
632    ::autoscroll::autoscroll $hscrollbar
633  }
634
635  method _conf-table { _ yesno } {
636    if { $yesno } {
637      set options(-table) 1
638      $tree configure -showbuttons 0 -showlines 0
639    } else {
640      set options(-table) 0
641      $tree configure -showbuttons 1 -showlines 1
642    }
643  }
644
645  method _conf-buttonstyle { _ bs } {
646    set valid_bstyles [ array names wtree::ButtonImageConf ]
647    if { [ lsearch $valid_bstyles $bs ] == -1 } {
648      error "invalid button style $bs should be one of $valid_bstyles"
649    } else {
650      set options(-buttonstyle) $bs
651      $tree configure -buttonimage $wtree::ButtonImageConf($bs)
652    }
653  }
654
655  method _conf-contextmenu { _ cmenu } {
656    set options(-contextmenu) $cmenu
657  }
658
659  method _conf-filter { _ value } {
660    if { [ string is boolean $value ] } {
661      if { ( $options(-filter)  && $value ) ||
662           ( !$options(-filter) && !$value ) } {
663        return
664      }
665      set options(-filter) $value
666      if { $value } {
667        grid $win.frameFilter
668      } else {
669        grid remove $win.frameFilter
670      }
671      set textFilter ""
672    } else {
673      error "invalid boolean value $value"
674    }
675  }
676
677  method _conf-columnfilter { _ value } {
678    # hago el chequeo en el postcommand del menu que despliegua las
679    # opciones y tambien a la hora de filtrar
680    set options(-columnfilter) $value
681        foreach {idx v} $value {
682          set columnsToFilter($idx) $v
683    }
684  }
685
686  #
687  # columnsSpec es una lista de "columnas" y una columna puede ser
688  #
689  #  - un unico elemento con valor: image, text o check
690  #
691  #  - una lista de elementos, donde cada elemento puede estar dado
692  #    por su tipo.
693  #
694  #    { image text }
695  #
696  #    en un futuro se le puede adjuntar propiedades de layout.
697  #
698  #    cada columna crea un stilo y el layout esa definido por el
699  #    orden en que se describen los elementos de la columna.
700  #
701  method _conf-columns { _ columnsSpec } {
702    if { $columnsSpec eq "" } {
703      set columnsSpec [ list { text -tags C0 -label "Column 0" -editable 0 } ]
704    }
705    set options(-columns) $columnsSpec
706    # elimino columnas
707    $tree column delete all
708    # elimino estilos
709    eval $tree style delete [ $tree style names ]
710
711    # limpio la informacion interna de las columnas
712    array unset column_info
713    array unset style_info
714    # crear las columnas y estilos
715    set idx 0
716    foreach col_spec $columnsSpec {
717      set col_elements [ lindex $col_spec 0 ]
718      # opciones por omision.
719      array set opts [ list -tags C${idx} -label "Column $idx" -image "" \
720                           -editable 0 ]
721      # opciones dadas en la llamada.
722      array set opts [ lrange $col_spec 1 end ]
723      set colID [ $tree column create \
724                      -text [ msgcat::mc $opts(-label) ] \
725                      -image $opts(-image) \
726                      -itembackground "\#e0e8f0 {}" \
727                      -tags $opts(-tags) ]
728      set column_info($idx,ID) $colID
729      set column_info(MAPIDX,$colID) $idx
730      set column_info($idx,elements) $col_elements
731      set column_info($idx,-editable) $opts(-editable)
732      # creamos el estilo para esta columna
733      set s [ $tree style create style$colID ]
734      set column_info($idx,style) $s
735      set eList [ list ]
736      # selecciono los id's de los elementos de esta columna
737      set eIMG ""
738      foreach e $col_elements {
739        lappend eList $elements($e)
740        if { $e eq "image" } {
741          set eIMG $elements($e)
742        }
743      }
744      # asocio los elementos al style
745      $tree style elements $s [ eval list eRECT $eList ]
746
747      # defino el layout de los elementos
748      $tree style layout   $s eRECT -union $eList -iexpand nswe -ipadx 2
749      if { $eIMG ne "" } {
750        $tree style layout   $s $eIMG -iexpand ns -padx 2
751      }
752      # asocio el style a la columna
753      $tree column configure $colID -itemstyle $s
754      incr idx
755    }
756    # Specifies a column description that determines which column
757    # displays the expand/collapse buttons and connecting lines between
758    # items. The default is unspecified.
759    $tree configure -treecolumn $column_info(0,ID)
760    # extraigo las columnas filtrables
761    set first ""
762    set hasCheck 0
763    foreach c [ $self getColumnLabels -onlyvisible yes -hastext yes ] {
764      array set cinfo $c
765      if { $first eq "" } {
766        set first $cinfo(-idx)
767      }
768      if { [ info exists columnsToFilter($cinfo(-idx)) ] } {
769        set _columnsToFilter($cinfo(-idx)) $columnsToFilter($cinfo(-idx))
770      } else {
771        set _columnsToFilter($cinfo(-idx)) 0
772      }
773      if { $_columnsToFilter($cinfo(-idx)) } {
774        set hasCheck 1
775      }
776    }
777    if { !$hasCheck } {
778      set _columnsToFilter($first) 1
779    }
780    array unset columnsToFilter
781    array set columnsToFilter [ array get _columnsToFilter ]
782  }
783
784  method insert { data args } {
785    array set insopt {
786      -at end
787      -relative root
788      -open 0
789      -button 1
790      -tags ""
791    }
792    array set insopt $args
793    # creamos el item, -button yes debe depender si el widget no es
794    # tipo tabla
795    set item [ $tree item create \
796                   -button $insopt(-button) \
797                   -tags insopt(-tags) ]
798    set idx 0
799    set idx_last  [ expr [ llength $options(-columns) ] - 1 ]
800    # 'data' es una lista con informacion para cada columna. Lo que
801    # venga de mas en data no lo considero.
802    foreach col_data [ lrange $data 0 $idx_last ] {
803      # los elementos de esta columna
804      set col_elements $column_info($idx,elements)
805      # col_data debe ser una lista de la misma cantidad de elementos
806      set e_count [ llength $col_elements ]
807      # esta lista es para usar la sintaxis de configurar + de un
808      # elemento al estilo
809      #
810      # $tree item element configure $item $columnID($idx) \
811      #      $elements(IMG) -image $opts(-image) + \
812      #      $elements(TXT) -text  $opts(-text)
813      #
814      set elements_config [ list ]
815      for { set i 0 } { $i < $e_count } { incr i } {
816        set e_data [ lindex $col_data $i ]
817        set e_conf [ $self _build_element_config \
818                         [ lindex $col_elements $i ] $e_data ]
819        eval $tree item element configure $item $column_info($idx,ID) $e_conf
820      }
821      incr idx
822    }
823    # insertamos el item en el arbol
824    $tree item collapse $item
825    switch $insopt(-at) {
826      end - child {
827        $tree item lastchild $insopt(-relative) $item
828      }
829      next {
830        $tree item nextsibling $insopt(-relative) $item
831      }
832      prev {
833        $tree item prevsibling $insopt(-relative) $item
834      }
835      default {
836        error "invalid index, should be: end (child), prev or next"
837      }
838    }
839    return $item
840  }
841
842  method _build_element_config { etype data } {
843    array set def_prop {
844      text -text
845      image -image
846    }
847    if { [ catch { llength $data } ] } {
848      set data [ list $data ]
849    }
850    if { [ llength $data ] } {
851      if { [ llength $data ] == 1 } {
852        return [ list $elements($etype) $def_prop($etype) [ string trim [ lindex $data 0 ] ] ]
853      } elseif { [ string index [ lindex $data 0 ] 0 ] ne "-" } {
854        return [ list $elements($etype) $def_prop($etype) [ string trim $data ] ]
855      } else {
856        return [ linsert $data 0 $elements($etype) ]
857      }
858    } else {
859      return [ list $elements($etype) $def_prop($etype) "" ]
860    }
861  }
862
863  method EditCallback { callback item column elem t } {
864    if { $callback eq "accept" } {
865      $tree item element configure $item $column $elem -text $t
866    }
867  }
868
869  method OnButtonPress1 { x y } {
870   
871    set treeInfo [ $tree identify $x $y ]
872    if { $treeInfo eq "" } {
873      $tree selection clear
874      return
875    }
876   
877    if { [ llength $treeInfo ] != 6 } {
878      return
879    }
880   
881    foreach { item itemNum column columnNum elem elemName } $treeInfo { }
882   
883    #puts "OnButtonPress1: $treeInfo"
884    if { $item == "item" && \
885             $column == "column" && \
886             $elem == "elem" && \
887             [ $tree selection includes $itemNum ] } {
888
889      #puts "$itemNum = [$tree selection includes $itemNum ]"
890
891      set idx_info $column_info(MAPIDX,$columnNum)
892
893      if { $column_info($idx_info,-editable) && $elemName eq "eTXT"} {
894        ::TreeCtrl::ButtonPress1 $tree $x $y
895        update
896        ::TreeCtrl::EntryOpen $tree $itemNum $columnNum $elemName
897        return -code break
898      }
899    }
900  }
901 
902  proc test_GetTextContents { T item } {
903    set textDump ""
904    # buscar las columnas que tiene elemento eTXT
905    foreach c [ $T column list ] {
906      # extraigo el style asociado al item
907      set style [ $T column cget $c -itemstyle ]
908      # extraigo los elementos del style
909      set elements [ $T style elements $style ]
910      # busco si eTXT es uno de esos elementos
911      if { [ lsearch $elements "eTXT" ] != -1 } {
912        append textDump [ $T item element cget $item $c eTXT -text ] " "
913      }
914    }
915    return $textDump
916  }
917
918  proc test_ListenSearch { t args } {
919    if { $::wtreeSearch eq "" } {
920      $t item configure all -visible 1
921    } else {
922      #puts "filtering on $::wtreeSearch"
923      #$t filterNodeSet [ $t item children root ] $::wtreeSearch
924      catch {
925        $t filterItems $::wtreeSearch
926      } msg
927      #puts "msg = $msg"
928    }
929  }
930
931  proc test_01 { w { istable 0 } } {
932    destroy $w
933    toplevel $w
934    if { 0 } {
935      frame $w.fsearch
936      label $w.fsearch.l -text "Search:"
937      entry $w.fsearch.e -textvar ::wtreeSearch
938      grid $w.fsearch.l -row 0 -column 1 -sticky "e"
939      grid $w.fsearch.e -row 0 -column 2 -sticky "ew"
940      grid columnconfigure $w.fsearch 0 -weight 1
941      grid $w.fsearch -row 0 -column 0 -sticky "snew"
942    }
943   
944    set T [ wtree $w.t -table $istable -filter yes -buttonstyle mac \
945                -columns [ list \
946                               { {image text} -tags ID   -label Identifier } \
947                               { text -tags DESC -label Description } ] ]
948   
949    $T notify bind $T <Expand-before> {
950      #puts "before openning %I"
951    }
952    $T notify bind $T <Collapse-after> {
953      #puts "after closing %I"
954    }
955    $T notify bind $T <ActiveItem> {
956      #puts "now active item is %c the previous was %p"
957    }
958    grid $T -row 1 -column 0 -sticky "snew"
959    grid rowconfigure $w 1 -weight 1
960    grid columnconfigure $w 0 -weight 1
961
962    if { 0 } {
963      trace remove variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
964      trace add    variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
965    }
966   
967    set bugValue {
968Serie AlgGetData(
969 "Lla_MetCon.Atendi.Ser_TNuXXX_GHLAll.Ind.ALL_TLlTLl.XXX_RotXXX.XXX.XXX.XXX_EspEsp.XXX_SegSeg.XXX_PerMHo.XXX_FecDia",
970 AlgProj1004)
971}
972    set id [ $T insert [list  [list folder-open "node bug"] [ list $bugValue ] ] ]
973    set id [ $T insert [list [list folder-open "node 1"] [list "Desc node\n 1"]] ]
974    set id [ $T insert { { folder-closed "node 2" } { "Desc node 2" } } \
975                 -at end -relative [ expr { $istable ? "root" : $id } ] ]
976    return .t01.t
977  }
978
979  proc TestSearch { w } {
980    destroy $w
981    toplevel $w
982
983    set T [ wtree $w.t -table 1 -filter yes -buttonstyle mac \
984                -columns [ list \
985                               { {image text} -tags ID   -label Identifier } \
986                               { text -tags DESC -label Description -editable yes} ] ]
987    grid $T -row 1 -column 0 -sticky "snew"
988    grid rowconfigure $w 1 -weight 1
989    grid columnconfigure $w 0 -weight 1
990   
991    for { set r 0 } { $r < 100 } { incr r } {
992      $T insert [list  [list folder-open "node $r"] [ list "Description for node $r" ] ]
993    }
994    return $T
995  }
996}
997
998package provide wtree 1.0
Note: See TracBrowser for help on using the repository browser.