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

devel
Last change on this file was c320946, checked in by Pedro Gea <pgea@…>, 10 years ago

Refs #1938
Se elimina la llamada a break en los bind de wtree para posibilitar que se añadan más llamadas como las de bcomboboxplus (que dependen de wtree via blistboxplus).

bind $lb <Double-Button-1>  +[list $self _CloseList]
bind $lb <KeyPress-Return>  +[list $self _CloseList]

Esto permite que la función TOL: TclDialogComboPlus funcione adecuadamente.

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@7383 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]
685    bind $self <Return>               [mymethod KeyPressReturn %W]
686    bind $self <ButtonPress-3>        [mymethod Button3 %W %x %y %X %Y]
687    bind $self <Escape>               [mymethod KeyPressEscape]
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.