Improved handling of case where child is killed by a signal
[tcl-tlc.git] / scripts / browse_treeview.itcl
blob416583180b8e35d46c4d57dbde54bb1bc4e93b2a
1 # vim: ft=tcl foldmarker=<<<,>>>
3 option add Browse_treeview_flat.width 130 widgetDefault
4 option add Browse_treeview_flat.height 200 widgetDefault
6 class tlc::Browse_treeview_flat {
7 inherit tlc::Mywidget tlc::Browsegeneric tlc::Baselog
9 constructor {args} {}
10 destructor {}
12 itk_option define -actionspos actionsPos ActionsPos \
13 {11,10 -fill x} need_relayout
14 itk_option define -actions_orient actionsOrient ActionsOrient h
15 itk_option define -action_buttonwidth actionButtonWidth ActionButtonWidth 9
16 itk_option define -column_widths columnWidths ColumnWidths {}
17 itk_option define -yscrollmode yScrollMode YScrollMode static
18 itk_option define -xscrollmode xScrollMode XScrollMode dynamic
20 public {
21 # inherited, overridden interface
22 method refresh {}
23 method clear {}
24 method add_item {}
25 method update_item {}
26 method remove_item {}
27 method select_items {itemlist}
28 method get_selected_items {}
29 method action_add {args}
30 method action_attach_signal {label signal {sense normal}}
31 method action_add_supported {}
32 method action_tips {args}
33 method filter_add {label varinfo {matchcode 1}}
34 method filter_add_standard {label varinfo matchcode}
35 method filter_data {args}
36 method filter_item {item}
37 method filter_add_cb {cb}
39 # specific to this implementation
40 variable hide_id_col 0
41 variable checkvars {}
42 variable col_justifies ""
43 variable col_paddings ""
44 variable col_widths ""
45 variable hidden_cols {}
46 variable item_bindings {}
47 # for the inbuilt functions: choose case insenstivity or not
48 # for the inbuilt functions: toggle display of case-sensitive checkbutton
49 variable trim_filter 0
50 variable filter_mode 1
51 variable tree_style "" {style_tree}
53 method sortcolumn {colname}
54 method hideshowcol {colname hidden}
55 method filter {searchstring {matchcode ""} {filternames {}}}
57 method get_criteria_form {}
58 method reload_from_criteria_form {}
59 # site references
60 method getsite {sitename}
61 method gen_popup {{pop_commands ""}}
62 method style_tree {{stylename ""}}
63 method get_tv_ref {}
64 method column_configure {colname args}
65 method component_actions {}
68 protected {
69 method relayout {}
72 private {
73 variable tv
74 variable tb
75 variable datatree
76 variable shadowdata {}
77 variable treeheaders {}
78 variable hidden_nodes ""
79 variable cf
80 variable itemform {}
81 variable itemform_header ""
82 variable last_headers {}
83 variable current_edit {}
84 variable filterform {}
85 variable filterform_schema {}
86 variable filtercode
87 variable filterframe
88 variable filter_after_id ""
89 variable last_filter
90 variable or_filter_ok_ids ""
91 variable filtered_nodes
92 variable filter_cbs {}
93 variable cached_col_widths
94 variable selection_present
95 variable delayed_select_afterid ""
97 method onclick {nodeid numclicks}
98 method selection_changed {}
99 method render_filters {}
100 method form_filter {{form_data {}}}
101 method partial_match {filterarray rowarray type
102 {applicable_cols {}} {filternames {}}}
103 method cache_col_widths {}
104 method restore_col_widths {}
105 method process_delayed_select {cb data row row_id}
110 configbody tlc::Browse_treeview_flat::yscrollmode { #<<<1
111 $w.treeframe.hs configure -mode $itk_option(-yscrollmode)
115 configbody tlc::Browse_treeview_flat::xscrollmode { #<<<1
116 $w.treeframe.hs configure -mode $itk_option(-xscrollmode)
120 configbody tlc::Browse_treeview_flat::actions_orient { #<<<1
121 if {[winfo exists $w.actionsframe.actions]} {
122 $w.actionsframe.actions configure -orient $itk_option(-actions_orient)
127 configbody tlc::Browse_treeview_flat::action_buttonwidth { #<<<1
128 #puts "Browse_treeview_flat: Reconfiguring action_buttonwidth: ($itk_option(-action_buttonwidth))"
129 if {[winfo exists $w.actionsframe.actions]} {
130 $w.actionsframe.actions configure \
131 -buttonwidth $itk_option(-action_buttonwidth)
136 configbody tlc::Browse_treeview_flat::column_widths { #<<<1
137 foreach {colname width} $itk_option(-column_widths) {
138 catch {$tv column configure $colname -width $width}
143 set ::fooseq 0
144 body tlc::Browse_treeview_flat::constructor {args} { #<<<1
145 tlc::Signal #auto selection_present -name "$this selection_present"
147 set datatree [blt::tree create]
148 # (a) create the treeview <<<
149 # (i) create widget
150 set f [frame $w.treeframe]
152 # puts "constructing:
153 # blt::treeview $f.list -width 130 -height 200 \\
154 # -xscrollcommand [list $f.hs set] \\
155 # -yscrollcommand [list $f.vs set] \\
156 # -hideroot 1 \\
157 # -exportselection 1 \\
158 # -selectcommand [code $this selection_changed] \\
159 # -flat 1 \\
160 # -tree $datatree
163 incr ::fooseq
164 itk_component add listview {
165 blt::treeview $f.list$::fooseq
167 keep -takefocus -selectmode -width -height
168 #rename -background -textbackground textBackground TextBackground
170 set tv $itk_component(listview)
171 $tv configure \
172 -xscrollcommand [list $f.hs set] \
173 -yscrollcommand [list $f.vs set] \
174 -hideroot 1 \
175 -exportselection 0 \
176 -selectcommand [code $this selection_changed] \
177 -flat 1 \
178 -tree $datatree
180 $tv style checkbox check \
181 -onvalue 1 -offvalue 0 \
182 -showvalue 0
184 $tv column configure treeView -hide 1
185 tlc::myscrollbar_win32 $f.hs -orient h -command [list $tv xview] -mode static
186 tlc::myscrollbar_win32 $f.vs -orient v -command [list $tv yview] -mode static
188 # Geometry <<<
189 blt::table $f \
190 $tv 1,1 -fill both \
191 $f.hs 2,1 -fill x \
192 $f.vs 1,2 -fill y
193 blt::table configure $f r2 c2 -resize none
194 # Geometry >>>
196 # Setup mousewheel x and y scrolling <<<
197 bind $tv <Button-4> {if {!$tk_strictMotif} {%W yview scroll -2 units}}
198 bind $tv <Button-5> {if {!$tk_strictMotif} {%W yview scroll 2 units}}
199 bind $tv <Control-Button-4> {if {!$tk_strictMotif} {%W xview scroll -2 units}}
200 bind $tv <Control-Button-5> {if {!$tk_strictMotif} {%W xview scroll 2 units}}
201 bind $tv <Key-F5> [code $this refresh]
202 bind $w <Key-F5> [code $this refresh]
203 # Setup mousewheel x and y scrolling >>>
204 # (a) create the treeview >>>
206 eval itk_initialize $args
208 # (b) create the criteria form (server-side searching) <<<
209 set f [frame $w.criteriaframe]
210 if {$show_criteria} {
211 label $f.criterialabel -text $criteria_label
213 set cf [tlc::Form $f.criteriaform -schema [$datasource get_criteria]]
214 $f.criteriaform set_data [$datasource get_criteria_values]
216 tlc::Tools $f.criteria_tools
217 $f.criteria_tools add "Refresh" \
218 [code $this reload_from_criteria_form] right
219 $f.criteria_tools attach_signal "Refresh" \
220 [$f.criteriaform signal_ref form_dirty]
222 if {$criteria_label != ""} {
223 blt::table $f \
224 $f.criterialabel 1,1 -fill x \
227 blt::table $f \
228 $f.criteriaform 2,1 -fill x -anchor n \
229 $f.criteria_tools 3,1 -fill x
230 blt::table configure $f r1 r3 -resize none
232 # (b) create the criteria form (server-side searching) >>>
234 # (c) create the toolbar (if necessary) <<<
235 set tb [tlc::Tools $w.actionsframe]
236 # (c) create the toolbar (if necessary) >>>
238 # (d) the filter (client-side searching) <<<
239 set filterframe [frame $w.filterframe]
240 # (d) the filter (client-side searching) >>>
242 # (e) layout with table <<<
243 relayout
244 # (e) end layout with table >>>
246 # get an item dialogue
247 set itemform [tlc::Formdialog $w.itemform -mode schema]
251 body tlc::Browse_treeview_flat::destructor {} { #<<<1
252 #destroy $w.treeframe.list ;# DIE DIE DIE
253 after cancel $delayed_select_afterid; set delayed_select_afterid ""
254 if {[winfo exists $w.itemform]} {
255 destroy $w.itemform
260 body tlc::Browse_treeview_flat::refresh {} { #<<<1
261 set headers {}
263 #after idle [code $this onclick "" 1] ;# Fake click to blank any existing selection
265 set id_colname [lindex [$datasource get_id_column] 1]
266 array set old_sel_ids {}
267 foreach row [get_selected_items] {
268 array unset tmp
269 array set tmp $row
270 if {[info exists tmp($id_colname)]} {
271 set row_id $tmp($id_colname)
272 set old_sel_ids($row_id) 1
274 array unset tmp
277 # get data to load <<<
278 set cv [$datasource get_criteria_values]
279 set list_data [$datasource get_labelled_list $cv headers]
280 set last_headers $headers
281 # get data to load >>>
283 clear
285 # if the data's headers have changed, update the treeview columns <<<
286 if {$headers != $treeheaders} {
287 set tmp [lrange [$tv column names] 1 end]
288 # but why? <<<
289 # the first "name" returned should be treeview,
290 # but is something not quite
291 # that; still we don't want to remove it
292 # (unless we are seeking segfaults)
293 # note that the placement of this column is only because
294 # i have chosen to insert
295 # after it -- it's not a hard rule that the first col
296 # is the treeview >>>
297 # remove old headers <<<
298 foreach col $tmp {
299 $tv column delete $col
301 # remove old headers >>>
302 # get format options for headers <<<
303 # (which may have, conceivably, changed, if the field list has changed)
304 foreach col $headers {
305 set jus($col) "left"
306 set pad($col) 2
307 set wid($col) 0
309 array set jus $col_justifies
310 array set pad $col_paddings
311 array set wid $col_widths
312 #array set colwidths $itk_option(-column_widths)
313 array set wid $itk_option(-column_widths)
314 # get format options for headers >>>
315 # Get id column and name <<<
316 set id_col [$datasource get_id_column]
317 set id_col_name [lindex $id_col 1]
318 #puts "Browse_treeview_flat::refresh: id_col: ($id_col) id_col_name: ($id_col_name)"
319 # Get id column and name >>>
321 foreach col $headers {
322 set colargs [list \
323 -justify $jus($col) \
324 -title "$col" \
325 -width $wid($col) \
326 -hide [expr {[lsearch $hidden_cols $col] != -1}] \
327 -command [code $this sortcolumn $col]]
329 if {[lsearch $checkvars $col] >= 0} {
330 lappend colargs -style check
333 if {[catch {
334 set curr_id [eval [list $tv column insert end $col] $colargs]
335 } msg]} {
336 error "Error inserting column ($col): $msg\n\tAll: ([$tv column names])\n$::errorInfo"
339 #if {[info exists colwidths($col)]} {
340 # $tv column configure $col -width $colwidths($col)
344 # if the data's headers have changed, update the treeview columns >>>
346 # Handle the id column (hide, etc) <<<
347 set id_colname [lindex [$datasource get_id_column] 1]
348 if {$hide_id_col && $id_colname != ""} {
349 $tv column configure $id_colname -hide [expr {$hide_id_col}]
351 # Handle the id column (hide, etc) >>>
353 # load up the data <<<
354 set m 0
355 set shadowdata {}
356 foreach row $list_data {
357 set tmpid [$datatree insert root -data $row]
358 lappend shadowdata $row
359 $tv bind $tmpid <Button-1> [code $this onclick $tmpid 1]
360 $tv bind $tmpid <Double-Button-1> [code $this onclick $tmpid 2]
361 $tv bind $tmpid <Key-F5> [code $this refresh]
362 foreach {binding action} $item_bindings {
363 $tv bind $tmpid $binding $action
365 set m [expr {($m + 1) % 2}]
367 # load up the data >>>
369 # Re-apply the filter
370 form_filter
372 style_tree
374 # Attempt to restore selection <<<
375 catch {unset tmp}
376 set id_colname [lindex [$datasource get_id_column] 1]
377 set retain 0
378 foreach {parent itemid nodename data tags} [$datatree dump root] {
379 array unset tmp
380 array set tmp $data
381 if {[info exists tmp($id_colname)]} {
382 set row_id $tmp($id_colname)
383 if {[info exists old_sel_ids($row_id)]} {
384 set retain 1
385 $tv selection set $itemid
386 } else {
388 } else {
389 log warning "row is missing id_colname ($id_colname):"
390 parray tmp
392 array unset tmp
394 if {!($retain)} {
395 after idle [code $this onclick "" 1]
397 # Attempt to restore selection >>>
399 selection_changed
402 body tlc::Browse_treeview_flat::clear {} { #<<<1
403 $datatree delete root
404 set shadowdata {}
407 body tlc::Browse_treeview_flat::sortcolumn {colname} { #<<<1
408 # sort the treeview on a certain column
409 # unscrupulously ripped from the blt treeview demo
410 set old [$tv sort cget -column]
411 set decreasing 0
412 if {"$old" == "$colname"} {
413 set decreasing [$tv sort cget -decreasing]
414 set decreasing [expr !$decreasing]
416 $tv sort configure -decreasing $decreasing -column $colname -mode integer
417 if {![$tv cget -flat]} {
418 $tv configure -flat yes
420 $tv sort auto yes
421 style_tree $tree_style
422 blt::busy hold $tv
423 update
424 blt::busy release $tv
428 body tlc::Browse_treeview_flat::hideshowcol {colname hidden} { #<<<1
429 if {$hidden} {
430 set hidden_cols [lsort -unique [concat $hidden_cols [list $colname]]]
431 } else {
432 set idx [lsearch $hidden_cols $colname]
433 if {$idx != -1} {
434 set hidden_cols [lreplace $hidden_cols $idx $idx]
437 catch {$tv configure $colname -hide $hidden}
440 body tlc::Browse_treeview_flat::add_item {} { #<<<1
441 # populate with defaults
442 array set res [$datasource get_defaults]
443 if {![$itemform ask res [$datasource get_item_schema]]} return
444 array set res [$datasource add_item [array get res]]
445 need_refresh
446 set row {}
447 foreach header [$datasource get_headers] {
448 if {[info exists res($header)]} {
449 lappend row $res($header)
450 } else {
451 lappend row ""
454 set row_id [lindex $row [lindex $id_col 0]]
455 invoke_handlers onadd_array [array get res]
456 invoke_handlers onadd $row
457 invoke_handlers onadd_id $row_id
460 body tlc::Browse_treeview_flat::update_item {} { #<<<1
461 set oldrows [get_selected_items]
462 # check that the oldrow only contains one row: updating multiple rows is not supported
463 if {[llength $oldrows] > 1} {
464 log debug "multiple row updates are not supported"
465 } else {
466 set oldrow [lindex $oldrows 0]
467 array set res $oldrow
468 if {![$itemform ask res [$datasource get_item_schema]]} return
470 array set res [$datasource update_item $oldrow [array get res]]
471 need_refresh
473 set row ""
474 foreach header [$datasource get_headers] {
475 lappend row $res($header)
477 set row_id [lindex $row [lindex $id_col 0]]
479 invoke_handlers onupdate_array [array get res]
480 invoke_handlers onupdate $row
481 invoke_handlers onupdate_id $row_id
485 body tlc::Browse_treeview_flat::remove_item {} { #<<<1
486 # set up a mini-description of the item
487 set sel_items [get_selected_items]
488 if {[llength $sel_items]>1} {
489 set plural 1
490 set msg "Are you sure you would like to remove these items?"
491 } else {
492 set plural 0
493 set msg "Are you sure you would like to remove this item?"
495 if {[.confirm ask $msg "Ok" "Cancel"] == "Ok"} {
496 set h [$datasource get_headers]
497 foreach item $sel_items {
498 $datasource remove_item $item
499 set row {}
500 foreach header [$datasource get_headers] {
501 if {[info exists res($header)]} {
502 lappend row $res($header)
503 } else {
504 lappend row ""
507 set row_id [lindex $row [lindex $id_col 0]]
508 invoke_handlers ondelete_array $item
509 invoke_handlers ondelete $row
510 invoke_handlers ondelete_id $row_id
512 need_refresh
516 body tlc::Browse_treeview_flat::select_items {itemlist} { #<<<1
519 body tlc::Browse_treeview_flat::getsite {sitename} { #<<<1
520 # returns a site referenced by a keyword -- to be used by a parent that wishes to inject something into one of the sites
521 switch -- [string tolower $sitename] {
522 "criteria" {return $w.criteriaframe}
523 "actions" {return $w.actionframe}
524 "filter" {return $w.filterframe}
525 "tree" -
526 "list" {return $w.treeframe}
527 default {
528 log error "Unable to return reference to site $sitename -- site should be one of: criteria,actions,filter,tree|list"
529 return ""
534 body tlc::Browse_treeview_flat::filter {searchstring {matchcode {}} {filternames {} }} { #<<<1
535 # comments about the function <<<
536 # for each node in the current treeview, we see if it matches up with
537 # the search string, and if so, it gets to stay.
538 # If not, it gets dumped in the removed_nodes variable (which is just
539 # a list of lists)
540 # This variable is also checked for matches, which are added back
541 # into the tree if found
542 # >>>
543 # set up the filter array
544 if {$filterform != ""} {
545 array set filter [$filterform get_data]
546 } else {
547 log error "filterform does not exist: ($filterform)"
548 array set filter {}
550 switch -- $filter_mode {
551 "and" -
553 log debug "enter"
554 foreach node [$datatree children root] {
555 set rawrow [$datatree get $node]
556 if {[llength $rawrow] > 0} {
557 array set row $rawrow
558 if {![expr $matchcode]} {
559 $datatree delete $node
563 log debug "leave"
565 default {
566 error "Bad filter_mode $filter_mode: only \"and\" is supported"
571 body tlc::Browse_treeview_flat::get_criteria_form {} { #<<<1
572 return $cf
575 body tlc::Browse_treeview_flat::reload_from_criteria_form {} { #<<<1
576 $datasource set_criteria_values [$cf get_data]
577 need_refresh
578 $w.criteriaframe.criteriaform mark_dirty 0
581 body tlc::Browse_treeview_flat::get_selected_items {} { #<<<1
582 set sel_ids [$tv curselection]
583 set ret {}
584 foreach id $sel_ids {
585 lappend ret [$datatree get $id]
587 return $ret
591 body tlc::Browse_treeview_flat::onclick {nodeid numclicks} { #<<<1
592 if {$nodeid != ""} {
593 set data [$datatree get $nodeid]
594 } else {
595 set data {}
597 array set tmp $data
599 set row {}
600 foreach h $last_headers {
601 if {![info exists tmp($h)]} {
602 lappend row {}
603 } else {
604 lappend row $tmp($h)
608 set row_id [$datasource extract_id $row]
610 set cb {}
611 switch -- $numclicks {
612 1 {set cb "onselect"}
613 2 {set cb "doubleclick"}
614 default {error "mad number of clicks sent through: ($numclicks)"}
617 #puts "onclick: nodeid: ($nodeid) data: ($data) selection present: ([$tv selection present])"
618 $item_selected set_state [expr {$nodeid != ""}]
619 if {$cb != ""} {
620 if {$delayed_select_afterid != ""} {
621 set pending [lindex [after info $delayed_select_afterid] 0]
622 if {$pending != ""} {
623 uplevel #0 $pending
626 set delayed_select_afterid [after idle \
627 [code $this process_delayed_select $cb $data $row $row_id]]
631 body tlc::Browse_treeview_flat::action_add {args} { #<<<1
632 set label [lindex $args 0]
633 if {![winfo exists $w.actionsframe.actions]} {
634 set f $w.actionsframe
635 #puts "\n\nBrowse_treeview_flat::action_add:\n\torient: ($itk_option(-actions_orient))\n\tbuttonwidth: ($itk_option(-action_buttonwidth))\n\n"
636 set tb [tlc::Tools $f.actions \
637 -buttonwidth $itk_option(-action_buttonwidth) \
638 -orient $itk_option(-actions_orient)]
639 blt::table $f $f.actions 1,1 -fill both
642 switch -- [string tolower [lindex $args 1]] {
643 "insert" -
644 "new" -
645 "add" {
646 if {[$datasource can_do insert]} {
647 set title [lindex $args 0]
648 set ltitle [expr {[string length $title]?"$title":"Add New"}]
649 $tb add $ltitle [code $this add_item]
650 lappend actions "$ltitle" "[code $this add_item]"
651 } else {
652 error "Datasource can't do insert; refusing to create add\
653 button."
656 "delete" -
657 "remove" -
658 "del" {
659 if {[$datasource can_do delete]} {
660 set title [lindex $args 0]
661 set ltitle [expr {[string length $title] ? "$title" : "Delete"}]
662 $tb add $ltitle [code $this remove_item]
663 $tb attach_signal $ltitle [selected_ref]
664 lappend actions "$ltitle" "[code $this remove_item]"
665 } else {
666 error "Datasource can't do delete; refusing to create delete\
667 button."
670 "update" -
671 "edit" -
672 "upd" {
673 if {[$datasource can_do update]} {
674 set title [lindex $args 0]
675 set ltitle [expr {[string length $title] ? "$title" : "Update"}]
676 $tb add $ltitle [code $this update_item]
677 $tb attach_signal $ltitle [selected_ref]
678 lappend actions "$ltitle" "[code $this update_item]"
679 } else {
680 error "Datasource can't do update; refusing to create update\
681 button."
684 "refresh" {
685 set title [lindex $args 0]
686 set ltitle [expr {[string length $title] ? "$title" : "Refresh"}]
687 $tb add $ltitle [code $this refresh]
688 lappend actions "$ltitle" "[code $this refresh]"
690 default {
691 eval [list $tb add] $args
692 lappend actions "[lindex $args 0]" "[lindex $args 1]"
697 body tlc::Browse_treeview_flat::action_add_supported {} { #<<<
698 foreach action "insert update delete" {
699 if {[$datasource can_do $action]} {
700 action_add "" $action
703 action_add "" "refresh"
706 body tlc::Browse_treeview_flat::action_attach_signal {label signal {sense normal}} { #<<<1
707 #puts "Browse_treeview_flat::action_attach_signal ($label) ($signal) ($sense)"
708 $tb attach_signal $label $signal $sense
712 body tlc::Browse_treeview_flat::action_tips {args} { #<<<1
713 eval [list $tb set_tips] $args
717 body tlc::Browse_treeview_flat::selection_changed {} { #<<<1
718 $selection_present set_state [$tv selection present]
721 body tlc::Browse_treeview_flat::gen_popup {{pop_commands ""}} { #<<<1
722 set menu_schema {}
723 if {$pop_follows_actions} {
724 foreach {action cmd} $actions {
725 lappend menu_schema "$action" "command [list $cmd]"
728 #puts "generating popup with schema: ($menu_schema $pop_commands)"
729 Mymenu $w.pop -schema "$menu_schema $pop_commands"
730 bind $tv <Button-3> [list tk_popup $w.pop %X %Y]
733 body tlc::Browse_treeview_flat::filter_add {label varinfo {matchcode 1}} { #<<<1
734 set varname [lindex $varinfo 0]
735 lappend filterform_schema $label $varinfo
736 set filtercode($varname) $matchcode
737 set last_filter($varname) ""
738 set filter_after_id [after idle [code $this render_filters]]
742 body tlc::Browse_treeview_flat::filter_add_standard {label varinfo matchcode} { #<<<1
743 # implement some fairly standard search capabilities
744 set orig_matchcode $matchcode
746 set mangle 0
747 switch -- [lindex $matchcode 0] {
748 "left" -
749 "left_match" -
750 "match_left" {
751 set matchcode {[eval partial_match [list [array get filter]] \
752 [list [array get row]] "left" \
753 {foo}
754 [list $filternames]]}
755 set mangle 1
757 "right" -
758 "right_match" -
759 "match_right" {
760 set matchcode {[partial_match [list [array get filter]] \
761 [list [array get row]] "right" \
762 {foo} \
763 [list $filternames]]}
764 set mangle 1
766 "exact" -
767 "exact_match" -
768 "match_exact" {
769 set matchcode {[partial_match [list [array get filter]] \
770 [list [array get row]] "exact" \
771 {foo} \
772 [list $filternames]]}
773 set mangle 1
775 "" -
776 "partial" -
777 "partial_match" -
778 "match_partial" {
779 set matchcode {[eval partial_match [list [array get filter]] \
780 [list [array get row]] "partial" \
781 {foo} \
782 [list $filternames]]}
783 set mangle 1
785 default { ;# default is to leave the match code alone
789 if {$mangle} {
790 set repidx [lsearch $matchcode "%%foo%%"]
791 set matchcode [lreplace $matchcode $repidx $repidx [lrange $orig_matchcode 1 end]]
794 return [filter_add $label $varinfo $matchcode]
798 body tlc::Browse_treeview_flat::render_filters {} { #<<<1
799 after cancel $filter_after_id
800 set filter_after_id ""
801 if {$filter_label != ""} {
802 label $filterframe.filterheader -text $filter_label
803 blt::table $filterframe \
804 $filterframe.filterheader 1,1 -columnspan 2
806 if {[winfo exists $filterframe.filterform]} {
807 $filterframe.filterform configure -schema $filterform_schema
808 } else {
809 set filterform [tlc::Form $filterframe.filterform -schema $filterform_schema]
812 blt::table $filterframe \
813 $filterframe.filterform 2,1 -columnspan 2
814 blt::table configure $filterframe r1 r2 -resize none
816 if {$filter_allow_sense_change} {
817 label $filterframe.insensitivelabel -text "Filter is case insensitive:"
818 checkbutton $filterframe.insensitive -variable $filter_insensitive
819 blt::table $filterframe \
820 $filterframe.insensitivelabel 3,1 -anchor ne \
821 $filterframe.insensitive 3,2 -anchor nw
822 blt::table configure $filterframe r3 -resize none
825 if {$filter_allow_mode_change && [llength [array names filtercode]] > 1} {
826 label $filterframe.modelabel -text "Require all conditions be met."
827 checkbutton $filterframe.mode \
828 -command [code $this form_filter] \
829 -variable [scope filter_mode]
830 blt::table $filterframe \
831 $filterframe.modelabel 4,1 -anchor ne \
832 $filterframe.mode 4,2 -anchor nw
833 blt::table configure $filterframe r4 -resize none
835 $filterform register_handler onchange [code $this form_filter]
838 body tlc::Browse_treeview_flat::form_filter {{form_data {}}} { #<<<1
839 if {[llength $form_data] == 0 && $filterform != ""} {
840 set form_data [$filterform get_data]
841 } else {
842 set form_data $form_data
844 # initial filtering method notes: <<<
845 # we have two possible filter methods: and and or (keeping it simple)
846 # for the case of an or, we start with a blank datatree and add in
847 # the elements from the shadowtree that make the grade.
848 # for the case of an and, we start with a filled datatree and remove
849 # items that violate the filtering rules
850 # >>>
851 set required_filters {}
852 array set lfilters $form_data
853 foreach {filter value} [array get lfilters] {
854 if {$value != ""} {
855 lappend required_filters $filter
858 cache_col_widths
859 switch -- $filter_mode {
860 "and" -
862 log debug "enter"
863 $datatree delete root
864 foreach dataarray $shadowdata {
865 set tmpid [$datatree insert root \
866 -data $dataarray]
867 $tv bind $tmpid <Button-1> [code $this onclick $tmpid 1]
868 $tv bind $tmpid <Double-Button-1> \
869 [code $this onclick $tmpid 2]
870 $tv bind $tmpid <Key-F5> [code $this refresh]
871 foreach {binding action} $item_bindings {
872 $tv bind $tmpid $binding $action
875 log debug "populated"
876 #puts "filtercode:"
877 #parray filtercode
878 foreach filter $required_filters {
879 filter $lfilters($filter) $filtercode($filter) $filter
881 foreach cb $filter_cbs {
882 foreach node [$datatree children root] {
883 set rawrow [$datatree get $node]
884 if {[llength $rawrow] > 0} {
885 if {![$cb $rawrow]} {
886 $datatree delete $node
891 log debug "leave"
894 restore_col_widths
897 body tlc::Browse_treeview_flat::partial_match {filterarray rowarray type
898 {applicable_cols {}} {filternames {}}} {
899 array set afilter $filterarray
900 array set arow $rowarray
901 if {[llength $applicable_cols] == 0} {
902 foreach idx [array names afilter] {
903 lappend applicable_cols $idx
906 if {[llength $filternames] == 0} {set filternames [array names afilter]}
908 switch -- $type {
909 "partial" {set pre "*"; set post "*"}
910 "left" {set pre ""; set post "*"}
911 "right" {set pre "*"; set post ""}
912 "exact" -
913 default {set pre ""; set post ""}
915 set hits 0
916 if {$trim_filter} {set val [string trim $val]}
917 # ok, bear with me: i'm trying to keep the miniumum of checks on the
918 # inner loop, so there are a lot of variations on a theme here
919 if {$filter_insensitive} {
920 foreach idx $filternames {
921 if {[lsearch $applicable_cols $idx] > -1} {
922 if {[info exists arow($idx)]} {
923 if {$afilter($idx) != ""} {
924 if {[eval string match -nocase \
925 {${pre}$afilter($idx)${post}} \
926 {$arow($idx)} ] == 1} {
927 return 1
933 } else {
934 foreach {idx val} $filternames {
935 if {[lsearch $applicable_cols $idx] > -1} {
936 if {[info exists arow($idx)]} {
937 if {$afilter($idx) != ""} {
938 if {[eval string match \
939 {${pre}$afilter($idx)${post}} \
940 {$arow($idx)} ] == 1} {
941 return 1
948 return 0
949 switch -- $filter_mode {
950 "and" -
951 1 {return [expr {[llength $applicable_cols] == $hits ? 1 : 0}]}
952 "or" -
953 0 {return [expr {$hits > 0 ? 1 : 0}]}
957 body tlc::Browse_treeview_flat::cache_col_widths {} { #<<<1
958 foreach col [$tv column names] {
959 set cached_col_widths($col) [$tv column cget $col -width]
963 body tlc::Browse_treeview_flat::restore_col_widths {} { #<<<1
964 foreach col [$tv column names] {
965 if {[info exists cached_col_widths($col)]} {
966 $tv column configure $col -width $cached_col_widths($col)
971 body tlc::Browse_treeview_flat::style_tree {{stylename ""}} { #<<<1
972 switch -- $stylename {
973 "webby" -
974 "web" {
975 # flat, light look
976 $tv configure \
977 -relief solid \
978 -borderwidth 1
979 set s "light1"
980 foreach col [$tv column names] {
981 $tv column configure $col \
982 -titlerelief raised \
983 -titlebackground "#e5e5e5" \
984 -activetitlebackground "#e5e5e5" \
985 -titleforeground "#000000" \
986 -borderwidth 0 \
987 -background "#f5f5f5" \
988 -titleborderwidth 1 \
989 -relief solid
990 # -titleshadow ""
993 default {
994 # tries to make the listview fit in with the chrome of the system
995 set chrome [$tlc::theme setting background]
996 set textbackground [$tlc::theme setting textbackground]
997 set fg [$tlc::theme setting foreground]
998 $tv configure \
999 -relief sunken \
1000 -selectbackground [$tlc::theme setting selectbackground] \
1001 -foreground $fg \
1002 -borderwidth 1 \
1003 -background $textbackground
1004 foreach col [$tv column names] {
1005 $tv column configure $col \
1006 -titlerelief raised \
1007 -background $textbackground \
1008 -titlebackground $chrome \
1009 -activetitlebackground $chrome \
1010 -titleforeground $fg \
1011 -borderwidth 1 \
1012 -activetitlebackground $chrome
1013 # -titleshadow ""
1019 body tlc::Browse_treeview_flat::get_tv_ref {} { #<<<1
1020 return $tv
1024 body tlc::Browse_treeview_flat::column_configure {colname args} { #<<<1
1025 if {[refresh_pending]} {
1026 do_refresh
1028 return [eval [list $tv column configure $colname] $args]
1032 body tlc::Browse_treeview_flat::relayout {} { #<<<1
1033 eval [list blt::table $w $w.treeframe] $listpos
1034 eval [list blt::table $w $w.filterframe] $filterpos
1035 set coords [split [lindex $filterpos 0] ","]
1037 eval [list blt::table $w $w.criteriaframe] $criteriapos
1038 set coords [split [lindex $criteriapos 0] ","]
1040 eval [list blt::table $w $w.actionsframe] $itk_option(-actionspos)
1041 set coords [split [lindex $itk_option(-actionspos) 0], ","]
1043 # Restrict row and col growth to those containing the list <<<
1044 array set targets {}
1045 foreach child [blt::table search $w -pattern *] {
1046 set table_info [blt::table info $w $child]
1047 foreach {row col} [split [lindex $table_info 0] ,] break
1048 set targets(r$row) 1
1049 set targets(c$col) 1
1051 eval [list blt::table configure $w] [array names targets] -resize none
1052 foreach {row col} [split [lindex [blt::table info $w $w.treeframe] 0] ,] break
1053 blt::table configure $w r$row c$col -resize both
1054 # Restrict row and col growth to those containing the list >>>
1058 body tlc::Browse_treeview_flat::component_actions {} { #<<<1
1059 if {[winfo exists $w.actionsframe.actions]} {
1060 return $w.actionsframe.actions
1061 } else {
1062 error "No actions configured"
1067 body tlc::Browse_treeview_flat::filter_data {args} { #<<<1
1068 if {$filter_after_id != ""} {render_filters}
1069 switch -- [llength $args] {
1071 if {$filterform != ""} {
1072 return [$filterform get_data]
1073 } else {
1074 log error "filterform does not exist: ($filterform)"
1075 return {}
1080 if {$filterform != ""} {
1081 return [$filterform set_data [lindex $args 0]]
1082 } else {
1083 log error "filterform does not exist: ($filterform)"
1084 return {}
1088 default {
1089 error "Incorrect number of arguments: ($args) must be ?newdata?"
1095 body tlc::Browse_treeview_flat::filter_item {item} { #<<<1
1096 return [$filterform path $item]
1100 body tlc::Browse_treeview_flat::filter_add_cb {cb} { #<<<1
1101 lappend filter_cbs $cb
1103 need_refresh
1107 body tlc::Browse_treeview_flat::process_delayed_select {cb data row row_id} { #<<<1
1108 after cancel $delayed_select_afterid; set delayed_select_afterid ""
1110 invoke_handlers ${cb}_array $data
1111 invoke_handlers $cb $row
1112 invoke_handlers ${cb}_id $row_id