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