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 #include "precomp.h"
10 
11 #define CXY_GRIP 3
12 
13 CTextEditWindow textEditWindow;
14 
15 /* FUNCTIONS ********************************************************/
16 
17 CTextEditWindow::CTextEditWindow() : m_hFont(NULL), m_hFontZoomed(NULL), m_nAppIsMovingOrSizing(0)
18 {
19     SetRectEmpty(&m_rc);
20 }
21 
22 INT CTextEditWindow::DoHitTest(RECT& rc, POINT pt)
23 {
24     switch (getSizeBoxHitTest(pt, &rc))
25     {
26         case HIT_NONE:          return HTNOWHERE;
27         case HIT_UPPER_LEFT:    return HTTOPLEFT;
28         case HIT_UPPER_CENTER:  return HTTOP;
29         case HIT_UPPER_RIGHT:   return HTTOPRIGHT;
30         case HIT_MIDDLE_LEFT:   return HTLEFT;
31         case HIT_MIDDLE_RIGHT:  return HTRIGHT;
32         case HIT_LOWER_LEFT:    return HTBOTTOMLEFT;
33         case HIT_LOWER_CENTER:  return HTBOTTOM;
34         case HIT_LOWER_RIGHT:   return HTBOTTOMRIGHT;
35         case HIT_BORDER:        return HTCAPTION; // Enable drag move
36         case HIT_INNER:         return HTCLIENT;
37     }
38     return HTNOWHERE;
39 }
40 
41 void CTextEditWindow::DrawGrip(HDC hDC, RECT& rc)
42 {
43     drawSizeBoxes(hDC, &rc, TRUE, NULL);
44 }
45 
46 void CTextEditWindow::FixEditPos(LPCTSTR pszOldText)
47 {
48     CString szText;
49     GetWindowText(szText);
50 
51     RECT rcParent;
52     ::GetWindowRect(m_hwndParent, &rcParent);
53 
54     RECT rc, rcWnd, rcText;
55     GetWindowRect(&rcWnd);
56     rcText = rcWnd;
57 
58     HDC hDC = GetDC();
59     if (hDC)
60     {
61         SelectObject(hDC, m_hFontZoomed);
62         TEXTMETRIC tm;
63         GetTextMetrics(hDC, &tm);
64         szText += TEXT("x"); // This is a trick to enable the last newlines
65         const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP |
66                              DT_EXPANDTABS | DT_WORDBREAK;
67         DrawText(hDC, szText, -1, &rcText, uFormat | DT_CALCRECT);
68         if (tm.tmDescent > 0)
69             rcText.bottom += tm.tmDescent;
70         ReleaseDC(hDC);
71     }
72 
73     UnionRect(&rc, &rcText, &rcWnd);
74     ::MapWindowPoints(NULL, m_hwndParent, (LPPOINT)&rc, 2);
75 
76     rcWnd = rc;
77     ::GetClientRect(m_hwndParent, &rcParent);
78     IntersectRect(&rc, &rcParent, &rcWnd);
79 
80     ++m_nAppIsMovingOrSizing;
81     MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
82     --m_nAppIsMovingOrSizing;
83 
84     DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
85     DefWindowProc(WM_VSCROLL, SB_TOP, 0);
86 
87     ::InvalidateRect(m_hwndParent, &rc, TRUE);
88 }
89 
90 LRESULT CTextEditWindow::OnChar(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
91 {
92     if (wParam == VK_TAB)
93         return 0; // FIXME: Tabs
94 
95     CString szText;
96     GetWindowText(szText);
97 
98     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
99     FixEditPos(szText);
100 
101     return ret;
102 }
103 
104 LRESULT CTextEditWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
105 {
106     if (wParam == VK_ESCAPE)
107     {
108         toolsModel.OnCancelDraw();
109         return 0;
110     }
111 
112     CString szText;
113     GetWindowText(szText);
114 
115     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
116     FixEditPos(szText);
117     return ret;
118 }
119 
120 LRESULT CTextEditWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
121 {
122     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
123     DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
124     DefWindowProc(WM_VSCROLL, SB_TOP, 0);
125     return ret;
126 }
127 
128 LRESULT CTextEditWindow::OnEraseBkGnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
129 {
130     HDC hDC = (HDC)wParam;
131     if (!toolsModel.IsBackgroundTransparent())
132     {
133         RECT rc;
134         GetClientRect(&rc);
135         HBRUSH hbr = CreateSolidBrush(paletteModel.GetBgColor());
136         FillRect(hDC, &rc, hbr);
137         DeleteObject(hbr);
138     }
139     SetTextColor(hDC, paletteModel.GetFgColor());
140     return TRUE;
141 }
142 
143 LRESULT CTextEditWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
144 {
145     RECT rc;
146     GetClientRect(&rc);
147 
148     DefWindowProc(nMsg, wParam, lParam);
149 
150     HDC hDC = GetDC();
151     if (hDC)
152     {
153         DrawGrip(hDC, rc);
154         ReleaseDC(hDC);
155     }
156 
157     return 0;
158 }
159 
160 LRESULT CTextEditWindow::OnNCPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
161 {
162     RECT rc;
163     GetWindowRect(&rc);
164 
165     HDC hDC = GetDCEx(NULL, DCX_WINDOW | DCX_PARENTCLIP);
166     if (hDC)
167     {
168         OffsetRect(&rc, -rc.left, -rc.top);
169         DrawGrip(hDC, rc);
170         ReleaseDC(hDC);
171     }
172 
173     return 0;
174 }
175 
176 LRESULT CTextEditWindow::OnNCCalcSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
177 {
178     return 0; // No frame.
179 }
180 
181 LRESULT CTextEditWindow::OnNCHitTest(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
182 {
183     POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
184     RECT rc;
185     GetWindowRect(&rc);
186     return DoHitTest(rc, pt);
187 }
188 
189 LRESULT CTextEditWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
190 {
191     UINT nHitTest = LOWORD(lParam);
192     if (nHitTest == HTCAPTION)
193     {
194         ::SetCursor(::LoadCursor(NULL, IDC_SIZEALL)); // Enable drag move
195         return FALSE;
196     }
197     return DefWindowProc(nMsg, wParam, lParam);
198 }
199 
200 LRESULT CTextEditWindow::OnMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
201 {
202     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
203 
204     if (m_nAppIsMovingOrSizing == 0)
205     {
206         Reposition();
207         InvalidateEditRect();
208     }
209     return ret;
210 }
211 
212 LRESULT CTextEditWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
213 {
214     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
215 
216     RECT rc;
217     GetClientRect(&rc);
218     SendMessage(EM_SETRECTNP, 0, (LPARAM)&rc);
219     SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
220 
221     if (m_nAppIsMovingOrSizing == 0)
222     {
223         Reposition();
224         InvalidateEditRect();
225     }
226 
227     return ret;
228 }
229 
230 // Hack: Use DECLARE_WND_SUPERCLASS instead!
231 HWND CTextEditWindow::Create(HWND hwndParent)
232 {
233     m_hwndParent = hwndParent;
234 
235     const DWORD style = ES_LEFT | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL |
236                         WS_CHILD | WS_THICKFRAME;
237     HWND hwnd = ::CreateWindowEx(0, WC_EDIT, NULL, style, 0, 0, 0, 0,
238                                  hwndParent, NULL, hProgInstance, NULL);
239     if (hwnd)
240     {
241 #undef SubclassWindow // Don't use this macro
242         SubclassWindow(hwnd);
243 
244         UpdateFont();
245 
246         PostMessage(WM_SIZE, 0, 0);
247     }
248 
249     return m_hWnd;
250 }
251 
252 void CTextEditWindow::DoFillBack(HWND hwnd, HDC hDC)
253 {
254     if (toolsModel.IsBackgroundTransparent())
255         return;
256 
257     RECT rc;
258     SendMessage(EM_GETRECT, 0, (LPARAM)&rc);
259     MapWindowPoints(hwnd, (LPPOINT)&rc, 2);
260 
261     HBRUSH hbr = CreateSolidBrush(paletteModel.GetBgColor());
262     FillRect(hDC, &rc, hbr);
263     DeleteObject(hbr);
264 }
265 
266 LRESULT CTextEditWindow::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
267 {
268     UpdateFont();
269     return 0;
270 }
271 
272 LRESULT CTextEditWindow::OnClose(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
273 {
274     ShowWindow(SW_HIDE);
275     if (m_hFont)
276     {
277         DeleteObject(m_hFont);
278         m_hFont = NULL;
279     }
280     if (m_hFontZoomed)
281     {
282         DeleteObject(m_hFontZoomed);
283         m_hFontZoomed = NULL;
284     }
285     return 0;
286 }
287 
288 void CTextEditWindow::InvalidateEditRect()
289 {
290     RECT rc;
291     GetWindowRect(&rc);
292     ::MapWindowPoints(NULL, m_hwndParent, (LPPOINT)&rc, 2);
293     ::InvalidateRect(m_hwndParent, &rc, TRUE);
294 
295     GetClientRect(&rc);
296     MapWindowPoints(imageArea, (LPPOINT)&rc, 2);
297     rc.left = UnZoomed(rc.left);
298     rc.top = UnZoomed(rc.top);
299     rc.right = UnZoomed(rc.right);
300     rc.bottom = UnZoomed(rc.bottom);
301     m_rc = rc;
302 }
303 
304 LRESULT CTextEditWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
305 {
306     UpdateFont();
307     return 0;
308 }
309 
310 LRESULT CTextEditWindow::OnToolsModelSettingsChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
311 {
312     UpdateFont();
313     return 0;
314 }
315 
316 LRESULT CTextEditWindow::OnToolsModelZoomChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
317 {
318     UpdateFont();
319     ValidateEditRect(NULL);
320     return 0;
321 }
322 
323 LRESULT CTextEditWindow::OnToolsModelToolChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
324 {
325     if (wParam == TOOL_TEXT)
326     {
327         UpdateFont();
328     }
329     else
330     {
331         ShowWindow(SW_HIDE);
332     }
333     return 0;
334 }
335 
336 void CTextEditWindow::UpdateFont()
337 {
338     if (m_hFont)
339     {
340         DeleteObject(m_hFont);
341         m_hFont = NULL;
342     }
343     if (m_hFontZoomed)
344     {
345         DeleteObject(m_hFontZoomed);
346         m_hFontZoomed = NULL;
347     }
348 
349     LOGFONT lf;
350     ZeroMemory(&lf, sizeof(lf));
351     lf.lfCharSet = DEFAULT_CHARSET; // registrySettings.CharSet; // Ignore
352     lf.lfWeight = (registrySettings.Bold ? FW_BOLD : FW_NORMAL);
353     lf.lfItalic = registrySettings.Italic;
354     lf.lfUnderline = registrySettings.Underline;
355     lstrcpyn(lf.lfFaceName, registrySettings.strFontName, _countof(lf.lfFaceName));
356 
357     HDC hdc = GetDC();
358     if (hdc)
359     {
360         INT nFontSize = registrySettings.PointSize;
361         lf.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
362         ReleaseDC(hdc);
363     }
364 
365     m_hFont = ::CreateFontIndirect(&lf);
366 
367     lf.lfHeight = Zoomed(lf.lfHeight);
368     m_hFontZoomed = ::CreateFontIndirect(&lf);
369 
370     SetWindowFont(m_hWnd, m_hFontZoomed, TRUE);
371     DefWindowProc(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
372 
373     FixEditPos(NULL);
374 
375     Invalidate();
376 }
377 
378 LRESULT CTextEditWindow::OnSetSel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
379 {
380     LRESULT ret = DefWindowProc(nMsg, wParam, lParam);
381     DefWindowProc(WM_HSCROLL, SB_LEFT, 0);
382     DefWindowProc(WM_VSCROLL, SB_TOP, 0);
383     InvalidateEditRect();
384     return ret;
385 }
386 
387 BOOL CTextEditWindow::GetEditRect(LPRECT prc) const
388 {
389     *prc = m_rc;
390     return TRUE;
391 }
392 
393 void CTextEditWindow::ValidateEditRect(LPCRECT prc OPTIONAL)
394 {
395     if (prc)
396         m_rc = *prc;
397     INT x0 = Zoomed(m_rc.left), y0 = Zoomed(m_rc.top);
398     INT x1 = Zoomed(m_rc.right), y1 = Zoomed(m_rc.bottom);
399 
400     ++m_nAppIsMovingOrSizing;
401     MoveWindow(x0, y0, x1 - x0, y1 - y0, TRUE);
402     --m_nAppIsMovingOrSizing;
403 }
404 
405 void CTextEditWindow::Reposition()
406 {
407     RECT rc, rcImage;
408     GetWindowRect(&rc);
409 
410     ::MapWindowPoints(NULL, imageArea, (LPPOINT)&rc, 2);
411     imageArea.GetClientRect(&rcImage);
412 
413     if (rc.bottom > rcImage.bottom)
414         ::OffsetRect(&rc, 0, rcImage.bottom - rc.bottom);
415 
416     if (rc.right > rcImage.right)
417         ::OffsetRect(&rc, rcImage.right - rc.right, 0);
418 
419     if (rc.left < 0)
420         ::OffsetRect(&rc, -rc.left, 0);
421 
422     if (rc.top < 0)
423         ::OffsetRect(&rc, 0, -rc.top);
424 
425     ++m_nAppIsMovingOrSizing;
426     MoveWindow(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
427     --m_nAppIsMovingOrSizing;
428 }
429 
430 LRESULT CTextEditWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
431 {
432     return ::SendMessage(GetParent(), nMsg, wParam, lParam);
433 }
434