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