2 ## Copyright 1996-7 Jeffrey Hobbs
5 ##------------------------------------------------------------------------
10 ## Implements a Progressbar mega-widget
13 ## progressbar <window pathname> <options>
16 ## (Any canvas widget option may be used in addition to these)
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.
22 ## -itembackground DEFAULT: {}
23 ## Default item background color. {} means transparent.
25 ## -itemfgfinished DEFAULT: #00ff00 (green)
26 ## Default item finished foreground color. Must be in #rgb format.
28 ## -itemtype DEFAULT: document
29 ## Default item type (currently 'document' and 'image' are supported).
31 ## -labelanchor anchor DEFAULT: c
32 ## Anchor for the label. Reasonable values are c, w and e.
34 ## -labeltext string DEFAULT: {}
37 ## -labelwidth # DEFAULT: 0 (self-sizing)
38 ## Width for the label
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%.
47 ## -orientation horizontal|vertical DEFAULT: horizontal
48 ## Orientation of the progressbar
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).
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.
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.
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.
67 ## RETURNS: the window pathname
69 ## BINDINGS (in addition to default widget bindings)
72 ## These are the methods that this megawidget recognizes. Aside from
73 ## those listed here, it accepts what is valid for canvas widgets.
75 ## configure ?option? ?value option value ...?
77 ## Standard tk widget routines.
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.
86 ## Remove the given item from the list of items being displayed.
87 ## Total progress is updated appropriately.
89 ## itemconfigure item ?-option value?
90 ## Sets option(s) for an item.
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
105 ## itemcget item -option
106 ## Returns the current value of the option for the given item
109 ## Returns the names of the progressbar's constituent items.
110 ## An optional pattern can limit the return result.
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.
119 ## Returns the true widget path of the specified widget. Valid
120 ## widgets are label, canvas.
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.
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 }
136 ##------------------------------------------------------------------------
138 #package require Widget 1.0
139 package provide Progressbar 1.1
141 array set Progressbar {
143 base {canvas canvas canvas {-highlightthickness 0 \
144 -bd 1 -relief ridge -width 100 -height 25}}
148 -borderwidth {borderWidth BorderWidth 0}
149 -font {ALIAS label -font}
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 {}}
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
174 ;proc Progressbar:construct {w} {
181 set data(items) $data(class)${w}ITEMS
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)
189 bind $data(canvas) <Configure> [list Progressbar:resize $w %w %h]
192 ;proc Progressbar:init {w} {
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) \
198 $data(basecmd) xview moveto 0
199 $data(basecmd) yview moveto 0
202 ;proc Progressbar:configure { w args } {
205 set truth {^(1|yes|true|on)$}
208 foreach {key val} $args {
210 -borderwidth - -relief { .$w configure $key $val }
212 $data(label) configure -font $val
213 $data(basecmd) itemconfigure text -font $val
216 $data(label) configure -foreground $val
217 $data(basecmd) itemconfigure text -fill $val
220 $data(basecmd) itemconfigure bar -fill $val
222 -labelanchor { $data(label) configure -anchor $val }
224 $data(label) configure -text $val
225 if {[string compare {} $val]} {
228 grid remove $data(label)
231 -labelwidth { $data(label) configure -width $val }
233 if {![regexp {^[0-9]+$} $val] || $val<0} {
234 return -code error "$key must be a positive integer"
239 if {[string match h* $val]} {
241 } elseif {[string match v* $val]} {
245 "orientation must be horizontal or vertical"
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
255 set val [regexp -nocase $truth $val]
258 -showerror { set val [regexp -nocase $truth $val] }
260 if {[catch {Progressbar:set $w $val} err] && \
262 return -code error $err
264 if {$resize} { set resize 0 }
265 if {$force} { set force 0 }
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) {}
275 if {[string compare {} $val]} {
276 set data(-variable) $val
278 if {![info exists var] || \
279 [catch {Progressbar:set $w $var} err]} {
280 set var $data(-value)
282 uplevel \#0 [list trace var $val wu "Progressbar:trace $w"]
284 ## avoid the set data($key)
290 if {$force || ($resize && [winfo ismapped $data(canvas)])} {
291 Progressbar:resize $w [winfo width $data(canvas)] \
292 [winfo height $data(canvas)]
296 ;proc Progressbar:destroy w {
298 catch { Progressbar:configure $w -variable {} }
301 ;proc Progressbar:trace {w name el op} {
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 }
313 ;proc Progressbar:resize {w W H} {
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))}]
320 set pcnt [expr {$data(-value)/100.0}]
322 if {[string match h* $data(-orientation)]} {
323 $data(basecmd) coords bar -1 0 [expr {$pcnt*$W}] $H
325 ## Vertical orientation needs testing
326 $data(basecmd) coords bar -1 $H $W [expr {$pcnt*$H}]
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]%
332 $data(basecmd) coords text $W $H
336 ;proc Progressbar:set {w val} {
338 if {![regexp {^[0-9]+$} $val] || $val<0} {
339 return -code error "value must be an integer greater than 0"
341 if {[string comp {} $data(-variable)]} {
342 upvar \#0 $data(-variable) var
343 if {[catch {set var $val} err]} {
344 return -code error $err
347 set data(-value) $val
348 Progressbar:resize $w [winfo width $data(canvas)] \
349 [winfo height $data(canvas)]
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.
357 ;proc Progressbar_create {w args} {
360 set cnt [incr data(counter)]
361 if {[string match -* [lindex $args 0]]} {
363 set item progress$cnt
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"
373 array set config [list \
374 -background $data(-itembackground) \
375 -fgstart $data(-indicatorcolor) \
376 -fgfinished $data(-itemfgfinished) \
378 -type $data(-itemtype) \
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)"
387 set config(-type) $configargs(-type)
388 unset configargs(-type)
390 incr data(-maxvalue) $config(-maxvalue)
391 incr data(-value) $config(-value)
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
402 grid $config(w) -row $nrows -column 0
405 set data(I:$item) [array get config]
407 if {[string compare {} $args]} {
408 eval Progressbar_itemconfigure [list $w] [list $item] \
409 [array get configargs]
411 Progressbar:set $w $data(-value)
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
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\""
428 set len [expr {$len/3}]
429 scan $hex "%${len}x%${len}x%${len}x" r g b
431 return [list $r $g $b]
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)}]]
447 ;proc Progressbar_delete {w args} {
451 ## Don't complain about unknown items when deleting
452 if {![info exists data(I:$item)]} continue
454 array set config $data(I:$item)
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 }
462 image delete $config(image)
465 Progressbar:set $w $data(-value)
468 ## Progressbar_itemconfigure
469 ## configure a progressar constituent item
471 ;proc Progressbar_itemconfigure {w item args} {
474 if {![info exists data(I:$item)]} {
475 return -code error "unknown item \"$item\""
478 array set config $data(I:$item)
479 if {[string match {} $args]} { return [array get config -*] }
482 foreach {key val} $args {
483 if {[string match {} [set arg [array names config $key]]]} {
484 set arg [array names config ${key}*]
486 set num [llength $arg]
488 return -code error "unknown option \"$key\", must be:\
489 [join [array names config -*] {, }]"
491 return -code error "ambiguous option \"$args\",\
492 must be one of: [join $arg {, }]"
498 if {![regexp {^[0-9]+$} $val] || $val<=0} {
499 return -code error "$key must be an integer greater than 0"
501 incr data(-maxvalue) [expr {$val-$config(-maxvalue)}]
502 if {$data(-maxvalue) < 0} { set data(-maxvalue) 0 }
506 if {![regexp {^[0-9]+$} $val] || $val<0} {
507 return -code error "$key must be a postive integer"
509 incr data(-value) [expr {$val-$config(-value)}]
510 if {$data(-value) < 0} { set data(-value) 0 }
514 ## Should we allow this to be changed?
515 return -code error "-type cannot be changed after creation"
518 if {![regexp {^\#([0-9a-f]+)$} $val]} {
519 return -code error "color value must be in \#rgb format"
523 if {![regexp {^\#([0-9a-f]+)$} $val]} {
524 return -code error "color value must be in \#rgb format"
528 set config($key) $val
530 set data(I:$item) [array get config]
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)}]]
538 if {$valChanged} { Progressbar:set $w $data(-value) }
541 ## Progressbar_itemcget
542 ## Returns a single item option
544 ;proc Progressbar_itemcget {w item opt} {
547 if {![info exists data(I:$item)]} {
548 return -code error "unknown item \"$item\""
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\""
559 ## Return a list of item names
561 ;proc Progressbar_names {w {pattern *}} {
565 foreach name [array names data I:$pattern] {
566 lappend items [string range $name 2 end]
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
575 ;proc Progressbar_recalculate {w} {
578 set items [array names data I:*]
579 if {[string compare {} $items]} {
580 set data(-maxvalue) 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]
591 if {$data(-value) < 0} {set data(-value) 0}
592 if {$data(-maxvalue) < 0} {set data(-maxvalue) 0}
594 Progressbar:set $w $data(-value)
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};
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};