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