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