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