]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/fl/hintanimpl.cpp
fixes for animated GIFs playing (patch 1097003)
[wxWidgets.git] / contrib / src / fl / hintanimpl.cpp
CommitLineData
8e08b761 1/////////////////////////////////////////////////////////////////////////////
4cbc57f0
JS
2// Name: hintanimpl.cpp
3// Purpose: cbHintAnimationPlugin implementation.
8e08b761
JS
4// Author: Aleksandras Gluchovas
5// Modified by:
6// Created: 9/11/98
7// RCS-ID: $Id$
8// Copyright: (c) Aleksandras Gluchovas
c82c42d4 9// Licence: wxWindows license
8e08b761
JS
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
13 #pragma implementation "hintanimpl.h"
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20#pragma hdrstop
21#endif
22
23#ifndef WX_PRECOMP
24#include "wx/wx.h"
25#endif
26
27#include "wx/fl/hintanimpl.h"
28
29#define POS_UNDEFINED -32768
30
31/***** Implementation for class cbHintAnimationPlugin *****/
32
33// FIXME:: some of the below code should be eliminated by
34// reusing parts of cbBarDragPlugin's implementation
35
36IMPLEMENT_DYNAMIC_CLASS( cbHintAnimationPlugin, cbPluginBase )
37
38BEGIN_EVENT_TABLE( cbHintAnimationPlugin, cbPluginBase )
39
4cbc57f0 40 EVT_PL_DRAW_HINT_RECT( cbHintAnimationPlugin::OnDrawHintRect )
8e08b761
JS
41
42END_EVENT_TABLE()
43
44cbHintAnimationPlugin::cbHintAnimationPlugin(void)
45
4cbc57f0
JS
46 : mpScrDc( NULL ),
47 mpAnimTimer( 0 ),
c82c42d4 48 mAnimStarted( false ),
4cbc57f0
JS
49
50 mMorphDelay ( 5 ),
51 mMaxFrames ( 20 ),
8e08b761 52 mInClientHintBorder( 4 ),
c82c42d4 53 mAccelerationOn( true )
8e08b761
JS
54{}
55
56cbHintAnimationPlugin::cbHintAnimationPlugin( wxFrameLayout* pPanel, int paneMask )
57
4cbc57f0
JS
58 : cbPluginBase( pPanel, paneMask ),
59 mpScrDc( NULL ),
60 mpAnimTimer( 0 ),
c82c42d4 61 mAnimStarted( false ),
8e08b761 62
4cbc57f0
JS
63 mMorphDelay ( 5 ),
64 mMaxFrames ( 20 ),
65 mInClientHintBorder( 4 ),
c82c42d4 66 mAccelerationOn( true )
8e08b761
JS
67{}
68
69cbHintAnimationPlugin::~cbHintAnimationPlugin()
70{
4cbc57f0 71 if ( mpScrDc ) delete mpScrDc;
8e08b761
JS
72}
73
74/*** rect-tracking related methods ***/
75
76void cbHintAnimationPlugin::OnDrawHintRect( cbDrawHintRectEvent& event )
77{
4cbc57f0
JS
78 if ( !mAnimStarted && !mpScrDc )
79 {
80 StartTracking();
8e08b761 81
4cbc57f0 82 mPrevInClient = event.mIsInClient;
8e08b761 83
4cbc57f0 84 mPrevRect = event.mRect;
8e08b761 85
c82c42d4 86 mStopPending = false;
4cbc57f0 87 }
8e08b761 88
4cbc57f0
JS
89 if ( !event.mEraseRect )
90 {
91 // pass on current hint-rect info to the animation "thread", in
92 // order to make adjustments to the morph-target on-the-fly
8e08b761 93
4cbc57f0
JS
94 mCurRect.x = event.mRect.x;
95 mCurRect.y = event.mRect.y;
96 mCurRect.width = event.mRect.width;
97 mCurRect.height = event.mRect.height;
98 }
8e08b761 99
4cbc57f0
JS
100 // check the amount of change in the shape of hint,
101 // and start morph-effect if change is "sufficient"
8e08b761 102
4cbc57f0
JS
103 int change = abs( mCurRect.width - mPrevRect.width ) +
104 abs( mCurRect.height - mPrevRect.height );
8e08b761 105
4cbc57f0
JS
106 if ( change > 10 && !event.mLastTime && !event.mEraseRect )
107 {
108 if ( !mpAnimTimer )
8e08b761 109
4cbc57f0 110 mpAnimTimer = new cbHintAnimTimer();
8e08b761 111
4cbc57f0 112 // init the animation "thread", or reinit if already started
8e08b761 113
4cbc57f0 114 mpAnimTimer->Init( this, mAnimStarted );
8e08b761 115
c82c42d4 116 mAnimStarted = true;
4cbc57f0
JS
117 }
118 else
119 if ( !mAnimStarted )
120 {
121 DoDrawHintRect( event.mRect, event.mIsInClient );
8e08b761 122
4cbc57f0 123 if ( event.mLastTime )
8e08b761 124
4cbc57f0 125 FinishTracking();
8e08b761 126
4cbc57f0
JS
127 mPrevInClient = event.mIsInClient;
128 }
129 else
130 {
131 mCurInClient = event.mIsInClient;
8e08b761 132
4cbc57f0
JS
133 if ( event.mLastTime && mpAnimTimer )
134 {
c82c42d4 135 mStopPending = true;
8e08b761 136
4cbc57f0 137 if ( mpAnimTimer->mPrevMorphed.x != POS_UNDEFINED )
8e08b761 138
4cbc57f0
JS
139 // erase previous rect
140 DoDrawHintRect( mpAnimTimer->mPrevMorphed, mPrevInClient );
141 }
142 }
8e08b761 143
4cbc57f0 144 mPrevRect = event.mRect;
8e08b761
JS
145}
146
147#define _IMG_A 0xAA // Note: modified from _A to _IMG_A, _A was already defined (cygwin)
148#define _IMG_B 0x00 // Note: modified from _B to _IMG_A, _B was already defined (cygwin)
149#define _IMG_C 0x55 // Note: modified from _C to _IMG_C, for consistency reasons.
150#define _IMG_D 0x00 // Note: modified from _D to _IMG_D, for consistency reasons.
151
152static const unsigned char _gCheckerImg[16] = { _IMG_A,_IMG_B,_IMG_C,_IMG_D,
4cbc57f0
JS
153 _IMG_A,_IMG_B,_IMG_C,_IMG_D,
154 _IMG_A,_IMG_B,_IMG_C,_IMG_D,
155 _IMG_A,_IMG_B,_IMG_C,_IMG_D
156 };
8e08b761
JS
157
158void cbHintAnimationPlugin::StartTracking()
159{
4cbc57f0 160 mpScrDc = new wxScreenDC;
8e08b761 161
4cbc57f0 162 wxScreenDC::StartDrawingOnTop(&mpLayout->GetParentFrame());
8e08b761
JS
163}
164
165void cbHintAnimationPlugin::DoDrawHintRect( wxRect& rect, bool isInClientRect)
166{
4cbc57f0 167 wxRect scrRect;
8e08b761 168
4cbc57f0 169 RectToScr( rect, scrRect );
8e08b761 170
4cbc57f0 171 int prevLF = mpScrDc->GetLogicalFunction();
8e08b761 172
4cbc57f0 173 mpScrDc->SetLogicalFunction( wxXOR );
8e08b761 174
4cbc57f0
JS
175 if ( isInClientRect )
176 {
177 // BUG BUG BUG (wx):: somehow stippled brush works only
178 // when the bitmap created on stack, not
179 // as a member of the class
8e08b761 180
4cbc57f0 181 wxBitmap checker( (const char*)_gCheckerImg, 8,8 );
8e08b761 182
4cbc57f0 183 wxBrush checkerBrush( checker );
8e08b761 184
4cbc57f0
JS
185 mpScrDc->SetPen( mpLayout->mNullPen );
186 mpScrDc->SetBrush( checkerBrush );
8e08b761 187
4cbc57f0 188 int half = mInClientHintBorder / 2;
8e08b761 189
4cbc57f0
JS
190 mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y - half,
191 scrRect.width + 2*half, mInClientHintBorder );
8e08b761 192
4cbc57f0
JS
193 mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y + scrRect.height - half,
194 scrRect.width + 2*half, mInClientHintBorder );
8e08b761 195
4cbc57f0
JS
196 mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y + half - 1,
197 mInClientHintBorder, scrRect.height - 2*half + 2);
8e08b761 198
4cbc57f0
JS
199 mpScrDc->DrawRectangle( scrRect.x + scrRect.width - half,
200 scrRect.y + half - 1,
201 mInClientHintBorder, scrRect.height - 2*half + 2);
8e08b761 202
4cbc57f0
JS
203 mpScrDc->SetBrush( wxNullBrush );
204 }
205 else
206 {
207 // otherwise draw 1-pixel thin borders
8e08b761 208
4cbc57f0 209 mpScrDc->SetPen( mpLayout->mBlackPen );
8e08b761 210
4cbc57f0
JS
211 mpScrDc->DrawLine( scrRect.x, scrRect.y,
212 scrRect.x + scrRect.width, scrRect.y );
8e08b761 213
4cbc57f0
JS
214 mpScrDc->DrawLine( scrRect.x, scrRect.y + 1,
215 scrRect.x, scrRect.y + scrRect.height );
8e08b761 216
4cbc57f0
JS
217 mpScrDc->DrawLine( scrRect.x+1, scrRect.y + scrRect.height,
218 scrRect.x + scrRect.width, scrRect.y + scrRect.height );
8e08b761 219
4cbc57f0
JS
220 mpScrDc->DrawLine( scrRect.x + scrRect.width , scrRect.y,
221 scrRect.x + scrRect.width, scrRect.y + scrRect.height + 1);
222 }
8e08b761 223
4cbc57f0 224 mpScrDc->SetLogicalFunction( prevLF );
8e08b761
JS
225}
226
227void cbHintAnimationPlugin::DrawHintRect ( wxRect& rect, bool isInClientRect)
228{
4cbc57f0 229 DoDrawHintRect( rect, isInClientRect );
8e08b761
JS
230}
231
232void cbHintAnimationPlugin::EraseHintRect( wxRect& rect, bool isInClientRect)
233{
4cbc57f0 234 DoDrawHintRect( rect, isInClientRect );
8e08b761
JS
235}
236
237void cbHintAnimationPlugin::FinishTracking()
238{
4cbc57f0 239 wxScreenDC::EndDrawingOnTop();
8e08b761 240
4cbc57f0 241 delete mpScrDc;
8e08b761 242
4cbc57f0 243 mpScrDc = NULL;
8e08b761
JS
244}
245
246void cbHintAnimationPlugin::RectToScr( wxRect& frameRect, wxRect& scrRect )
247{
4cbc57f0 248 scrRect = frameRect;
8e08b761 249
4cbc57f0 250 int x = frameRect.x, y = frameRect.y;
8e08b761 251
4cbc57f0 252 mpLayout->GetParentFrame().ClientToScreen( &x, &y );
8e08b761 253
4cbc57f0
JS
254 scrRect.x = x;
255 scrRect.y = y;
8e08b761
JS
256}
257
258/***** Implementation for class cbHintAnimTimer *****/
259
260cbHintAnimTimer::cbHintAnimTimer(void)
261{
262#ifdef __WINDOWS__
4cbc57f0 263 mLock = 0L;
8e08b761
JS
264#endif
265
4cbc57f0 266 mPrevMorphed.x = POS_UNDEFINED;
8e08b761
JS
267}
268
269void cbHintAnimTimer::MorphPoint( wxPoint& origin, MorphInfoT& info, wxPoint& point )
270{
4cbc57f0 271 // simulate lienar movement (FOR NOW:: without acceleration)
8e08b761 272
4cbc57f0
JS
273 double k;
274
275 if ( mpPl->mAccelerationOn )
276
277 k = double( mCurIter*mCurIter ) /
278 double( (mpPl->mMaxFrames - 1)*(mpPl->mMaxFrames - 1) );
279 else
280 k = double( mCurIter ) / double( mpPl->mMaxFrames - 1 );
8e08b761 281
4cbc57f0 282 point.x = int ( double ( info.mFrom.x + double (info.mTill.x - info.mFrom.x) * k ) );
8e08b761 283
4cbc57f0 284 point.y = int ( double ( info.mFrom.y + double (info.mTill.y - info.mFrom.y) * k ) );
8e08b761 285
4cbc57f0
JS
286 point.x += origin.x;
287 point.y += origin.y;
8e08b761
JS
288}
289
290void cbHintAnimTimer::Notify(void)
291{
4cbc57f0
JS
292 // FIXME:: "clean" implementation should use mutex to sync
293 // between GUI and animation threads
8e08b761 294
4cbc57f0
JS
295 if ( mpPl->mStopPending )
296 {
297 Stop(); // top timer
8e08b761 298
4cbc57f0 299 mpPl->FinishTracking();
8e08b761 300
c82c42d4 301 mpPl->mStopPending = false;
4cbc57f0 302 mpPl->mpAnimTimer = NULL;
c82c42d4 303 mpPl->mAnimStarted = false;
8e08b761 304
4cbc57f0 305 mPrevMorphed.x = POS_UNDEFINED;
8e08b761 306
4cbc57f0 307 delete this;
8e08b761 308
4cbc57f0
JS
309 return;
310 }
8e08b761 311
4cbc57f0 312 wxPoint origin( mpPl->mCurRect.x, mpPl->mCurRect.y );
8e08b761 313
4cbc57f0 314 wxPoint curUpper, curLower;
8e08b761 315
4cbc57f0
JS
316 MorphPoint( origin, mUpperLeft, curUpper );
317 MorphPoint( origin, mLowerRight, curLower );
8e08b761 318
4cbc57f0 319 if ( mPrevMorphed.x != POS_UNDEFINED )
8e08b761 320
4cbc57f0
JS
321 // erase previous rect
322 mpPl->DoDrawHintRect( mPrevMorphed, mpPl->mPrevInClient );
8e08b761 323
4cbc57f0
JS
324 wxRect morphed( curUpper.x, curUpper.y,
325 curLower.x - curUpper.x,
326 curLower.y - curUpper.y );
8e08b761 327
4cbc57f0
JS
328 // draw rect of current iteration
329 mpPl->DoDrawHintRect( morphed,
330 ( mCurIter != mpPl->mMaxFrames - 1 )
331 ? mpPl->mPrevInClient : mpPl->mCurInClient );
8e08b761 332
4cbc57f0 333 mPrevMorphed = morphed;
8e08b761 334
4cbc57f0
JS
335 if ( mCurIter == mpPl->mMaxFrames - 1 )
336 {
337 Stop(); // top timer
338
339 mpPl->FinishTracking();
340 mpPl->mpAnimTimer = NULL;
c82c42d4 341 mpPl->mAnimStarted = false;
8e08b761 342
4cbc57f0 343 mPrevMorphed.x = POS_UNDEFINED;
8e08b761 344
4cbc57f0
JS
345 delete this;
346 }
347 else
348 ++mCurIter;
8e08b761
JS
349}
350
351bool cbHintAnimTimer::Init( cbHintAnimationPlugin* pAnimPl, bool reinit )
352{
353
4cbc57f0 354 mpPl = pAnimPl;
8e08b761 355
4cbc57f0
JS
356 // morph-points are set up relatively to the upper-left corner
357 // of the current hint-rectangle
8e08b761 358
4cbc57f0
JS
359 if ( !reinit )
360 {
361 mUpperLeft.mFrom.x = mpPl->mPrevRect.x - mpPl->mCurRect.x;
362 mUpperLeft.mFrom.y = mpPl->mPrevRect.y - mpPl->mCurRect.y;
8e08b761 363
4cbc57f0
JS
364 mLowerRight.mFrom.x = ( mUpperLeft.mFrom.x + mpPl->mPrevRect.width );
365 mLowerRight.mFrom.y = ( mUpperLeft.mFrom.y + mpPl->mPrevRect.height );
366 }
367 else
368 {
369 wxPoint origin( mpPl->mPrevRect.x, mpPl->mPrevRect.y );
8e08b761 370
4cbc57f0 371 wxPoint curUpper, curLower;
8e08b761 372
4cbc57f0
JS
373 MorphPoint( origin, mUpperLeft, curUpper );
374 MorphPoint( origin, mLowerRight, curLower );
8e08b761 375
4cbc57f0
JS
376 mUpperLeft.mFrom.x = curUpper.x - mpPl->mCurRect.x;
377 mUpperLeft.mFrom.y = curUpper.y - mpPl->mCurRect.y;
8e08b761 378
4cbc57f0
JS
379 mLowerRight.mFrom.x = ( mUpperLeft.mFrom.x + curLower.x - curUpper.x );
380 mLowerRight.mFrom.y = ( mUpperLeft.mFrom.y + curLower.y - curUpper.y );
381 }
8e08b761 382
4cbc57f0
JS
383 mUpperLeft.mTill.x = 0;
384 mUpperLeft.mTill.y = 0;
8e08b761 385
4cbc57f0
JS
386 mLowerRight.mTill.x = mpPl->mCurRect.width;
387 mLowerRight.mTill.y = mpPl->mCurRect.height;
8e08b761 388
4cbc57f0
JS
389 mCurIter = 1;
390
391 if ( !reinit )
8e08b761 392
4cbc57f0 393 Start( mpPl->mMorphDelay );
8e08b761 394
c82c42d4 395 return true;
8e08b761
JS
396}
397