File Coverage

blib/lib/Wx/Perl/Throbber.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #############################################################################
2             ## Name: Wx::Perl::Throbber
3             ## Purpose: An animated throbber/spinner
4             ## Author: Simon Flack
5             ## Modified by: $Author: simonflack $ on $Date: 2005/03/25 13:38:55 $
6             ## Created: 22/03/2004
7             ## RCS-ID: $Id: Throbber.pm,v 1.5 2005/03/25 13:38:55 simonflack Exp $
8             #############################################################################
9              
10             package Wx::Perl::Throbber;
11              
12 1     1   873 use strict;
  1         2  
  1         40  
13 1     1   5 use vars qw/@ISA $VERSION @EXPORT_OK/;
  1         2  
  1         72  
14 1     1   385 use Wx qw/:misc wxWHITE/;
  0            
  0            
15             use Wx::Event qw/EVT_PAINT EVT_TIMER/;
16             use Wx::Perl::Carp;
17             use Exporter;
18              
19             $VERSION = sprintf'%d.%02d', q$Revision: 1.5 $ =~ /: (\d+)\.(\d+)/;
20             @ISA = qw/Exporter Wx::Panel/;
21             @EXPORT_OK = qw/EVT_UPDATE_THROBBER/;
22              
23             use constant DFLT_FRAMEDELAY => 75;
24             use constant THROBBER_EVENT => Wx::NewEventType;
25              
26             sub EVT_UPDATE_THROBBER { $_[0]->Connect(-1, -1, THROBBER_EVENT, $_[1]) }
27              
28             sub UpdateThrobberEvent {
29             my $event = new Wx::PlEvent($_[0]->GetId, THROBBER_EVENT);
30             }
31              
32             sub new {
33             my $class = shift;
34             my ($parent, $id, $bitmap, $pos, $size, $frameDelay, $frames, $framesWidth,
35             $label, $overlay, $reverse, $style, $name) = @_;
36              
37             $id = '-1' unless defined $id;
38             $name = 'throbber' unless defined $name;
39             $pos = wxDefaultPosition unless defined $pos;
40             $size = wxDefaultSize unless defined $size;
41             $label = '' unless defined $label;
42             $reverse = 0 unless defined $reverse;
43              
44             my $self = $class -> SUPER::new ($parent, $id, $pos, $size, $style, $name);
45              
46             $self -> SetClientSize ($size);
47             $self -> SetFrameDelay ($frameDelay ? $frameDelay : DFLT_FRAMEDELAY);
48             $self -> SetAutoReverse ($reverse);
49              
50             if (defined $bitmap) {
51             $self -> SetBitmap ($bitmap, $frames, $framesWidth);
52             $self -> SetLabel ($label) if defined $label;
53             $self -> SetOverlay ($overlay) if defined $overlay;
54             $self -> ShowLabel (defined $label);
55             }
56             $self -> _init($reverse, defined $label);
57              
58             EVT_UPDATE_THROBBER ($self, \&Rotate);
59             EVT_PAINT ($self, \&OnPaint);
60             EVT_TIMER ($self, $self -> {timerID}, \&OnTimer);
61             bless $self, $class;
62             }
63              
64             sub _init {
65             my $self = shift;
66             my ($reverse, $show_label) = @_;
67             $self -> {running} = 0;
68             $self -> {current} = 0;
69             $self -> {direction} = 1;
70             $self -> {timerID} = Wx::NewId;
71             $self -> {timer} = Wx::Timer -> new ($self, $self -> {timerID});
72             }
73              
74             sub OnTimer {
75             my $self = shift;
76             $self -> ProcessEvent ($self -> UpdateThrobberEvent());
77             }
78              
79             sub DESTROY {
80             my $self = shift;
81             $self -> Stop;
82             }
83              
84             # Draw the throbber
85             sub Draw {
86             my $self = shift;
87             my ($dc) = @_;
88              
89             $dc -> DrawBitmap (
90             $self -> {submaps} [$self -> {current}],
91             0,
92             0,
93             1
94             );
95             if ($self -> {overlay} && $self -> {showOverlay}) {
96             $dc->DrawBitmap (
97             $self -> {overlay},
98             $self -> {overlayX},
99             $self -> {overlayY},
100             1
101             );
102             }
103             if ($self -> {label} && $self -> {showLabel}) {
104             $dc->DrawText (
105             $self -> {label},
106             $self -> {labelX},
107             $self -> {labelY}
108             );
109             $dc->SetTextForeground (wxWHITE);
110             $dc->DrawText(
111             $self -> {label},
112             $self -> {labelX} - 1,
113             $self -> {labelY} - 1
114             );
115             }
116             }
117              
118             sub OnPaint {
119             my $self = shift;
120             my ($event) = @_;
121             $self -> Draw(new Wx::PaintDC($self));
122             $event -> Skip();
123             }
124              
125             # Change the frame
126             sub Rotate {
127             my $self = shift;
128             my ($event) = @_;
129              
130             $self -> {current} += $self -> {direction};
131              
132             # Have we reached the last frame
133             if ($self -> {current} == scalar @{$self -> {sequence}}) {
134             if ($self -> {autoReverse}) {
135             $self -> Reverse();
136             $self -> {current} = scalar @{$self -> {sequence}} - 1;
137             } else {
138             $self -> {current} = 1;
139             }
140             }
141              
142             # Have we reached the first frame
143             if ($self -> {current} == 0) {
144             if ($self -> {autoReverse}) {
145             $self -> Reverse();
146             $self -> {current} = 1;
147             } else {
148             $self -> {current} = scalar @{$self -> {sequence}} - 1;
149             }
150             }
151              
152             $self -> Draw(new Wx::ClientDC($self));
153             }
154              
155             ##############################################################################
156             # Public Methods
157              
158             sub SetBitmap {
159             my $self = shift;
160             my ($bitmap, $frames, $framesWidth) = @_;
161              
162             croak "SetBitmap: requires a bitmap" unless ref $bitmap;
163             croak "SetBitmap: Not a valid bitmap"
164             unless ref $bitmap eq 'ARRAY'
165             || UNIVERSAL::isa($bitmap,'Wx::Bitmap');
166              
167             $frames = 1 unless defined $frames;
168             $framesWidth = 0 unless defined $framesWidth;
169             $self -> _set_bitmap_size ($bitmap, $framesWidth);
170              
171             if (ref $bitmap eq 'ARRAY') {
172             $self -> {submaps} = $bitmap;
173             $self -> {frames} = scalar @$bitmap;
174             } elsif ($bitmap -> isa ('Wx::Bitmap')) {
175             $self -> {frames} = $frames;
176             $self -> {submaps} = [];
177              
178             # Slice the bitmap into 0 + $frames frames
179             # Wx::Bitmap->GetSubBitmap is broken in wxMSW 2.4, so we convert to an
180             # image, and convert each SubImage back to a Wx::Bitmap
181             my $image = new Wx::Image($bitmap);
182             for (0 .. $frames - 1) {
183             my $rect = new Wx::Rect(
184             $_ * $framesWidth,
185             0,
186             $self -> {width},
187             $self -> {height}
188             );
189             my $subimage = $image -> GetSubImage ($rect);
190             my $submap = new Wx::Bitmap ($subimage);
191             push @{$self -> {submaps}}, $submap;
192             }
193             }
194              
195             # Set the sequence
196             $self -> {sequence} = [1 .. $self -> {frames}];
197             return 1;
198             }
199              
200             sub SetFrameDelay {
201             my $self = shift;
202             my ($frameDelay) = @_;
203              
204             croak "USAGE: SetFrameDelay(miliseconds)"
205             unless defined $frameDelay && !ref $frameDelay;
206              
207             $self->{frameDelay} = int $frameDelay;
208             if ($self -> IsRunning) {
209             $self -> Stop;
210             $self -> Start;
211             }
212             return 1;
213             }
214              
215             sub GetFrameDelay {
216             my $self = shift;
217             return $self -> {frameDelay};
218             }
219              
220             sub GetCurrentFrame {
221             my $self = shift;
222             return $self -> {current};
223             }
224              
225             sub GetFrameCount {
226             my $self = shift;
227             return $self -> {frames} - 1;
228             }
229              
230             sub Start {
231             my $self = shift;
232             unless ($self -> {running}) {
233             $self -> {running} = 1;
234             $self -> {timer} -> Start (int $self -> {frameDelay});
235             }
236             return 1;
237             }
238              
239             sub Stop {
240             my $self = shift;
241             if ($self -> {running}) {
242             $self -> {timer} -> Stop;
243             $self -> {running} = 0;
244             }
245             return 1;
246             }
247              
248             sub Rest {
249             my $self = shift;
250             $self -> Stop ();
251             $self -> {current} = 0;
252             $self -> Draw(new Wx::ClientDC($self));
253             return 1;
254             }
255              
256             sub IsRunning {
257             my $self = shift;
258             return $self -> {running};
259             }
260              
261             sub Reverse {
262             my $self = shift;
263             $self -> {direction} = - $self -> {direction};
264             return 1;
265             }
266              
267             sub SetAutoReverse {
268             my $self = shift;
269             my ($state) = @_;
270              
271             $self -> {autoReverse} = not (defined $state && !$state);
272             return 1;
273             }
274              
275             sub GetAutoReverse {
276             my $self = shift;
277             return $self -> {autoReverse};
278             }
279              
280             sub SetOverlay {
281             my $self = shift;
282             my $overlay = shift;
283              
284             croak "SetOverlay: requires a bitmap"
285             unless ref $overlay && UNIVERSAL::isa($overlay, 'Wx::Bitmap');
286              
287             return unless $self -> {sequence} && scalar $self -> {sequence};
288             if ($overlay) {
289             $self -> {overlay} = $overlay;
290             $self -> {overlayX} = int(($self->{width} - $overlay -> GetWidth)/2);
291             $self -> {overlayY} = int(($self->{height} - $overlay -> GetHeight)/2);
292             return 1;
293             }
294             }
295              
296             sub GetOverlay {
297             my $self = shift;
298             return unless $self -> {overlay};
299             return new Wx::Bitmap ($self -> {overlay});
300             }
301              
302             sub ShowOverlay {
303             my $self = shift;
304             my ($state) = @_;
305              
306             $self -> {showOverlay} = not (defined $state && !$state);
307             $self -> Draw(new Wx::ClientDC($self));
308             return 1;
309             }
310              
311             sub GetLabel {
312             my $self = shift;
313             return $self -> {label};
314             }
315              
316             sub ShowLabel {
317             my $self = shift;
318             my ($state) = @_;
319              
320             $self -> {showLabel} = not (defined $state && !$state);
321             $self -> Draw(new Wx::ClientDC($self));
322             return $self -> {label};
323             }
324              
325             sub SetLabel {
326             my $self = shift;
327             my ($label) = @_;
328              
329             croak "USAGE: SetLabel (label)"
330             unless defined $label && !ref $label;
331              
332             return unless $self -> {sequence} && scalar $self -> {sequence};
333             if (defined $label) {
334             $self -> {label} = $label;
335             my ($extentx, $extenty) = $self -> GetTextExtent ($label);
336             $self -> {labelX} = int(($self -> {width} - $extentx) / 2);
337             $self -> {labelY} = int(($self -> {height} - $extenty) / 2);
338             return 1
339             }
340             }
341              
342             sub SetFont {
343             my $self = shift;
344             my ($font) = @_;
345             croak "SetFont: requires a Wx::Font"
346             unless ref $font && UNIVERSAL::isa($font, 'Wx::Font');
347              
348             $self -> SetFont ($font);
349             $self -> SetLabel ($self -> {label});
350             $self -> Draw(new Wx::ClientDC($self));
351             return 1;
352             }
353              
354             # Private
355              
356             # Set the bitmap with and size (for use by overlay/label)
357             sub _set_bitmap_size {
358             my $self = shift;
359             my ($bitmap, $framesWidth) = @_;
360              
361             my ($width, $height) = $self -> GetSizeWH();
362             if ($width == -1) {
363             if (ref $bitmap && ref $bitmap eq 'ARRAY') {
364             $width = $bitmap -> [0] -> GetWidth;
365             } else {
366             $width = $framesWidth ? $framesWidth : $width
367             }
368             }
369             if ($height == -1) {
370             if (ref $bitmap && ref $bitmap eq 'ARRAY') {
371             $width = $bitmap -> [0] -> GetHeight;
372             } else {
373             $width = $bitmap -> GetHeight;
374             }
375             }
376             if ($width == -1 || $height == -1) {
377             croak "Unable to determine size";
378             }
379              
380             $self -> {width} = $width;
381             $self -> {height} = $height;
382             }
383              
384             =pod
385              
386             =head1 NAME
387              
388             Wx::Perl::Throbber - An animated throbber/spinner
389              
390             =head1 SYNOPSIS
391              
392             use Wx::Perl::Throbber;
393              
394             my @frames;
395             foreach ('1.gif', '2.gif', '3.gif') {
396             push @frames, new Wx::Bitmap($_, wxBITMAP_TYPE_ANY);
397             }
398              
399             my $throbber = new Wx::Perl::Throbber($parent, -1, \@frames, $pos, $size);
400             $throbber->SetLabel('Please Wait');
401             $throbber->ShowLabel(1);
402             $throbber->Start();
403              
404             ...
405             $throbber->Rest(); # or Stop()
406              
407             =head1 DESCRIPTION
408              
409             This control is based on the Python library wx.throbber.
410              
411             A throbber displays an animated image that can be started, stopped, reversed,
412             etc. Useful for showing an ongoing process (like most web browsers use) or
413             simply for adding eye-candy to an application.
414              
415             Throbbers utilize a Wx::Timer so that normal processing can continue
416             unencumbered.
417              
418             =head1 METHODS
419              
420             =over 4
421              
422             =item $throbber = new($parent, $id, $bitmap, $position, $size, $frameDelay, $frames, $framesWidth, $label, $overlay, $reverse, $style, $name)
423              
424             $parent (parent window)
425             $id = -1 (window identifier)
426             $bitmap = undef (throbber bitmap. see SetBitmap())
427             $position = wxDefaultPosition (window position)
428             $size = wxDefaultSize (window size)
429             $frameDelay = 75 (milliseconds. See SetFrameDelay)
430             $frames = undef (number of frames. see SetBitmap())
431             $framesWidth = undef (width of frames. see SetBitmap())
432             $label = '' (text label. see SetLabel())
433             $overlay = undef (overlay bitmap. see SetOverlay())
434             $reverse = 0 (auto-reverse)
435             $style = undef (window style)
436             $name = "throbber" (window name)
437              
438             =item SetBitmap($bitmap, $frames, $framesWidth)
439              
440             C<$bitmap> is either a single C that will be split into frames (a
441             composite image) or a list of C objects that will be treated as
442             individual frames.
443              
444             If a single (composite) image is given, then additional information must be
445             provided: the number of frames in the image (C<$frames>) and the width of each
446             frame (C<$framesWidth>).
447              
448             The first frame is treated as the "at rest" frame (it is not shown during
449             animation, but only when C is called.
450              
451             =item SetFrameDelay($milliseconds)
452              
453             Set the delay between frames I
454              
455             Default is 75 milliseconds
456              
457             =item GetFrameDelay()
458              
459             Returns the frame delay
460              
461             =item Start()
462              
463             Start the animation
464              
465             =item Stop()
466              
467             Stop the animation
468              
469             =item Rest()
470              
471             Stop the animation and return to the I (frame 0)
472              
473             =item IsRunning()
474              
475             Returns C if the animation is running
476              
477             =item GetCurrentFrame()
478              
479             Returns the frame index that is currently displayed. Starts at 0 (the I
480             frame>)
481              
482             =item GetFrameCount()
483              
484             Returns the number of frames in the animation (excluding the I)
485              
486             =item Reverse()
487              
488             Change the direction of the animation
489              
490             =item SetAutoReverse($bool)
491              
492             Turn on/off auto-reverse. When auto-reverse is set, the throbber will change
493             direction when it reaches the start/end of the animation. Otherwise it jumps
494             back to the beginning.
495              
496             =item GetAutoReverse()
497              
498             Get the auto-reverse state
499              
500             =item SetOverlay($bitmap)
501              
502             Sets an overlay bitmap to be displayed above the throbber animation
503              
504             =item GetOverlay()
505              
506             Returns a copy of the overlay bitmap set for the throbber
507              
508             =item ShowOverlay($state)
509              
510             Set true/false whether the overlay bitmap is shown
511              
512             =item SetLabel($label)
513              
514             Set the text of the label. The text label appears above the throbber animation
515             and overlay (if applicable)
516              
517             =item GetLabel()
518              
519             Returns the label set for the throbber
520              
521             =item ShowLabel($state)
522              
523             Set true/false whether the text label is shown
524              
525             =item SetFont ($font)
526              
527             Set the font for the label. Expects a Wx::Font object.
528              
529             =back
530              
531             =head1 EVENTS
532              
533             =over 4
534              
535             =item EVT_UPDATE_THROBBER($throbber, \&func)
536              
537             This event is processed while the throbber is running, every $frameDelay
538             milliseconds
539              
540             This function is exported on request:
541              
542             use Wx::Perl::Throbber 'EVT_UPDATE_THROBBER';
543              
544             =back
545              
546             =head1 AUTHOR
547              
548             Simon Flack
549              
550             =head1 COPYRIGHT
551              
552             This module is released under the wxWindows/GPL license
553              
554             =head1 ACKNOWLEDGEMENTS
555              
556             Wx::Perl::Throbber is based on the Python library wx.throbber by Cliff Wells
557              
558             =cut