source: tol/tolbase/lib/wtree/wtree.tcl @ 935c832

1941-docker-image-to-testdevel
Last change on this file since 935c832 was cc64b09, checked in by Pedro Gea <pgea@…>, 10 years ago

Refs #1933
Se revisan los binds respecto a wtree y su treectrl.
Se quitan los métodos específicos para la creación de columnas e inserción de nodos que quedaban en tolinspe.
Se añade un update del nodo de paquetes para que se actualice ante un "require".
Se revisan los métodos de botón derecho para que aparezcan sólo sobre los objetos seleccionados.
Se añade un método a wtree para gestionar el color de todas las columnas de un nodo.
Se añaden las columnas al root de wtree.

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

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