80f87682e32307927cc53f9b28370fff400f466e
[reactos.git] / base / applications / mspaint / textedit.cpp
1 /*
2 * PROJECT: PAINT for ReactOS
3 * LICENSE: LGPL
4 * FILE: base/applications/mspaint/textedit.cpp
5 * PURPOSE: Text editor and font chooser for the text tool
6 * PROGRAMMERS: Benedikt Freisen
7 */
8
9 /* INCLUDES *********************************************************/
10
11 #include "precomp.h"
12
13 #define CXY_GRIP 3
14
15 /* FUNCTIONS ********************************************************/
16
17 CTextEditWindow::CTextEditWindow() : m_hFont(NULL), m_hFontZoomed(NULL), m_nAppIsMovingOrSizing(0)
18 {
19 SetRectEmpty(&m_rc);
20 }
21
22 #define X0 rc.left
23 #define X1 ((rc.left + rc.right - CXY_GRIP) / 2)
24 #define X2 (rc.right - CXY_GRIP)
25 #define Y0 rc.top
26 #define Y1 ((rc.top + rc.bottom - CXY_GRIP) / 2)
27 #define Y2 (rc.bottom - CXY_GRIP)
28 #define RECT0 X0, Y0, X0 + CXY_GRIP, Y0 + CXY_GRIP // Upper Left
29 #define RECT1 X1, Y0, X1 + CXY_GRIP, Y0 + CXY_GRIP // Top
30 #define RECT2 X2, Y0, X2 + CXY_GRIP, Y0 + CXY_GRIP // Upper Right
31 #define RECT3 X0, Y1, X0 + CXY_GRIP, Y1 + CXY_GRIP // Left
32 #define RECT4 X2, Y1, X2 + CXY_GRIP, Y1 + CXY_GRIP // Right
33 #define RECT5 X0, Y2, X0 + CXY_GRIP, Y2 + CXY_GRIP // Lower Left
34 #define RECT6 X1, Y2, X1 + CXY_GRIP, Y2 + CXY_GRIP // Bottom
35 #define RECT7 X2, Y2, X2 + CXY_GRIP, Y2 + CXY_GRIP // Lower Right
36
37 INT CTextEditWindow::DoHitTest(RECT& rc, POINT pt)
38 {
39 RECT rcGrip;
40
41 SetRect(&rcGrip, RECT0);
42 if (PtInRect(&rcGrip, pt))
43 return HTTOPLEFT;
44 SetRect(&rcGrip, RECT1);
45 if (PtInRect(&rcGrip, pt))
46 return HTTOP;
47 SetRect(&rcGrip, RECT2);
48 if (PtInRect(&rcGrip, pt))
49 return HTTOPRIGHT;
50
51 SetRect(&rcGrip, RECT3);
52 if (PtInRect(&rcGrip, pt))
53 return HTLEFT;
54 SetRect(&rcGrip, RECT4);
55 if (PtInRect(&rcGrip, pt))
56 return HTRIGHT;
57
58 SetRect(&rcGrip, RECT5);
59 if (PtInRect(&rcGrip, pt))
60 return HTBOTTOMLEFT;
61 SetRect(&rcGrip, RECT6);
62 if (PtInRect(&rcGrip, pt))
63 return HTBOTTOM;
64 SetRect(&rcGrip, RECT7);
65 if (PtInRect(&rcGrip, pt))
66 return HTBOTTOMRIGHT;
67
68 // On border line?
69 RECT rcInner = rc;
70 InflateRect(&rcInner, -3, -3);
71 if (!PtInRect(&rcInner, pt) && PtInRect(&rc, pt))
72 return HTCAPTION;
73
74 return HTCLIENT;
75 }
76
77 void CTextEditWindow::DrawGrip(HDC hDC, RECT& rc)
78 {
79 HGDIOBJ hbrOld = SelectObject(hDC, GetStockObject(NULL_BRUSH));
80 HPEN hPen = CreatePen(PS_DOT, 1, GetSysColor(COLOR_HIGHLIGHT));
81 HGDIOBJ hPenOld = SelectObject(hDC, hPen);
82 InflateRect(&rc, -1, -1);
83 Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
84 InflateRect(&rc, 1, 1);
85 SelectObject(hDC, hPenOld);
86 SelectObject(hDC, hbrOld);
87 DeleteObject(hPen);
88
89 RECT rcGrip;
90 HBRUSH hbrHighlight = GetSysColorBrush(COLOR_HIGHLIGHT);
91
92 SetRect(&rcGrip, RECT0);
93 FillRect(hDC, &rcGrip, hbrHighlight);
94 SetRect(&rcGrip, RECT1);
95 FillRect(hDC, &rcGrip, hbrHighlight);
96 SetRect(&rcGrip, RECT2);
97 FillRect(hDC, &rcGrip, hbrHighlight);
98
99 SetRect(&rcGrip, RECT3);
100 FillRect(hDC, &rcGrip, hbrHighlight);
101 SetRect(&rcGrip, RECT4);
102 FillRect(hDC, &rcGrip, hbrHighlight);
103
104 SetRect(&rcGrip, RECT5);
105 FillRect(hDC, &rcGrip, hbrHighlight);
106 SetRect(&rcGrip, RECT6);
107 FillRect(hDC, &rcGrip, hbrHighlight);
108 SetRect(&rcGrip, RECT7);
109 FillRect(hDC, &rcGrip, hbrHighlight);
110 }
111
112 void CTextEditWindow::FixEditPos(LPCTSTR pszOldText)
113 {
114 CString szText;
115 GetWindowText(szText);
116
117 RECT rcParent;
118 ::GetWindowRect(m_hwndParent, &rcParent);
119
120 RECT rc, rcWnd, rcText;
121 GetWindowRect(&rcWnd);
122 rcText = rcWnd;
123
124 HDC hDC = GetDC();
125 if (hDC)
126 {
127 SelectObject(hDC, m_hFontZoomed);
128 TEXTMETRIC tm;
129 GetTextMetrics(hDC, &tm);
130 szText += TEXT("x"); // This is a trick to enable the last newlines
131 const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP |
132 DT_EXPANDTABS | DT_WORDBREAK;
133 DrawText(hDC, szText, -1, &rcText, uFormat | DT_CALCRECT);
134 if (tm.tmDescent > 0)
135 rcText.bottom += tm.tmDescent;
136 ReleaseDC(hDC);
137 }
138
139 UnionRect(&rc, &rcText, &rcWnd);
140 ::MapWindowPoints(NULL, m_hwndParent, (LPPOINT)&rc, 2);
141
142 rcWnd = rc;
143 ::GetClientRect(m_hwndParent, &rcParent);
144 IntersectRect(&rc, &rcParent, &rcWnd);
145
146 ++m_nAppIsMovingOrSizing;
147 MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
148 --m_nAppIsMovingOrSizing;
149
150 DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
151 DefWindowProc(WM_VSCROLL, SB_TOP, 0);
152
153 ::InvalidateRect(m_hwndParent, &rc, TRUE);
154 }
155
156 LRESULT CTextEditWindow::OnChar(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
157 {
158 if (wParam == VK_TAB)
159 return 0; // FIXME: Tabs
160
161 CString szText;
162 GetWindowText(szText);
163
164 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
165 FixEditPos(szText);
166
167 return ret;
168 }
169
170 LRESULT CTextEditWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
171 {
172 if (wParam == VK_ESCAPE)
173 {
174 toolsModel.OnCancelDraw();
175 return 0;
176 }
177
178 CString szText;
179 GetWindowText(szText);
180
181 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
182 FixEditPos(szText);
183 return ret;
184 }
185
186 LRESULT CTextEditWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
187 {
188 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
189 DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
190 DefWindowProc(WM_VSCROLL, SB_TOP, 0);
191 return ret;
192 }
193
194 LRESULT CTextEditWindow::OnEraseBkGnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
195 {
196 HDC hDC = (HDC)wParam;
197 if (!toolsModel.IsBackgroundTransparent())
198 {
199 RECT rc;
200 GetClientRect(&rc);
201 HBRUSH hbr = CreateSolidBrush(paletteModel.GetBgColor());
202 FillRect(hDC, &rc, hbr);
203 DeleteObject(hbr);
204 }
205 SetTextColor(hDC, paletteModel.GetFgColor());
206 return TRUE;
207 }
208
209 LRESULT CTextEditWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
210 {
211 RECT rc;
212 GetClientRect(&rc);
213
214 DefWindowProc(nMsg, wParam, lParam);
215
216 HDC hDC = GetDC();
217 if (hDC)
218 {
219 DrawGrip(hDC, rc);
220 ReleaseDC(hDC);
221 }
222
223 return 0;
224 }
225
226 LRESULT CTextEditWindow::OnNCPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
227 {
228 RECT rc;
229 GetWindowRect(&rc);
230
231 HDC hDC = GetDCEx(NULL, DCX_WINDOW | DCX_PARENTCLIP);
232 if (hDC)
233 {
234 OffsetRect(&rc, -rc.left, -rc.top);
235 DrawGrip(hDC, rc);
236 ReleaseDC(hDC);
237 }
238
239 return 0;
240 }
241
242 LRESULT CTextEditWindow::OnNCCalcSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
243 {
244 return 0; // No frame.
245 }
246
247 LRESULT CTextEditWindow::OnNCHitTest(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
248 {
249 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
250 RECT rc;
251 GetWindowRect(&rc);
252 return DoHitTest(rc, pt);
253 }
254
255 LRESULT CTextEditWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
256 {
257 UINT nHitTest = LOWORD(lParam);
258 if (nHitTest == HTCAPTION)
259 {
260 SetCursor(LoadCursor(NULL, IDC_SIZEALL));
261 return FALSE;
262 }
263 return DefWindowProc(nMsg, wParam, lParam);
264 }
265
266 LRESULT CTextEditWindow::OnMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
267 {
268 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
269
270 if (m_nAppIsMovingOrSizing == 0)
271 {
272 Reposition();
273 InvalidateEditRect();
274 }
275 return ret;
276 }
277
278 LRESULT CTextEditWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
279 {
280 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
281
282 RECT rc;
283 GetClientRect(&rc);
284 SendMessage(EM_SETRECTNP, 0, (LPARAM)&rc);
285 SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
286
287 if (m_nAppIsMovingOrSizing == 0)
288 {
289 Reposition();
290 InvalidateEditRect();
291 }
292
293 return ret;
294 }
295
296 // Hack: Use DECLARE_WND_SUPERCLASS instead!
297 HWND CTextEditWindow::Create(HWND hwndParent)
298 {
299 m_hwndParent = hwndParent;
300
301 const DWORD style = ES_LEFT | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL |
302 WS_CHILD | WS_THICKFRAME;
303 HWND hwnd = ::CreateWindowEx(0, WC_EDIT, NULL, style, 0, 0, 0, 0,
304 hwndParent, NULL, hProgInstance, NULL);
305 if (hwnd)
306 {
307 #undef SubclassWindow // Don't use this macro
308 SubclassWindow(hwnd);
309
310 UpdateFont();
311
312 PostMessage(WM_SIZE, 0, 0);
313 }
314
315 return m_hWnd;
316 }
317
318 void CTextEditWindow::DoFillBack(HWND hwnd, HDC hDC)
319 {
320 if (toolsModel.IsBackgroundTransparent())
321 return;
322
323 RECT rc;
324 SendMessage(EM_GETRECT, 0, (LPARAM)&rc);
325 MapWindowPoints(hwnd, (LPPOINT)&rc, 2);
326
327 HBRUSH hbr = CreateSolidBrush(paletteModel.GetBgColor());
328 FillRect(hDC, &rc, hbr);
329 DeleteObject(hbr);
330 }
331
332 LRESULT CTextEditWindow::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
333 {
334 UpdateFont();
335 return 0;
336 }
337
338 LRESULT CTextEditWindow::OnClose(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
339 {
340 ShowWindow(SW_HIDE);
341 if (m_hFont)
342 {
343 DeleteObject(m_hFont);
344 m_hFont = NULL;
345 }
346 if (m_hFontZoomed)
347 {
348 DeleteObject(m_hFontZoomed);
349 m_hFontZoomed = NULL;
350 }
351 return 0;
352 }
353
354 void CTextEditWindow::InvalidateEditRect()
355 {
356 RECT rc;
357 GetWindowRect(&rc);
358 ::MapWindowPoints(NULL, m_hwndParent, (LPPOINT)&rc, 2);
359 ::InvalidateRect(m_hwndParent, &rc, TRUE);
360
361 GetClientRect(&rc);
362 MapWindowPoints(imageArea, (LPPOINT)&rc, 2);
363 rc.left = UnZoomed(rc.left);
364 rc.top = UnZoomed(rc.top);
365 rc.right = UnZoomed(rc.right);
366 rc.bottom = UnZoomed(rc.bottom);
367 m_rc = rc;
368 }
369
370 LRESULT CTextEditWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
371 {
372 UpdateFont();
373 return 0;
374 }
375
376 LRESULT CTextEditWindow::OnToolsModelSettingsChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
377 {
378 UpdateFont();
379 return 0;
380 }
381
382 LRESULT CTextEditWindow::OnToolsModelZoomChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
383 {
384 UpdateFont();
385 ValidateEditRect(NULL);
386 return 0;
387 }
388
389 LRESULT CTextEditWindow::OnToolsModelToolChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
390 {
391 if (wParam == TOOL_TEXT)
392 {
393 UpdateFont();
394 }
395 else
396 {
397 ShowWindow(SW_HIDE);
398 }
399 return 0;
400 }
401
402 void CTextEditWindow::UpdateFont()
403 {
404 if (m_hFont)
405 {
406 DeleteObject(m_hFont);
407 m_hFont = NULL;
408 }
409 if (m_hFontZoomed)
410 {
411 DeleteObject(m_hFontZoomed);
412 m_hFontZoomed = NULL;
413 }
414
415 LOGFONT lf;
416 ZeroMemory(&lf, sizeof(lf));
417 lf.lfCharSet = DEFAULT_CHARSET; // registrySettings.CharSet; // Ignore
418 lf.lfWeight = (registrySettings.Bold ? FW_BOLD : FW_NORMAL);
419 lf.lfItalic = registrySettings.Italic;
420 lf.lfUnderline = registrySettings.Underline;
421 lstrcpyn(lf.lfFaceName, registrySettings.strFontName, _countof(lf.lfFaceName));
422
423 HDC hdc = GetDC();
424 if (hdc)
425 {
426 INT nFontSize = registrySettings.PointSize;
427 lf.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
428 ReleaseDC(hdc);
429 }
430
431 m_hFont = ::CreateFontIndirect(&lf);
432
433 lf.lfHeight = Zoomed(lf.lfHeight);
434 m_hFontZoomed = ::CreateFontIndirect(&lf);
435
436 SetWindowFont(m_hWnd, m_hFontZoomed, TRUE);
437 DefWindowProc(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
438
439 FixEditPos(NULL);
440
441 Invalidate();
442 }
443
444 LRESULT CTextEditWindow::OnSetSel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
445 {
446 LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
447 DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
448 DefWindowProc(WM_VSCROLL, SB_TOP, 0);
449 InvalidateEditRect();
450 return ret;
451 }
452
453 BOOL CTextEditWindow::GetEditRect(LPRECT prc) const
454 {
455 *prc = m_rc;
456 return TRUE;
457 }
458
459 void CTextEditWindow::ValidateEditRect(LPCRECT prc OPTIONAL)
460 {
461 if (prc)
462 m_rc = *prc;
463 INT x0 = Zoomed(m_rc.left), y0 = Zoomed(m_rc.top);
464 INT x1 = Zoomed(m_rc.right), y1 = Zoomed(m_rc.bottom);
465
466 ++m_nAppIsMovingOrSizing;
467 MoveWindow(x0, y0, x1 - x0, y1 - y0, TRUE);
468 --m_nAppIsMovingOrSizing;
469 }
470
471 void CTextEditWindow::Reposition()
472 {
473 RECT rc, rcImage;
474 GetWindowRect(&rc);
475
476 ::MapWindowPoints(NULL, imageArea, (LPPOINT)&rc, 2);
477 imageArea.GetClientRect(&rcImage);
478
479 if (rc.bottom > rcImage.bottom)
480 {
481 rc.top = rcImage.bottom - (rc.bottom - rc.top);
482 rc.bottom = rcImage.bottom;
483 }
484
485 if (rc.right > rcImage.right)
486 {
487 rc.left = rcImage.right - (rc.right - rc.left);
488 rc.right = rcImage.right;
489 }
490
491 if (rc.left < 0)
492 {
493 rc.right += -rc.left;
494 rc.left = 0;
495 }
496
497 if (rc.top < 0)
498 {
499 rc.bottom += -rc.top;
500 rc.top = 0;
501 }
502
503 ++m_nAppIsMovingOrSizing;
504 MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
505 --m_nAppIsMovingOrSizing;
506 }
507
508 LRESULT CTextEditWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
509 {
510 return ::SendMessage(GetParent(), nMsg, wParam, lParam);
511 }