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