PageRenderTime 341ms CodeModel.GetById 243ms app.highlight 32ms RepoModel.GetById 62ms app.codeStats 0ms

/lib/Wx/Perl/Throbber.pm

https://github.com/gitpan/Wx-Perl-Throbber
Perl | 558 lines | 300 code | 63 blank | 195 comment | 45 complexity | 7919f546dc4862571650801ad5ff507a MD5 | raw file
  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
 10package Wx::Perl::Throbber;
 11
 12use strict;
 13use vars      qw/@ISA $VERSION @EXPORT_OK/;
 14use Wx        qw/:misc wxWHITE/;
 15use Wx::Event qw/EVT_PAINT EVT_TIMER/;
 16use Wx::Perl::Carp;
 17use 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
 23use constant DFLT_FRAMEDELAY => 75;
 24use constant THROBBER_EVENT  => Wx::NewEventType;
 25
 26sub EVT_UPDATE_THROBBER { $_[0]->Connect(-1, -1, THROBBER_EVENT, $_[1]) }
 27
 28sub UpdateThrobberEvent {
 29    my $event = new Wx::PlEvent($_[0]->GetId, THROBBER_EVENT);
 30}
 31
 32sub 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
 64sub _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
 74sub OnTimer {
 75    my $self = shift;
 76    $self -> ProcessEvent ($self -> UpdateThrobberEvent());
 77}
 78
 79sub DESTROY {
 80    my $self = shift;
 81    $self -> Stop;
 82}
 83
 84# Draw the throbber
 85sub 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
118sub OnPaint {
119    my $self = shift;
120    my ($event) = @_;
121    $self -> Draw(new Wx::PaintDC($self));
122    $event -> Skip();
123}
124
125# Change the frame
126sub 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
158sub 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
200sub 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
215sub GetFrameDelay {
216    my $self = shift;
217    return $self -> {frameDelay};
218}
219
220sub GetCurrentFrame {
221    my $self = shift;
222    return $self -> {current};
223}
224
225sub GetFrameCount {
226    my $self = shift;
227    return $self -> {frames} - 1;
228}
229
230sub 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
239sub Stop {
240    my $self = shift;
241    if ($self -> {running}) {
242        $self -> {timer} -> Stop;
243        $self -> {running} = 0;
244    }
245    return 1;
246}
247
248sub Rest {
249    my $self = shift;
250    $self -> Stop ();
251    $self -> {current} = 0;
252    $self -> Draw(new Wx::ClientDC($self));
253    return 1;
254}
255
256sub IsRunning {
257    my $self = shift;
258    return $self -> {running};
259}
260
261sub Reverse {
262    my $self = shift;
263    $self -> {direction} = - $self -> {direction};
264    return 1;
265}
266
267sub SetAutoReverse {
268    my $self = shift;
269    my ($state) = @_;
270
271    $self -> {autoReverse} = not (defined $state && !$state);
272    return 1;
273}
274
275sub GetAutoReverse {
276    my $self = shift;
277    return $self -> {autoReverse};
278}
279
280sub 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
296sub GetOverlay {
297    my $self = shift;
298    return unless $self -> {overlay};
299    return new Wx::Bitmap ($self -> {overlay});
300}
301
302sub 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
311sub GetLabel {
312    my $self = shift;
313    return $self -> {label};
314}
315
316sub 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
325sub 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
342sub 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)
357sub _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
388Wx::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
409This control is based on the Python library wx.throbber.
410
411A throbber displays an animated image that can be started, stopped, reversed,
412etc.  Useful for showing an ongoing process (like most web browsers use) or
413simply for adding eye-candy to an application.
414
415Throbbers utilize a Wx::Timer so that normal processing can continue
416unencumbered.
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
440C<$bitmap> is either a single C<Wx::Bitmap> that will be split into frames (a
441composite image) or a list of C<Wx::Bitmap> objects that will be treated as
442individual frames.
443
444If a single (composite) image is given, then additional information must be
445provided: the number of frames in the image (C<$frames>) and the width of each
446frame (C<$framesWidth>).
447
448The first frame is treated as the "at rest" frame (it is not shown during
449animation, but only when C<Rest()> is called.
450
451=item SetFrameDelay($milliseconds)
452
453Set the delay between frames I<in milliseconds>
454
455Default is 75 milliseconds
456
457=item GetFrameDelay()
458
459Returns the frame delay
460
461=item Start()
462
463Start the animation
464
465=item Stop()
466
467Stop the animation
468
469=item Rest()
470
471Stop the animation and return to the I<rest frame> (frame 0)
472
473=item IsRunning()
474
475Returns C<true> if the animation is running
476
477=item GetCurrentFrame()
478
479Returns the frame index that is currently displayed. Starts at 0 (the I<rest 
480frame>)
481
482=item GetFrameCount()
483
484Returns the number of frames in the animation (excluding the I<rest frame>)
485
486=item Reverse()
487
488Change the direction of the animation
489
490=item SetAutoReverse($bool)
491
492Turn on/off auto-reverse. When auto-reverse is set, the throbber will change
493direction when it reaches the start/end of the animation. Otherwise it jumps
494back to the beginning.
495
496=item GetAutoReverse()
497
498Get the auto-reverse state
499
500=item SetOverlay($bitmap)
501
502Sets an overlay bitmap to be displayed above the throbber animation
503
504=item GetOverlay()
505
506Returns a copy of the overlay bitmap set for the throbber
507
508=item ShowOverlay($state)
509
510Set true/false whether the overlay bitmap is shown
511
512=item SetLabel($label)
513
514Set the text of the label. The text label appears above the throbber animation
515and overlay (if applicable)
516
517=item GetLabel()
518
519Returns the label set for the throbber
520
521=item ShowLabel($state)
522
523Set true/false whether the text label is shown
524
525=item SetFont ($font)
526
527Set 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
537This event is processed while the throbber is running, every $frameDelay
538milliseconds
539
540This function is exported on request:
541
542    use Wx::Perl::Throbber 'EVT_UPDATE_THROBBER';
543
544=back
545
546=head1 AUTHOR
547
548Simon Flack
549
550=head1 COPYRIGHT
551
552This module is released under the wxWindows/GPL license
553
554=head1 ACKNOWLEDGEMENTS
555
556Wx::Perl::Throbber is based on the Python library wx.throbber by Cliff Wells
557
558=cut