| 1 | /////////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: animateg.cpp |
| 3 | // Purpose: wxAnimation and wxAnimationCtrl |
| 4 | // Author: Julian Smart and Guillermo Rodriguez Garcia |
| 5 | // Modified by: Francesco Montorsi |
| 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 | #include "wx/wxprec.h" |
| 13 | |
| 14 | #ifdef __BORLANDC__ |
| 15 | #pragma hdrstop |
| 16 | #endif //__BORLANDC__ |
| 17 | |
| 18 | #if wxUSE_ANIMATIONCTRL && (!defined(__WXGTK20__) || defined(__WXUNIVERSAL__)) |
| 19 | |
| 20 | #include "wx/animate.h" |
| 21 | |
| 22 | #ifndef WX_PRECOMP |
| 23 | #include "wx/log.h" |
| 24 | #include "wx/image.h" |
| 25 | #include "wx/dcmemory.h" |
| 26 | #include "wx/dcclient.h" |
| 27 | #include "wx/module.h" |
| 28 | #endif |
| 29 | |
| 30 | #include "wx/wfstream.h" |
| 31 | #include "wx/gifdecod.h" |
| 32 | #include "wx/anidecod.h" |
| 33 | |
| 34 | #include "wx/listimpl.cpp" |
| 35 | WX_DEFINE_LIST(wxAnimationDecoderList) |
| 36 | |
| 37 | wxAnimationDecoderList wxAnimation::sm_handlers; |
| 38 | |
| 39 | |
| 40 | // ---------------------------------------------------------------------------- |
| 41 | // wxAnimation |
| 42 | // ---------------------------------------------------------------------------- |
| 43 | |
| 44 | IMPLEMENT_DYNAMIC_CLASS(wxAnimation, wxAnimationBase) |
| 45 | #define M_ANIMDATA wx_static_cast(wxAnimationDecoder*, m_refData) |
| 46 | |
| 47 | wxSize wxAnimation::GetSize() const |
| 48 | { |
| 49 | wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") ); |
| 50 | |
| 51 | return M_ANIMDATA->GetAnimationSize(); |
| 52 | } |
| 53 | |
| 54 | size_t wxAnimation::GetFrameCount() const |
| 55 | { |
| 56 | wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") ); |
| 57 | |
| 58 | return M_ANIMDATA->GetFrameCount(); |
| 59 | } |
| 60 | |
| 61 | wxImage wxAnimation::GetFrame(size_t i) const |
| 62 | { |
| 63 | wxCHECK_MSG( IsOk(), wxNullImage, wxT("invalid animation") ); |
| 64 | |
| 65 | wxImage ret; |
| 66 | if (!M_ANIMDATA->ConvertToImage(i, &ret)) |
| 67 | return wxNullImage; |
| 68 | return ret; |
| 69 | } |
| 70 | |
| 71 | int wxAnimation::GetDelay(size_t i) const |
| 72 | { |
| 73 | wxCHECK_MSG( IsOk(), 0, wxT("invalid animation") ); |
| 74 | |
| 75 | return M_ANIMDATA->GetDelay(i); |
| 76 | } |
| 77 | |
| 78 | wxPoint wxAnimation::GetFramePosition(size_t frame) const |
| 79 | { |
| 80 | wxCHECK_MSG( IsOk(), wxDefaultPosition, wxT("invalid animation") ); |
| 81 | |
| 82 | return M_ANIMDATA->GetFramePosition(frame); |
| 83 | } |
| 84 | |
| 85 | wxSize wxAnimation::GetFrameSize(size_t frame) const |
| 86 | { |
| 87 | wxCHECK_MSG( IsOk(), wxDefaultSize, wxT("invalid animation") ); |
| 88 | |
| 89 | return M_ANIMDATA->GetFrameSize(frame); |
| 90 | } |
| 91 | |
| 92 | wxAnimationDisposal wxAnimation::GetDisposalMethod(size_t frame) const |
| 93 | { |
| 94 | wxCHECK_MSG( IsOk(), wxANIM_UNSPECIFIED, wxT("invalid animation") ); |
| 95 | |
| 96 | return M_ANIMDATA->GetDisposalMethod(frame); |
| 97 | } |
| 98 | |
| 99 | wxColour wxAnimation::GetTransparentColour(size_t frame) const |
| 100 | { |
| 101 | wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") ); |
| 102 | |
| 103 | return M_ANIMDATA->GetTransparentColour(frame); |
| 104 | } |
| 105 | |
| 106 | wxColour wxAnimation::GetBackgroundColour() const |
| 107 | { |
| 108 | wxCHECK_MSG( IsOk(), wxNullColour, wxT("invalid animation") ); |
| 109 | |
| 110 | return M_ANIMDATA->GetBackgroundColour(); |
| 111 | } |
| 112 | |
| 113 | bool wxAnimation::LoadFile(const wxString& filename, wxAnimationType type) |
| 114 | { |
| 115 | wxFileInputStream stream(filename); |
| 116 | if ( !stream.IsOk() ) |
| 117 | return false; |
| 118 | |
| 119 | return Load(stream, type); |
| 120 | } |
| 121 | |
| 122 | bool wxAnimation::Load(wxInputStream &stream, wxAnimationType type) |
| 123 | { |
| 124 | UnRef(); |
| 125 | |
| 126 | const wxAnimationDecoder *handler; |
| 127 | if ( type == wxANIMATION_TYPE_ANY ) |
| 128 | { |
| 129 | for ( wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); |
| 130 | node; node = node->GetNext() ) |
| 131 | { |
| 132 | handler=(const wxAnimationDecoder*)node->GetData(); |
| 133 | |
| 134 | if ( handler->CanRead(stream) ) |
| 135 | { |
| 136 | // do a copy of the handler from the static list which we will own |
| 137 | // as our reference data |
| 138 | m_refData = handler->Clone(); |
| 139 | return M_ANIMDATA->Load(stream); |
| 140 | } |
| 141 | |
| 142 | } |
| 143 | |
| 144 | wxLogWarning( _("No handler found for animation type.") ); |
| 145 | return false; |
| 146 | } |
| 147 | |
| 148 | handler = FindHandler(type); |
| 149 | |
| 150 | // do a copy of the handler from the static list which we will own |
| 151 | // as our reference data |
| 152 | m_refData = handler->Clone(); |
| 153 | |
| 154 | if (handler == NULL) |
| 155 | { |
| 156 | wxLogWarning( _("No animation handler for type %ld defined."), type ); |
| 157 | |
| 158 | return false; |
| 159 | } |
| 160 | |
| 161 | if (stream.IsSeekable() && !M_ANIMDATA->CanRead(stream)) |
| 162 | { |
| 163 | wxLogError(_("Animation file is not of type %ld."), type); |
| 164 | return false; |
| 165 | } |
| 166 | else |
| 167 | return M_ANIMDATA->Load(stream); |
| 168 | } |
| 169 | |
| 170 | |
| 171 | // ---------------------------------------------------------------------------- |
| 172 | // animation decoders |
| 173 | // ---------------------------------------------------------------------------- |
| 174 | |
| 175 | void wxAnimation::AddHandler( wxAnimationDecoder *handler ) |
| 176 | { |
| 177 | // Check for an existing handler of the type being added. |
| 178 | if (FindHandler( handler->GetType() ) == 0) |
| 179 | { |
| 180 | sm_handlers.Append( handler ); |
| 181 | } |
| 182 | else |
| 183 | { |
| 184 | // This is not documented behaviour, merely the simplest 'fix' |
| 185 | // for preventing duplicate additions. If someone ever has |
| 186 | // a good reason to add and remove duplicate handlers (and they |
| 187 | // may) we should probably refcount the duplicates. |
| 188 | // also an issue in InsertHandler below. |
| 189 | |
| 190 | wxLogDebug( _T("Adding duplicate animation handler for '%d' type"), |
| 191 | handler->GetType() ); |
| 192 | delete handler; |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | void wxAnimation::InsertHandler( wxAnimationDecoder *handler ) |
| 197 | { |
| 198 | // Check for an existing handler of the type being added. |
| 199 | if (FindHandler( handler->GetType() ) == 0) |
| 200 | { |
| 201 | sm_handlers.Insert( handler ); |
| 202 | } |
| 203 | else |
| 204 | { |
| 205 | // see AddHandler for additional comments. |
| 206 | wxLogDebug( _T("Inserting duplicate animation handler for '%d' type"), |
| 207 | handler->GetType() ); |
| 208 | delete handler; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | const wxAnimationDecoder *wxAnimation::FindHandler( wxAnimationType animType ) |
| 213 | { |
| 214 | wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); |
| 215 | while (node) |
| 216 | { |
| 217 | const wxAnimationDecoder *handler = (const wxAnimationDecoder *)node->GetData(); |
| 218 | if (handler->GetType() == animType) return handler; |
| 219 | node = node->GetNext(); |
| 220 | } |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | void wxAnimation::InitStandardHandlers() |
| 225 | { |
| 226 | #if wxUSE_GIF |
| 227 | AddHandler(new wxGIFDecoder); |
| 228 | #endif // wxUSE_GIF |
| 229 | #if wxUSE_ICO_CUR |
| 230 | AddHandler(new wxANIDecoder); |
| 231 | #endif // wxUSE_ICO_CUR |
| 232 | } |
| 233 | |
| 234 | void wxAnimation::CleanUpHandlers() |
| 235 | { |
| 236 | wxAnimationDecoderList::compatibility_iterator node = sm_handlers.GetFirst(); |
| 237 | while (node) |
| 238 | { |
| 239 | wxAnimationDecoder *handler = (wxAnimationDecoder *)node->GetData(); |
| 240 | wxAnimationDecoderList::compatibility_iterator next = node->GetNext(); |
| 241 | delete handler; |
| 242 | node = next; |
| 243 | } |
| 244 | |
| 245 | sm_handlers.Clear(); |
| 246 | } |
| 247 | |
| 248 | |
| 249 | // A module to allow wxAnimation initialization/cleanup |
| 250 | // without calling these functions from app.cpp or from |
| 251 | // the user's application. |
| 252 | |
| 253 | class wxAnimationModule: public wxModule |
| 254 | { |
| 255 | DECLARE_DYNAMIC_CLASS(wxAnimationModule) |
| 256 | public: |
| 257 | wxAnimationModule() {} |
| 258 | bool OnInit() { wxAnimation::InitStandardHandlers(); return true; }; |
| 259 | void OnExit() { wxAnimation::CleanUpHandlers(); }; |
| 260 | }; |
| 261 | |
| 262 | IMPLEMENT_DYNAMIC_CLASS(wxAnimationModule, wxModule) |
| 263 | |
| 264 | |
| 265 | // ---------------------------------------------------------------------------- |
| 266 | // wxAnimationCtrl |
| 267 | // ---------------------------------------------------------------------------- |
| 268 | |
| 269 | IMPLEMENT_CLASS(wxAnimationCtrl, wxAnimationCtrlBase) |
| 270 | BEGIN_EVENT_TABLE(wxAnimationCtrl, wxAnimationCtrlBase) |
| 271 | EVT_PAINT(wxAnimationCtrl::OnPaint) |
| 272 | EVT_SIZE(wxAnimationCtrl::OnSize) |
| 273 | EVT_TIMER(wxID_ANY, wxAnimationCtrl::OnTimer) |
| 274 | END_EVENT_TABLE() |
| 275 | |
| 276 | void wxAnimationCtrl::Init() |
| 277 | { |
| 278 | m_currentFrame = 0; |
| 279 | m_looped = false; |
| 280 | m_isPlaying = false; |
| 281 | |
| 282 | // use the window background colour by default to be consistent |
| 283 | // with the GTK+ native version |
| 284 | m_useWinBackgroundColour = true; |
| 285 | } |
| 286 | |
| 287 | bool wxAnimationCtrl::Create(wxWindow *parent, wxWindowID id, |
| 288 | const wxAnimation& animation, const wxPoint& pos, |
| 289 | const wxSize& size, long style, const wxString& name) |
| 290 | { |
| 291 | m_animation = animation; |
| 292 | m_timer.SetOwner(this); |
| 293 | |
| 294 | if (!base_type::Create(parent, id, pos, size, style, wxDefaultValidator, name)) |
| 295 | return false; |
| 296 | |
| 297 | // by default we get the same background colour of our parent |
| 298 | SetBackgroundColour(parent->GetBackgroundColour()); |
| 299 | return true; |
| 300 | } |
| 301 | |
| 302 | wxAnimationCtrl::~wxAnimationCtrl() |
| 303 | { |
| 304 | Stop(); |
| 305 | } |
| 306 | |
| 307 | bool wxAnimationCtrl::LoadFile(const wxString& filename, wxAnimationType type) |
| 308 | { |
| 309 | wxAnimation anim; |
| 310 | if (!anim.LoadFile(filename, type) || |
| 311 | !anim.IsOk()) |
| 312 | return false; |
| 313 | |
| 314 | SetAnimation(anim); |
| 315 | return true; |
| 316 | } |
| 317 | |
| 318 | wxSize wxAnimationCtrl::DoGetBestSize() const |
| 319 | { |
| 320 | if (m_animation.IsOk() && !this->HasFlag(wxAC_NO_AUTORESIZE)) |
| 321 | return m_animation.GetSize(); |
| 322 | |
| 323 | return wxSize(100, 100); |
| 324 | } |
| 325 | |
| 326 | void wxAnimationCtrl::SetAnimation(const wxAnimation& animation) |
| 327 | { |
| 328 | if (IsPlaying()) |
| 329 | Stop(); |
| 330 | |
| 331 | m_animation = animation; |
| 332 | |
| 333 | if (m_animation.GetBackgroundColour() == wxNullColour) |
| 334 | SetUseWindowBackgroundColour(); |
| 335 | if (!this->HasFlag(wxAC_NO_AUTORESIZE)) |
| 336 | FitToAnimation(); |
| 337 | |
| 338 | // reset frame counter |
| 339 | m_currentFrame = 0; |
| 340 | |
| 341 | UpdateBackingStoreWithStaticImage(); |
| 342 | } |
| 343 | |
| 344 | void wxAnimationCtrl::SetInactiveBitmap(const wxBitmap &bmp) |
| 345 | { |
| 346 | wxAnimationCtrlBase::SetInactiveBitmap(bmp); |
| 347 | |
| 348 | // if not playing, update the backing store now |
| 349 | if (!IsPlaying()) |
| 350 | UpdateBackingStoreWithStaticImage(); |
| 351 | } |
| 352 | |
| 353 | void wxAnimationCtrl::FitToAnimation() |
| 354 | { |
| 355 | SetSize(m_animation.GetSize()); |
| 356 | } |
| 357 | |
| 358 | |
| 359 | // ---------------------------------------------------------------------------- |
| 360 | // wxAnimationCtrl - stop/play methods |
| 361 | // ---------------------------------------------------------------------------- |
| 362 | |
| 363 | void wxAnimationCtrl::Stop() |
| 364 | { |
| 365 | m_timer.Stop(); |
| 366 | m_isPlaying = false; |
| 367 | |
| 368 | UpdateBackingStoreWithStaticImage(); |
| 369 | } |
| 370 | |
| 371 | bool wxAnimationCtrl::Play(bool looped) |
| 372 | { |
| 373 | if (!m_animation.IsOk()) |
| 374 | return false; |
| 375 | |
| 376 | int oldframe = m_currentFrame; |
| 377 | m_looped = looped; |
| 378 | m_currentFrame = 0; |
| 379 | |
| 380 | // small optimization: if the back store was already updated to the |
| 381 | // first frame, don't rebuild it |
| 382 | if (oldframe != 0) |
| 383 | if (!RebuildBackingStoreUpToFrame(0)) |
| 384 | return false; |
| 385 | |
| 386 | m_isPlaying = true; |
| 387 | |
| 388 | // do a ClearBackground() to avoid that e.g. the custom static bitmap which |
| 389 | // was eventually shown previously remains partially drawn |
| 390 | ClearBackground(); |
| 391 | |
| 392 | // DrawCurrentFrame() will use our updated backing store |
| 393 | wxClientDC clientDC(this); |
| 394 | DrawCurrentFrame(clientDC); |
| 395 | |
| 396 | // start the timer |
| 397 | int delay = m_animation.GetDelay(0); |
| 398 | if (delay == 0) |
| 399 | delay = 1; // 0 is invalid timeout for wxTimer. |
| 400 | m_timer.Start(delay); |
| 401 | |
| 402 | return true; |
| 403 | } |
| 404 | |
| 405 | |
| 406 | |
| 407 | // ---------------------------------------------------------------------------- |
| 408 | // wxAnimationCtrl - rendering methods |
| 409 | // ---------------------------------------------------------------------------- |
| 410 | |
| 411 | bool wxAnimationCtrl::RebuildBackingStoreUpToFrame(size_t frame) |
| 412 | { |
| 413 | // if we've not created the backing store yet or it's too |
| 414 | // small, then recreate it |
| 415 | wxSize sz = m_animation.GetSize(), |
| 416 | winsz = GetClientSize(); |
| 417 | int w = wxMin(sz.GetWidth(), winsz.GetWidth()); |
| 418 | int h = wxMin(sz.GetHeight(), winsz.GetHeight()); |
| 419 | |
| 420 | if ( !m_backingStore.IsOk() || |
| 421 | m_backingStore.GetWidth() < w || m_backingStore.GetHeight() < h ) |
| 422 | { |
| 423 | if (!m_backingStore.Create(w, h)) |
| 424 | return false; |
| 425 | } |
| 426 | |
| 427 | wxMemoryDC dc; |
| 428 | dc.SelectObject(m_backingStore); |
| 429 | |
| 430 | // Draw the background |
| 431 | DisposeToBackground(dc); |
| 432 | |
| 433 | // Draw all intermediate frames that haven't been removed from the animation |
| 434 | for (size_t i = 0; i < frame; i++) |
| 435 | { |
| 436 | if (m_animation.GetDisposalMethod(i) == wxANIM_DONOTREMOVE || |
| 437 | m_animation.GetDisposalMethod(i) == wxANIM_UNSPECIFIED) |
| 438 | { |
| 439 | DrawFrame(dc, i); |
| 440 | } |
| 441 | else if (m_animation.GetDisposalMethod(i) == wxANIM_TOBACKGROUND) |
| 442 | DisposeToBackground(dc, m_animation.GetFramePosition(i), |
| 443 | m_animation.GetFrameSize(i)); |
| 444 | } |
| 445 | |
| 446 | // finally draw this frame |
| 447 | DrawFrame(dc, frame); |
| 448 | dc.SelectObject(wxNullBitmap); |
| 449 | |
| 450 | return true; |
| 451 | } |
| 452 | |
| 453 | void wxAnimationCtrl::IncrementalUpdateBackingStore() |
| 454 | { |
| 455 | wxMemoryDC dc; |
| 456 | dc.SelectObject(m_backingStore); |
| 457 | |
| 458 | // OPTIMIZATION: |
| 459 | // since wxAnimationCtrl can only play animations forward, without skipping |
| 460 | // frames, we can be sure that m_backingStore contains the m_currentFrame-1 |
| 461 | // frame and thus we just need to dispose the m_currentFrame-1 frame and |
| 462 | // render the m_currentFrame-th one. |
| 463 | |
| 464 | if (m_currentFrame == 0) |
| 465 | { |
| 466 | // before drawing the first frame always dispose to bg colour |
| 467 | DisposeToBackground(dc); |
| 468 | } |
| 469 | else |
| 470 | { |
| 471 | switch (m_animation.GetDisposalMethod(m_currentFrame-1)) |
| 472 | { |
| 473 | case wxANIM_TOBACKGROUND: |
| 474 | DisposeToBackground(dc, m_animation.GetFramePosition(m_currentFrame-1), |
| 475 | m_animation.GetFrameSize(m_currentFrame-1)); |
| 476 | break; |
| 477 | |
| 478 | case wxANIM_TOPREVIOUS: |
| 479 | // this disposal should never be used too often. |
| 480 | // E.g. GIF specification explicitely say to keep the usage of this |
| 481 | // disposal limited to the minimum. |
| 482 | // In fact it may require a lot of time to restore |
| 483 | if (m_currentFrame == 1) |
| 484 | { |
| 485 | // if 0-th frame disposal is to restore to previous frame, |
| 486 | // the best we can do is to restore to background |
| 487 | DisposeToBackground(dc); |
| 488 | } |
| 489 | else |
| 490 | if (!RebuildBackingStoreUpToFrame(m_currentFrame-2)) |
| 491 | Stop(); |
| 492 | break; |
| 493 | |
| 494 | case wxANIM_DONOTREMOVE: |
| 495 | case wxANIM_UNSPECIFIED: |
| 496 | break; |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | // now just draw the current frame on the top of the backing store |
| 501 | DrawFrame(dc, m_currentFrame); |
| 502 | dc.SelectObject(wxNullBitmap); |
| 503 | } |
| 504 | |
| 505 | void wxAnimationCtrl::UpdateBackingStoreWithStaticImage() |
| 506 | { |
| 507 | wxASSERT(!IsPlaying()); |
| 508 | |
| 509 | if (m_bmpStatic.IsOk()) |
| 510 | { |
| 511 | // copy the inactive bitmap in the backing store |
| 512 | m_backingStore = m_bmpStatic; |
| 513 | } |
| 514 | else |
| 515 | { |
| 516 | // put in the backing store the first frame of the animation |
| 517 | if (!m_animation.IsOk() || |
| 518 | !RebuildBackingStoreUpToFrame(0)) |
| 519 | { |
| 520 | m_animation = wxNullAnimation; |
| 521 | DisposeToBackground(); |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | Refresh(); |
| 526 | } |
| 527 | |
| 528 | void wxAnimationCtrl::DrawFrame(wxDC &dc, size_t frame) |
| 529 | { |
| 530 | // PERFORMANCE NOTE: |
| 531 | // this draw stuff is not as fast as possible: the wxAnimationDecoder |
| 532 | // needs first to convert from its internal format to wxImage RGB24; |
| 533 | // the wxImage is then converted as a wxBitmap and finally blitted. |
| 534 | // If wxAnimationDecoder had a function to convert directly from its |
| 535 | // internal format to a port-specific wxBitmap, it would be somewhat faster. |
| 536 | wxBitmap bmp(m_animation.GetFrame(frame)); |
| 537 | dc.DrawBitmap(bmp, m_animation.GetFramePosition(frame), |
| 538 | true /* use mask */); |
| 539 | } |
| 540 | |
| 541 | void wxAnimationCtrl::DrawCurrentFrame(wxDC& dc) |
| 542 | { |
| 543 | wxASSERT( m_backingStore.IsOk() ); |
| 544 | |
| 545 | // m_backingStore always contains the current frame |
| 546 | dc.DrawBitmap(m_backingStore, 0, 0, true /* use mask in case it's present */); |
| 547 | } |
| 548 | |
| 549 | void wxAnimationCtrl::DisposeToBackground() |
| 550 | { |
| 551 | // clear the backing store |
| 552 | wxMemoryDC dc; |
| 553 | dc.SelectObject(m_backingStore); |
| 554 | DisposeToBackground(dc); |
| 555 | } |
| 556 | |
| 557 | void wxAnimationCtrl::DisposeToBackground(wxDC& dc) |
| 558 | { |
| 559 | wxColour col = IsUsingWindowBackgroundColour() |
| 560 | ? GetBackgroundColour() |
| 561 | : m_animation.GetBackgroundColour(); |
| 562 | wxBrush brush(col); |
| 563 | dc.SetBackground(brush); |
| 564 | dc.Clear(); |
| 565 | } |
| 566 | |
| 567 | void wxAnimationCtrl::DisposeToBackground(wxDC& dc, const wxPoint &pos, const wxSize &sz) |
| 568 | { |
| 569 | wxColour col = IsUsingWindowBackgroundColour() |
| 570 | ? GetBackgroundColour() |
| 571 | : m_animation.GetBackgroundColour(); |
| 572 | wxBrush brush(col); |
| 573 | dc.SetBrush(brush); // SetBrush and not SetBackground !! |
| 574 | dc.SetPen(*wxTRANSPARENT_PEN); |
| 575 | dc.DrawRectangle(pos, sz); |
| 576 | } |
| 577 | |
| 578 | // ---------------------------------------------------------------------------- |
| 579 | // wxAnimationCtrl - event handlers |
| 580 | // ---------------------------------------------------------------------------- |
| 581 | |
| 582 | void wxAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) |
| 583 | { |
| 584 | // VERY IMPORTANT: the wxPaintDC *must* be created in any case |
| 585 | wxPaintDC dc(this); |
| 586 | |
| 587 | if ( m_backingStore.IsOk() ) |
| 588 | DrawCurrentFrame(dc); |
| 589 | else |
| 590 | { |
| 591 | // m_animation is not valid and thus we don't have a valid backing store... |
| 592 | // clear then our area to the background colour |
| 593 | DisposeToBackground(dc); |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | void wxAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event)) |
| 598 | { |
| 599 | m_currentFrame++; |
| 600 | if (m_currentFrame == m_animation.GetFrameCount()) |
| 601 | { |
| 602 | // Should a non-looped animation display the last frame? |
| 603 | if (!m_looped) |
| 604 | { |
| 605 | Stop(); |
| 606 | return; |
| 607 | } |
| 608 | else |
| 609 | m_currentFrame = 0; // let's restart |
| 610 | } |
| 611 | |
| 612 | IncrementalUpdateBackingStore(); |
| 613 | |
| 614 | wxClientDC dc(this); |
| 615 | DrawCurrentFrame(dc); |
| 616 | |
| 617 | #ifdef __WXMAC__ |
| 618 | // without this, the animation currently doesn't redraw under Mac |
| 619 | Refresh(); |
| 620 | #endif // __WXMAC__ |
| 621 | |
| 622 | // Set the timer for the next frame |
| 623 | int delay = m_animation.GetDelay(m_currentFrame); |
| 624 | if (delay == 0) |
| 625 | delay = 1; // 0 is invalid timeout for wxTimer. |
| 626 | m_timer.Start(delay); |
| 627 | } |
| 628 | |
| 629 | void wxAnimationCtrl::OnSize(wxSizeEvent &WXUNUSED(event)) |
| 630 | { |
| 631 | // NB: resizing an animation control may take a lot of time |
| 632 | // for big animations as the backing store must be |
| 633 | // extended and rebuilt. Try to avoid it!! |
| 634 | if (m_animation.IsOk()) |
| 635 | if (!RebuildBackingStoreUpToFrame(m_currentFrame)) |
| 636 | Stop(); // in case we are playing |
| 637 | } |
| 638 | |
| 639 | #endif // wxUSE_ANIMATIONCTRL |
| 640 | |