5 # single bar graph pseudo-widget, built out of Tk canvases.
10 $font=MGM::Font::new(MGM::Xpm::read($main::fontfile));
13 my($w,$width,$height,$relief,$dim,$lit)=@_;
16 if($relief eq 'sunken' ||
18 $w->createLine($width-1,0, 0,0, 0,$height-1, -capstyle=>'projecting',
20 $w->createLine($width-1,0, $width-1,$height-1, 0,$height-1,
21 -capstyle=>'projecting',-fill=>$lit);
23 if($relief eq 'groove'){
30 if($relief eq 'raised'){
31 $w->createLine($width-1,$z, $width-1,$height-1, $z,$height-1,
32 -capstyle=>'projecting',-fill=>$dim);
33 $w->createLine($width-1,$z, $z,$z, $z,$height-1,
34 -capstyle=>'projecting',-fill=>$lit);
40 $this->{"below"}->place(@_);
45 my ($ref,$p,$width,$height,$num)=@_;
47 $orient=$main::orient;
49 # we're not as flat as we like to think
52 my$below=$this->{"below"}=$p->Canvas(Name=>$num);
54 my$above=$this->{"above"}=$below->Canvas()->place(-relx=>0,-rely=>0,
56 -bordermode=>'outside');
58 # we configure the color and label via a configurable X resource;
59 # it's cleaner than setting up a system that alters a single X resource
61 $this->{"aforeXr"}='litforeground';
62 $this->{"abackXr"}='litbackground';
63 $this->{"labelXr"}='label';
65 $this->{"width"}=$width;
66 $this->{"height"}=$height;
77 while(defined($arg=shift)){
80 if(!defined($val)){last;}
84 $this->{'below'}->delete('xpmlabel');
85 $this->{'above'}->delete('xpmlabel');
88 $this->set($this->{'value'});
93 my$below=$this->{'below'};
94 my$above=$this->{'above'};
96 $orient=$main::orient;
98 my$width=$this->{"width"};
99 my$height=$this->{"height"};
100 my$num=$this->{"num"};
102 my $pad=$below->optionGet("textpad","");
103 my $relief=$below->optionGet('dimrelief','');
104 my $arelief=$below->optionGet('litrelief','');
106 my $afore=$below->optionGet($this->{"aforeXr"},'');
107 my $aback=$below->optionGet($this->{"abackXr"},'');
108 my $label=$below->optionGet($this->{"labelXr"},'');
110 my $fore=$below->optionGet('dimforeground','');
111 my $back=$below->optionGet('dimbackground','');
112 my $ratio=$below->optionGet('ratio','');
115 # $border=2 if ($relief eq 'groove');
116 # $border=1 if ($relief eq 'sunken');
117 # $border=1 if ($relief eq 'raised');
120 $aborder=2 if ($arelief eq 'groove');
121 $aborder=1 if ($arelief eq 'sunken');
122 $aborder=1 if ($arelief eq 'raised');
124 my($r,$g,$b)=$below->rgb($back);
128 my$lum=$r*.3+$g*.5+$b*.2;
129 my$dimdim=sprintf "#%02x%02x%02x", $r*.5,$g*.5,$b*.5;
130 my$dimlit=sprintf "#%02x%02x%02x",
131 &main::min($r+$lum*.7,255),&main::min($g+$lum*.7,255),
132 &main::min($b+$lum*.7,255);
134 if($orient eq 'vertical'){
135 my$text=$font->maketext($label,$width-($pad*2),$height-($pad*2),
138 my$unlittext=$below->Pixmap($below->PathName.".unlittext",
139 -data=>$text->write($fore,$back,32));
140 my$littext=$below->Pixmap($below->PathName.".littext",
141 -data=>$text->write($afore,$aback,32));
143 $this->{"barlength"}=$height;
144 $this->{"litrelief"}=$arelief;
145 $this->{"cfborder"}=$aborder;
147 $below->configure(-borderwidth=>$aborder,
149 -highlightthickness=>0,
150 -width=>($width-$aborder*2),
151 -height=>($height-$aborder*2),
154 $above->configure(-borderwidth=>0,
156 -highlightthickness=>0,
161 $this->{"reliefhack"}->destroy if(defined($this->{"reliefhack"}));
163 $this->{"reliefhack"}=$above->
164 Label(-borderwidth=>$aborder,
166 -highlightthickness=>0,
167 -width=>($width-$aborder*2),
170 place('-x'=>0,-rely=>1.0,'-y'=>-$aborder,
171 -anchor=>'nw',-bordermode=>'outside');
176 $below->createImage($pad,$height-$pad,-image=>$littext,-anchor=>'sw',
177 -tags=>['xpmlabel']);
178 $above->createImage($pad,$height-$pad,-image=>$unlittext,
179 -anchor=>'sw',-tags=>['xpmlabel']);
180 &drawborder($above,$width,$height,$relief,$dimdim,$dimlit);
183 my$text=$font->maketext($label,$height-($pad*2),$width-($pad*2),
185 my$unlittext=$below->Pixmap($below->PathName.".unlittext",
186 -data=>$text->write($fore,$back,32));
187 my$littext=$below->Pixmap($below->PathName.".littext",
188 -data=>$text->write($afore,$aback,32));
190 $this->{"barlength"}=$width;
191 $this->{"cfborder"}=$aborder;
192 $below->configure(-borderwidth=>0,
194 -highlightthickness=>0,
196 -height=>$height-$border*2,
199 $above->configure(-borderwidth=>$aborder,
201 -highlightthickness=>0,
202 -width=>($width-2*$aborder),
203 -height=>($height-2*$aborder),
206 $above->createImage($pad,$height-$pad,-image=>$littext,-anchor=>'sw',
207 -tags=>['xpmlabel']);
208 $below->createImage($pad,$height-$pad,-image=>$unlittext,
209 -anchor=>'sw',-tags=>['xpmlabel']);
210 &drawborder($below,$width,$height,$relief,$dimdim,$dimlit);
221 $bar->{'value'}=$per;
222 my $orient=$main::orient;
223 if($orient eq 'vertical'){
225 configure('-height'=>($bar->{"barlength"}-
226 $per+$bar->{"cfborder"}));
228 # handle another one-off bug in Tk
229 my $border=$bar->{"cfborder"};
230 if($per-2*$border > 0){
234 configure('-width'=>($per-2*$border));
237 configure('-width'=>(0));
245 use vars qw(%numbers %prompts);
247 # all the labels are the same size/color, but we have to do this after the toplevel is created so we have the size/color resources
250 my$size=$main::toplevel->optionGet("labelsize","");
251 my$fg=$main::foreground;
252 my$bg=$main::background;
253 my$orient=$main::orient;
254 while(defined(my$num=shift)){
255 if(!defined($numbers{"$num"})){
256 $numbers{"$num"}=$main::toplevel->
257 Pixmap(-data=>$MGM::Bar::font->
258 maketext("$num",$size,undef,1,
259 $orient eq 'vertical'?1:undef)->
260 write($fg,$bg,32,32));
266 if(!defined($numbers)){
267 &cacheem("0","1","2","4","8","16","32","64","128","256","512");
274 if(!defined($prompts{"$prompt"})){
275 my$size=$main::toplevel->optionGet("labelsize","");
276 my$fg=$main::foreground;
277 my$bg=$main::background;
278 my$orient=$main::orient;
280 $prompts{"$prompt"}=$main::toplevel->Pixmap(-data=>
281 $MGM::Bar::font->maketext("$prompt",$size,undef,1,
282 $orient eq 'vertical'?1:undef)->
283 write($fg,$bg,32,32));
284 $prompts{"k$prompt"}=$main::toplevel->Pixmap(-data=>
285 $MGM::Bar::font->maketext("k$prompt",$size,undef,1,
286 $orient eq 'vertical'?1:undef)->
287 write($fg,$bg,32,32));
288 $prompts{"M$prompt"}=$main::toplevel->Pixmap(-data=>
289 $MGM::Bar::font->maketext("M$prompt",$size,undef,1,
290 $orient eq 'vertical'?1:undef)->
291 write($fg,$bg,32,32));
292 $prompts{"G$prompt"}=$main::toplevel->Pixmap(-data=>
293 $MGM::Bar::font->maketext("G$prompt",$size,undef,1,
294 $orient eq 'vertical'?1:undef)->
295 write($fg,$bg,32,32));
300 my($prompt,$setting)=@_;
302 if($main::orient eq 'vertical'){
303 ($numbers{"$setting"}->width,
304 $numbers{"$setting"}->height+$prompts{"$prompt"}->height);
306 ($numbers{"$setting"}->height,
307 $numbers{"$setting"}->width+$prompts{"$prompt"}->width);
327 $scaleset=int($scaleset+.5);
328 ($scaleset,$multiplier);
332 my($p,$scaleset,$prompt)=@_;
333 my $orient=$main::orient;
337 ($scaleset,$mult)=&scalemod($scaleset);
340 my($textw,$textl)=&promptsize("$mult$prompt",$scaleset);
342 if($orient eq 'vertical'){
343 $label=$p->{"widget"}->Canvas(Name=>'scalerange',
345 -highlightthickness=>0,
349 place('-x'=>0,'-y'=>2,-anchor=>'nw');
350 $label->createImage(0,0,
351 -image=>$prompts{"$mult$prompt"},-anchor=>'nw');
352 $label->createImage(0,$prompts{"$mult$prompt"}->height,
353 -image=>$numbers{"$scaleset"},-anchor=>'nw');
355 $label=$p->{"widget"}->Canvas(Name=>'scalerange',
357 -highlightthickness=>0,
364 $label->createImage($numbers{"$scaleset"}->width,0,
365 -image=>$prompts{"$mult$prompt"},-anchor=>'nw');
366 $label->createImage(0,0,
367 -image=>$numbers{"$scaleset"},-anchor=>'nw');
376 my$toplevel=$p->{"toplevel"};
378 my$ret=bless {( 'width'=> $p->{"width"},
379 'height'=> $p->{"height"},
380 'name' => $p->{"name"},
386 $ret->{"scaletimer"}=0;
387 $ret->{"scalemax"}=0;
389 $ret->_configure(@_);
391 my$widget=$ret->{"widget"}=
392 $toplevel->Canvas(-class=>$ret->{"name"},
393 Name=>$p->{"sequence"});
395 $ret->{"scalep"}=($widget->optionGet("scale","") eq 'true');
396 $ret->{"scroll"}=($widget->optionGet("scalescroll","") eq 'true');
397 $ret->{"scalereturn"}=$widget->optionGet("scalereturn","");
398 $ret->{"scalethresh"}=$widget->optionGet("scalethresh","");
400 $ret->{"meter"}=[(map{-1}(1..$ret->{"num"}))];
405 my($this,$scaleset,$prompt,$num)=@_;
413 if (&main::moption($this,"scale") eq 'true'){
414 my($set,$mult)=&scalemod($scaleset);
416 &cacheprompt($prompt);
419 ($textw,$textl)=&promptsize("$mult$prompt",$set);
425 $scalew=&main::max($scalew,$textw);
426 $barl=&main::max($barl,$textl);
428 ($barw+$scalew,$barl,$scalew);
432 my($w,$l)=&calcsize(@_);
434 if($main::orient eq 'vertical'){
444 # a 'graph' consists of a scale, and <n> bars under central control
446 my $num=$this->{"num"};
449 my $name=$this->{"name"};
450 my $prompt=$this->{"prompt"};
451 my $fixed=$this->{"fixed"};
453 my $height=$this->{"height"};
454 my $width=$this->{"width"};
455 my $widget=$this->{"widget"};
457 $widget->configure(-borderwidth=>0,
458 -highlightthickness=>0,
463 my $rangesetting=$this->{"rangesetting"};
464 my($dummy,$dummy2,$scalew)=&calcsize($this,$rangesetting,$prompt,$num);
465 $this->{"scalew"}=$scalew;
466 $this->{"numlabel"}=$this->promptlabel($rangesetting,$prompt);
468 my $orient=$main::orient;
469 if($orient eq 'vertical'){
470 $this->drawvscale if($scalew);
472 $this->{"barlength"}=$height;
473 my$barwidth=$width-$scalew;
474 my$frame=$this->{"barframe"}=
475 $widget->Frame(Name=>'bar',
484 $barwidth=int($barwidth);
486 for(my$i=0;$i<$num;$i++){
487 $this->{$i}=MGM::Bar->new($frame,$barwidth-1,
489 $i)->place('-x'=>$x,'-y'=>0,
494 $this->drawhscale if($scalew);
496 $this->{"barlength"}=$width;
497 my$barwidth=$height-$scalew;
498 my$frame=$this->{"barframe"}=
499 $widget->Frame(Name=>'bar',
508 $barwidth=int($barwidth);
510 for(my$i=0;$i<$num;$i++){
511 $this->{$i}=MGM::Bar->new($frame,$width,
513 $i)->place('-x'=>0,'-y'=>$y,
523 $this->{"widget"}->place(@_);
530 while(defined($arg=shift)){
533 if(!defined($val)){last;}
534 $this->{"$arg"}=$val;
537 # enforce power of two dynamic ranging
538 $this->{"rangesetting"}=$this->{"minscale"} if($this->{"minscale"}>
539 $this->{"rangesetting"});
540 $this->{"rangecurrent"}=$this->{"minscale"} if($this->{"minscale"}>
541 $this->{"rangecurrent"});
542 if($this->{"fixed"}==0){
544 while($i<$this->{"rangesetting"}){
547 $this->{"rangesetting"}=$i;
549 $this->{"rangediff"}=$this->{"rangesetting"}-
550 $this->{"rangecurrent"};
553 # we can't configure anything that requires size changes.
558 $this->_configure(@_);
560 if($copy{"rangesetting"} != $this->{"rangesetting"}){
561 my$val=int($this->{'rangesetting'});
563 $this->{"numlabel"}->destroy;
564 $this->{"numlabel"}=$this->promptlabel($val,$this->{'prompt'});
565 $this->{"rangediff"}=$this->{"rangesetting"}-
566 $this->{"rangecurrent"};
567 $this->{"scaletimer"}=0; # reset the timer
568 $this->{"scalemax"}=0;
573 # a way to configure individual bars
584 # length is in barlength
585 # current scale setting in rangecurrent
586 # scale 'goal' in rangesetting (may smooth-scroll)
587 # current max meter setting in metermax
588 # (other meter settings in meter0, meter1, meter2....)
591 my$widget=$this->{"widget"};
592 my$color=$widget->optionGet("scalecolor","");
593 my$height=$this->{"height"};
595 my$xo=$this->{"scalew"}-2;
597 $widget->createLine($xo,0,$xo,$height-1,$xo-$width,$height-1,
598 -tags=>['newscale'],-fill=>$color,
599 -capstyle=>'projecting');
601 # powers of two. tics no closer than every 4 pixels
604 if($this->{"rangesetting"}){
605 $max=$height/$this->{"rangecurrent"}*$this->{"rangesetting"};
607 if($this->{"rangecurrent"}){
608 $max=$height/$this->{"rangecurrent"};
611 if($this->{"rangecurrent"}==$this->{"rangesetting"}){
612 $widget->createLine($xo,0,$xo-$width,0,
613 -tags=>['newscale'],-fill=>$color,
614 -capstyle=>'projecting');
618 for(my$div=2;;$div*=2){
621 last if($max/$div<4);
622 for(my$tic=1;;$tic+=$step){
623 my$y=$height-$max/$div*$tic;
625 $widget->createLine($xo,$y,$xo-$width,$y,
626 -tags=>['newscale'],-fill=>$color,
627 -capstyle=>'projecting');
637 # replace old scale with new scale
639 $widget->delete('scale');
640 $widget->addtag('scale',withtag=>'newscale');
646 # length is in barlength
647 # current scale setting in rangecurrent
648 # scale 'goal' in rangesetting (may smooth-scroll)
649 # current max meter setting in metermax
650 # (other meter settings in meter0, meter1, meter2....)
653 my$widget=$this->{"widget"};
654 my$color=$widget->optionGet("scalecolor","Scalecolor");
655 my$yo=$this->{"scalew"}-2;
656 my$width=$this->{"width"};
659 $widget->createLine(0,$yo-$height,0,$yo,$width-1,$yo,
660 -tags=>['newscale'],-fill=>$color,
661 -capstyle=>'projecting');
663 # powers of two. tics no closer than every 4 pixels
665 if($this->{"rangesetting"}){
666 $max=$width/$this->{"rangecurrent"}*$this->{"rangesetting"};
668 if($this->{"rangecurrent"}){
669 $max=$width/$this->{"rangecurrent"};
672 if($this->{"rangecurrent"}==$this->{"rangesetting"}){
673 $widget->createLine($width-1,$yo-$height,$width-1,$yo,
674 -tags=>['newscale'],-fill=>$color,
675 -capstyle=>'projecting');
679 for(my$div=2;;$div*=2){
681 last if($max/$div<4);
683 for(my$tic=1;;$tic+=$step){
686 $widget->createLine($x,$yo-$height,$x,$yo,
687 -tags=>['newscale'],-fill=>$color,
688 -capstyle=>'projecting');
694 $height-- if ($flag);
698 # replace old scale with new scale
700 $widget->delete('scale');
701 $widget->addtag('scale',withtag=>'newscale');
706 my$val=$this->{'rangesetting'};
708 $this->{"numlabel"}->destroy;
709 $this->{"numlabel"}=$this->promptlabel($val,$this->{'prompt'});
710 $this->{"rangediff"}=$this->{"rangesetting"}-$this->{"rangecurrent"};
717 $scalep=$this->{"scalep"};
718 $scrollp=$this->{"scroll"};
720 if(!$scalep || !$scrollp){
721 $this->{"rangecurrent"}=$this->{"rangesetting"};
722 undef $this->{"anitimer"};
724 if($this->{"rangesetting"}==0){
725 if($this->{"rangecurrent"}<.02){
726 $this->{"rangecurrent"}=$this->{"rangesetting"};
727 undef $this->{"anitimer"};
729 $this->{"rangecurrent"}+=$this->{"rangediff"}*.1;
730 $this->{"anitimer"}->cancel if(defined($this->{"anitimer"}));
731 $this->{"anitimer"}=$this->{"widget"}->after(30,sub{$this->set;});
734 if(abs($this->{"rangecurrent"}/($this->{"rangesetting"})-1)<.02){
735 $this->{"rangecurrent"}=$this->{"rangesetting"};
736 undef $this->{"anitimer"};
738 $this->{"rangecurrent"}+=$this->{"rangediff"}*.1;
739 $this->{"anitimer"}->cancel if(defined($this->{"anitimer"}));
740 $this->{"anitimer"}=$this->{"widget"}->after(30,sub{$this->set;});
745 if($main::orient eq 'vertical'){
750 my$length=$this->{"barlength"};
751 my$num=$this->{"num"};
752 if($this->{"rangecurrent"}){
753 for(my$i=0;$i<$num;$i++){
755 set($this->{"meter"}->[$i]*$length/$this->{"rangecurrent"});
758 for(my$i=0;$i<$num;$i++){
767 my$num=$this->{"num"};
770 my$length=$this->{"barlength"};
772 # length is in barlength
773 # current scale setting in rangecurrent
774 # scale 'goal' in rangesetting (may smooth-scroll)
777 # we have new values incoming. We need to do full maneuvers
780 # might as well. prevents spurious error printing when
781 # something unexpected happens to a module (like a CPU
784 push @_,$#_+1,map{0}(1..$num); #lazy
788 # find the largest value for scale adjustment
790 map{$metermax=$_ if ($metermax<$_)}(@_, @{$this->{"meter"}},
791 $this->{"scalemax"});
792 $this->{"scalemax"}=$metermax;
793 my$fixed=$this->{"fixed"};
795 # raise the scale range?
797 if($fixed==0 && $metermax>$this->{"rangesetting"}){
798 splice @{$this->{"meter"}},0,$#_+1,@_;
799 while($metermax>$this->{"rangesetting"}){
800 if($this->{"rangesetting"} == 0){
801 $this->{"rangesetting"}=1;
803 $this->{"rangesetting"}*=2;
805 $this->{"scaletimer"}=0; # reset the timer
806 $this->{"scalemax"}=$metermax;
810 # lower the scale range?
811 my$scaletimer=$this->{"scaletimer"}+1;
812 if($fixed==0 && $scaletimer>=$this->{"scalereturn"} &&
813 $this->{"rangesetting"}>$this->{"minscale"} &&
814 $metermax*$this->{"scalethresh"}<$this->{"rangesetting"} &&
815 $this->{"rangesetting"}==$this->{"rangecurrent"}){
817 $this->{"rangesetting"}=int($this->{"rangesetting"}/2);
818 $this->{"scaletimer"}=0;
819 $this->{"scalemax"}=0;
820 splice @{$this->{"meter"}},0,$#_+1,@_;
824 my$meter=$this->{"meter"};
825 for(my$i=0;defined(my$val=shift);$i++){
826 if($meter->[$i]!=$val){
828 $val*=$length/$this->{"rangecurrent"};
829 $this->{$i}->set($val);
833 if($scaletimer>=$this->{"scalereturn"}){
834 $this->{"scaletimer"}=0;
835 $this->{"scalemax"}=0;
837 $this->{"scaletimer"}=$scaletimer;
838 $this->{"scalemax"}=$metermax;
844 # just scale animation. No reconfig