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