source: tol/tolbase/lib/wtree/wtree.tcl @ 606ed76

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

Se eliminan espacios en blanco al final de línea.

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

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