imported catch-22 sol clone with authors permission
[reactos.git] / rosapps / games / solitaire / cardlib / cardrgnmouse.cpp
1 //
2 // CardLib - CardRegion mouse-related stuff
3 //
4 // Freeware
5 // Copyright J Brown 2001
6 //
7 #include <windows.h>
8 #include <math.h>
9
10 #include "cardlib.h"
11 #include "cardwindow.h"
12 #include "cardregion.h"
13
14 double __CARDZOOMSPEED = 32;
15
16 int ClipCard(HDC hdc, int x, int y, int width, int height);
17 void DrawCard(HDC hdc, int x, int y, HDC hdcSource, int width, int height);
18
19 #ifdef _DEBUG
20
21 static pDebugClickProc DebugStackClickProc = 0;
22
23 void CardLib_SetStackClickProc(pDebugClickProc proc)
24 {
25 DebugStackClickProc = proc;
26 }
27
28 #endif
29
30 CardRegion *CardWindow::GetBestStack(int x, int y, int w, int h)
31 {
32 int maxoverlap = 0;
33 int maxoverlapidx = -1;
34
35 //find the stack which is most covered by the dropped
36 //cards. Only include those which allow drops.
37 //
38 for(int i = 0; i < nNumCardRegions; i++)
39 {
40 int percent = Regions[i]->GetOverlapRatio(x, y, w, h);
41
42 //if this stack has the biggest coverage yet
43 if(percent > maxoverlap && Regions[i]->IsVisible())
44 {
45 maxoverlap = percent;
46 maxoverlapidx = i;
47 }
48 }
49
50 //if we found a stack to drop onto
51 if(maxoverlapidx != -1)
52 {
53 return Regions[maxoverlapidx];
54 }
55 else
56 {
57 return 0;
58 }
59 }
60
61 bool CardRegion::IsPointInStack(int x, int y)
62 {
63 int axpos = xoffset < 0 ? xpos + (nNumApparentCards-1)*xoffset : xpos;
64 int aypos = yoffset < 0 ? ypos + (nNumApparentCards-1)*yoffset : ypos;
65
66 if(x >= axpos && x < axpos + width && y >= aypos && y < aypos + height && fVisible)
67 return true;
68 else
69 return false;
70 }
71
72 int CardRegion::GetNumDragCards(int x, int y)
73 {
74 int cardindex = 0; //index from stack start
75 int maxidx;
76
77 //make x,y relative to the stack's upper left corner
78 x -= xpos + (xoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * xoffset : 0);
79 y -= ypos + (yoffset < 0 ? (nNumApparentCards/*cardstack.NumCards()*/ - 1) * yoffset : 0);
80
81 //if stack is empty, cannot drag any cards from it
82 if(cardstack.NumCards() <= 0)
83 return 0;
84
85 //see which card in the stack has been clicked on
86 //top-bottom ordering
87 if(yoffset > 0)
88 {
89 if(y < height - __cardheight)
90 cardindex = y / yoffset;
91 else
92 cardindex = cardstack.NumCards() - 1;
93 }
94 else if(yoffset < 0)
95 {
96 if(y < __cardheight)
97 cardindex = cardstack.NumCards() - 1;
98 else
99 cardindex = cardstack.NumCards() - ((y - __cardheight) / -yoffset) - 2;
100 }
101 else //yoffset == 0
102 {
103 cardindex = cardstack.NumCards() - 1;
104 }
105
106 maxidx = cardindex;
107
108 //if left-right
109 if(xoffset > 0)
110 {
111 if(x < width - __cardwidth)
112 cardindex = x / xoffset;
113 else
114 cardindex = cardstack.NumCards() - 1;
115 }
116 else if(xoffset < 0)
117 {
118 if(x < __cardwidth)
119 cardindex = cardstack.NumCards() - 1;
120 else
121 cardindex = cardstack.NumCards() - ((x - __cardwidth) / -xoffset) - 2;
122 }
123 else
124 {
125 cardindex = cardstack.NumCards() - 1;
126 }
127
128 if(cardindex > maxidx) cardindex = maxidx;
129
130 if(cardindex > cardstack.NumCards())
131 cardindex = 1;
132
133 //if are trying to drag too many cards at once
134 return cardstack.NumCards() - cardindex;
135 }
136
137 bool CardRegion::CanDragCards(int iNumCards)
138 {
139 if(iNumCards <= 0) return false;
140 if(nThreedCount > 1 && iNumCards > 1) return false;
141
142 if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0)
143 {
144 // TRACE("Failed to gain access to card stack\n");
145 return false;
146 }
147
148 ReleaseMutex(mxlock);
149
150 switch(uDragRule)
151 {
152 case CS_DRAG_ALL:
153 return true;
154
155 case CS_DRAG_TOP:
156
157 if(iNumCards == 1)
158 return true;
159 else
160 return false;
161
162 case CS_DRAG_NONE:
163 return false;
164
165 case CS_DRAG_CALLBACK:
166
167 if(CanDragCallback)
168 {
169 return CanDragCallback(*this, iNumCards);
170 }
171 else
172 {
173 return false;
174 }
175
176 default:
177 return false;
178 }
179 }
180
181 bool CardRegion::CanDropCards(CardStack &cards)
182 {
183 if(WaitForSingleObject(mxlock, 0) != WAIT_OBJECT_0)
184 {
185 return false;
186 }
187
188 ReleaseMutex(mxlock);
189
190 switch(uDropRule)
191 {
192 case CS_DROP_ALL:
193 return true;
194
195 case CS_DROP_NONE:
196 return false;
197
198 case CS_DROP_CALLBACK:
199
200 if(CanDropCallback)
201 {
202 return CanDropCallback(*this, cards);
203 }
204 else
205 {
206 return false;
207 }
208
209 default:
210 return false;
211 }
212 }
213
214 bool CardRegion::OnLButtonDblClk(int x, int y)
215 {
216 iNumDragCards = GetNumDragCards(x, y);
217
218 if(DblClickCallback)
219 DblClickCallback(*this, iNumDragCards);
220
221 return true;
222 }
223
224 bool CardRegion::OnLButtonDown(int x, int y)
225 {
226 iNumDragCards = GetNumDragCards(x, y);
227
228 #ifdef _DEBUG
229 if(DebugStackClickProc)
230 {
231 if(!DebugStackClickProc(*this))
232 return false;
233 }
234 #endif
235
236 if(ClickCallback)
237 ClickCallback(*this, iNumDragCards);
238
239 if(CanDragCards(iNumDragCards) != false)
240 {
241
242 //offset of the mouse cursor relative to the top-left corner
243 //of the cards that are being dragged
244 mousexoffset = x - xpos - xoffset * (nNumApparentCards - iNumDragCards);
245 mouseyoffset = y - ypos - yoffset * (nNumApparentCards - iNumDragCards);
246
247 if(xoffset < 0)
248 mousexoffset += -xoffset * (iNumDragCards - 1);
249
250 if(yoffset < 0)
251 mouseyoffset += -yoffset * (iNumDragCards - 1);
252
253 //remove the cards from the source stack
254 dragstack = cardstack.Pop(iNumDragCards);
255
256 //prepare the back buffer, and the drag image
257 PrepareDragBitmaps(iNumDragCards);
258
259 oldx = x - mousexoffset;
260 oldy = y - mouseyoffset;
261
262 Update(); //Update this stack's card count + size
263
264 SetCapture((HWND)parentWnd);
265
266 //set AFTER settings the dragstack...
267 fMouseDragging = true;
268
269 return true;
270 }
271
272 return false;
273 }
274
275 bool CardRegion::OnLButtonUp(int x, int y)
276 {
277 CardRegion *pDestStack = 0;
278 HDC hdc;
279 int dropstackid = CS_DROPZONE_NODROP;
280
281 RECT dragrect;
282 DropZone *dropzone;
283
284 fMouseDragging = false;
285
286 //first of all, see if any drop zones have been registered
287 SetRect(&dragrect, x-mousexoffset, y-mouseyoffset, x-mousexoffset+nDragCardWidth, y-mouseyoffset+nDragCardHeight);
288
289 dropzone = parentWnd.GetDropZoneFromRect(&dragrect);
290
291 if(dropzone)
292 {
293 dropstackid = dropzone->DropCards(dragstack);
294
295 if(dropstackid != CS_DROPZONE_NODROP)
296 pDestStack = parentWnd.CardRegionFromId(dropstackid);
297 else
298 pDestStack = 0;
299 }
300 else
301 {
302 pDestStack = parentWnd.GetBestStack(x - mousexoffset, y - mouseyoffset, nDragCardWidth, nDragCardHeight);
303 }
304
305 // If have found a stack to drop onto
306 //
307 if(pDestStack && pDestStack->CanDropCards(dragstack))
308 {
309 hdc = GetDC((HWND)parentWnd);
310 // UseNicePalette(hdc);
311 ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, pDestStack);
312 ReleaseDC((HWND)parentWnd, hdc);
313
314 //
315 //add the cards to the destination stack
316 //
317 CardStack temp = pDestStack->GetCardStack();
318 temp.Push(dragstack);
319
320 pDestStack->SetCardStack(temp);
321 // pDestStack->Update(); //Update this stack's card count + size
322 // pDestStack->UpdateFaceDir(temp);
323
324 // Call the remove callback on THIS stack, if one is specified
325 //
326 if(RemoveCallback)
327 RemoveCallback(*this, iNumDragCards);
328
329 // Call the add callback, if one is specified
330 //
331 if(pDestStack->AddCallback)
332 pDestStack->AddCallback(*pDestStack, pDestStack->cardstack);//index, deststack->numcards);
333
334 RedrawIfNotDim(pDestStack, true);
335 }
336
337 //
338 // Otherwise, let the cards snap back onto this stack
339 //
340 else
341 {
342
343 hdc = GetDC((HWND)parentWnd);
344 ZoomCard(hdc, x - mousexoffset, y - mouseyoffset, this);
345 cardstack += dragstack;
346 ReleaseDC((HWND)parentWnd, hdc);
347
348 Update(); //Update this stack's card count + size
349 }
350
351 ReleaseDragBitmaps();
352 ReleaseCapture();
353
354 return true;
355 }
356
357 bool CardRegion::OnMouseMove(int x, int y)
358 {
359 HDC hdc;
360
361 hdc = GetDC((HWND)parentWnd);
362
363 x -= mousexoffset;
364 y -= mouseyoffset;
365
366 MoveDragCardTo(hdc, x, y);
367
368 //BitBlt(hdc, nDragCardWidth+10, 0, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY);
369 //BitBlt(hdc, 0, 0, nDragCardWidth, nDragCardHeight, hdcDragCard, 0, 0, SRCCOPY);
370
371 ReleaseDC((HWND)parentWnd, hdc);
372
373 oldx = x;
374 oldy = y;
375
376 return true;
377 }
378
379 //
380 // There is a bug in BitBlt when the source x,y
381 // become < 0. So this wrapper function simply adjusts
382 // the coords so that we never try to blt in from this range
383 //
384 BOOL ClippedBitBlt(HDC hdcDest, int x, int y, int width, int height, HDC hdcSrc, int srcx, int srcy, DWORD dwROP)
385 {
386 if(srcx < 0)
387 {
388 x = 0 - srcx;
389 width = width + srcx;
390 srcx = 0;
391 }
392
393 if(srcy < 0)
394 {
395 y = 0 - srcy;
396 height = height + srcy;
397 srcy = 0;
398 }
399
400 return BitBlt(hdcDest, x, y, width, height, hdcSrc, srcx, srcy, dwROP);
401 }
402
403 void CardRegion::MoveDragCardTo(HDC hdc, int x, int y)
404 {
405 RECT inter, rect1, rect2;
406
407 //mask off the new position of the drag-card, so
408 //that it will not be painted over
409 ClipCard(hdc, x, y, nDragCardWidth, nDragCardHeight);
410
411 //restore the area covered by the card at its previous position
412 BitBlt(hdc, oldx, oldy, nDragCardWidth, nDragCardHeight, hdcBackGnd, 0, 0, SRCCOPY);
413
414 //remove clipping so we can draw the card at its new place
415 SelectClipRgn(hdc, NULL);
416
417 //if the card's old and new positions overlap, then we
418 //need some funky code to update the "saved background" image,
419 SetRect(&rect1, oldx, oldy, oldx+nDragCardWidth, oldy+nDragCardHeight);
420 SetRect(&rect2, x, y, x+nDragCardWidth, y+nDragCardHeight);
421
422 if(IntersectRect(&inter, &rect1, &rect2))
423 {
424 int interwidth = inter.right-inter.left;
425 int interheight = inter.bottom-inter.top;
426 int destx, desty, srcx, srcy;
427
428 if(rect2.left > rect1.left)
429 {
430 destx = 0; srcx = nDragCardWidth - interwidth;
431 }
432 else
433 {
434 destx = nDragCardWidth - interwidth; srcx = 0;
435 }
436
437 if(rect2.top > rect1.top)
438 {
439 desty = 0; srcy = nDragCardHeight - interheight;
440 }
441 else
442 {
443 desty = nDragCardHeight - interheight; srcy = 0;
444 }
445
446 //shift the bit we didn't use for the restore (due to the clipping)
447 //into the opposite corner
448 BitBlt(hdcBackGnd, destx,desty, interwidth, interheight, hdcBackGnd, srcx, srcy, SRCCOPY);
449
450 ExcludeClipRect(hdcBackGnd, destx, desty, destx+interwidth, desty+interheight);
451
452 //this bit requires us to clip the BitBlt (from screen to background)
453 //as BitBlt is a bit buggy it seems
454 ClippedBitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY);
455 SelectClipRgn(hdcBackGnd, NULL);
456 }
457 else
458 {
459 BitBlt(hdcBackGnd, 0,0, nDragCardWidth, nDragCardHeight, hdc, x, y, SRCCOPY);
460 }
461
462 //finally draw the card to the screen
463 DrawCard(hdc, x, y, hdcDragCard, nDragCardWidth, nDragCardHeight);
464 }
465
466
467 //extern "C" int _fltused(void) { return 0; }
468 //extern "C" int _ftol(void) { return 0; }
469
470 //
471 // Better do this in fixed-point, to stop
472 // VC from linking in floatingpoint-long conversions
473 //
474 //#define FIXED_PREC_MOVE
475 #ifdef FIXED_PREC_MOVE
476 #define PRECISION 12
477 void ZoomCard(HDC hdc, int xpos, int ypos, CARDSTACK *dest)
478 {
479 long dx, dy, x , y;
480
481
482 int apparentcards;
483 x = xpos << PRECISION; y = ypos << PRECISION;
484
485 oldx = (int)xpos;
486 oldy = (int)ypos;
487
488 apparentcards=dest->numcards/dest->threedcount;
489
490 int idestx = dest->xpos + dest->xoffset * (apparentcards);// - iNumDragCards);
491 int idesty = dest->ypos + dest->yoffset * (apparentcards);// - iNumDragCards);
492
493 //normalise the motion vector
494 dx = (idestx<<PRECISION) - x;
495 dy = (idesty<<PRECISION) - y;
496 long recip = (1 << PRECISION) / 1;//sqrt(dx*dx + dy*dy);
497
498 dx *= recip * 16;//CARDZOOMSPEED;
499 dy *= recip * 16;//CARDZOOMSPEED;
500
501 //if(dx < 0) dxinc = 1.001; else
502
503 for(;;)
504 {
505 int ix, iy;
506 x += dx;
507 y += dy;
508
509 ix = (int)x>>PRECISION;
510 iy = (int)y>>PRECISION;
511 if(dx < 0 && ix < idestx) ix = idestx;
512 else if(dx > 0 && ix > idestx) ix = idestx;
513
514 if(dy < 0 && iy < idesty) iy = idesty;
515 else if(dy > 0 && iy > idesty) iy = idesty;
516
517 MoveDragCardTo(hdc, ix, iy);
518
519 if(ix == idestx && iy == idesty)
520 break;
521
522 oldx = (int)x >> PRECISION;
523 oldy = (int)y >> PRECISION;
524
525 //dx *= 1.2;
526 //dy *= 1.2;
527
528 Sleep(10);
529 }
530 }
531 #else
532 void CardRegion::ZoomCard(HDC hdc, int xpos, int ypos, CardRegion *pDestStack)
533 {
534 double dx, dy, x ,y;
535 int apparentcards;
536 x = (double)xpos; y = (double)ypos;
537
538 oldx = (int)x;
539 oldy = (int)y;
540
541 apparentcards = pDestStack->cardstack.NumCards() / pDestStack->nThreedCount;
542
543 int idestx = pDestStack->xpos + pDestStack->xoffset * (apparentcards);
544 int idesty = pDestStack->ypos + pDestStack->yoffset * (apparentcards);
545
546 if(pDestStack->yoffset < 0)
547 idesty += pDestStack->yoffset * (iNumDragCards-1);
548
549 if(pDestStack->xoffset < 0)
550 idestx += pDestStack->xoffset * (iNumDragCards-1);
551
552 //normalise the motion vector
553 dx = idestx - x;
554 dy = idesty - y;
555 double recip = 1.0 / sqrt(dx*dx + dy*dy);
556 dx *= recip * __CARDZOOMSPEED; dy *= recip * __CARDZOOMSPEED;
557
558 //if(dx < 0) dxinc = 1.001; else
559
560 for(;;)
561 {
562 bool attarget = true;
563 int ix, iy;
564 x += dx;
565 y += dy;
566
567 ix = (int)x;
568 iy = (int)y;
569 if(dx < 0.0 && ix < idestx) ix = idestx;
570 else if(dx > 0.0 && ix > idestx) ix = idestx;
571 else attarget = false;
572
573 if(dy < 0.0 && iy < idesty) iy = idesty;
574 else if(dy > 0.0 && iy > idesty) iy = idesty;
575 else attarget = false;
576
577 //if the target stack wants the drag cards drawn differently
578 //to how they are, then redraw the drag card image just before
579 //the cards land
580 /*if(attarget == true)
581 {
582 for(int i = 0; i < iNumDragCards; i++)
583 {
584 int xdraw = pDestStack->xoffset*i;
585 int ydraw = pDestStack->yoffset*i;
586
587 if(pDestStack->yoffset < 0)
588 ydraw = -pDestStack->yoffset * (iNumDragCards-i-1);
589 if(pDestStack->xoffset < 0)
590 xdraw = -pDestStack->xoffset * (iNumDragCards-i-1);
591
592 if(pDestStack->facedirection == CS_FACEUP &&
593 pDestStack->numcards+i >= dest->numfacedown)
594 {
595 //cdtDraw(hdcDragCard, xdraw, ydraw, iDragCards[i], ectFACES, 0);
596 }
597 else
598 {
599 //cdtDraw(hdcDragCard, xdraw, ydraw, CARDSTACK::backcard, ectBACKS, 0);
600 }
601 }
602 }*/
603
604 MoveDragCardTo(hdc, ix, iy);
605
606 if(attarget || ix == idestx && iy == idesty)
607 break;
608
609 oldx = (int)x;
610 oldy = (int)y;
611
612 //dx *= 1.2;
613 //dy *= 1.2;
614
615 Sleep(10);
616 }
617 }
618 #endif