Sync to trunk head (r47736)
[reactos.git] / base / applications / paint / 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(short x0, short y0, short *x1, short *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(short x0, short y0, short *x1, short *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, short x, short y, int fg, int 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, short x, short y, int fg, int 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 short tempX;
153 short 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 (short *)&pointStack[pointSP].x, (short *)&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, short x, short y, int fg, int 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 }
288 HeapFree(GetProcessHeap(), 0, ptStack);
289 ptStack = NULL;
290 break;
291 }
292 case TOOL_RECTSEL:
293 resetToU1();
294 if ((rectSel_src[2] != 0) && (rectSel_src[3] != 0))
295 {
296 DeleteObject(hSelMask);
297 hSelMask = CreateBitmap(rectSel_src[2], rectSel_src[3], 1, 1, NULL);
298 DeleteObject(SelectObject(hSelDC, hSelMask));
299 Rect(hSelDC, 0, 0, rectSel_src[2], rectSel_src[3], 0x00ffffff, 0x00ffffff, 1, 2);
300 SelectObject(hSelDC, hSelBm = CreateDIBWithProperties(rectSel_src[2], rectSel_src[3]));
301 resetToU1();
302 BitBlt(hSelDC, 0, 0, rectSel_src[2], rectSel_src[3], hDrawingDC, rectSel_src[0],
303 rectSel_src[1], SRCCOPY);
304 Rect(hdc, rectSel_src[0], rectSel_src[1], rectSel_src[0] + rectSel_src[2],
305 rectSel_src[1] + rectSel_src[3], bgColor, bgColor, 0, TRUE);
306 newReversible();
307
308 BitBlt(hDrawingDC, rectSel_src[0], rectSel_src[1], rectSel_src[2], rectSel_src[3], hSelDC, 0,
309 0, SRCCOPY);
310
311 placeSelWin();
312 ShowWindow(hSelection, SW_SHOW);
313 }
314 break;
315 case TOOL_RUBBER:
316 Erase(hdc, lastX, lastY, x, y, bg, rubberRadius);
317 break;
318 case TOOL_PEN:
319 Line(hdc, lastX, lastY, x, y, fg, 1);
320 SetPixel(hdc, x, y, fg);
321 break;
322 case TOOL_LINE:
323 resetToU1();
324 if (GetAsyncKeyState(VK_SHIFT) < 0)
325 roundTo8Directions(startX, startY, &x, &y);
326 Line(hdc, startX, startY, x, y, fg, lineWidth);
327 break;
328 case TOOL_BEZIER:
329 pointSP++;
330 if (pointSP == 4)
331 pointSP = 0;
332 break;
333 case TOOL_RECT:
334 resetToU1();
335 if (GetAsyncKeyState(VK_SHIFT) < 0)
336 regularize(startX, startY, &x, &y);
337 Rect(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
338 break;
339 case TOOL_SHAPE:
340 resetToU1();
341 pointStack[pointSP].x = x;
342 pointStack[pointSP].y = y;
343 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
344 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
345 (short *)&pointStack[pointSP].x, (short *)&pointStack[pointSP].y);
346 pointSP++;
347 if (pointSP >= 2)
348 {
349 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
350 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
351 {
352 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, TRUE);
353 pointSP = 0;
354 }
355 else
356 {
357 Poly(hdc, pointStack, pointSP, fg, bg, lineWidth, shapeStyle, FALSE);
358 }
359 }
360 if (pointSP == 255)
361 pointSP--;
362 break;
363 case TOOL_ELLIPSE:
364 resetToU1();
365 if (GetAsyncKeyState(VK_SHIFT) < 0)
366 regularize(startX, startY, &x, &y);
367 Ellp(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
368 break;
369 case TOOL_RRECT:
370 resetToU1();
371 if (GetAsyncKeyState(VK_SHIFT) < 0)
372 regularize(startX, startY, &x, &y);
373 RRect(hdc, startX, startY, x, y, fg, bg, lineWidth, shapeStyle);
374 break;
375 }
376 }
377
378 void
379 startPaintingR(HDC hdc, short x, short y, int fg, int bg)
380 {
381 startX = x;
382 startY = y;
383 lastX = x;
384 lastY = y;
385 switch (activeTool)
386 {
387 case TOOL_FREESEL:
388 case TOOL_TEXT:
389 case TOOL_LINE:
390 case TOOL_RECT:
391 case TOOL_ELLIPSE:
392 case TOOL_RRECT:
393 newReversible();
394 break;
395 case TOOL_RUBBER:
396 newReversible();
397 Replace(hdc, x, y, x, y, fg, bg, rubberRadius);
398 break;
399 case TOOL_FILL:
400 newReversible();
401 Fill(hdc, x, y, bg);
402 break;
403 case TOOL_PEN:
404 newReversible();
405 SetPixel(hdc, x, y, bg);
406 break;
407 case TOOL_BRUSH:
408 newReversible();
409 Brush(hdc, x, y, x, y, bg, brushStyle);
410 break;
411 case TOOL_AIRBRUSH:
412 newReversible();
413 Airbrush(hdc, x, y, bg, airBrushWidth);
414 break;
415 case TOOL_BEZIER:
416 pointStack[pointSP].x = x;
417 pointStack[pointSP].y = y;
418 if (pointSP == 0)
419 {
420 newReversible();
421 pointSP++;
422 }
423 break;
424 case TOOL_SHAPE:
425 pointStack[pointSP].x = x;
426 pointStack[pointSP].y = y;
427 if (pointSP + 1 >= 2)
428 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
429 if (pointSP == 0)
430 {
431 newReversible();
432 pointSP++;
433 }
434 break;
435 }
436 }
437
438 void
439 whilePaintingR(HDC hdc, short x, short y, int fg, int bg)
440 {
441 switch (activeTool)
442 {
443 case TOOL_RUBBER:
444 Replace(hdc, lastX, lastY, x, y, fg, bg, rubberRadius);
445 break;
446 case TOOL_PEN:
447 Line(hdc, lastX, lastY, x, y, bg, 1);
448 break;
449 case TOOL_BRUSH:
450 Brush(hdc, lastX, lastY, x, y, bg, brushStyle);
451 break;
452 case TOOL_AIRBRUSH:
453 Airbrush(hdc, x, y, bg, airBrushWidth);
454 break;
455 case TOOL_LINE:
456 resetToU1();
457 if (GetAsyncKeyState(VK_SHIFT) < 0)
458 roundTo8Directions(startX, startY, &x, &y);
459 Line(hdc, startX, startY, x, y, bg, lineWidth);
460 break;
461 case TOOL_BEZIER:
462 resetToU1();
463 pointStack[pointSP].x = x;
464 pointStack[pointSP].y = y;
465 switch (pointSP)
466 {
467 case 1:
468 Line(hdc, pointStack[0].x, pointStack[0].y, pointStack[1].x, pointStack[1].y, bg,
469 lineWidth);
470 break;
471 case 2:
472 Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], pointStack[1], bg, lineWidth);
473 break;
474 case 3:
475 Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], pointStack[1], bg, lineWidth);
476 break;
477 }
478 break;
479 case TOOL_RECT:
480 resetToU1();
481 if (GetAsyncKeyState(VK_SHIFT) < 0)
482 regularize(startX, startY, &x, &y);
483 Rect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
484 break;
485 case TOOL_SHAPE:
486 resetToU1();
487 pointStack[pointSP].x = x;
488 pointStack[pointSP].y = y;
489 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
490 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
491 (short *)&pointStack[pointSP].x, (short *)&pointStack[pointSP].y);
492 if (pointSP + 1 >= 2)
493 Poly(hdc, pointStack, pointSP + 1, bg, fg, lineWidth, shapeStyle, FALSE);
494 break;
495 case TOOL_ELLIPSE:
496 resetToU1();
497 if (GetAsyncKeyState(VK_SHIFT) < 0)
498 regularize(startX, startY, &x, &y);
499 Ellp(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
500 break;
501 case TOOL_RRECT:
502 resetToU1();
503 if (GetAsyncKeyState(VK_SHIFT) < 0)
504 regularize(startX, startY, &x, &y);
505 RRect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
506 break;
507 }
508
509 lastX = x;
510 lastY = y;
511 }
512
513 void
514 endPaintingR(HDC hdc, short x, short y, int fg, int bg)
515 {
516 switch (activeTool)
517 {
518 case TOOL_RUBBER:
519 Replace(hdc, lastX, lastY, x, y, fg, bg, rubberRadius);
520 break;
521 case TOOL_PEN:
522 Line(hdc, lastX, lastY, x, y, bg, 1);
523 SetPixel(hdc, x, y, bg);
524 break;
525 case TOOL_LINE:
526 resetToU1();
527 if (GetAsyncKeyState(VK_SHIFT) < 0)
528 roundTo8Directions(startX, startY, &x, &y);
529 Line(hdc, startX, startY, x, y, bg, lineWidth);
530 break;
531 case TOOL_BEZIER:
532 pointSP++;
533 if (pointSP == 4)
534 pointSP = 0;
535 break;
536 case TOOL_RECT:
537 resetToU1();
538 if (GetAsyncKeyState(VK_SHIFT) < 0)
539 regularize(startX, startY, &x, &y);
540 Rect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
541 break;
542 case TOOL_SHAPE:
543 resetToU1();
544 pointStack[pointSP].x = x;
545 pointStack[pointSP].y = y;
546 if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
547 roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 1].y,
548 (short *)&pointStack[pointSP].x, (short *)&pointStack[pointSP].y);
549 pointSP++;
550 if (pointSP >= 2)
551 {
552 if ((pointStack[0].x - x) * (pointStack[0].x - x) +
553 (pointStack[0].y - y) * (pointStack[0].y - y) <= lineWidth * lineWidth + 1)
554 {
555 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, TRUE);
556 pointSP = 0;
557 }
558 else
559 {
560 Poly(hdc, pointStack, pointSP, bg, fg, lineWidth, shapeStyle, FALSE);
561 }
562 }
563 if (pointSP == 255)
564 pointSP--;
565 break;
566 case TOOL_ELLIPSE:
567 resetToU1();
568 if (GetAsyncKeyState(VK_SHIFT) < 0)
569 regularize(startX, startY, &x, &y);
570 Ellp(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
571 break;
572 case TOOL_RRECT:
573 resetToU1();
574 if (GetAsyncKeyState(VK_SHIFT) < 0)
575 regularize(startX, startY, &x, &y);
576 RRect(hdc, startX, startY, x, y, bg, fg, lineWidth, shapeStyle);
577 break;
578 }
579 }