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