source: tol/tolbase/lib/wtree/wtree.tcl @ 6dc91d2

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

Refs #1933
Se incorporan las barras a la ventana de salida del inspector (tolbase.tcl).
Se corrige un bug con el botón derecho sobre el árbol del inspector (tolinspewtree2.tcl).
Se revisan los binds del editor de PRJs (tolprj.tcl) y se hace uso de la nueva opción de wtree: -selectcommand.
Se añade el método focus y dos opciones de configuración (selectcommand y bindtags) al wtree (wtree.tcl).

Se revisa listboxplus:

+ Se añaden algunas opciones auxiliares (para evitar errores)
+ Se suspenden las barras para que no salgan duplicadas (wtree ya las tiene)
+ Se redirigen los mecanismos de ordenación a los de wtree (véase _SortByColumn, por ejemplo).

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

  • Property mode set to 100644
File size: 34.9 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  constructor { args } {
284    # frame para el filtro de nodos
285    frame $win.frameFilter
286    label $win.frameFilter.l -text "[ msgcat::mc Search ]:"
287    menubutton $win.frameFilter.b -image mac-collapse \
288        -menu $win.frameFilter.b.m
289    menu $win.frameFilter.b.m \
290        -postcommand [ mymethod _postFilterMenu $win.frameFilter.b.m ] \
291        -tearoff no
292    frame $win.frameFilter.fentry -relief sunken -bd 1 -highlightthickness 1
293    set widgets(filter,entry) \
294        [ entry $win.frameFilter.fentry.e \
295              -bd 0 -highlightthickness 0 \
296              -textvar [ myvar textFilter ] ]
297    set widgets(filter,bcancel) \
298        [ button $win.frameFilter.fentry.b \
299              -bd 0 -highlightthickness 0 \
300              -image actcross16 \
301              -command [ mymethod _cancelFilter ] ]
302    grid $widgets(filter,entry) -row 0 -column 0 -sticky snew
303    grid $widgets(filter,bcancel) -row 0 -column 1 -sticky ew
304    grid rowconfigure $win.frameFilter.fentry 0 -weight 1
305    grid columnconfigure $win.frameFilter.fentry 0 -weight 1
306    grid remove $widgets(filter,bcancel)
307
308    bind $widgets(filter,entry) <Key-Return> [ mymethod _applyFilter ]
309    grid $win.frameFilter.l -row 0 -column 1 -sticky "e"
310    grid $win.frameFilter.b -row 0 -column 2 -sticky "e"
311    grid $win.frameFilter.fentry -row 0 -column 3 -sticky "ew"
312    grid columnconfigure $win.frameFilter 0 -weight 1
313    grid columnconfigure $win.frameFilter 3 -weight 1
314    grid $win.frameFilter -row 0 -column 0 -sticky "snew"
315
316    trace add variable [ myvar textFilter ] write [ mymethod _onChangeFilter ]
317
318    # frame para tree widget
319    frame $win.frameTree
320    grid $win.frameTree -row 1 -column 0 -sticky "snew"
321    grid rowconfigure $win.frameTree 0 -weight 1
322    grid columnconfigure $win.frameTree 0 -weight 1
323
324    install tree as treectrl $win.frameTree.t \
325        -highlightthickness 0 -borderwidth 0 \
326        -xscrollincrement 20 -showheader 1 -indent 19 \
327        -selectmode extended -showroot no \
328        -showrootbutton no -showbuttons yes -showlines yes \
329        -scrollmargin 16 -xscrolldelay "500 50" -yscrolldelay "500 50"
330   
331    bindtags $tree [linsert [bindtags $tree] 0 $win Wtree]
332
333    $tree notify install <<ItemSelected>>
334    $tree notify install <<Escape>>
335
336    $tree notify install <Header-invoke>
337
338    $tree notify install <Edit-begin>
339    $tree notify install <Edit-end>
340    $tree notify install <Edit-accept>
341
342    $tree notify bind $tree <Edit-begin> \
343        [ mymethod EditCallback begin %I %C %E %t ]
344    $tree notify bind $tree <Edit-end> \
345        [ mymethod EditCallback end %I %C %E %t ]
346    $tree notify bind $tree <Edit-accept>  \
347        [ mymethod EditCallback accept %I %C %E %t ]
348
349    bind $tree <ButtonPress-1> [ mymethod OnButtonPress1 %x %y ]
350    bind $tree <Control-ButtonPress-1> { }
351    bind $tree <Shift-ButtonPress-1> { }
352
353    set height [font metrics [$tree cget -font] -linespace]
354    if {$height < 18} {
355      set height 18
356    }
357    $tree configure -itemheight $height
358
359    # create the elements & styles
360
361    set elements(image) [ $tree element create eIMG image ]
362    set elements(text) [ $tree element create eTXT text \
363                           -fill [list $wtree::SystemHighlightText \
364                                      {selected focus}] ]
365    set elements(check) [ $tree element create eCHK window ]
366
367    set elements(rect) [ $tree element create eRECT rect \
368                             -showfocus yes -open ew \
369                             -fill [ list \
370                                         $wtree::SystemHighlight \
371                                         {selected focus} \
372                                         gray {selected !focus} ] ]
373
374
375    # vertical scrollbar
376    if { [ string compare $::tcl_version "8.5" ] >= 0 } {
377      set wscroll ttk::scrollbar
378    } else {
379      set wscroll scrollbar
380    }
381    install vscrollbar as $wscroll $win.frameTree.sv \
382        -orient vertical -command [list $win.frameTree.t yview]
383
384    # horizontal scrollbar
385    install hscrollbar as $wscroll $win.frameTree.sh \
386        -orient horizontal -command [list $win.frameTree.t xview]
387
388    grid $tree       -row 0 -column 0 -sticky nsew
389    grid $vscrollbar -row 0 -column 1 -sticky ns
390    grid $hscrollbar -row 1 -column 0 -sticky ew
391    grid columnconfigure $win 0 -weight 1
392    grid rowconfigure    $win 1 -weight 1
393
394    focus $tree
395
396    $self _configure_bindings
397    $self _conf-columns -columns $options(-columns)
398    $self _conf-contextmenu -contextmenu $options(-contextmenu)
399    $self configurelist $args
400    if { !$options(-filter) } {
401      grid remove $win.frameFilter
402    }
403    #return $win
404  }
405
406  method AcceptEdit { } {
407    ::TreeCtrl::EditClose $tree entry 1 0
408  }
409
410  method CancelEdit { } {
411    ::TreeCtrl::EditClose $tree entry 0 0
412  }
413
414  method getItemText { item } {
415    set itemText ""
416    # buscar las columnas que tiene elemento eTXT
417    foreach c [ $tree column list ] {
418      if { !$columnsToFilter($c) } {
419        continue
420      }
421      # extraigo el style asociado a la columna
422      set columnStyle [ $tree column cget $c -itemstyle ]
423      # extraigo los elementos del style
424      set styleElements [ $tree style elements $columnStyle ]
425      # busco si eTXT es uno de esos elementos
426      if { [ lsearch $styleElements "eTXT" ] != -1 } {
427        # el root no tiene styles
428        catch {
429          append itemText [ $tree item element cget $item $c eTXT -text ] " "
430        }
431      }
432    }
433    return $itemText
434  }
435
436  method getColumnLabels { args } {
437    array set opts {
438      -onlyvisible yes
439      -hastext no
440    }
441    array set opts $args
442    set labels [ list ]
443    foreach c [ $tree column list ] {
444      if { $opts(-hastext) } {
445        # extraigo el style asociado a la columna
446        set columnStyle [ $tree column cget $c -itemstyle ]
447        # extraigo los elementos del style
448        set styleElements [ $tree style elements $columnStyle ]
449        # busco si eTXT es uno de esos elementos
450        if { [ lsearch $styleElements "eTXT" ] == -1 } {
451          # no tiene elemento Text
452          continue
453        }
454      }
455      if { $opts(-onlyvisible) && ![ $tree column cget $c -visible ] } {
456        # no es visible
457        continue
458      }
459      lappend labels [ list -idx $c -text [ $tree column cget $c -text ] ]
460    }
461    return $labels
462  }
463
464  method _postFilterMenu { m } {
465    $m delete 0 end
466    foreach c [ $self getColumnLabels ] {
467      array set cinfo $c
468      $m add checkbutton -label $cinfo(-text) -onvalue 1 -offvalue 0 \
469          -variable [ myvar columnsToFilter($cinfo(-idx)) ]
470    }
471  }
472
473  method _setWaitCursor { } {
474    $win configure -cursor watch
475    $widgets(filter,entry) configure -cursor watch
476    update
477  }
478
479  method _setNormalCursor { } {
480    $win configure -cursor {}
481    $widgets(filter,entry) configure -cursor {}
482    update
483  }
484
485  method _cancelFilter { } {
486    set textFilter ""
487    grid remove $widgets(filter,bcancel)
488  }
489
490  method filterItems { text } {
491    #puts "filterItems $text"
492    $self _setWaitCursor
493    set negativeWords [ list ]
494    set positiveWords [ list ]
495    set negating 0
496    set wordList [regexp -inline -all -- {\S+} $text]
497    foreach word $wordList {
498      if { $negating } {
499        lappend negative $word
500        set negating 0
501      } elseif { [ string index $word 0 ] eq "-" } {
502        # se pide negar la palabra
503        set _word [ string range $word 1 end ]
504        if { $_word eq "" } {
505          set negating 1
506        } else {
507          lappend negativeWords $_word
508        }
509      } else {
510        lappend positiveWords $word
511      }
512    }
513    # regexp {^(?!(.*(foo|bar).*))} atttcokebar
514    if { [ llength $negativeWords ] } {
515      set negativeRegExp "^(?!(.*([ join $negativeWords | ]).*))"
516    } else {
517      set negativeRegExp ""
518    }
519    set breakFilter 0
520    set numItemsFiltered 0
521    set listFilterOn [ list ]
522    set listFilterOff [ list ]
523    $self _filterNodeSet [ $tree item children root ] \
524        $positiveWords $negativeRegExp
525    $self _setNormalCursor
526  }
527
528  method _filterNodesMatching { } {
529    if { [ llength $listFilterOff ] } {
530      $tree item configure [ list list $listFilterOff ] -visible 0
531      set listFilterOff [ list ]
532    }
533    if { [ llength $listFilterOn ] } {
534      $tree item configure [ list list $listFilterOn ] -visible 1
535      if { !$options(-table) } {
536        foreach nid $listFilterOn {
537          $tree item configure \
538              [ list list [ $tree item ancestors $nid ] ] -visible 1
539        }
540      }
541      set listFilterOn [ list ]
542    }
543  }
544
545  method _filterNodeSet { nodes positiveWords negativeRegExp } {
546    #puts "filterNodeSet '$nodes' '$positiveWords'"
547    # recorro cada uno de los nodos
548    foreach n $nodes {
549      # filtro el nodo
550
551      #esto es para el filtrado automatico incremental
552      if { $breakFilter } {
553        return 0
554      }
555
556      $self _filterNode $n $positiveWords $negativeRegExp
557      # desciendo a los hijos
558      $self _filterNodeSet [ $tree item children $n ] \
559          $positiveWords $negativeRegExp
560
561      if { 0 } {
562      if { [ $self _filterNode $n $positiveWords $negativeRegExp ] } {
563        # si el resultado es visible desciendo y filtro a los hijos
564        $self _filterNodeSet [ $tree item children $n ] \
565            $positiveWords $negativeRegExp
566      }
567      }
568
569      # esto es para el filtrado automatico incremental
570      incr numItemsFiltered
571      if { !$breakFilter && !( $numItemsFiltered % 1000 ) } {
572        # refresco la cola de eventos
573        $self _filterNodesMatching
574        update
575      }
576    }
577    $self _filterNodesMatching
578    return 1
579  }
580
581  method _filterNode { node positiveWords negativeRegExp } {
582    #puts "filterNode '$node' '$positiveWords' '$negativeRegExp'"
583    set itemText [ $self getItemText $node ]
584    #puts "itemText $itemText"
585    # hago match, luego hago algo mas sofisticado con regexp
586    # set visible [ string match "*${text}*" $itemText ]
587    if { [ string length $negativeRegExp ] &&
588         ![ regexp -nocase $negativeRegExp $itemText ] } {
589      # itemText contiene alguna de las palabra prohibidas
590      #puts "itemText contiene alguna de las palabra prohibidas"
591      #$tree item configure $node -visible 0
592      lappend listFilterOff $node
593      return 0
594    }
595    foreach word $positiveWords {
596      if { ![ string match -nocase "*${word}*" $itemText ] } {
597        # itemText no contiene una de las palabras deseadas
598        #puts "itemText no contiene una de las palabras deseadas: $word"
599        #puts "string match \"*${word}*\" $itemText"
600        #$tree item configure $node -visible 0
601        lappend listFilterOff $node
602        return 0
603      }
604    }
605    # finalmente todas las palabras deseadas y niguna de las
606    # prohibidas estan.
607    #$tree item configure $node -visible 1
608    lappend listFilterOn $node
609    return 1
610  }
611
612  method _applyFilter { } {
613    set columnFound -1
614    foreach c [ array name columnsToFilter ] {
615      if { $columnsToFilter($c) } {
616        set columnFound $c
617        break
618      }
619    }
620    if { $columnFound == -1 } {
621      return
622    }
623    if { $textFilter eq "" } {
624      $tree item configure all -visible 1
625    } else {
626      $self filterItems $textFilter
627    }
628  }
629
630  method _onChangeFilter { args } {
631    if { $textFilter eq "" } {
632      grid remove $widgets(filter,bcancel)
633      $tree item configure all -visible 1
634    } else {
635      #puts "filtering on $textFilter"
636      grid $widgets(filter,bcancel)
637      update
638      after cancel $idWaitFilter
639      set breakFilter 1
640      set idWaitFilter [ after 500 $self _scheduleFilter ]
641    }
642  }
643
644  method _scheduleFilter { } {
645    after cancel $idIdleFilter
646    set idIdleFilter [ after idle $self _applyFilter ]
647  }
648
649  method _configure_bindings { } {
650
651    bind $tree <Double-ButtonPress-1> "[mymethod DoubleButton1 %W %x %y] ; break"
652    bind $tree <Return> "[mymethod KeyPressReturn %W] ; break"
653
654    bind $tree <ButtonPress-3> [ mymethod Button3 %W %x %y %X %Y]
655
656    bind $tree <Escape> "[mymethod KeyPressEscape] ; break"
657
658    $tree notify bind $tree <Header-invoke> [mymethod SortbyColumn %W %C]
659
660    $tree notify bind $vscrollbar <Scroll-y> { %W set %l %u }
661    bind $vscrollbar <ButtonPress-1> [list focus $tree]
662
663    $tree notify bind $hscrollbar <Scroll-x> { %W set %l %u }
664    bind $hscrollbar <ButtonPress-1> [list focus $tree]
665
666    ::autoscroll::autoscroll $vscrollbar
667    ::autoscroll::autoscroll $hscrollbar
668  }
669
670  method _conf-table { _ yesno } {
671    if { $yesno } {
672      set options(-table) 1
673      $tree configure -showbuttons 0 -showlines 0
674    } else {
675      set options(-table) 0
676      $tree configure -showbuttons 1 -showlines 1
677    }
678  }
679
680  method _conf-buttonstyle { _ bs } {
681    set valid_bstyles [ array names wtree::ButtonImageConf ]
682    if { [ lsearch $valid_bstyles $bs ] == -1 } {
683      error "invalid button style $bs should be one of $valid_bstyles"
684    } else {
685      set options(-buttonstyle) $bs
686      $tree configure -buttonimage $wtree::ButtonImageConf($bs)
687    }
688  }
689
690  method _conf-contextmenu { _ cmenu } {
691    set options(-contextmenu) $cmenu
692  }
693
694  method _conf-filter { _ value } {
695    if { [ string is boolean $value ] } {
696      if { ( $options(-filter)  && $value ) ||
697           ( !$options(-filter) && !$value ) } {
698        return
699      }
700      set options(-filter) $value
701      if { $value } {
702        grid $win.frameFilter
703      } else {
704        grid remove $win.frameFilter
705      }
706      set textFilter ""
707    } else {
708      error "invalid boolean value $value"
709    }
710  }
711
712  method _conf-columnfilter { _ value } {
713    # hago el chequeo en el postcommand del menu que despliegua las
714    # opciones y tambien a la hora de filtrar
715    set options(-columnfilter) $value
716        foreach {idx v} $value {
717          set columnsToFilter($idx) $v
718    }
719  }
720 
721  method _conf-bindtags { _ addtags } {
722    set options(-bindtags) $addtags
723    bindtags $self [concat $addtags [bindtags $self]]
724        bindtags $tree [concat $addtags [bindtags $tree]]
725  }
726 
727  method _conf-selectcommand { _ cmd } {
728    set options(-selectcommand) $cmd
729    if { $cmd eq "" } return
730    $self notify bind $self <Selection> $cmd
731  } 
732
733  #
734  # columnsSpec es una lista de "columnas" y una columna puede ser
735  #
736  #  - un unico elemento con valor: image, text o check
737  #
738  #  - una lista de elementos, donde cada elemento puede estar dado
739  #    por su tipo.
740  #
741  #    { image text }
742  #
743  #    en un futuro se le puede adjuntar propiedades de layout.
744  #
745  #    cada columna crea un stilo y el layout esa definido por el
746  #    orden en que se describen los elementos de la columna.
747  #
748
749  method _conf-columns { _ columnsSpec } {
750    if { $columnsSpec eq "" } {
751      set columnsSpec [ list { text -tags C0 -label "Column 0" -editable 0 } ]
752    }
753    set options(-columns) $columnsSpec
754    # elimino columnas
755    $tree column delete all
756    # elimino estilos
757    eval $tree style delete [ $tree style names ]
758
759    # limpio la informacion interna de las columnas
760    array unset column_info
761    array unset style_info
762    # crear las columnas y estilos
763    set idx 0
764    foreach col_spec $columnsSpec {
765      set col_elements [ lindex $col_spec 0 ]
766      # opciones por omision.
767      array set opts [ list -tags C${idx} -label "Column $idx" -image "" \
768                           -editable 0 ]
769      # opciones dadas en la llamada.
770      array set opts [ lrange $col_spec 1 end ]
771      set colID [ $tree column create \
772                      -text $opts(-label) \
773                      -image $opts(-image) \
774                      -itembackground "\#e0e8f0 {}" \
775                      -tags $opts(-tags) ]
776      # "
777      set column_info($idx,ID) $colID
778      set column_info(MAPIDX,$colID) $idx
779      set column_info($idx,elements) $col_elements
780      set column_info($idx,-editable) $opts(-editable)
781      # creamos el estilo para esta columna
782      set s [ $tree style create style$colID ]
783      set column_info($idx,style) $s
784      set eList [ list ]
785      # selecciono los id's de los elementos de esta columna
786      set eIMG ""
787      foreach e $col_elements {
788        lappend eList $elements($e)
789        if { $e eq "image" } {
790          set eIMG $elements($e)
791        }
792      }
793      # asocio los elementos al style
794      $tree style elements $s [ eval list eRECT $eList ]
795
796      # defino el layout de los elementos
797      $tree style layout   $s eRECT -union $eList -iexpand nswe -ipadx 2
798      if { $eIMG ne "" } {
799        $tree style layout   $s $eIMG -iexpand ns -padx 2
800      }
801      # asocio el style a la columna
802      $tree column configure $colID -itemstyle $s
803      incr idx
804    }
805    # Specifies a column description that determines which column
806    # displays the expand/collapse buttons and connecting lines between
807    # items. The default is unspecified.
808    $tree configure -treecolumn $column_info(0,ID)
809    # extraigo las columnas filtrables
810    set first ""
811    set hasCheck 0
812    foreach c [ $self getColumnLabels -onlyvisible yes -hastext yes ] {
813      array set cinfo $c
814      if { $first eq "" } {
815        set first $cinfo(-idx)
816      }
817      if { [ info exists columnsToFilter($cinfo(-idx)) ] } {
818        set _columnsToFilter($cinfo(-idx)) $columnsToFilter($cinfo(-idx))
819      } else {
820        set _columnsToFilter($cinfo(-idx)) 0
821      }
822      if { $_columnsToFilter($cinfo(-idx)) } {
823        set hasCheck 1
824      }
825    }
826    if { !$hasCheck } {
827      set _columnsToFilter($first) 1
828    }
829    array unset columnsToFilter
830    array set columnsToFilter [ array get _columnsToFilter ]
831  }
832
833  method itemcolumn { item col args } {
834    set colid [ $tree column id $col ]
835    if { $colid eq "" } {
836      puts "$col does not reference a valid column"
837      return
838    }
839    set ret {}
840    foreach elemdata $args {
841      set v ""
842      foreach { e v } $elemdata break
843      if { $v eq "" } {
844        array set def_prop {
845          text -text
846          image -image
847        }
848        lappend ret [ list $e [ $tree item element cget $item $colid $elements($e) $def_prop($e) ] ]
849      } else {
850        set e_conf [ $self _build_element_config $e $v ]
851        eval $tree item element configure $item $colid $e_conf
852      }
853    }
854    return $ret
855  }
856
857  method find { {first first} {last last} } {
858    if { [string match "-*" $first] } {
859      error "no flags allowed!"
860    } else {
861      return [$tree item range $first $last]
862    }
863  }
864 
865  method selection { args } {
866    if { [lindex $args 0] eq "set" } {
867      switch [llength $args] {
868        1 { $tree selection clear all }
869        2 { $tree selection modify [lindex $args 1] all }
870        3 { $tree selection modify [$tree item range [lindex $args 1] [lindex $args 2]] all }
871        default {
872          error "invalid number of arguments"       
873        }       
874      }
875    } elseif { [lindex $args 0] eq "clearall" } {
876      $tree selection clear all
877    }  {
878      eval $tree selection $args
879    } 
880  }
881 
882  method index { args } {
883    error "invalid method index"
884  }
885 
886  method move { item how dest } {
887    if { $item eq $dest } { return }
888    switch $how {
889      into {
890        $tree item lastchild $dest $item
891      }
892      after {
893        if { $dest eq "" } {
894          $tree item prevsibling $item [$tree item nextsibling $item]
895        } else {
896          $tree item nextsibling $dest $item
897          #$tree item prevsibling $item $dest
898        }
899      }
900      before {
901        if { $dest eq "" } {
902          $tree item nextsibling $item [$tree item prevsibling $item]
903        } else {
904          $tree item prevsibling $dest $item
905          #$tree item nextsibling $item $dest
906        }
907      }
908      default {
909        error "invalid argument 'how', should be: into (child), before or after"
910      }
911    }
912  } 
913 
914  method insert { data args } {
915    array set insopt {
916      -at end
917      -relative root
918      -open 0
919      -button 1
920      -tags ""
921    }
922    array set insopt $args
923    # creamos el item, -button yes debe depender si el widget no es
924    # tipo tabla
925    set item [ $tree item create \
926                   -button $insopt(-button) \
927                   -tags $insopt(-tags) ]
928    set idx 0
929    set idx_last  [ expr [ llength $options(-columns) ] - 1 ]
930    # 'data' es una lista con informacion para cada columna. Lo que
931    # venga de mas en data no lo considero.
932    foreach col_data [ lrange $data 0 $idx_last ] {
933      # los elementos de esta columna
934      set col_elements $column_info($idx,elements)
935      # col_data debe ser una lista de la misma cantidad de elementos
936      set e_count [ llength $col_elements ]
937      # esta lista es para usar la sintaxis de configurar + de un
938      # elemento al estilo
939      #
940      # $tree item element configure $item $columnID($idx) \
941      #      $elements(IMG) -image $opts(-image) + \
942      #      $elements(TXT) -text  $opts(-text)
943      #
944      set elements_config [ list ]
945      for { set i 0 } { $i < $e_count } { incr i } {
946        set e_data [ lindex $col_data $i ]
947        set e_conf [ $self _build_element_config \
948                         [ lindex $col_elements $i ] $e_data ]
949        eval $tree item element configure $item $column_info($idx,ID) $e_conf
950      }
951      incr idx
952    }
953    # insertamos el item en el arbol
954    $tree item collapse $item
955    switch $insopt(-at) {
956      end - child {
957        $tree item lastchild $insopt(-relative) $item
958      }
959      next {
960        $tree item nextsibling $insopt(-relative) $item
961      }
962      prev {
963        $tree item prevsibling $insopt(-relative) $item
964      }
965      default {
966        error "invalid index, should be: end (child), prev or next"
967      }
968    }
969    return $item
970  }
971
972  method _build_element_config { etype data } {
973    array set def_prop {
974      text -text
975      image -image
976    }
977    if { [ catch { llength $data } ] } {
978      set data [ list $data ]
979    }
980    if { [ llength $data ] } {
981      if { [ llength $data ] == 1 } {
982        return [ list $elements($etype) $def_prop($etype) [ string trim [ lindex $data 0 ] ] ]
983      } elseif { [ string index [ lindex $data 0 ] 0 ] ne "-" } {
984        return [ list $elements($etype) $def_prop($etype) [ string trim $data ] ]
985      } else {
986        return [ linsert $data 0 $elements($etype) ]
987      }
988    } else {
989      return [ list $elements($etype) $def_prop($etype) "" ]
990    }
991  }
992
993  method EditCallback { callback item column elem t } {
994    if { $callback eq "accept" } {
995      $tree item element configure $item $column $elem -text $t
996    }
997  }
998
999  method OnButtonPress1 { x y } {
1000
1001    set treeInfo [ $tree identify $x $y ]
1002    if { $treeInfo eq "" } {
1003      $tree selection clear
1004      return
1005    }
1006
1007    if { [ llength $treeInfo ] != 6 } {
1008      return
1009    }
1010
1011    foreach { item I column C elem E } $treeInfo { }
1012
1013    if { $item == "item" && \
1014             $column == "column" && \
1015             $elem == "elem" && \
1016             [ $tree selection includes $I ] } {
1017
1018      #puts "$itemNum = [$tree selection includes $itemNum ]"
1019
1020      set idx_info $column_info(MAPIDX,$C)
1021
1022      if { $column_info($idx_info,-editable) &&
1023           ( $E eq "eTXT" || $E eq "eRECT" ) } {
1024        set exists [winfo exists $tree.entry]
1025        ::TreeCtrl::EntryOpen $tree $I $C eTXT
1026        if {!$exists} {
1027          $tree.entry configure -borderwidth 1 -justify left  \
1028              -background white -relief sunken
1029          update idletask
1030        }
1031        $tree.entry selection clear
1032        scan [ $tree item bbox $I $C eRECT ] "%d %d %d %d" x1 y1 x2 y2
1033        set left $x1
1034        set right $x2
1035        place $tree.entry -y $y1 -x $left -width [expr {$right - $left + 1}]
1036        $tree.entry icursor [$tree.entry index @[expr {$x - $x1}]]
1037        return -code break
1038
1039        ::TreeCtrl::ButtonPress1 $tree $x $y
1040        update
1041        ::TreeCtrl::EntryOpen $tree $itemNum $columnNum $elemName
1042        return -code break
1043      }
1044    }
1045  }
1046
1047  proc test_GetTextContents { T item } {
1048    set textDump ""
1049    # buscar las columnas que tiene elemento eTXT
1050    foreach c [ $T column list ] {
1051      # extraigo el style asociado al item
1052      set style [ $T column cget $c -itemstyle ]
1053      # extraigo los elementos del style
1054      set elements [ $T style elements $style ]
1055      # busco si eTXT es uno de esos elementos
1056      if { [ lsearch $elements "eTXT" ] != -1 } {
1057        append textDump [ $T item element cget $item $c eTXT -text ] " "
1058      }
1059    }
1060    return $textDump
1061  }
1062
1063  proc test_ListenSearch { t args } {
1064    if { $::wtreeSearch eq "" } {
1065      $t item configure all -visible 1
1066    } else {
1067      #puts "filtering on $::wtreeSearch"
1068      #$t filterNodeSet [ $t item children root ] $::wtreeSearch
1069      catch {
1070        $t filterItems $::wtreeSearch
1071      } msg
1072      #puts "msg = $msg"
1073    }
1074  }
1075
1076  proc test_01 { w { istable 0 } } {
1077    destroy $w
1078    toplevel $w
1079    if { 0 } {
1080      frame $w.fsearch
1081      label $w.fsearch.l -text "Search:"
1082      entry $w.fsearch.e -textvar ::wtreeSearch
1083      grid $w.fsearch.l -row 0 -column 1 -sticky "e"
1084      grid $w.fsearch.e -row 0 -column 2 -sticky "ew"
1085      grid columnconfigure $w.fsearch 0 -weight 1
1086      grid $w.fsearch -row 0 -column 0 -sticky "snew"
1087    }
1088
1089    set T [ wtree $w.t -table $istable -filter yes -buttonstyle mac \
1090                -columns [ list \
1091                               { {image text} -tags ID   -label Identifier } \
1092                               { text -tags DESC -label Description -editable yes } ] ]
1093
1094    $T notify bind $T <Expand-before> {
1095      #puts "before openning %I"
1096    }
1097    $T notify bind $T <Collapse-after> {
1098      #puts "after closing %I"
1099    }
1100    $T notify bind $T <ActiveItem> {
1101      puts "now active item is %c the previous was %p"
1102    }
1103    grid $T -row 1 -column 0 -sticky "snew"
1104    grid rowconfigure $w 1 -weight 1
1105    grid columnconfigure $w 0 -weight 1
1106
1107    if { 0 } {
1108      trace remove variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
1109      trace add    variable ::wtreeSearch write "::wtree::test_ListenSearch $T"
1110    }
1111
1112    set bugValue {
1113Serie AlgGetData(
1114 "Lla_MetCon.Atendi.Ser_TNuXXX_GHLAll.Ind.ALL_TLlTLl.XXX_RotXXX.XXX.XXX.XXX_EspEsp.XXX_SegSeg.XXX_PerMHo.XXX_FecDia",
1115 AlgProj1004)
1116}
1117    set id [ $T insert [list  [list folder-open "node bug"] [ list $bugValue ] ] ]
1118    set id [ $T insert [list [list folder-open "node 1"] [list "Desc node\n 1"]] ]
1119    set id [ $T insert { { folder-closed "node 2" } { "Desc node 2" } } \
1120                 -at end -relative [ expr { $istable ? "root" : $id } ] ]
1121    return .t01.t
1122  }
1123
1124  proc TestSearch { w } {
1125    destroy $w
1126    toplevel $w
1127
1128    set T [ wtree $w.t -table 1 -filter yes -buttonstyle mac \
1129                -columns [ list \
1130                               { {image text} -tags ID   -label Identifier } \
1131                               { text -tags DESC -label Description -editable yes} ] ]
1132    grid $T -row 0 -column 0 -sticky "snew"
1133    button $w.btn1 -text "OK" \
1134        -command [ string map [ list "%T" $T ] {
1135          %T AcceptEdit
1136        } ]
1137    button $w.btn2 -text "Cancel" \
1138        -command [ string map [ list "%T" $T ] {
1139          %T CancelEdit
1140        } ]
1141    grid $w.btn1 -row 1 -column 0
1142    grid $w.btn2 -row 1 -column 1
1143    grid rowconfigure $w 1 -weight 1
1144    grid columnconfigure $w 0 -weight 1
1145
1146    for { set r 0 } { $r < 100 } { incr r } {
1147      $T insert [list  [list folder-open "node $r"] [ list "Description for node $r" ] ]
1148    }
1149    return $T
1150  }
1151}
1152
1153msgcat::mcload [ file join [ file dir [ info script ] ] msgs ]
1154
1155package provide wtree 1.0
Note: See TracBrowser for help on using the repository browser.