]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/animate/animate.cpp
fixed bug with the caret positioning after SetValue() introduced by the last commit
[wxWidgets.git] / contrib / src / animate / animate.cpp
CommitLineData
4638d697
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: animate.cpp
3// Purpose: Implementation of wxAnimation classes
4// Author: Julian Smart and Guillermo Rodriguez Garcia
5// Modified by:
6// Created: 13/8/99
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Guillermo Rodriguez Garcia
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13 #pragma implementation "animate.h"
14#endif
15
16#include "wx/wxprec.h"
17
18#ifdef __BORLANDC__
19 #pragma hdrstop
20#endif //__BORLANDC__
21
22#include "wx/wfstream.h"
23#include "wx/image.h"
24#include "wx/gifdecod.h"
25#include "wx/animate/animate.h"
26
27/*
28 * wxAnimationPlayer
29 */
30
31IMPLEMENT_CLASS(wxAnimationPlayer, wxObject)
32
33wxAnimationPlayer::wxAnimationPlayer(wxAnimationBase *animation, bool destroyAnimation)
34{
35 m_animation = animation;
36 m_destroyAnimation = destroyAnimation;
37 m_currentFrame = 0;
38 m_window = (wxWindow*) NULL;
39 m_position = wxPoint(0, 0);
40 m_looped = TRUE;
41 m_isPlaying = FALSE;
42 m_useBackgroundColour = FALSE;
43 m_customBackgroundColour = wxColour(0, 0, 0);
44 m_useCustomBackgroundColour = FALSE;
45 m_useParentBackground = FALSE;
46 m_timer.SetPlayer(this);
47}
48
49wxAnimationPlayer::~wxAnimationPlayer()
50{
51 Stop();
52 ClearCache();
53
54 if (m_destroyAnimation)
55 delete m_animation;
56}
57
58void wxAnimationPlayer::SetAnimation(wxAnimationBase* animation, bool destroyAnimation)
59{
60 ClearCache();
61 if (m_destroyAnimation)
62 delete m_animation;
63 m_animation = animation;
64 m_destroyAnimation = destroyAnimation;
65}
66
67// Play
68bool wxAnimationPlayer::Play(wxWindow& window, const wxPoint& pos, bool looped)
69{
70 m_window = & window;
71
72 if (!m_animation || !m_animation->IsValid())
73 return FALSE;
74
75 wxSize sz = GetLogicalScreenSize();
76 wxRect rect(pos, sz);
77 SaveBackground(rect);
78
79 if (m_frames.Number() == 0)
80 {
81 if (!Build())
82 {
83 wxLogWarning("wxAnimationPlayer::Play: could not build the image cache.");
84 return FALSE;
85 }
86 }
87 m_currentFrame = 0;
88
89 // Create the backing store
90 m_backingStore.Create(sz.x, sz.y);
91
92 PlayFrame();
93
94 return TRUE;
95}
96
97// Build animation (list of wxImages). If not called before Play
98// is called, Play will call this automatically.
99bool wxAnimationPlayer::Build()
100{
101 ClearCache();
102 if (m_animation)
103 {
104 int n = GetFrameCount();
105 int i;
106 for (i = 0; i < n; i++)
107 {
108 wxBitmap* bitmap = NULL;
109 wxImage* image = GetFrame(i);
110 if (image)
111 {
112 // If the frame has transparency,
113 // set the colour so converting to a bitmap
114 // will create a mask
115 wxColour transparentColour;
116 if (GetTransparentColour(transparentColour))
117 image->SetMaskColour(transparentColour.Red(), transparentColour.Green(), transparentColour.Blue());
118
119 bitmap = new wxBitmap(image->ConvertToBitmap());
120 delete image;
121 if (bitmap)
122 m_frames.Append(bitmap);
123 else
124 return FALSE;
125 }
126 else
127 return FALSE;
128 }
129 return TRUE;
130 }
131 else
132 return FALSE;
133}
134
135// Stop the animation
136void wxAnimationPlayer::Stop()
137{
138 m_timer.Stop();
139 m_isPlaying = FALSE;
140}
141
142// Draw the current view of the animation into this DC.
143// Call this from your OnPaint, for example.
144void wxAnimationPlayer::Draw(wxDC& dc)
145{
146 dc.DrawBitmap(m_backingStore, m_position.x, m_position.y);
147}
148
149
150int wxAnimationPlayer::GetFrameCount() const
151{
152 if (m_animation)
153 return m_animation->GetFrameCount();
154 else
155 return 0;
156}
157
158wxImage* wxAnimationPlayer::GetFrame(int i) const
159{
160 if (m_animation)
161 return m_animation->GetFrame(i);
162 else
163 return (wxImage*) NULL;
164}
165
166wxAnimationDisposal wxAnimationPlayer::GetDisposalMethod(int i) const
167{
168 if (m_animation)
169 return m_animation->GetDisposalMethod(i);
170 else
171 return wxANIM_UNSPECIFIED;
172}
173
174wxRect wxAnimationPlayer::GetFrameRect(int i) const
175{
176 if (m_animation)
177 return m_animation->GetFrameRect(i);
178 else
179 return wxRect(0, 0, 0, 0);
180}
181
182int wxAnimationPlayer::GetDelay(int i) const
183{
184 if (m_animation)
185 return m_animation->GetDelay(i);
186 else
187 return 0;
188}
189
190wxSize wxAnimationPlayer::GetLogicalScreenSize() const
191{
192 if (m_animation)
193 return m_animation->GetLogicalScreenSize();
194 else
195 return wxSize(0, 0);
196}
197
198bool wxAnimationPlayer::GetBackgroundColour(wxColour& col) const
199{
200 if (m_animation)
201 return m_animation->GetBackgroundColour(col);
202 else
203 return FALSE;
204}
205
206bool wxAnimationPlayer::GetTransparentColour(wxColour& col) const
207{
208 if (m_animation)
209 return m_animation->GetTransparentColour(col);
210 else
211 return FALSE;
212}
213
214// Play the frame
215bool wxAnimationPlayer::PlayFrame(int frame, wxWindow& window, wxPoint& pos)
216{
217 wxMemoryDC dc;
218 dc.SelectObject(m_backingStore);
219
220 // Draw the background: colour or area beneath animation
221 wxColour col(255, 255, 255);
222
223 if (UsingBackgroundColour())
224 {
225 if (UsingCustomBackgroundColour())
226 col = GetCustomBackgroundColour();
227 else
228 {
229 GetBackgroundColour(col);
230 }
231
232 // Draw the background colour loaded from the animation
233 // (or set by the user)
234 DrawBackground(dc, wxPoint(0, 0), col);
235 }
236 else
237 {
238 // Draw background we saved
239 dc.DrawBitmap(m_savedBackground, 0, 0);
240 }
241
242 // Draw all intermediate frames that haven't been removed from the
243 // animation
244 int i;
245 for (i = 0; i < (frame - 1); i++)
246 {
247 if ((GetDisposalMethod(i) == wxANIM_DONOTREMOVE) || (GetDisposalMethod(i) == wxANIM_UNSPECIFIED))
248 {
249 DrawFrame(i, dc, wxPoint(0, 0));
250 }
251 }
252 DrawFrame(frame, dc, wxPoint(0, 0));
253
254 dc.SelectObject(wxNullBitmap);
255
256 // Draw from backing bitmap onto window
257 wxClientDC clientDC(& window);
258 Draw(clientDC);
259
260 return TRUE;
261}
262
263bool wxAnimationPlayer::PlayFrame()
264{
265 m_isPlaying = TRUE;
266
267 PlayFrame(GetCurrentFrame(), * GetWindow(), GetPosition());
268
269 // Set the timer for the next frame
270 m_timer.Start(GetDelay(GetCurrentFrame()));
271
272 m_currentFrame ++;
273
274 if (m_currentFrame == GetFrameCount())
275 {
276 // Should a non-looped animation display the last frame?
277 if (!m_looped)
278 {
279 m_timer.Stop();
280 m_isPlaying = FALSE;
281 }
282 else
283 m_currentFrame = 0;
284 }
285
286 return TRUE;
287}
288
289// Clear the wxImage cache
290void wxAnimationPlayer::ClearCache()
291{
292 wxNode* node = m_frames.First();
293 while (node)
294 {
295 wxNode* next = node->Next();
296 wxBitmap* bitmap = (wxBitmap*) node->Data();
297 delete bitmap;
298 delete node;
299 node = next;
300 }
301}
302
303// Draw the background colour
304void wxAnimationPlayer::DrawBackground(wxDC& dc, const wxPoint& pos, const wxColour& colour)
305{
306 wxASSERT_MSG( (m_animation != NULL), "Animation not present in wxAnimationPlayer");
307 wxASSERT_MSG( (m_frames.Number() != 0), "Animation cache not present in wxAnimationPlayer");
308
309 // Optimization: if the first frame fills the whole area, and is non-transparent,
310 // don't bother drawing the background
311
312 wxBitmap* firstBitmap = (wxBitmap*) m_frames.First()->Data() ;
313 wxSize screenSize = GetLogicalScreenSize();
314 if (!firstBitmap->GetMask() && (firstBitmap->GetWidth() == screenSize.x) && (firstBitmap->GetHeight() == screenSize.y))
315 {
316 return;
317 }
318
319 wxBrush brush(colour, wxSOLID);
320 wxPen pen(colour, 1, wxSOLID);
321 dc.SetBrush(brush);
322 dc.SetPen(pen);
323 dc.SetLogicalFunction(wxCOPY);
324
325 dc.DrawRectangle(pos.x, pos.y, screenSize.x, screenSize.y);
326}
327
328// Save the pertinent area of the window so we can restore
329// it if drawing transparently
330void wxAnimationPlayer::SaveBackground(const wxRect& rect)
331{
332 wxASSERT( GetWindow() );
333
334 if (!GetWindow())
335 return;
336
337 m_savedBackground.Create(rect.width, rect.height);
338
339 wxMemoryDC memDC;
340 memDC.SelectObject(m_savedBackground);
341
342 if (m_useParentBackground && GetWindow()->GetParent())
343 {
344 wxWindow* parent = GetWindow()->GetParent();
345 wxClientDC dc(parent);
346
347 // Translate the point to coordinates in the
348 // parent's client area, going via screen coordinates
349 wxPoint pt(rect.x, rect.y);
350 wxPoint screenPt = GetWindow()->ClientToScreen(pt);
351 wxPoint parentPt = parent->ScreenToClient(screenPt);
352
353 memDC.Blit(0, 0, rect.width, rect.height, & dc, parentPt.x, parentPt.y);
354 }
355 else
356 {
357 wxClientDC dc(GetWindow());
358
359 memDC.Blit(0, 0, rect.width, rect.height, & dc, rect.x, rect.y);
360 }
361 memDC.SelectObject(wxNullBitmap);
362}
363
364// Draw this frame
365void wxAnimationPlayer::DrawFrame(int frame, wxDC& dc, const wxPoint& pos)
366{
367 wxASSERT_MSG( (m_animation != NULL), "Animation not present in wxAnimationPlayer");
368 wxASSERT_MSG( (m_frames.Number() != 0), "Animation cache not present in wxAnimationPlayer");
369 wxASSERT_MSG( (m_frames.Nth(frame) != (wxNode*) NULL), "Image not present in wxAnimationPlayer::DrawFrame");
370
371 wxBitmap* bitmap = (wxBitmap*) m_frames.Nth(frame)->Data() ;
372
373 wxRect rect = GetFrameRect(frame);
374
375 dc.DrawBitmap(* bitmap, pos.x + rect.x, pos.y + rect.y, (bitmap->GetMask() != NULL));
376}
377
378void wxAnimationTimer::Notify()
379{
380 m_player->PlayFrame();
381}
382
383/*
384 * wxAnimationBase
385 */
386
387IMPLEMENT_ABSTRACT_CLASS(wxAnimationBase, wxObject)
388
389/*
390 * wxGIFAnimation
391 */
392
393IMPLEMENT_CLASS(wxGIFAnimation, wxAnimationBase)
394
395wxGIFAnimation::wxGIFAnimation()
396{
397 m_decoder = (wxGIFDecoder*) NULL;
398}
399
400wxGIFAnimation::~wxGIFAnimation()
401{
402 delete m_decoder;
403}
404
405int wxGIFAnimation::GetFrameCount() const
406{
407 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
408
409 return m_decoder->GetNumberOfFrames();
410}
411
412wxImage* wxGIFAnimation::GetFrame(int i) const
413{
414 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
415
416 m_decoder->GoFrame(i);
417
418 wxImage* image = new wxImage;
419 m_decoder->ConvertToImage(image);
420 return image;
421}
422
423wxAnimationDisposal wxGIFAnimation::GetDisposalMethod(int i) const
424{
425 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
426
427 m_decoder->GoFrame(i);
428
429 int disposalMethod = m_decoder->GetDisposalMethod();
430 return (wxAnimationDisposal) disposalMethod;
431}
432
433wxRect wxGIFAnimation::GetFrameRect(int i) const
434{
435 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
436
437 m_decoder->GoFrame(i);
438
439 wxRect rect(m_decoder->GetLeft(), m_decoder->GetTop(), m_decoder->GetWidth(), m_decoder->GetHeight());
440 return rect;
441}
442
443int wxGIFAnimation::GetDelay(int i) const
444{
445 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
446
447 m_decoder->GoFrame(i);
448 return m_decoder->GetDelay();
449}
450
451wxSize wxGIFAnimation::GetLogicalScreenSize() const
452{
453 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
454
455 return wxSize(m_decoder->GetLogicalScreenWidth(), m_decoder->GetLogicalScreenHeight());
456}
457
458bool wxGIFAnimation::GetBackgroundColour(wxColour& col) const
459{
460 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
461
462 int i = m_decoder->GetBackgroundColour();
463 if (i == -1)
464 return FALSE;
465 else
466 {
467 unsigned char* pal = m_decoder->GetPalette();
468
469 if (pal)
470 {
471 col = wxColour(pal[3*i + 0], pal[3*i + 1], pal[3*i + 2]);
472 return TRUE;
473 }
474 else
475 return FALSE;
476 }
477}
478
479bool wxGIFAnimation::GetTransparentColour(wxColour& col) const
480{
481 wxASSERT_MSG( (m_decoder != (wxGIFDecoder*) NULL), "m_decoder must be non-NULL");
482
483 int i = m_decoder->GetTransparentColour();
484 if (i == -1)
485 return FALSE;
486 else
487 {
488 unsigned char* pal = m_decoder->GetPalette();
489
490 if (pal)
491 {
492 col = wxColour(pal[3*i + 0], pal[3*i + 1], pal[3*i + 2]);
493 return TRUE;
494 }
495 else
496 return FALSE;
497 }
498}
499
500bool wxGIFAnimation::IsValid() const
501{
502 return ((m_decoder != NULL) && (m_decoder->IsAnimation()));
503}
504
505bool wxGIFAnimation::LoadFile(const wxString& filename)
506{
507 if (m_decoder)
508 delete m_decoder;
509 m_decoder = NULL;
510
511 if (wxFileExists(filename))
512 {
513 wxFileInputStream stream(filename);
514 m_decoder = new wxGIFDecoder(& stream, TRUE);
515
516 if (m_decoder->ReadGIF() != wxGIF_OK)
517 {
518 delete m_decoder;
519 m_decoder = NULL;
520 return FALSE;
521 }
522
523 if (!m_decoder->IsAnimation())
524 {
525 delete m_decoder;
526 m_decoder = NULL;
527
528 return FALSE;
529 }
530 else
531 return TRUE;
532 }
533 else
534 return FALSE;
535}
536
537/*
538 * wxAnimationCtrlBase
539 * Abstract base class for format-specific animation controls.
540 * This class implements most of the functionality; all a derived
541 * class has to do is create the appropriate animation class on demand.
542 */
543
544IMPLEMENT_ABSTRACT_CLASS(wxAnimationCtrlBase, wxControl)
545
546BEGIN_EVENT_TABLE(wxAnimationCtrlBase, wxControl)
547 EVT_PAINT(wxAnimationCtrlBase::OnPaint)
548END_EVENT_TABLE()
549
550bool wxAnimationCtrlBase::Create(wxWindow *parent, wxWindowID id,
551 const wxString& filename, const wxPoint& pos,
552 const wxSize& size, long style, const wxString& name)
553{
554 m_animation = NULL;
555 m_filename = filename;
556
557 if (!wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name))
558 return FALSE;
559
560 SetBackgroundColour(parent->GetBackgroundColour());
561
562 m_animationPlayer.SetCustomBackgroundColour(GetBackgroundColour());
563
564 // Want to give the impression of transparency by painting
565 // the parent background
566// if (parent)
567// m_animationPlayer.UseParentBackground(TRUE);
568 m_animationPlayer.SetWindow(this);
569 m_animationPlayer.SetPosition(wxPoint(0, 0));
570 m_animationPlayer.SetDestroyAnimation(FALSE);
571
572 return TRUE;
573}
574
575wxAnimationCtrlBase::~wxAnimationCtrlBase()
576{
577 if (m_animationPlayer.IsPlaying())
578 m_animationPlayer.Stop();
579 m_animationPlayer.SetAnimation(NULL, FALSE);
580 delete m_animation;
581}
582
583bool wxAnimationCtrlBase::LoadFile(const wxString& filename)
584{
585 if (m_animationPlayer.IsPlaying())
586 m_animationPlayer.Stop();
587
588 wxString filename1(filename);
589
590 if (filename1.IsEmpty())
591 filename1 = m_filename;
592
593 if (filename1.IsEmpty())
594 return FALSE;
595
596 if (m_animation)
597 {
598 delete m_animation;
599 m_animation = NULL;
600 }
601
602 m_animation = DoCreateAnimation(filename1);
603 if (!m_animation)
604 return FALSE;
605
606 if (!m_animation->LoadFile(filename) || !m_animation->IsValid())
607 {
608 delete m_animation;
609 m_animation = NULL;
610 return FALSE;
611 }
612 m_animationPlayer.SetAnimation(m_animation, FALSE);
613
614 if (GetWindowStyle() & wxAN_FIT_ANIMATION)
615 FitToAnimation();
616
617 return TRUE;
618}
619
620bool wxAnimationCtrlBase::Play(bool looped)
621{
622 return m_animationPlayer.Play(*this, wxPoint(0, 0), looped);
623}
624
625wxSize wxAnimationCtrlBase::DoGetBestSize() const
626{
627 if (m_animationPlayer.HasAnimation() && (GetWindowStyle() & wxAN_FIT_ANIMATION))
628 {
629 return m_animationPlayer.GetLogicalScreenSize();
630 }
631 else
632 {
633 return GetSize();
634 }
635}
636
637void wxAnimationCtrlBase::FitToAnimation()
638{
639 if (!m_animationPlayer.HasAnimation())
640 return;
641
642 wxSize sz = m_animationPlayer.GetLogicalScreenSize();
643 SetClientSize(sz);
644}
645
646void wxAnimationCtrlBase::OnPaint(wxPaintEvent& event)
647{
648 wxPaintDC dc(this);
649
650 if (GetPlayer().IsPlaying())
651 {
652 GetPlayer().Draw(dc);
653 }
654}
655
656/*
657 * wxGIFAnimationCtrl
658 * Provides a GIF animation class when required.
659 */
660
661IMPLEMENT_ABSTRACT_CLASS(wxGIFAnimationCtrl, wxAnimationCtrlBase)
662
663wxAnimationBase* wxGIFAnimationCtrl::DoCreateAnimation(const wxString& WXUNUSED(filename))
664{
665 return new wxGIFAnimation;
666}