[Win32k]
[reactos.git] / base / applications / mspaint / mouse.c
1 /*
2 * PROJECT: PAINT for ReactOS
3 * LICENSE: LGPL
4 * FILE: base/applications/paint/mouse.c
5 * PURPOSE: Things which should not be in the mouse event handler itself
6 * PROGRAMMERS: Benedikt Freisen
7 */
8
9 /* INCLUDES *********************************************************/
10
11 #include "precomp.h"
12
13 /* FUNCTIONS ********************************************************/
14
15 void
16 placeSelWin()
17 {
18 MoveWindow(hSelection, rectSel_dest.left * zoom / 1000, rectSel_dest.top * zoom / 1000,
19 RECT_WIDTH(rectSel_dest) * zoom / 1000 + 6, RECT_HEIGHT(rectSel_dest) * zoom / 1000 + 6, TRUE);
20 BringWindowToTop(hSelection);
21 InvalidateRect(hImageArea, NULL, FALSE);
22 }
23
24 void
25 regularize(LONG x0, LONG y0, LONG *x1, LONG *y1)
26 {
27 if (abs(*x1 - x0) >= abs(*y1 - y0))
28 *y1 = y0 + (*y1 > y0 ? abs(*x1 - x0) : -abs(*x1 - x0));
29 else
30 *x1 = x0 + (*x1 > x0 ? abs(*y1 - y0) : -abs(*y1 - y0));
31 }
32
33 void
34 roundTo8Directions(LONG x0, LONG y0, LONG *x1, LONG *y1)
35 {
36 if (abs(*x1 - x0) >= abs(*y1 - y0))
37 {
38 if (abs(*y1 - y0) * 5 < abs(*x1 - x0) * 2)
39 *y1 = y0;
40 else
41 *y1 = y0 + (*y1 > y0 ? abs(*x1 - x0) : -abs(*x1 - x0));
42 }
43 else
44 {
45 if (abs(*x1 - x0) * 5 < abs(*y1 - y0) * 2)
46 *x1 = x0;
47 else
48 *x1 = x0 + (*x1 > x0 ? abs(*y1 - y0) : -abs(*y1 - y0));
49 }
50 }
51
52 POINT pointStack[256];
53 short pointSP;
54 POINT *ptStack = NULL;
55 int ptSP = 0;
56
57 void
58 startPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
59 {
60 start.x = x;
61 start.y = y;
62 last.x = x;
63 last.y = y;
64 switch (activeTool)
65 {
66 case TOOL_FREESEL:
67 ShowWindow(hSelection, SW_HIDE);
68 if (ptStack != NULL)
69 HeapFree(GetProcessHeap(), 0, ptStack);
70 ptStack = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * 1024);
71 ptSP = 0;
72 ptStack[0].x = x;
73 ptStack[0].y = y;
74 break;
75 case TOOL_LINE:
76 case TOOL_RECT:
77 case TOOL_ELLIPSE:
78 case TOOL_RRECT:
79 newReversible();
80 break;
81 case TOOL_RECTSEL:
82 case TOOL_TEXT:
83 newReversible();
84 ShowWindow(hSelection, SW_HIDE);
85 rectSel_src.right = rectSel_src.left;
86 rectSel_src.bottom = rectSel_src.top;
87 break;
88 case TOOL_RUBBER:
89 newReversible();
90 Erase(hdc, x, y, x, y, bg, rubberRadius);
91 break;
92 case TOOL_FILL:
93 newReversible();
94 Fill(hdc, x, y, fg);
95 break;
96 case TOOL_PEN:
97 newReversible();
98 SetPixel(hdc, x, y, fg);
99 break;
100 case TOOL_BRUSH:
101 newReversible();
102 Brush(hdc, x, y, x, y, fg, brushStyle);
103 break;
104 case TOOL_AIRBRUSH:
105 newReversible();
106 Airbrush(hdc, x, y, fg, airBrushWidth);
107 break;
108 case TOOL_BEZIER:
109 pointStack[pointSP].x = x;
110 pointStack[pointSP].y = y;
111 if (pointSP == 0)
112 {
113 newReversible();
114 pointSP++;
115 }
116 break;
117 case TOOL_SHAPE:
118 pointStack[pointSP].x = x;
119 pointStack[pointSP].y = y;
120 if (pointSP + 1 >= 2)
121 Poly(hdc, pointStack, pointSP + 1, fg, bg, lineWidth, shapeStyle, FALSE);
122 if (pointSP == 0)
123 {
124 newReversible();
125 pointSP++;
126 }
127 break;
128 }
129 }
130
131 void
132 whilePaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
133 {
134 switch (activeTool)
135 {
136 case TOOL_FREESEL:
137 if (ptSP == 0)
138 newReversible();
139 ptSP++;
140 if (ptSP % 1024 == 0)
141 ptStack = HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, ptStack, sizeof(POINT) * (ptSP + 1024));
142 ptStack[ptSP].x = max(0, min(x, imgXRes));
143 ptStack[ptSP].y = max(0, min(y, imgYRes));
144 resetToU1();
145 Poly(hdc, ptStack, ptSP + 1, 0, 0, 2, 0, FALSE);
146 break;
147 case TOOL_RECTSEL:
148 case TOOL_TEXT:
149 {
150 POINT temp;
151 resetToU1();
152 temp.x = max(0, min(x, imgXRes));
153 temp.y = max(0, min(y, imgYRes));
154 rectSel_dest.left = rectSel_src.left = min(start.x, temp.x);
155 rectSel_dest.top = rectSel_src.top = min(start.y, temp.y);
156 rectSel_dest.right = rectSel_src.right = max(start.x, temp.x);
157 rectSel_dest.bottom = rectSel_src.bottom = max(start.y, temp.y);
158 RectSel(hdc, start.x, start.y, temp.x, temp.y);
159 break;
160 }
161 case TOOL_RUBBER:
162 Erase(hdc, last.x, last.y, x, y, bg, rubberRadius);
163 break;
164 case TOOL_PEN:
165 Line(hdc, last.x, last.y, x, y, fg, 1);
166 break;
167 case TOOL_BRUSH:
168 Brush(hdc, last.x, last.y, x, y, fg, brushStyle);
169 break;
170 case TOOL_AIRBRUSH:
171 Airbrush(hdc, x, y, fg, airBrushWidth);
172 break;
173 case TOOL_LINE:
174 resetToU1();
175 if (GetAsyncKeyState(VK_SHIFT) < 0)
176 roundTo8Directions(start.x, start.y, &x, &y);
177 Line(hdc, start.x, start.y, x, y, fg, lineWidth);
178 break;
179 case TOOL_BEZIER:
180 resetToU1();
181 pointStack[pointSP].x = x;
182 pointStack[pointSP].y = y;
183 switch (pointSP)
184 {
185 case 1:
186 Line(hdc, pointStack[0].x, pointStack[0].y, pointStack[1].x, pointStack[1].y, fg,
187 lineWidth);
188 break;
189 case 2:
190 Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], pointStack[1], fg, lineWidth);
191 break;
192 case 3:
193 Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], pointStack[1], fg, lineWidth);
194 break;
195 }
196 break;
197 case TOOL_RECT:
198 resetToU1();
199 if (GetAsyncKeyState(VK_SHIFT) < 0)
200 regularize(start.x, start.y, &x, &y);
201 Rect(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
202 break;
203 case TOOL_SHAPE:
204 resetToU1();
205 pointStack[pointSP].x = x;
206 pointStack[pointSP].y = y;
207 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
208 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
209 &pointStack[pointSP].x, &pointStack[pointSP].y);
210 if (pointSP + 1 >= 2)
211 Poly(hdc, pointStack, pointSP + 1, fg, bg, lineWidth, shapeStyle, FALSE);
212 break;
213 case TOOL_ELLIPSE:
214 resetToU1();
215 if (GetAsyncKeyState(VK_SHIFT) < 0)
216 regularize(start.x, start.y, &x, &y);
217 Ellp(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
218 break;
219 case TOOL_RRECT:
220 resetToU1();
221 if (GetAsyncKeyState(VK_SHIFT) < 0)
222 regularize(start.x, start.y, &x, &y);
223 RRect(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
224 break;
225 }
226
227 last.x = x;
228 last.y = y;
229 }
230
231 void
232 endPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
233 {
234 switch (activeTool)
235 {
236 case TOOL_FREESEL:
237 {
238 POINT *ptStackCopy;
239 int i;
240 rectSel_src.left = rectSel_src.top = MAXLONG;
241 rectSel_src.right = rectSel_src.bottom = 0;
242 for (i = 0; i <= ptSP; i++)
243 {
244 if (ptStack[i].x < rectSel_src.left)
245 rectSel_src.left = ptStack[i].x;
246 if (ptStack[i].y < rectSel_src.top)
247 rectSel_src.top = ptStack[i].y;
248 if (ptStack[i].x > rectSel_src.right)
249 rectSel_src.right = ptStack[i].x;
250 if (ptStack[i].y > rectSel_src.bottom)
251 rectSel_src.bottom = ptStack[i].y;
252 }
253 rectSel_src.right += 1;
254 rectSel_src.bottom += 1;
255 rectSel_dest.left = rectSel_src.left;
256 rectSel_dest.top = rectSel_src.top;
257 rectSel_dest.right = rectSel_src.right;
258 rectSel_dest.bottom = rectSel_src.bottom;
259 if (ptSP != 0)
260 {
261 DeleteObject(hSelMask);
262 hSelMask = CreateBitmap(RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), 1, 1, NULL);
263 DeleteObject(SelectObject(hSelDC, hSelMask));
264 ptStackCopy = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * (ptSP + 1));
265 for (i = 0; i <= ptSP; i++)
266 {
267 ptStackCopy[i].x = ptStack[i].x - rectSel_src.left;
268 ptStackCopy[i].y = ptStack[i].y - rectSel_src.top;
269 }
270 Poly(hSelDC, ptStackCopy, ptSP + 1, 0x00ffffff, 0x00ffffff, 1, 2, TRUE);
271 HeapFree(GetProcessHeap(), 0, ptStackCopy);
272 SelectObject(hSelDC, hSelBm = CreateDIBWithProperties(RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src)));
273 resetToU1();
274 MaskBlt(hSelDC, 0, 0, RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), hDrawingDC, rectSel_src.left,
275 rectSel_src.top, hSelMask, 0, 0, MAKEROP4(SRCCOPY, WHITENESS));
276 Poly(hdc, ptStack, ptSP + 1, bg, bg, 1, 2, TRUE);
277 newReversible();
278
279 MaskBlt(hDrawingDC, rectSel_src.left, rectSel_src.top, RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), hSelDC, 0,
280 0, hSelMask, 0, 0, MAKEROP4(SRCCOPY, SRCAND));
281
282 placeSelWin();
283 ShowWindow(hSelection, SW_SHOW);
284 /* force refresh of selection contents */
285 SendMessage(hSelection, WM_LBUTTONDOWN, 0, 0);
286 SendMessage(hSelection, WM_MOUSEMOVE, 0, 0);
287 SendMessage(hSelection, WM_LBUTTONUP, 0, 0);
288 }
289 HeapFree(GetProcessHeap(), 0, ptStack);
290 ptStack = NULL;
291 break;
292 }
293 case TOOL_RECTSEL:
294 resetToU1();
295 if ((RECT_WIDTH(rectSel_src) != 0) && (RECT_HEIGHT(rectSel_src) != 0))
296 {
297 DeleteObject(hSelMask);
298 hSelMask = CreateBitmap(RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), 1, 1, NULL);
299 DeleteObject(SelectObject(hSelDC, hSelMask));
300 Rect(hSelDC, 0, 0, RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), 0x00ffffff, 0x00ffffff, 1, 2);
301 SelectObject(hSelDC, hSelBm = CreateDIBWithProperties(RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src)));
302 resetToU1();
303 BitBlt(hSelDC, 0, 0, RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), hDrawingDC, rectSel_src.left,
304 rectSel_src.top, SRCCOPY);
305 Rect(hdc, rectSel_src.left, rectSel_src.top, rectSel_src.right,
306 rectSel_src.bottom, bgColor, bgColor, 0, TRUE);
307 newReversible();
308
309 BitBlt(hDrawingDC, rectSel_src.left, rectSel_src.top, RECT_WIDTH(rectSel_src), RECT_HEIGHT(rectSel_src), hSelDC, 0,
310 0, SRCCOPY);
311
312 placeSelWin();
313 ShowWindow(hSelection, SW_SHOW);
314 ForceRefreshSelectionContents();
315 }
316 break;
317 case TOOL_TEXT:
318 resetToU1();
319 if ((RECT_WIDTH(rectSel_src) != 0) && (RECT_HEIGHT(rectSel_src) != 0))
320 {
321 newReversible();
322
323 placeSelWin();
324 ShowWindow(hSelection, SW_SHOW);
325 ForceRefreshSelectionContents();
326 }
327 break;
328 case TOOL_RUBBER:
329 Erase(hdc, last.x, last.y, x, y, bg, rubberRadius);
330 break;
331 case TOOL_PEN:
332 Line(hdc, last.x, last.y, x, y, fg, 1);
333 SetPixel(hdc, x, y, fg);
334 break;
335 case TOOL_LINE:
336 resetToU1();
337 if (GetAsyncKeyState(VK_SHIFT) < 0)
338 roundTo8Directions(start.x, start.y, &x, &y);
339 Line(hdc, start.x, start.y, x, y, fg, lineWidth);
340 break;
341 case TOOL_BEZIER:
342 pointSP++;
343 if (pointSP == 4)
344 pointSP = 0;
345 break;
346 case TOOL_RECT:
347 resetToU1();
348 if (GetAsyncKeyState(VK_SHIFT) < 0)
349 regularize(start.x, start.y, &x, &y);
350 Rect(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
351 break;
352 case TOOL_SHAPE:
353 resetToU1();
354 pointStack[pointSP].x = x;
355 pointStack[pointSP].y = y;
356 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
357 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
358 &pointStack[pointSP].x, &pointStack[pointSP].y);
359 pointSP++;
360 if (pointSP >= 2)
361 {
362 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
363 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
364 {
365 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, TRUE);
366 pointSP = 0;
367 }
368 else
369 {
370 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, FALSE);
371 }
372 }
373 if (pointSP == 255)
374 pointSP--;
375 break;
376 case TOOL_ELLIPSE:
377 resetToU1();
378 if (GetAsyncKeyState(VK_SHIFT) < 0)
379 regularize(start.x, start.y, &x, &y);
380 Ellp(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
381 break;
382 case TOOL_RRECT:
383 resetToU1();
384 if (GetAsyncKeyState(VK_SHIFT) < 0)
385 regularize(start.x, start.y, &x, &y);
386 RRect(hdc, start.x, start.y, x, y, fg, bg, lineWidth, shapeStyle);
387 break;
388 }
389 }
390
391 void
392 startPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
393 {
394 start.x = x;
395 start.y = y;
396 last.x = x;
397 last.y = y;
398 switch (activeTool)
399 {
400 case TOOL_FREESEL:
401 case TOOL_TEXT:
402 case TOOL_LINE:
403 case TOOL_RECT:
404 case TOOL_ELLIPSE:
405 case TOOL_RRECT:
406 newReversible();
407 break;
408 case TOOL_RUBBER:
409 newReversible();
410 Replace(hdc, x, y, x, y, fg, bg, rubberRadius);
411 break;
412 case TOOL_FILL:
413 newReversible();
414 Fill(hdc, x, y, bg);
415 break;
416 case TOOL_PEN:
417 newReversible();
418 SetPixel(hdc, x, y, bg);
419 break;
420 case TOOL_BRUSH:
421 newReversible();
422 Brush(hdc, x, y, x, y, bg, brushStyle);
423 break;
424 case TOOL_AIRBRUSH:
425 newReversible();
426 Airbrush(hdc, x, y, bg, airBrushWidth);
427 break;
428 case TOOL_BEZIER:
429 pointStack[pointSP].x = x;
430 pointStack[pointSP].y = y;
431 if (pointSP == 0)
432 {
433 newReversible();
434 pointSP++;
435 }
436 break;
437 case TOOL_SHAPE:
438 pointStack[pointSP].x = x;
439 pointStack[pointSP].y = y;
440 if (pointSP + 1 >= 2)
441 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
442 if (pointSP == 0)
443 {
444 newReversible();
445 pointSP++;
446 }
447 break;
448 }
449 }
450
451 void
452 whilePaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
453 {
454 switch (activeTool)
455 {
456 case TOOL_RUBBER:
457 Replace(hdc, last.x, last.y, x, y, fg, bg, rubberRadius);
458 break;
459 case TOOL_PEN:
460 Line(hdc, last.x, last.y, x, y, bg, 1);
461 break;
462 case TOOL_BRUSH:
463 Brush(hdc, last.x, last.y, x, y, bg, brushStyle);
464 break;
465 case TOOL_AIRBRUSH:
466 Airbrush(hdc, x, y, bg, airBrushWidth);
467 break;
468 case TOOL_LINE:
469 resetToU1();
470 if (GetAsyncKeyState(VK_SHIFT) < 0)
471 roundTo8Directions(start.x, start.y, &x, &y);
472 Line(hdc, start.x, start.y, x, y, bg, lineWidth);
473 break;
474 case TOOL_BEZIER:
475 resetToU1();
476 pointStack[pointSP].x = x;
477 pointStack[pointSP].y = y;
478 switch (pointSP)
479 {
480 case 1:
481 Line(hdc, pointStack[0].x, pointStack[0].y, pointStack[1].x, pointStack[1].y, bg,
482 lineWidth);
483 break;
484 case 2:
485 Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], pointStack[1], bg, lineWidth);
486 break;
487 case 3:
488 Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], pointStack[1], bg, lineWidth);
489 break;
490 }
491 break;
492 case TOOL_RECT:
493 resetToU1();
494 if (GetAsyncKeyState(VK_SHIFT) < 0)
495 regularize(start.x, start.y, &x, &y);
496 Rect(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
497 break;
498 case TOOL_SHAPE:
499 resetToU1();
500 pointStack[pointSP].x = x;
501 pointStack[pointSP].y = y;
502 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
503 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
504 &pointStack[pointSP].x, &pointStack[pointSP].y);
505 if (pointSP + 1 >= 2)
506 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
507 break;
508 case TOOL_ELLIPSE:
509 resetToU1();
510 if (GetAsyncKeyState(VK_SHIFT) < 0)
511 regularize(start.x, start.y, &x, &y);
512 Ellp(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
513 break;
514 case TOOL_RRECT:
515 resetToU1();
516 if (GetAsyncKeyState(VK_SHIFT) < 0)
517 regularize(start.x, start.y, &x, &y);
518 RRect(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
519 break;
520 }
521
522 last.x = x;
523 last.y = y;
524 }
525
526 void
527 endPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
528 {
529 switch (activeTool)
530 {
531 case TOOL_RUBBER:
532 Replace(hdc, last.x, last.y, x, y, fg, bg, rubberRadius);
533 break;
534 case TOOL_PEN:
535 Line(hdc, last.x, last.y, x, y, bg, 1);
536 SetPixel(hdc, x, y, bg);
537 break;
538 case TOOL_LINE:
539 resetToU1();
540 if (GetAsyncKeyState(VK_SHIFT) < 0)
541 roundTo8Directions(start.x, start.y, &x, &y);
542 Line(hdc, start.x, start.y, x, y, bg, lineWidth);
543 break;
544 case TOOL_BEZIER:
545 pointSP++;
546 if (pointSP == 4)
547 pointSP = 0;
548 break;
549 case TOOL_RECT:
550 resetToU1();
551 if (GetAsyncKeyState(VK_SHIFT) < 0)
552 regularize(start.x, start.y, &x, &y);
553 Rect(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
554 break;
555 case TOOL_SHAPE:
556 resetToU1();
557 pointStack[pointSP].x = x;
558 pointStack[pointSP].y = y;
559 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
560 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
561 &pointStack[pointSP].x, &pointStack[pointSP].y);
562 pointSP++;
563 if (pointSP >= 2)
564 {
565 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
566 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
567 {
568 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, TRUE);
569 pointSP = 0;
570 }
571 else
572 {
573 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, FALSE);
574 }
575 }
576 if (pointSP == 255)
577 pointSP--;
578 break;
579 case TOOL_ELLIPSE:
580 resetToU1();
581 if (GetAsyncKeyState(VK_SHIFT) < 0)
582 regularize(start.x, start.y, &x, &y);
583 Ellp(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
584 break;
585 case TOOL_RRECT:
586 resetToU1();
587 if (GetAsyncKeyState(VK_SHIFT) < 0)
588 regularize(start.x, start.y, &x, &y);
589 RRect(hdc, start.x, start.y, x, y, bg, fg, lineWidth, shapeStyle);
590 break;
591 }
592 }