[NTOSKRNL]
[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[0] * zoom / 1000, rectSel_dest[1] * zoom / 1000,
19 rectSel_dest[2] * zoom / 1000 + 6, rectSel_dest[3] * zoom / 1000 + 6, TRUE);
20 BringWindowToTop(hSelection);
21 SendMessage(hImageArea, WM_PAINT, 0, 0);
22 //SendMessage(hSelection, WM_PAINT, 0, 0);
23 }
24
25 void
26 regularize(LONG x0, LONG y0, LONG *x1, LONG *y1)
27 {
28 if (abs(*x1 - x0) >= abs(*y1 - y0))
29 *y1 = y0 + (*y1 > y0 ? abs(*x1 - x0) : -abs(*x1 - x0));
30 else
31 *x1 = x0 + (*x1 > x0 ? abs(*y1 - y0) : -abs(*y1 - y0));
32 }
33
34 void
35 roundTo8Directions(LONG x0, LONG y0, LONG *x1, LONG *y1)
36 {
37 if (abs(*x1 - x0) >= abs(*y1 - y0))
38 {
39 if (abs(*y1 - y0) * 5 < abs(*x1 - x0) * 2)
40 *y1 = y0;
41 else
42 *y1 = y0 + (*y1 > y0 ? abs(*x1 - x0) : -abs(*x1 - x0));
43 }
44 else
45 {
46 if (abs(*x1 - x0) * 5 < abs(*y1 - y0) * 2)
47 *x1 = x0;
48 else
49 *x1 = x0 + (*x1 > x0 ? abs(*y1 - y0) : -abs(*y1 - y0));
50 }
51 }
52
53 POINT pointStack[256];
54 short pointSP;
55 POINT *ptStack = NULL;
56 int ptSP = 0;
57
58 void
59 startPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
60 {
61 startX = x;
62 startY = y;
63 lastX = x;
64 lastY = y;
65 switch (activeTool)
66 {
67 case TOOL_FREESEL:
68 ShowWindow(hSelection, SW_HIDE);
69 if (ptStack != NULL)
70 HeapFree(GetProcessHeap(), 0, ptStack);
71 ptStack = HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * 1024);
72 ptSP = 0;
73 ptStack[0].x = x;
74 ptStack[0].y = y;
75 break;
76 case TOOL_TEXT:
77 case TOOL_LINE:
78 case TOOL_RECT:
79 case TOOL_ELLIPSE:
80 case TOOL_RRECT:
81 newReversible();
82 break;
83 case TOOL_RECTSEL:
84 newReversible();
85 ShowWindow(hSelection, SW_HIDE);
86 rectSel_src[2] = rectSel_src[3] = 0;
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 {
149 int tempX;
150 int tempY;
151 resetToU1();
152 tempX = max(0, min(x, imgXRes));
153 tempY = max(0, min(y, imgYRes));
154 rectSel_dest[0] = rectSel_src[0] = min(startX, tempX);
155 rectSel_dest[1] = rectSel_src[1] = min(startY, tempY);
156 rectSel_dest[2] = rectSel_src[2] = max(startX, tempX) - min(startX, tempX);
157 rectSel_dest[3] = rectSel_src[3] = max(startY, tempY) - min(startY, tempY);
158 RectSel(hdc, startX, startY, tempX, tempY);
159 break;
160 }
161 case TOOL_RUBBER:
162 Erase(hdc, lastX, lastY, x, y, bg, rubberRadius);
163 break;
164 case TOOL_PEN:
165 Line(hdc, lastX, lastY, x, y, fg, 1);
166 break;
167 case TOOL_BRUSH:
168 Brush(hdc, lastX, lastY, 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(startX, startY, &x, &y);
177 Line(hdc, startX, startY, 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(startX, startY, &x, &y);
201 Rect(hdc, startX, startY, 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(startX, startY, &x, &y);
217 Ellp(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
218 break;
219 case TOOL_RRECT:
220 resetToU1();
221 if (GetAsyncKeyState(VK_SHIFT) < 0)
222 regularize(startX, startY, &x, &y);
223 RRect(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
224 break;
225 }
226
227 lastX = x;
228 lastY = 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[0] = rectSel_src[1] = 0x7fffffff;
241 rectSel_src[2] = rectSel_src[3] = 0;
242 for (i = 0; i <= ptSP; i++)
243 {
244 if (ptStack[i].x < rectSel_src[0])
245 rectSel_src[0] = ptStack[i].x;
246 if (ptStack[i].y < rectSel_src[1])
247 rectSel_src[1] = ptStack[i].y;
248 if (ptStack[i].x > rectSel_src[2])
249 rectSel_src[2] = ptStack[i].x;
250 if (ptStack[i].y > rectSel_src[3])
251 rectSel_src[3] = ptStack[i].y;
252 }
253 rectSel_src[2] += 1 - rectSel_src[0];
254 rectSel_src[3] += 1 - rectSel_src[1];
255 rectSel_dest[0] = rectSel_src[0];
256 rectSel_dest[1] = rectSel_src[1];
257 rectSel_dest[2] = rectSel_src[2];
258 rectSel_dest[3] = rectSel_src[3];
259 if (ptSP != 0)
260 {
261 DeleteObject(hSelMask);
262 hSelMask = CreateBitmap(rectSel_src[2], rectSel_src[3], 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[0];
268 ptStackCopy[i].y = ptStack[i].y - rectSel_src[1];
269 }
270 Poly(hSelDC, ptStackCopy, ptSP + 1, 0x00ffffff, 0x00ffffff, 1, 2, TRUE);
271 HeapFree(GetProcessHeap(), 0, ptStackCopy);
272 SelectObject(hSelDC, hSelBm = CreateDIBWithProperties(rectSel_src[2], rectSel_src[3]));
273 resetToU1();
274 MaskBlt(hSelDC, 0, 0, rectSel_src[2], rectSel_src[3], hDrawingDC, rectSel_src[0],
275 rectSel_src[1], 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[0], rectSel_src[1], rectSel_src[2], rectSel_src[3], 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 ((rectSel_src[2] != 0) && (rectSel_src[3] != 0))
296 {
297 DeleteObject(hSelMask);
298 hSelMask = CreateBitmap(rectSel_src[2], rectSel_src[3], 1, 1, NULL);
299 DeleteObject(SelectObject(hSelDC, hSelMask));
300 Rect(hSelDC, 0, 0, rectSel_src[2], rectSel_src[3], 0x00ffffff, 0x00ffffff, 1, 2);
301 SelectObject(hSelDC, hSelBm = CreateDIBWithProperties(rectSel_src[2], rectSel_src[3]));
302 resetToU1();
303 BitBlt(hSelDC, 0, 0, rectSel_src[2], rectSel_src[3], hDrawingDC, rectSel_src[0],
304 rectSel_src[1], SRCCOPY);
305 Rect(hdc, rectSel_src[0], rectSel_src[1], rectSel_src[0] + rectSel_src[2],
306 rectSel_src[1] + rectSel_src[3], bgColor, bgColor, 0, TRUE);
307 newReversible();
308
309 BitBlt(hDrawingDC, rectSel_src[0], rectSel_src[1], rectSel_src[2], rectSel_src[3], hSelDC, 0,
310 0, SRCCOPY);
311
312 placeSelWin();
313 ShowWindow(hSelection, SW_SHOW);
314 /* force refresh of selection contents */
315 SendMessage(hSelection, WM_LBUTTONDOWN, 0, 0);
316 SendMessage(hSelection, WM_MOUSEMOVE, 0, 0);
317 SendMessage(hSelection, WM_LBUTTONUP, 0, 0);
318 }
319 break;
320 case TOOL_RUBBER:
321 Erase(hdc, lastX, lastY, x, y, bg, rubberRadius);
322 break;
323 case TOOL_PEN:
324 Line(hdc, lastX, lastY, x, y, fg, 1);
325 SetPixel(hdc, x, y, fg);
326 break;
327 case TOOL_LINE:
328 resetToU1();
329 if (GetAsyncKeyState(VK_SHIFT) < 0)
330 roundTo8Directions(startX, startY, &x, &y);
331 Line(hdc, startX, startY, x, y, fg, lineWidth);
332 break;
333 case TOOL_BEZIER:
334 pointSP++;
335 if (pointSP == 4)
336 pointSP = 0;
337 break;
338 case TOOL_RECT:
339 resetToU1();
340 if (GetAsyncKeyState(VK_SHIFT) < 0)
341 regularize(startX, startY, &x, &y);
342 Rect(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
343 break;
344 case TOOL_SHAPE:
345 resetToU1();
346 pointStack[pointSP].x = x;
347 pointStack[pointSP].y = y;
348 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
349 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
350 &pointStack[pointSP].x, &pointStack[pointSP].y);
351 pointSP++;
352 if (pointSP >= 2)
353 {
354 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
355 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
356 {
357 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, TRUE);
358 pointSP = 0;
359 }
360 else
361 {
362 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, FALSE);
363 }
364 }
365 if (pointSP == 255)
366 pointSP--;
367 break;
368 case TOOL_ELLIPSE:
369 resetToU1();
370 if (GetAsyncKeyState(VK_SHIFT) < 0)
371 regularize(startX, startY, &x, &y);
372 Ellp(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
373 break;
374 case TOOL_RRECT:
375 resetToU1();
376 if (GetAsyncKeyState(VK_SHIFT) < 0)
377 regularize(startX, startY, &x, &y);
378 RRect(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
379 break;
380 }
381 }
382
383 void
384 startPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
385 {
386 startX = x;
387 startY = y;
388 lastX = x;
389 lastY = y;
390 switch (activeTool)
391 {
392 case TOOL_FREESEL:
393 case TOOL_TEXT:
394 case TOOL_LINE:
395 case TOOL_RECT:
396 case TOOL_ELLIPSE:
397 case TOOL_RRECT:
398 newReversible();
399 break;
400 case TOOL_RUBBER:
401 newReversible();
402 Replace(hdc, x, y, x, y, fg, bg, rubberRadius);
403 break;
404 case TOOL_FILL:
405 newReversible();
406 Fill(hdc, x, y, bg);
407 break;
408 case TOOL_PEN:
409 newReversible();
410 SetPixel(hdc, x, y, bg);
411 break;
412 case TOOL_BRUSH:
413 newReversible();
414 Brush(hdc, x, y, x, y, bg, brushStyle);
415 break;
416 case TOOL_AIRBRUSH:
417 newReversible();
418 Airbrush(hdc, x, y, bg, airBrushWidth);
419 break;
420 case TOOL_BEZIER:
421 pointStack[pointSP].x = x;
422 pointStack[pointSP].y = y;
423 if (pointSP == 0)
424 {
425 newReversible();
426 pointSP++;
427 }
428 break;
429 case TOOL_SHAPE:
430 pointStack[pointSP].x = x;
431 pointStack[pointSP].y = y;
432 if (pointSP + 1 >= 2)
433 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
434 if (pointSP == 0)
435 {
436 newReversible();
437 pointSP++;
438 }
439 break;
440 }
441 }
442
443 void
444 whilePaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
445 {
446 switch (activeTool)
447 {
448 case TOOL_RUBBER:
449 Replace(hdc, lastX, lastY, x, y, fg, bg, rubberRadius);
450 break;
451 case TOOL_PEN:
452 Line(hdc, lastX, lastY, x, y, bg, 1);
453 break;
454 case TOOL_BRUSH:
455 Brush(hdc, lastX, lastY, x, y, bg, brushStyle);
456 break;
457 case TOOL_AIRBRUSH:
458 Airbrush(hdc, x, y, bg, airBrushWidth);
459 break;
460 case TOOL_LINE:
461 resetToU1();
462 if (GetAsyncKeyState(VK_SHIFT) < 0)
463 roundTo8Directions(startX, startY, &x, &y);
464 Line(hdc, startX, startY, x, y, bg, lineWidth);
465 break;
466 case TOOL_BEZIER:
467 resetToU1();
468 pointStack[pointSP].x = x;
469 pointStack[pointSP].y = y;
470 switch (pointSP)
471 {
472 case 1:
473 Line(hdc, pointStack[0].x, pointStack[0].y, pointStack[1].x, pointStack[1].y, bg,
474 lineWidth);
475 break;
476 case 2:
477 Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], pointStack[1], bg, lineWidth);
478 break;
479 case 3:
480 Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], pointStack[1], bg, lineWidth);
481 break;
482 }
483 break;
484 case TOOL_RECT:
485 resetToU1();
486 if (GetAsyncKeyState(VK_SHIFT) < 0)
487 regularize(startX, startY, &x, &y);
488 Rect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
489 break;
490 case TOOL_SHAPE:
491 resetToU1();
492 pointStack[pointSP].x = x;
493 pointStack[pointSP].y = y;
494 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
495 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
496 &pointStack[pointSP].x, &pointStack[pointSP].y);
497 if (pointSP + 1 >= 2)
498 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
499 break;
500 case TOOL_ELLIPSE:
501 resetToU1();
502 if (GetAsyncKeyState(VK_SHIFT) < 0)
503 regularize(startX, startY, &x, &y);
504 Ellp(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
505 break;
506 case TOOL_RRECT:
507 resetToU1();
508 if (GetAsyncKeyState(VK_SHIFT) < 0)
509 regularize(startX, startY, &x, &y);
510 RRect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
511 break;
512 }
513
514 lastX = x;
515 lastY = y;
516 }
517
518 void
519 endPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
520 {
521 switch (activeTool)
522 {
523 case TOOL_RUBBER:
524 Replace(hdc, lastX, lastY, x, y, fg, bg, rubberRadius);
525 break;
526 case TOOL_PEN:
527 Line(hdc, lastX, lastY, x, y, bg, 1);
528 SetPixel(hdc, x, y, bg);
529 break;
530 case TOOL_LINE:
531 resetToU1();
532 if (GetAsyncKeyState(VK_SHIFT) < 0)
533 roundTo8Directions(startX, startY, &x, &y);
534 Line(hdc, startX, startY, x, y, bg, lineWidth);
535 break;
536 case TOOL_BEZIER:
537 pointSP++;
538 if (pointSP == 4)
539 pointSP = 0;
540 break;
541 case TOOL_RECT:
542 resetToU1();
543 if (GetAsyncKeyState(VK_SHIFT) < 0)
544 regularize(startX, startY, &x, &y);
545 Rect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
546 break;
547 case TOOL_SHAPE:
548 resetToU1();
549 pointStack[pointSP].x = x;
550 pointStack[pointSP].y = y;
551 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
552 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
553 &pointStack[pointSP].x, &pointStack[pointSP].y);
554 pointSP++;
555 if (pointSP >= 2)
556 {
557 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
558 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
559 {
560 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, TRUE);
561 pointSP = 0;
562 }
563 else
564 {
565 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, FALSE);
566 }
567 }
568 if (pointSP == 255)
569 pointSP--;
570 break;
571 case TOOL_ELLIPSE:
572 resetToU1();
573 if (GetAsyncKeyState(VK_SHIFT) < 0)
574 regularize(startX, startY, &x, &y);
575 Ellp(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
576 break;
577 case TOOL_RRECT:
578 resetToU1();
579 if (GetAsyncKeyState(VK_SHIFT) < 0)
580 regularize(startX, startY, &x, &y);
581 RRect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
582 break;
583 }
584 }