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