]> www.wagner.pp.ru Git - oss/fgis.git/blob - tcl/progressbar.tcl
The second attempt to automate building :-) A lot of work here should be
[oss/fgis.git] / tcl / progressbar.tcl
1 ##
2 ## Copyright 1996-7 Jeffrey Hobbs
3 ##
4
5 ##------------------------------------------------------------------------
6 ## PROCEDURE
7 ##      progressbar
8 ##
9 ## DESCRIPTION
10 ##      Implements a Progressbar mega-widget
11 ##
12 ## ARGUMENTS
13 ##      progressbar <window pathname> <options>
14 ##
15 ## OPTIONS
16 ##      (Any canvas widget option may be used in addition to these)
17 ##
18 ## -indicatorcolor                      DEFAULT: #5ae6fe
19 ##      The color of the progressbar.  Must be in #rgb format.
20 ##      This is also the default item start foreground color.
21 ##
22 ## -itembackground                      DEFAULT: {}
23 ##      Default item background color.  {} means transparent.
24 ##
25 ## -itemfgfinished                      DEFAULT: #00ff00 (green)
26 ##      Default item finished foreground color.  Must be in #rgb format.
27 ##
28 ## -itemtype                            DEFAULT: document
29 ##      Default item type (currently 'document' and 'image' are supported).
30 ##
31 ## -labelanchor anchor                  DEFAULT: c
32 ##      Anchor for the label.  Reasonable values are c, w and e.
33 ##
34 ## -labeltext string                    DEFAULT: {}
35 ##      Text for the label
36 ##
37 ## -labelwidth #                        DEFAULT: 0 (self-sizing)
38 ##      Width for the label
39 ##
40 ## -maxvalue #                          DEFAULT: 0 (percentage-based)
41 ##      This represents what the representative max value of the progress
42 ##      bar is.  If it is 0, the progress bar interprets the -value option
43 ##      like a percentage (with an implicit 100 value for -maxvalue),
44 ##      otherwise it is representative of what -value would have to reach
45 ##      for the progress to be at 100%.
46 ##
47 ## -orientation horizontal|vertical     DEFAULT: horizontal
48 ##      Orientation of the progressbar
49 ##
50 ## -showvalue TCL_BOOLEAN               DEFAULT: 1
51 ##      Whether or not to show the exact value beside the bar (it is
52 ##      displayed as a percentage of the possible max value).
53 ##
54 ## -showerror TCL_BOOLEAN               DEFAULT: 1
55 ##      Whether to raise an error in the trace on the -variable if the
56 ##      appropriate range is exceeded.
57 ##
58 ## -value #                             DEFAULT: 0
59 ##      The value of the progress bar.  This will be used to calculate the
60 ##      overall progress percentage in conjunction with the -maxvalue option.
61 ##
62 ## -variable varname                    DEFAULT: {}
63 ##      A variable from which to get the value for the bar.  This variable
64 ##      will have a trace set upon it that forces a postive value.  It cannot
65 ##      be unset until the widget is destroyed or you change this option.
66 ##
67 ## RETURNS: the window pathname
68 ##
69 ## BINDINGS (in addition to default widget bindings)
70 ##
71 ## METHODS
72 ##      These are the methods that this megawidget recognizes.  Aside from
73 ##      those listed here, it accepts what is valid for canvas widgets.
74 ##
75 ## configure ?option? ?value option value ...?
76 ## cget option
77 ##      Standard tk widget routines.
78 ##
79 ## create ?item? ?-option value ...?
80 ##      Start displaying the progress of an item.  "item" is the
81 ##      name to associate with the item.  If no name is supplied, a unique
82 ##      name is generated.  If an item of the same name already exists, then
83 ##      a new unique name is generated.  Returns the name of the created item.
84 ##
85 ## delete item
86 ##      Remove the given item from the list of items being displayed.
87 ##      Total progress is updated appropriately.
88 ##
89 ## itemconfigure item ?-option value?
90 ##      Sets option(s) for an item.
91 ##
92 ##      VALID ITEM OPTIONS
93 ##
94 ##      -background color       Background color of icon associated with item.
95 ##      -fgstart #rgb           Initial foreground color of item's icon.
96 ##      -fgfinished #rgb        Final foreground color of item's icon.
97 ##                              The progressbar changes the shade of the icon
98 ##                              from the initial to the final color in
99 ##                              conjunction with the %age of maxvalue.
100 ##      -maxvalue #     max value that represents 100% of possible value
101 ##      -type type      item type (document and image currently supported)
102 ##                      This can only be set at creation.
103 ##      -value #        current progress toward full value of maxvalue
104 ##
105 ## itemcget item -option
106 ##      Returns the current value of the option for the given item
107 ##
108 ## names ?pattern?
109 ##      Returns the names of the progressbar's constituent items.
110 ##      An optional pattern can limit the return result.
111 ##
112 ## recalculate
113 ##      Recalculates the value and maxvalue of the progressbar based
114 ##      on the values of the consituent items, if any.  This is only
115 ##      necessary when changing from using the progressbar without items
116 ##      to using it with items.
117 ##
118 ## subwidget widget
119 ##      Returns the true widget path of the specified widget.  Valid
120 ##      widgets are label, canvas.
121 ##
122 ## NAMESPACE & STATE
123 ##      The megawidget creates a global array with the classname, and a
124 ## global array which is the name of each megawidget created.  The latter
125 ## array is deleted when the megawidget is destroyed.
126 ##      Public procs of $CLASSNAME and [string tolower $CLASSNAME] are used.
127 ## Other procs that begin with $CLASSNAME are private.  For each widget,
128 ## commands named .$widgetname and $CLASSNAME$widgetname are created.
129 ##
130 ## EXAMPLE USAGE:
131 ##
132 ## pack [progressbar .p -labeltext "Usage:" -variable usage] -fill x -exp 1
133 ## for {set i 0} {$i <= 10} {incr i} { set usage ${i}0; after 1000 }
134 ##
135 ##
136 ##------------------------------------------------------------------------
137
138 #package require Widget 1.0
139 package provide Progressbar 1.1
140
141 array set Progressbar {
142     type                frame
143     base                {canvas canvas canvas {-highlightthickness 0 \
144             -bd 1 -relief ridge -width 100 -height 25}}
145     components          {label}
146
147     -bd                 -borderwidth
148     -borderwidth        {borderWidth    BorderWidth     0}
149     -font               {ALIAS label -font}
150     -fg                 -foreground
151     -foreground         {ALIAS label -foreground}
152     -indicatorcolor     {indicatorColor Color           #5ae6fe}
153     -indicatorcolour    -indicatorcolor
154     -itembackground     {itemBackground Background      {}}
155     -itemfgfinished     {itemForegroundFinished Foreground #00ff00}
156     -itemtype           {itemType       ItemType        document}
157     -labelanchor        {labelAnchor    Anchor          c}
158     -labeltext          {labelText      Text            {}}
159     -labelwidth         {labelWidth     Width           0}
160     -maxvalue           {maxValue       Value           0}
161     -orientation        {orientation    Orientation     horizontal}
162     -relief             {relief         Relief          flat}
163     -showvalue          {showValue      ShowValue       1}
164     -showerror          {showError      ShowError       1}
165     -value              {value          Value           0}
166     -variable           {variable       Variable        {}}
167 }
168 # Create this to make sure there are registered in auto_mkindex
169 # these must come before the [widget create ...]
170 proc Progressbar args {}
171 proc progressbar args {}
172 widget create Progressbar
173
174 ;proc Progressbar:construct {w} {
175     upvar \#0 $w data
176
177     ## Private variables
178     array set data {
179         counter         0
180     }
181     set data(items) $data(class)${w}ITEMS
182
183     grid $data(label) $data(canvas) -in $w -sticky ns
184     grid configure $data(canvas) -sticky news
185     grid columnconfig $w 1 -weight 1
186     grid rowconfig $w 0 -weight 1
187     grid remove $data(label)
188
189     bind $data(canvas) <Configure> [list Progressbar:resize $w %w %h]
190 }
191
192 ;proc Progressbar:init {w} {
193     upvar \#0 $w data
194     $data(basecmd) create rect -1 0 0 25 -fill $data(-indicatorcolor) \
195             -tags bar -outline {}
196     $data(basecmd) create text 25 12 -fill $data(-foreground) \
197             -tags text -anchor c
198     $data(basecmd) xview moveto 0
199     $data(basecmd) yview moveto 0
200 }
201
202 ;proc Progressbar:configure { w args } {
203     upvar \#0 $w data
204
205     set truth {^(1|yes|true|on)$}
206     set resize 0
207     set force  0
208     foreach {key val} $args {
209         switch -- $key {
210             -borderwidth - -relief { .$w configure $key $val }
211             -font       {
212                 $data(label) configure -font $val
213                 $data(basecmd) itemconfigure text -font $val
214             }
215             -foreground         {
216                 $data(label) configure -foreground $val
217                 $data(basecmd) itemconfigure text -fill $val
218             }
219             -indicatorcolor     {
220                 $data(basecmd) itemconfigure bar -fill $val
221             }
222             -labelanchor        { $data(label) configure -anchor $val }
223             -labeltext          {
224                 $data(label) configure -text $val
225                 if {[string compare {} $val]} {
226                     grid $data(label)
227                 } else {
228                     grid remove $data(label)
229                 }
230             }
231             -labelwidth         { $data(label) configure -width $val }
232             -maxvalue           {
233                 if {![regexp {^[0-9]+$} $val] || $val<0} {
234                     return -code error "$key must be a positive integer"
235                 }
236                 set force 1
237             }
238             -orientation        {
239                 if {[string match h* $val]} {
240                     set val horizontal
241                 } elseif {[string match v* $val]} {
242                     set val vertical
243                 } else {
244                     return -code error \
245                             "orientation must be horizontal or vertical"
246                 }
247                 if {[string compare $data($key) $val]} {
248                     set W [$data(basecmd) cget -width]
249                     set H [$data(basecmd) cget -height]
250                     $data(basecmd) configure -height $W -width $H
251                     set resize 1
252                 }
253             }
254             -showvalue  {
255                 set val [regexp -nocase $truth $val]
256                 set resize 1
257             }
258             -showerror  { set val [regexp -nocase $truth $val] }
259             -value      {
260                 if {[catch {Progressbar:set $w $val} err] && \
261                         $data(-showerror)} {
262                     return -code error $err
263                 }
264                 if {$resize} { set resize 0 }
265                 if {$force} { set force 0 }
266                 continue
267             }
268             -variable   {
269                 if {![string compare $val $data(-variable)]} return
270                 if {[string compare {} $data(-variable)]} {
271                     uplevel \#0 [list trace vdelete $data(-variable) wu \
272                             "Progressbar:trace $w"]
273                     set data(-variable) {}
274                 }
275                 if {[string compare {} $val]} {
276                     set data(-variable) $val
277                     upvar \#0 $val var
278                     if {![info exists var] || \
279                             [catch {Progressbar:set $w $var} err]} {
280                             set var $data(-value)
281                     }
282                     uplevel \#0 [list trace var $val wu "Progressbar:trace $w"]
283                 }
284                 ## avoid the set data($key)
285                 continue
286             }
287         }
288         set data($key) $val
289     }
290     if {$force || ($resize && [winfo ismapped $data(canvas)])} {
291         Progressbar:resize $w [winfo width $data(canvas)] \
292                 [winfo height $data(canvas)]
293     }
294 }
295
296 ;proc Progressbar:destroy w {
297     upvar \#0 $w data
298     catch { Progressbar:configure $w -variable {} }
299 }
300
301 ;proc Progressbar:trace {w name el op} {
302     upvar \#0 $w data
303     upvar \#0 $data(-variable) var
304     if {[string match u $op]} {
305         set var $data(-value)
306         uplevel \#0 [list trace var $data(-variable) wu "Progressbar:trace $w"]
307     } elseif {[catch {Progressbar:set $w $var} err]} {
308         set var $data(-value)
309         if $data(-showerror) { return -code error $err }
310     }
311 }
312
313 ;proc Progressbar:resize {w W H} {
314     upvar \#0 $w data
315
316     ## Assume a maxvalue of 100 if maxvalue is 0 (works like %age)
317     if {$data(-maxvalue)} {
318         set pcnt [expr {$data(-value)/double($data(-maxvalue))}]
319     } else {
320         set pcnt [expr {$data(-value)/100.0}]
321     }
322     if {[string match h* $data(-orientation)]} {
323         $data(basecmd) coords bar -1 0 [expr {$pcnt*$W}] $H
324     } else {
325         ## Vertical orientation needs testing
326         $data(basecmd) coords bar -1 $H $W [expr {$pcnt*$H}]
327     }
328     if $data(-showvalue) {
329         $data(basecmd) coords text [expr {$W/2}] [expr {$H/2-2}]
330         $data(basecmd) itemconfigure text -text [expr $pcnt*100.0]%
331     } else {
332         $data(basecmd) coords text $W $H
333     }
334 }
335
336 ;proc Progressbar:set {w val} {
337     upvar \#0 $w data
338     if {![regexp {^[0-9]+$} $val] || $val<0} {
339         return -code error "value must be an integer greater than 0"
340     }
341     if {[string comp {} $data(-variable)]} {
342         upvar \#0 $data(-variable) var
343         if {[catch {set var $val} err]} {
344             return -code error $err
345         }
346     }
347     set data(-value) $val
348     Progressbar:resize $w [winfo width $data(canvas)] \
349             [winfo height $data(canvas)]
350 }
351
352 # Manage progress items.  These may be documents or images.
353 # (There needs to be an extensible system to allow other types, eg. Tclets)
354 # Each item may have a max value and a current value.
355 # The total download progress is calculated from the sums of item sizes.
356
357 ;proc Progressbar_create {w args} {
358     upvar \#0 $w data
359
360     set cnt [incr data(counter)] 
361     if {[string match -* [lindex $args 0]]} {
362         # Invent a name
363         set item progress$cnt
364     } else {
365         set item [lindex $args 0]
366         set args [lrange $args 1 end]
367         if {[info exists data(I:$item)]} {
368             # Ensure name doesn't already exist
369             return -code error "item \"$item\" already exists"
370         }
371     }
372
373     array set config [list \
374             -background $data(-itembackground) \
375             -fgstart    $data(-indicatorcolor) \
376             -fgfinished $data(-itemfgfinished) \
377             -maxvalue   100 \
378             -type       $data(-itemtype) \
379             -value      0 \
380             ]
381     array set configargs $args
382     if {[info exists configargs(-type)]} {
383         if {[string match {} \
384                 [info commands Progressbar:icon:$configargs(-type)]]} {
385             return -code error "invalid item type $configargs(-type)"
386         }
387         set config(-type) $configargs(-type)
388         unset configargs(-type)
389     }
390     incr data(-maxvalue) $config(-maxvalue)
391     incr data(-value) $config(-value)
392     # Add to display
393     set config(image) [image create bitmap $w:$item \
394             -data [Progressbar:icon:$config(-type) cget -data] \
395             -foreground $config(-fgstart) \
396             -background $config(-background)]
397     set config(w) [label $w.item$cnt -image $config(image)]
398     foreach {ncols nrows} [grid size $w] break
399     if {[string match h* $data(-orientation)]} {
400         grid $config(w) -row 0 -column $ncols
401     } else {
402         grid $config(w) -row $nrows -column 0
403     }
404
405     set data(I:$item) [array get config]
406
407     if {[string compare {} $args]} {
408         eval Progressbar_itemconfigure [list $w] [list $item] \
409                 [array get configargs]
410     } else {
411         Progressbar:set $w $data(-value)
412     }
413
414     return $item
415 }
416
417 # Turns #rgb into 3 elem list of decimal vals.
418 ;proc Progressbar:parse_color c {
419     set c [string tolower $c]
420     if {[regexp {^\#([0-9a-f])([0-9a-f])([0-9a-f])$} $c x r g b]} {
421         # appending "0" right-shifts 4 bits
422         scan "${r}0 ${g}0 ${b}0" "%x %x %x" r g b
423     } else {
424         if {![regexp {^\#([0-9a-f]+)$} $c junk hex] || \
425                 [set len [string length $hex]]>12 || $len%3 != 0} {
426             return -code error "bad color value \"$c\""
427         }
428         set len [expr {$len/3}]
429         scan $hex "%${len}x%${len}x%${len}x" r g b
430     }
431     return [list $r $g $b]
432 }
433
434 ## Returns a shade between two colors based on the frac (0.0-1.0)
435 ;proc Progressbar:shade {orig dest frac} {
436     if {$frac >= 1.0} { return $dest } elseif {$frac <= 0.0} { return $orig }
437     foreach {origR origG origB} [Progressbar:parse_color $orig] \
438             {destR destG destB} [Progressbar:parse_color $dest] {
439         set shade [format "\#%02x%02x%02x" \
440                 [expr {int($origR+double($destR-$origR)*$frac)}] \
441                 [expr {int($origG+double($destG-$origG)*$frac)}] \
442                 [expr {int($origB+double($destB-$origB)*$frac)}]]
443         return $shade
444     }
445 }
446
447 ;proc Progressbar_delete {w args} {
448     upvar \#0 $w data
449
450     foreach item $args {
451         ## Don't complain about unknown items when deleting
452         if {![info exists data(I:$item)]} continue
453
454         array set config $data(I:$item)
455
456         incr data(-value) -$config(-value)
457         incr data(-maxvalue) -$config(-maxvalue)
458         if {$data(-value) < 0} { set data(-value) 0 }
459         if {$data(-maxvalue) < 0} { set data(-maxvalue) 0 }
460
461         destroy $config(w)
462         image delete $config(image)
463         unset data(I:$item)
464     }
465     Progressbar:set $w $data(-value)
466 }
467
468 ## Progressbar_itemconfigure
469 ## configure a progressar constituent item
470 ##
471 ;proc Progressbar_itemconfigure {w item args} {
472     upvar \#0 $w data
473
474     if {![info exists data(I:$item)]} {
475         return -code error "unknown item \"$item\""
476     }
477
478     array set config $data(I:$item)
479     if {[string match {} $args]} { return [array get config -*] }
480
481     set valChanged 0
482     foreach {key val} $args {
483         if {[string match {} [set arg [array names config $key]]]} {
484             set arg [array names config ${key}*]
485         }
486         set num [llength $arg]
487         if {$num==0} {
488             return -code error "unknown option \"$key\", must be:\
489                     [join [array names config -*] {, }]"
490         } elseif {$num>1} {
491             return -code error "ambiguous option \"$args\",\
492                     must be one of: [join $arg {, }]"
493         } else {
494             set key $arg
495         }
496         switch -- $key {
497             -maxvalue   {
498                 if {![regexp {^[0-9]+$} $val] || $val<=0} {
499                     return -code error "$key must be an integer greater than 0"
500                 }
501                 incr data(-maxvalue) [expr {$val-$config(-maxvalue)}]
502                 if {$data(-maxvalue) < 0} { set data(-maxvalue) 0 }
503                 set valChanged 1
504             }
505             -value      {
506                 if {![regexp {^[0-9]+$} $val] || $val<0} {
507                     return -code error "$key must be a postive integer"
508                 }
509                 incr data(-value) [expr {$val-$config(-value)}]
510                 if {$data(-value) < 0} { set data(-value) 0 }
511                 set valChanged 1
512             }
513             -type       {
514                 ## Should we allow this to be changed?
515                 return -code error "-type cannot be changed after creation"
516             }
517             -fgstart    {
518                 if {![regexp {^\#([0-9a-f]+)$} $val]} {
519                     return -code error "color value must be in \#rgb format"
520                 }
521             }
522             -fgfinished {
523                 if {![regexp {^\#([0-9a-f]+)$} $val]} {
524                     return -code error "color value must be in \#rgb format"
525                 }
526             }
527         }
528         set config($key) $val
529     }
530     set data(I:$item) [array get config]
531
532     if {$config(-maxvalue)} {
533         $config(image) configure -background $config(-background) \
534                 -foreground [Progressbar:shade \
535                 $config(-fgstart) $config(-fgfinished) \
536                 [expr {double($config(-value))/$config(-maxvalue)}]]
537     }
538     if {$valChanged} { Progressbar:set $w $data(-value) }
539 }
540
541 ## Progressbar_itemcget
542 ## Returns a single item option
543 ##
544 ;proc Progressbar_itemcget {w item opt} {
545     upvar \#0 $w data
546
547     if {![info exists data(I:$item)]} {
548         return -code error "unknown item \"$item\""
549     }
550     array set config $data(I:$item)
551     ## Ensure that we are getting a -'ed value
552     if {![info exists config(-[string range $opt 1 end])]} {
553         return -code error "unknown option \"$opt\""
554     }
555     return $config($opt)
556 }
557
558 ## Progressbar_names
559 ## Return a list of item names
560 ##
561 ;proc Progressbar_names {w {pattern *}} {
562     upvar \#0 $w data
563
564     set items {}
565     foreach name [array names data I:$pattern] {
566         lappend items [string range $name 2 end]
567     }
568     return $items
569 }
570
571 ## Progressbar_recalculate
572 ## recalculates the percentage based purely on the constituent items
573 ## If there are no items, it just ensures that -(max)value is >= 0
574 ##
575 ;proc Progressbar_recalculate {w} {
576     upvar \#0 $w data
577
578     set items [array names data I:*]
579     if {[string compare {} $items]} {
580         set data(-maxvalue) 0
581         set data(-value) 0
582         foreach item $items {
583             array set config $data($item)
584             if {$config(-value) < 0} {set config(-value) 0}
585             if {$config(-maxvalue) < 0} {set config(-maxvalue) 0}
586             incr data(-value) $config(-value)
587             incr data(-maxvalue) $config(-maxvalue)
588             set data($item) [array get config]
589         }
590     } else {
591         if {$data(-value) < 0} {set data(-value) 0}
592         if {$data(-maxvalue) < 0} {set data(-maxvalue) 0}
593     }
594     Progressbar:set $w $data(-value)
595     return
596 }
597
598 image create bitmap Progressbar:icon:document -data {#define document_width 20
599 #define document_height 23
600 static char document_bits[] = {
601    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602    0xfc, 0x1f, 0x00, 0x04, 0x30, 0x00, 0x04, 0x50, 0x00, 0x04, 0x90, 0x00,
603    0x04, 0x10, 0x01, 0x04, 0xf0, 0x03, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02,
604    0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02,
605    0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0x04, 0x00, 0x02,
606    0x04, 0x00, 0x02, 0x04, 0x00, 0x02, 0xfc, 0xff, 0x03};
607 }
608
609 image create bitmap Progressbar:icon:image -data {#define image_width 20
610 #define image_height 23
611 static char image_bits[] = {
612    0xe0, 0xff, 0xff, 0x20, 0xe0, 0xff, 0xe0, 0xff, 0xff, 0x30, 0xff, 0xff,
613    0xe8, 0xf8, 0xff, 0xdf, 0xf7, 0xff, 0xbb, 0xff, 0xff, 0x7b, 0xff, 0xff,
614    0xfb, 0xfe, 0xff, 0xfb, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff,
615    0xcf, 0x1f, 0xfc, 0x03, 0x0e, 0xf8, 0x20, 0x70, 0xf0, 0x18, 0x80, 0xf1,
616    0x07, 0x00, 0xf0, 0x00, 0x1e, 0xf0, 0xf8, 0x01, 0xf0, 0x00, 0x00, 0xf0,
617    0xc0, 0x7f, 0xf3, 0x00, 0x80, 0xf0, 0x40, 0x00, 0xf0};
618 }
619 return