source: tol/tolbase/lib/wtree/wtree.tcl @ 9cd63c3

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

refs #1443, metodo para forzar que se acepten/rechacen los cambios que se estan editando

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