]>
Commit | Line | Data |
---|---|---|
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$ | |
8 | // Copyright: (c) Aleksandras Gluchovas | |
9 | // Licence: wxWindows license | |
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 | ||
36 | IMPLEMENT_DYNAMIC_CLASS( cbHintAnimationPlugin, cbPluginBase ) | |
37 | ||
38 | BEGIN_EVENT_TABLE( cbHintAnimationPlugin, cbPluginBase ) | |
39 | ||
40 | EVT_PL_DRAW_HINT_RECT( cbHintAnimationPlugin::OnDrawHintRect ) | |
41 | ||
42 | END_EVENT_TABLE() | |
43 | ||
44 | cbHintAnimationPlugin::cbHintAnimationPlugin(void) | |
45 | ||
46 | : mpScrDc( NULL ), | |
47 | mpAnimTimer( 0 ), | |
48 | mAnimStarted( FALSE ), | |
49 | ||
50 | mMorphDelay ( 5 ), | |
51 | mMaxFrames ( 20 ), | |
52 | mInClientHintBorder( 4 ), | |
53 | mAccelerationOn( TRUE ) | |
54 | {} | |
55 | ||
56 | cbHintAnimationPlugin::cbHintAnimationPlugin( wxFrameLayout* pPanel, int paneMask ) | |
57 | ||
58 | : cbPluginBase( pPanel, paneMask ), | |
59 | mpScrDc( NULL ), | |
60 | mpAnimTimer( 0 ), | |
61 | mAnimStarted( FALSE ), | |
62 | ||
63 | mMorphDelay ( 5 ), | |
64 | mMaxFrames ( 20 ), | |
65 | mInClientHintBorder( 4 ), | |
66 | mAccelerationOn( TRUE ) | |
67 | {} | |
68 | ||
69 | cbHintAnimationPlugin::~cbHintAnimationPlugin() | |
70 | { | |
71 | if ( mpScrDc ) delete mpScrDc; | |
72 | } | |
73 | ||
74 | /*** rect-tracking related methods ***/ | |
75 | ||
76 | void cbHintAnimationPlugin::OnDrawHintRect( cbDrawHintRectEvent& event ) | |
77 | { | |
78 | if ( !mAnimStarted && !mpScrDc ) | |
79 | { | |
80 | StartTracking(); | |
81 | ||
82 | mPrevInClient = event.mIsInClient; | |
83 | ||
84 | mPrevRect = event.mRect; | |
85 | ||
86 | mStopPending = FALSE; | |
87 | } | |
88 | ||
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 | |
93 | ||
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 | } | |
99 | ||
100 | // check the amount of change in the shape of hint, | |
101 | // and start morph-effect if change is "sufficient" | |
102 | ||
103 | int change = abs( mCurRect.width - mPrevRect.width ) + | |
104 | abs( mCurRect.height - mPrevRect.height ); | |
105 | ||
106 | if ( change > 10 && !event.mLastTime && !event.mEraseRect ) | |
107 | { | |
108 | if ( !mpAnimTimer ) | |
109 | ||
110 | mpAnimTimer = new cbHintAnimTimer(); | |
111 | ||
112 | // init the animation "thread", or reinit if already started | |
113 | ||
114 | mpAnimTimer->Init( this, mAnimStarted ); | |
115 | ||
116 | mAnimStarted = TRUE; | |
117 | } | |
118 | else | |
119 | if ( !mAnimStarted ) | |
120 | { | |
121 | DoDrawHintRect( event.mRect, event.mIsInClient ); | |
122 | ||
123 | if ( event.mLastTime ) | |
124 | ||
125 | FinishTracking(); | |
126 | ||
127 | mPrevInClient = event.mIsInClient; | |
128 | } | |
129 | else | |
130 | { | |
131 | mCurInClient = event.mIsInClient; | |
132 | ||
133 | if ( event.mLastTime && mpAnimTimer ) | |
134 | { | |
135 | mStopPending = TRUE; | |
136 | ||
137 | if ( mpAnimTimer->mPrevMorphed.x != POS_UNDEFINED ) | |
138 | ||
139 | // erase previous rect | |
140 | DoDrawHintRect( mpAnimTimer->mPrevMorphed, mPrevInClient ); | |
141 | } | |
142 | } | |
143 | ||
144 | mPrevRect = event.mRect; | |
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 | ||
152 | static const unsigned char _gCheckerImg[16] = { _IMG_A,_IMG_B,_IMG_C,_IMG_D, | |
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 | }; | |
157 | ||
158 | void cbHintAnimationPlugin::StartTracking() | |
159 | { | |
160 | mpScrDc = new wxScreenDC; | |
161 | ||
162 | wxScreenDC::StartDrawingOnTop(&mpLayout->GetParentFrame()); | |
163 | } | |
164 | ||
165 | void cbHintAnimationPlugin::DoDrawHintRect( wxRect& rect, bool isInClientRect) | |
166 | { | |
167 | wxRect scrRect; | |
168 | ||
169 | RectToScr( rect, scrRect ); | |
170 | ||
171 | int prevLF = mpScrDc->GetLogicalFunction(); | |
172 | ||
173 | mpScrDc->SetLogicalFunction( wxXOR ); | |
174 | ||
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 | |
180 | ||
181 | wxBitmap checker( (const char*)_gCheckerImg, 8,8 ); | |
182 | ||
183 | wxBrush checkerBrush( checker ); | |
184 | ||
185 | mpScrDc->SetPen( mpLayout->mNullPen ); | |
186 | mpScrDc->SetBrush( checkerBrush ); | |
187 | ||
188 | int half = mInClientHintBorder / 2; | |
189 | ||
190 | mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y - half, | |
191 | scrRect.width + 2*half, mInClientHintBorder ); | |
192 | ||
193 | mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y + scrRect.height - half, | |
194 | scrRect.width + 2*half, mInClientHintBorder ); | |
195 | ||
196 | mpScrDc->DrawRectangle( scrRect.x - half, scrRect.y + half - 1, | |
197 | mInClientHintBorder, scrRect.height - 2*half + 2); | |
198 | ||
199 | mpScrDc->DrawRectangle( scrRect.x + scrRect.width - half, | |
200 | scrRect.y + half - 1, | |
201 | mInClientHintBorder, scrRect.height - 2*half + 2); | |
202 | ||
203 | mpScrDc->SetBrush( wxNullBrush ); | |
204 | } | |
205 | else | |
206 | { | |
207 | // otherwise draw 1-pixel thin borders | |
208 | ||
209 | mpScrDc->SetPen( mpLayout->mBlackPen ); | |
210 | ||
211 | mpScrDc->DrawLine( scrRect.x, scrRect.y, | |
212 | scrRect.x + scrRect.width, scrRect.y ); | |
213 | ||
214 | mpScrDc->DrawLine( scrRect.x, scrRect.y + 1, | |
215 | scrRect.x, scrRect.y + scrRect.height ); | |
216 | ||
217 | mpScrDc->DrawLine( scrRect.x+1, scrRect.y + scrRect.height, | |
218 | scrRect.x + scrRect.width, scrRect.y + scrRect.height ); | |
219 | ||
220 | mpScrDc->DrawLine( scrRect.x + scrRect.width , scrRect.y, | |
221 | scrRect.x + scrRect.width, scrRect.y + scrRect.height + 1); | |
222 | } | |
223 | ||
224 | mpScrDc->SetLogicalFunction( prevLF ); | |
225 | } | |
226 | ||
227 | void cbHintAnimationPlugin::DrawHintRect ( wxRect& rect, bool isInClientRect) | |
228 | { | |
229 | DoDrawHintRect( rect, isInClientRect ); | |
230 | } | |
231 | ||
232 | void cbHintAnimationPlugin::EraseHintRect( wxRect& rect, bool isInClientRect) | |
233 | { | |
234 | DoDrawHintRect( rect, isInClientRect ); | |
235 | } | |
236 | ||
237 | void cbHintAnimationPlugin::FinishTracking() | |
238 | { | |
239 | wxScreenDC::EndDrawingOnTop(); | |
240 | ||
241 | delete mpScrDc; | |
242 | ||
243 | mpScrDc = NULL; | |
244 | } | |
245 | ||
246 | void cbHintAnimationPlugin::RectToScr( wxRect& frameRect, wxRect& scrRect ) | |
247 | { | |
248 | scrRect = frameRect; | |
249 | ||
250 | int x = frameRect.x, y = frameRect.y; | |
251 | ||
252 | mpLayout->GetParentFrame().ClientToScreen( &x, &y ); | |
253 | ||
254 | scrRect.x = x; | |
255 | scrRect.y = y; | |
256 | } | |
257 | ||
258 | /***** Implementation for class cbHintAnimTimer *****/ | |
259 | ||
260 | cbHintAnimTimer::cbHintAnimTimer(void) | |
261 | { | |
262 | #ifdef __WINDOWS__ | |
263 | mLock = 0L; | |
264 | #endif | |
265 | ||
266 | mPrevMorphed.x = POS_UNDEFINED; | |
267 | } | |
268 | ||
269 | void cbHintAnimTimer::MorphPoint( wxPoint& origin, MorphInfoT& info, wxPoint& point ) | |
270 | { | |
271 | // simulate lienar movement (FOR NOW:: without acceleration) | |
272 | ||
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 ); | |
281 | ||
282 | point.x = int ( double ( info.mFrom.x + double (info.mTill.x - info.mFrom.x) * k ) ); | |
283 | ||
284 | point.y = int ( double ( info.mFrom.y + double (info.mTill.y - info.mFrom.y) * k ) ); | |
285 | ||
286 | point.x += origin.x; | |
287 | point.y += origin.y; | |
288 | } | |
289 | ||
290 | void cbHintAnimTimer::Notify(void) | |
291 | { | |
292 | // FIXME:: "clean" implementation should use mutex to sync | |
293 | // between GUI and animation threads | |
294 | ||
295 | if ( mpPl->mStopPending ) | |
296 | { | |
297 | Stop(); // top timer | |
298 | ||
299 | mpPl->FinishTracking(); | |
300 | ||
301 | mpPl->mStopPending = FALSE; | |
302 | mpPl->mpAnimTimer = NULL; | |
303 | mpPl->mAnimStarted = FALSE; | |
304 | ||
305 | mPrevMorphed.x = POS_UNDEFINED; | |
306 | ||
307 | delete this; | |
308 | ||
309 | return; | |
310 | } | |
311 | ||
312 | wxPoint origin( mpPl->mCurRect.x, mpPl->mCurRect.y ); | |
313 | ||
314 | wxPoint curUpper, curLower; | |
315 | ||
316 | MorphPoint( origin, mUpperLeft, curUpper ); | |
317 | MorphPoint( origin, mLowerRight, curLower ); | |
318 | ||
319 | if ( mPrevMorphed.x != POS_UNDEFINED ) | |
320 | ||
321 | // erase previous rect | |
322 | mpPl->DoDrawHintRect( mPrevMorphed, mpPl->mPrevInClient ); | |
323 | ||
324 | wxRect morphed( curUpper.x, curUpper.y, | |
325 | curLower.x - curUpper.x, | |
326 | curLower.y - curUpper.y ); | |
327 | ||
328 | // draw rect of current iteration | |
329 | mpPl->DoDrawHintRect( morphed, | |
330 | ( mCurIter != mpPl->mMaxFrames - 1 ) | |
331 | ? mpPl->mPrevInClient : mpPl->mCurInClient ); | |
332 | ||
333 | mPrevMorphed = morphed; | |
334 | ||
335 | if ( mCurIter == mpPl->mMaxFrames - 1 ) | |
336 | { | |
337 | Stop(); // top timer | |
338 | ||
339 | mpPl->FinishTracking(); | |
340 | mpPl->mpAnimTimer = NULL; | |
341 | mpPl->mAnimStarted = FALSE; | |
342 | ||
343 | mPrevMorphed.x = POS_UNDEFINED; | |
344 | ||
345 | delete this; | |
346 | } | |
347 | else | |
348 | ++mCurIter; | |
349 | } | |
350 | ||
351 | bool cbHintAnimTimer::Init( cbHintAnimationPlugin* pAnimPl, bool reinit ) | |
352 | { | |
353 | ||
354 | mpPl = pAnimPl; | |
355 | ||
356 | // morph-points are set up relatively to the upper-left corner | |
357 | // of the current hint-rectangle | |
358 | ||
359 | if ( !reinit ) | |
360 | { | |
361 | mUpperLeft.mFrom.x = mpPl->mPrevRect.x - mpPl->mCurRect.x; | |
362 | mUpperLeft.mFrom.y = mpPl->mPrevRect.y - mpPl->mCurRect.y; | |
363 | ||
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 ); | |
370 | ||
371 | wxPoint curUpper, curLower; | |
372 | ||
373 | MorphPoint( origin, mUpperLeft, curUpper ); | |
374 | MorphPoint( origin, mLowerRight, curLower ); | |
375 | ||
376 | mUpperLeft.mFrom.x = curUpper.x - mpPl->mCurRect.x; | |
377 | mUpperLeft.mFrom.y = curUpper.y - mpPl->mCurRect.y; | |
378 | ||
379 | mLowerRight.mFrom.x = ( mUpperLeft.mFrom.x + curLower.x - curUpper.x ); | |
380 | mLowerRight.mFrom.y = ( mUpperLeft.mFrom.y + curLower.y - curUpper.y ); | |
381 | } | |
382 | ||
383 | mUpperLeft.mTill.x = 0; | |
384 | mUpperLeft.mTill.y = 0; | |
385 | ||
386 | mLowerRight.mTill.x = mpPl->mCurRect.width; | |
387 | mLowerRight.mTill.y = mpPl->mCurRect.height; | |
388 | ||
389 | mCurIter = 1; | |
390 | ||
391 | if ( !reinit ) | |
392 | ||
393 | Start( mpPl->mMorphDelay ); | |
394 | ||
395 | return TRUE; | |
396 | } | |
397 |