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

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

refs #1382, edition is triggered when clicked over the whole column no only when clicked over the text

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

  • Property mode set to 100644
File size: 32.3 KB
RevLine 
[a8dad5c]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 
[48947f4]503  method _filterNodesMatching { } {
504    if { [ llength $listFilterOff ] } {
505      $tree item configure [ list list $listFilterOff ] -visible 0
506      set listFilterOff [ list ]
507    }
508    if { [ llength $listFilterOn ] } {
509      $tree item configure [ list list $listFilterOn ] -visible 1
510      if { !$options(-table) } {
511        foreach nid $listFilterOn {
512          $tree item configure \
513              [ list list [ $tree item ancestors $nid ] ] -visible 1
514        }
515      }
516      set listFilterOn [ list ]
517    }
518  }
519
[a8dad5c]520  method _filterNodeSet { nodes positiveWords negativeRegExp } {
521    #puts "filterNodeSet '$nodes' '$positiveWords'"
522    # recorro cada uno de los nodos
523    foreach n $nodes {
524      # filtro el nodo
525     
526      #esto es para el filtrado automatico incremental
527      if { $breakFilter } {
528        return 0
529      }
530
[48947f4]531      $self _filterNode $n $positiveWords $negativeRegExp
532      # desciendo a los hijos
533      $self _filterNodeSet [ $tree item children $n ] \
534          $positiveWords $negativeRegExp
535     
536      if { 0 } {
[a8dad5c]537      if { [ $self _filterNode $n $positiveWords $negativeRegExp ] } {
538        # si el resultado es visible desciendo y filtro a los hijos
539        $self _filterNodeSet [ $tree item children $n ] \
540            $positiveWords $negativeRegExp
541      }
[48947f4]542      }
[a8dad5c]543
544      # esto es para el filtrado automatico incremental
545      incr numItemsFiltered
546      if { !$breakFilter && !( $numItemsFiltered % 1000 ) } {
547        # refresco la cola de eventos
[48947f4]548        $self _filterNodesMatching
[a8dad5c]549        update
550      }
551    }
[48947f4]552    $self _filterNodesMatching
[a8dad5c]553    return 1
554  }
555
556  method _filterNode { node positiveWords negativeRegExp } {
557    #puts "filterNode '$node' '$positiveWords' '$negativeRegExp'"
558    set itemText [ $self getItemText $node ]
559    #puts "itemText $itemText"
560    # hago match, luego hago algo mas sofisticado con regexp
561    # set visible [ string match "*${text}*" $itemText ]
562    if { [ string length $negativeRegExp ] &&
563         ![ regexp -nocase $negativeRegExp $itemText ] } {
564      # itemText contiene alguna de las palabra prohibidas
565      #puts "itemText contiene alguna de las palabra prohibidas"
566      #$tree item configure $node -visible 0
567      lappend listFilterOff $node
568      return 0
569    }
570    foreach word $positiveWords {
571      if { ![ string match -nocase "*${word}*" $itemText ] } {
572        # itemText no contiene una de las palabras deseadas
573        #puts "itemText no contiene una de las palabras deseadas: $word"
574        #puts "string match \"*${word}*\" $itemText"
575        #$tree item configure $node -visible 0
576        lappend listFilterOff $node
577        return 0
578      }
579    }
580    # finalmente todas las palabras deseadas y niguna de las
581    # prohibidas estan.
582    #$tree item configure $node -visible 1
583    lappend listFilterOn $node
584    return 1
585  }
586
587  method _applyFilter { } {
588    set columnFound -1
589    foreach c [ array name columnsToFilter ] {
590      if { $columnsToFilter($c) } {
591        set columnFound $c
592        break
593      }
594    }
595    if { $columnFound == -1 } {
596      return
597    }
598    if { $textFilter eq "" } {
599      $tree item configure all -visible 1
600    } else {
601      $self filterItems $textFilter
602    }
603  }
604
605  method _onChangeFilter { args } {
606    if { $textFilter eq "" } {
607      grid remove $widgets(filter,bcancel)
608      $tree item configure all -visible 1
609    } else {
610      #puts "filtering on $textFilter"
611      grid $widgets(filter,bcancel)
612      update
613      after cancel $idWaitFilter
614      set breakFilter 1
615      set idWaitFilter [ after 500 $self _scheduleFilter ]
616    }
617  }
618 
619  method _scheduleFilter { } {
620    after cancel $idIdleFilter
621    set idIdleFilter [ after idle $self _applyFilter ]
622  }
623
624  method _configure_bindings { } {
625   
626    bind $tree <Double-ButtonPress-1> "[mymethod DoubleButton1 %W %x %y] ; break"
627    bind $tree <Return> "[mymethod KeyPressReturn %W] ; break"
628
629    bind $tree <ButtonPress-3> [ mymethod Button3 %W %x %y %X %Y]
630
631    bind $tree <Escape> "[mymethod KeyPressEscape] ; break"
632   
633    $tree notify bind $tree <Header-invoke> [mymethod SortbyColumn %W %C]
634
635    $tree notify bind $vscrollbar <Scroll-y> { %W set %l %u }
636    bind $vscrollbar <ButtonPress-1> [list focus $tree]
637   
638    $tree notify bind $hscrollbar <Scroll-x> { %W set %l %u }
639    bind $hscrollbar <ButtonPress-1> [list focus $tree]
640   
641    ::autoscroll::autoscroll $vscrollbar
642    ::autoscroll::autoscroll $hscrollbar
643  }
644
645  method _conf-table { _ yesno } {
646    if { $yesno } {
647      set options(-table) 1
648      $tree configure -showbuttons 0 -showlines 0
649    } else {
650      set options(-table) 0
651      $tree configure -showbuttons 1 -showlines 1
652    }
653  }
654
655  method _conf-buttonstyle { _ bs } {
656    set valid_bstyles [ array names wtree::ButtonImageConf ]
657    if { [ lsearch $valid_bstyles $bs ] == -1 } {
658      error "invalid button style $bs should be one of $valid_bstyles"
659    } else {
660      set options(-buttonstyle) $bs
661      $tree configure -buttonimage $wtree::ButtonImageConf($bs)
662    }
663  }
664
665  method _conf-contextmenu { _ cmenu } {
666    set options(-contextmenu) $cmenu
667  }
668
669  method _conf-filter { _ value } {
670    if { [ string is boolean $value ] } {
671      if { ( $options(-filter)  && $value ) ||
672           ( !$options(-filter) && !$value ) } {
673        return
674      }
675      set options(-filter) $value
676      if { $value } {
677        grid $win.frameFilter
678      } else {
679        grid remove $win.frameFilter
680      }
681      set textFilter ""
682    } else {
683      error "invalid boolean value $value"
684    }
685  }
686
687  method _conf-columnfilter { _ value } {
688    # hago el chequeo en el postcommand del menu que despliegua las
689    # opciones y tambien a la hora de filtrar
690    set options(-columnfilter) $value
691        foreach {idx v} $value {
692          set columnsToFilter($idx) $v
693    }
694  }
695
696  #
697  # columnsSpec es una lista de "columnas" y una columna puede ser
698  #
699  #  - un unico elemento con valor: image, text o check
700  #
701  #  - una lista de elementos, donde cada elemento puede estar dado
702  #    por su tipo.
703  #
704  #    { image text }
705  #
706  #    en un futuro se le puede adjuntar propiedades de layout.
707  #
708  #    cada columna crea un stilo y el layout esa definido por el
709  #    orden en que se describen los elementos de la columna.
710  #
711  method _conf-columns { _ columnsSpec } {
712    if { $columnsSpec eq "" } {
713      set columnsSpec [ list { text -tags C0 -label "Column 0" -editable 0 } ]
714    }
715    set options(-columns) $columnsSpec
716    # elimino columnas
717    $tree column delete all
718    # elimino estilos
719    eval $tree style delete [ $tree style names ]
720
721    # limpio la informacion interna de las columnas
722    array unset column_info
723    array unset style_info
724    # crear las columnas y estilos
725    set idx 0
726    foreach col_spec $columnsSpec {
727      set col_elements [ lindex $col_spec 0 ]
728      # opciones por omision.
729      array set opts [ list -tags C${idx} -label "Column $idx" -image "" \
730                           -editable 0 ]
731      # opciones dadas en la llamada.
732      array set opts [ lrange $col_spec 1 end ]
733      set colID [ $tree column create \
734                      -text [ msgcat::mc $opts(-label) ] \
735                      -image $opts(-image) \
736                      -itembackground "\#e0e8f0 {}" \
737                      -tags $opts(-tags) ]
738      set column_info($idx,ID) $colID
739      set column_info(MAPIDX,$colID) $idx
740      set column_info($idx,elements) $col_elements
741      set column_info($idx,-editable) $opts(-editable)
742      # creamos el estilo para esta columna
743      set s [ $tree style create style$colID ]
744      set column_info($idx,style) $s
745      set eList [ list ]
746      # selecciono los id's de los elementos de esta columna
747      set eIMG ""
748      foreach e $col_elements {
749        lappend eList $elements($e)
750        if { $e eq "image" } {
751          set eIMG $elements($e)
752        }
753      }
754      # asocio los elementos al style
755      $tree style elements $s [ eval list eRECT $eList ]
756
757      # defino el layout de los elementos
758      $tree style layout   $s eRECT -union $eList -iexpand nswe -ipadx 2
759      if { $eIMG ne "" } {
760        $tree style layout   $s $eIMG -iexpand ns -padx 2
761      }
762      # asocio el style a la columna
763      $tree column configure $colID -itemstyle $s
764      incr idx
765    }
766    # Specifies a column description that determines which column
767    # displays the expand/collapse buttons and connecting lines between
768    # items. The default is unspecified.
769    $tree configure -treecolumn $column_info(0,ID)
770    # extraigo las columnas filtrables
771    set first ""
772    set hasCheck 0
773    foreach c [ $self getColumnLabels -onlyvisible yes -hastext yes ] {
774      array set cinfo $c
775      if { $first eq "" } {
776        set first $cinfo(-idx)
777      }
778      if { [ info exists columnsToFilter($cinfo(-idx)) ] } {
779        set _columnsToFilter($cinfo(-idx)) $columnsToFilter($cinfo(-idx))
780      } else {
781        set _columnsToFilter($cinfo(-idx)) 0
782      }
783      if { $_columnsToFilter($cinfo(-idx)) } {
784        set hasCheck 1
785      }
786    }
787    if { !$hasCheck } {
788      set _columnsToFilter($first) 1
789    }
790    array unset columnsToFilter
791    array set columnsToFilter [ array get _columnsToFilter ]
792  }
793
[2c0158d]794  method itemcolumn { item col args } {
795    set colid [ $tree column id $col ]
796    if { $colid eq "" } {
797      puts "$col does not reference a valid column"
798      return
799    }
800    set ret {}
801    foreach elemdata $args {
802      set v ""
803      foreach { e v } $elemdata break
804      if { $v eq "" } {
805        array set def_prop {
806          text -text
807          image -image
808        }
809        lappend ret [ list $e [ $tree item element cget $item $colid $elements($e) $def_prop($e) ] ]
810      } else {
811        set e_conf [ $self _build_element_config $e $v ]
812        eval $tree item element configure $item $colid $e_conf
813      }
814    }
815    return $ret
816  }
817
[a8dad5c]818  method insert { data args } {
819    array set insopt {
820      -at end
821      -relative root
822      -open 0
823      -button 1
824      -tags ""
825    }
826    array set insopt $args
827    # creamos el item, -button yes debe depender si el widget no es
828    # tipo tabla
829    set item [ $tree item create \
830                   -button $insopt(-button) \
831                   -tags insopt(-tags) ]
832    set idx 0
833    set idx_last  [ expr [ llength $options(-columns) ] - 1 ]
834    # 'data' es una lista con informacion para cada columna. Lo que
835    # venga de mas en data no lo considero.
836    foreach col_data [ lrange $data 0 $idx_last ] {
837      # los elementos de esta columna
838      set col_elements $column_info($idx,elements)
839      # col_data debe ser una lista de la misma cantidad de elementos
840      set e_count [ llength $col_elements ]
841      # esta lista es para usar la sintaxis de configurar + de un
842      # elemento al estilo
843      #
844      # $tree item element configure $item $columnID($idx) \
845      #      $elements(IMG) -image $opts(-image) + \
846      #      $elements(TXT) -text  $opts(-text)
847      #
848      set elements_config [ list ]
849      for { set i 0 } { $i < $e_count } { incr i } {
850        set e_data [ lindex $col_data $i ]
851        set e_conf [ $self _build_element_config \
852                         [ lindex $col_elements $i ] $e_data ]
853        eval $tree item element configure $item $column_info($idx,ID) $e_conf
854      }
855      incr idx
856    }
857    # insertamos el item en el arbol
858    $tree item collapse $item
859    switch $insopt(-at) {
860      end - child {
861        $tree item lastchild $insopt(-relative) $item
862      }
863      next {
864        $tree item nextsibling $insopt(-relative) $item
865      }
866      prev {
867        $tree item prevsibling $insopt(-relative) $item
868      }
869      default {
870        error "invalid index, should be: end (child), prev or next"
871      }
872    }
873    return $item
874  }
875
876  method _build_element_config { etype data } {
877    array set def_prop {
878      text -text
879      image -image
880    }
881    if { [ catch { llength $data } ] } {
882      set data [ list $data ]
883    }
884    if { [ llength $data ] } {
885      if { [ llength $data ] == 1 } {
886        return [ list $elements($etype) $def_prop($etype) [ string trim [ lindex $data 0 ] ] ]
887      } elseif { [ string index [ lindex $data 0 ] 0 ] ne "-" } {
888        return [ list $elements($etype) $def_prop($etype) [ string trim $data ] ]
889      } else {
890        return [ linsert $data 0 $elements($etype) ]
891      }
892    } else {
893      return [ list $elements($etype) $def_prop($etype) "" ]
894    }
895  }
896
897  method EditCallback { callback item column elem t } {
898    if { $callback eq "accept" } {
899      $tree item element configure $item $column $elem -text $t
900    }
901  }
902
903  method OnButtonPress1 { x y } {
904   
905    set treeInfo [ $tree identify $x $y ]
906    if { $treeInfo eq "" } {
907      $tree selection clear
908      return
909    }
910   
911    if { [ llength $treeInfo ] != 6 } {
912      return
913    }
914   
[09b79f5]915    foreach { item I column C elem E } $treeInfo { }
[a8dad5c]916   
917    if { $item == "item" && \
918             $column == "column" && \
919             $elem == "elem" && \
[09b79f5]920             [ $tree selection includes $I ] } {
[a8dad5c]921
922      #puts "$itemNum = [$tree selection includes $itemNum ]"
923
[09b79f5]924      set idx_info $column_info(MAPIDX,$C)
925
[e259ad9]926      if { $column_info($idx_info,-editable) &&
927           ( $E eq "eTXT" || $E eq "eRECT" ) } {
[09b79f5]928        set exists [winfo exists $tree.entry]
[e259ad9]929        ::TreeCtrl::EntryOpen $tree $I $C eTXT
[09b79f5]930        if {!$exists} {
[e259ad9]931          $tree.entry configure -borderwidth 1 -justify left  \
[09b79f5]932              -background white -relief sunken
933          update idletask
934        }
935        $tree.entry selection clear
[e259ad9]936        scan [ $tree item bbox $I $C eRECT ] "%d %d %d %d" x1 y1 x2 y2
[09b79f5]937        set left $x1
938        set right $x2
[e259ad9]939        place $tree.entry -y $y1 -x $left -width [expr {$right - $left + 1}]
940        $tree.entry icursor [$tree.entry index @[expr {$x - $x1}]]
[09b79f5]941        return -code break
[a8dad5c]942
943        ::TreeCtrl::ButtonPress1 $tree $x $y
944        update
945        ::TreeCtrl::EntryOpen $tree $itemNum $columnNum $elemName
946        return -code break
947      }
948    }
949  }
950 
951  proc test_GetTextContents { T item } {
952    set textDump ""
953    # buscar las columnas que tiene elemento eTXT
954    foreach c [ $T column list ] {
955      # extraigo el style asociado al item
956      set style [ $T column cget $c -itemstyle ]
957      # extraigo los elementos del style
958      set elements [ $T style elements $style ]
959      # busco si eTXT es uno de esos elementos
960      if { [ lsearch $elements "eTXT" ] != -1 } {
961        append textDump [ $T item element cget $item $c eTXT -text ] " "
962      }
963    }
964    return $textDump
965  }
966
967  proc test_ListenSearch { t args } {
968    if { $::wtreeSearch eq "" } {
969      $t item configure all -visible 1
970    } else {
971      #puts "filtering on $::wtreeSearch"
972      #$t filterNodeSet [ $t item children root ] $::wtreeSearch
973      catch {
974        $t filterItems $::wtreeSearch
975      } msg
976      #puts "msg = $msg"
977    }
978  }
979
980  proc test_01 { w { istable 0 } } {
981    destroy $w
982    toplevel $w
983    if { 0 } {
984      frame $w.fsearch
985      label $w.fsearch.l -text "Search:"
986      entry $w.fsearch.e -textvar ::wtreeSearch
987      grid $w.fsearch.l -row 0 -column 1 -sticky "e"
988      grid $w.fsearch.e -row 0 -column 2 -sticky "ew"
989      grid columnconfigure $w.fsearch 0 -weight 1
990      grid $w.fsearch -row 0 -column 0 -sticky "snew"
991    }
992   
993    set T [ wtree $w.t -table $istable -filter yes -buttonstyle mac \
994                -columns [ list \
995                               { {image text} -tags ID   -label Identifier } \
[e259ad9]996                               { text -tags DESC -label Description -editable yes } ] ]
[a8dad5c]997   
998    $T notify bind $T <Expand-before> {
999      #puts "before openning %I"
1000    }
1001    $T notify bind $T <Collapse-after> {
1002      #puts "after closing %I"
1003    }
1004    $T notify bind $T <ActiveItem> {
[e259ad9]1005      puts "now active item is %c the previous was %p"
[a8dad5c]1006    }
1007    grid $T -row 1 -column 0 -sticky "snew"
1008    grid rowconfigure $w 1 -weight 1
1009    grid columnconfigure $w 0 -weight 1
1010
1011    if { 0 } {
1012      trace remove variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
1013      trace add    variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
1014    }
1015   
1016    set bugValue {
1017Serie AlgGetData(
1018 "Lla_MetCon.Atendi.Ser_TNuXXX_GHLAll.Ind.ALL_TLlTLl.XXX_RotXXX.XXX.XXX.XXX_EspEsp.XXX_SegSeg.XXX_PerMHo.XXX_FecDia",
1019 AlgProj1004)
1020}
1021    set id [ $T insert [list  [list folder-open "node bug"] [ list $bugValue ] ] ]
1022    set id [ $T insert [list [list folder-open "node 1"] [list "Desc node\n 1"]] ]
1023    set id [ $T insert { { folder-closed "node 2" } { "Desc node 2" } } \
1024                 -at end -relative [ expr { $istable ? "root" : $id } ] ]
1025    return .t01.t
1026  }
1027
1028  proc TestSearch { w } {
1029    destroy $w
1030    toplevel $w
1031
1032    set T [ wtree $w.t -table 1 -filter yes -buttonstyle mac \
1033                -columns [ list \
1034                               { {image text} -tags ID   -label Identifier } \
1035                               { text -tags DESC -label Description -editable yes} ] ]
1036    grid $T -row 1 -column 0 -sticky "snew"
1037    grid rowconfigure $w 1 -weight 1
1038    grid columnconfigure $w 0 -weight 1
1039   
1040    for { set r 0 } { $r < 100 } { incr r } {
1041      $T insert [list  [list folder-open "node $r"] [ list "Description for node $r" ] ]
1042    }
1043    return $T
1044  }
1045}
1046
[2c0158d]1047msgcat::mcload [ file join [ file dir [ info script ] ] msgs ]
1048
[a8dad5c]1049package provide wtree 1.0
Note: See TracBrowser for help on using the repository browser.