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 if (CWaitCursor::IsWaiting()) 191 { 192 bHandled = FALSE; 193 return 0; 194 } 195 196 UINT nHitTest = LOWORD(lParam); 197 if (nHitTest == HTCAPTION) 198 { 199 ::SetCursor(::LoadCursor(NULL, IDC_SIZEALL)); // Enable drag move 200 return FALSE; 201 } 202 return DefWindowProc(nMsg, wParam, lParam); 203 } 204 205 LRESULT CTextEditWindow::OnMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 206 { 207 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 208 return ret; 209 } 210 211 LRESULT CTextEditWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 212 { 213 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 214 215 RECT rc; 216 GetClientRect(&rc); 217 SendMessage(EM_SETRECTNP, 0, (LPARAM)&rc); 218 SendMessage(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0)); 219 220 return ret; 221 } 222 223 // Hack: Use DECLARE_WND_SUPERCLASS instead! 224 HWND CTextEditWindow::Create(HWND hwndParent) 225 { 226 m_hwndParent = hwndParent; 227 228 const DWORD style = ES_LEFT | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL | 229 WS_CHILD | WS_THICKFRAME; 230 HWND hwnd = ::CreateWindowEx(0, WC_EDIT, NULL, style, 0, 0, 0, 0, 231 hwndParent, NULL, g_hinstExe, NULL); 232 if (hwnd) 233 { 234 #undef SubclassWindow // Don't use this macro 235 SubclassWindow(hwnd); 236 237 UpdateFont(); 238 239 PostMessage(WM_SIZE, 0, 0); 240 } 241 242 return m_hWnd; 243 } 244 245 void CTextEditWindow::DoFillBack(HWND hwnd, HDC hDC) 246 { 247 if (toolsModel.IsBackgroundTransparent()) 248 return; 249 250 RECT rc; 251 SendMessage(EM_GETRECT, 0, (LPARAM)&rc); 252 MapWindowPoints(hwnd, (LPPOINT)&rc, 2); 253 254 HBRUSH hbr = CreateSolidBrush(paletteModel.GetBgColor()); 255 FillRect(hDC, &rc, hbr); 256 DeleteObject(hbr); 257 } 258 259 LRESULT CTextEditWindow::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 260 { 261 UpdateFont(); 262 return 0; 263 } 264 265 LRESULT CTextEditWindow::OnClose(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 266 { 267 ShowWindow(SW_HIDE); 268 if (m_hFont) 269 { 270 DeleteObject(m_hFont); 271 m_hFont = NULL; 272 } 273 if (m_hFontZoomed) 274 { 275 DeleteObject(m_hFontZoomed); 276 m_hFontZoomed = NULL; 277 } 278 return 0; 279 } 280 281 void CTextEditWindow::InvalidateEditRect() 282 { 283 RECT rc; 284 GetWindowRect(&rc); 285 ::MapWindowPoints(NULL, m_hwndParent, (LPPOINT)&rc, 2); 286 ::InvalidateRect(m_hwndParent, &rc, TRUE); 287 288 GetClientRect(&rc); 289 MapWindowPoints(canvasWindow, (LPPOINT)&rc, 2); 290 canvasWindow.CanvasToImage(rc); 291 m_rc = rc; 292 } 293 294 LRESULT CTextEditWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 295 { 296 UpdateFont(); 297 return 0; 298 } 299 300 LRESULT CTextEditWindow::OnToolsModelSettingsChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 301 { 302 UpdateFont(); 303 return 0; 304 } 305 306 LRESULT CTextEditWindow::OnToolsModelZoomChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 307 { 308 UpdateFont(); 309 ValidateEditRect(NULL); 310 return 0; 311 } 312 313 LRESULT CTextEditWindow::OnToolsModelToolChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 314 { 315 if (wParam == TOOL_TEXT) 316 { 317 UpdateFont(); 318 } 319 else 320 { 321 ShowWindow(SW_HIDE); 322 } 323 return 0; 324 } 325 326 void CTextEditWindow::UpdateFont() 327 { 328 if (m_hFont) 329 { 330 DeleteObject(m_hFont); 331 m_hFont = NULL; 332 } 333 if (m_hFontZoomed) 334 { 335 DeleteObject(m_hFontZoomed); 336 m_hFontZoomed = NULL; 337 } 338 339 LOGFONT lf; 340 ZeroMemory(&lf, sizeof(lf)); 341 lf.lfCharSet = DEFAULT_CHARSET; // registrySettings.CharSet; // Ignore 342 lf.lfWeight = (registrySettings.Bold ? FW_BOLD : FW_NORMAL); 343 lf.lfItalic = registrySettings.Italic; 344 lf.lfUnderline = registrySettings.Underline; 345 lstrcpyn(lf.lfFaceName, registrySettings.strFontName, _countof(lf.lfFaceName)); 346 347 HDC hdc = GetDC(); 348 if (hdc) 349 { 350 INT nFontSize = registrySettings.PointSize; 351 lf.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72); 352 ReleaseDC(hdc); 353 } 354 355 m_hFont = ::CreateFontIndirect(&lf); 356 357 lf.lfHeight = Zoomed(lf.lfHeight); 358 m_hFontZoomed = ::CreateFontIndirect(&lf); 359 360 SetWindowFont(m_hWnd, m_hFontZoomed, TRUE); 361 DefWindowProc(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0)); 362 363 FixEditPos(NULL); 364 365 Invalidate(); 366 } 367 368 LRESULT CTextEditWindow::OnSetSel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 369 { 370 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 371 DefWindowProc(WM_HSCROLL, SB_LEFT, 0); 372 DefWindowProc(WM_VSCROLL, SB_TOP, 0); 373 InvalidateEditRect(); 374 return ret; 375 } 376 377 BOOL CTextEditWindow::GetEditRect(LPRECT prc) const 378 { 379 *prc = m_rc; 380 return TRUE; 381 } 382 383 void CTextEditWindow::ValidateEditRect(LPCRECT prc OPTIONAL) 384 { 385 if (prc) 386 m_rc = *prc; 387 388 CRect rc = m_rc; 389 canvasWindow.ImageToCanvas(rc); 390 391 MoveWindow(rc.left, rc.top, rc.Width(), rc.Height(), TRUE); 392 } 393 394 LRESULT CTextEditWindow::OnMoving(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 395 { 396 // Restrict the window position to the image area 397 LPRECT prcMoving = (LPRECT)lParam; 398 CRect rcMoving = *prcMoving; 399 400 CRect rcImage; 401 canvasWindow.GetImageRect(rcImage); 402 canvasWindow.ImageToCanvas(rcImage); 403 canvasWindow.MapWindowPoints(NULL, &rcImage); 404 405 CRect rcWnd; 406 GetWindowRect(&rcWnd); 407 INT cx = rcWnd.Width(), cy = rcWnd.Height(); 408 409 if (rcMoving.left < rcImage.left) 410 { 411 rcMoving.left = rcImage.left; 412 rcMoving.right = rcImage.left + cx; 413 } 414 else if (rcMoving.right > rcImage.right) 415 { 416 rcMoving.right = rcImage.right; 417 rcMoving.left = rcImage.right - cx; 418 } 419 420 if (rcMoving.top < rcImage.top) 421 { 422 rcMoving.top = rcImage.top; 423 rcMoving.bottom = rcImage.top + cy; 424 } 425 else if (rcMoving.bottom > rcImage.bottom) 426 { 427 rcMoving.bottom = rcImage.bottom; 428 rcMoving.top = rcImage.bottom - cy; 429 } 430 431 *prcMoving = rcMoving; 432 Invalidate(TRUE); 433 return TRUE; 434 } 435 436 LRESULT CTextEditWindow::OnSizing(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 437 { 438 // Restrict the window size to the image area 439 LPRECT prcSizing = (LPRECT)lParam; 440 CRect rcSizing = *prcSizing; 441 442 CRect rcImage; 443 canvasWindow.GetImageRect(rcImage); 444 canvasWindow.ImageToCanvas(rcImage); 445 canvasWindow.MapWindowPoints(NULL, &rcImage); 446 447 // Horizontally 448 switch (wParam) 449 { 450 case WMSZ_BOTTOMLEFT: 451 case WMSZ_LEFT: 452 case WMSZ_TOPLEFT: 453 if (rcSizing.left < rcImage.left) 454 rcSizing.left = rcImage.left; 455 break; 456 case WMSZ_BOTTOMRIGHT: 457 case WMSZ_RIGHT: 458 case WMSZ_TOPRIGHT: 459 if (rcSizing.right > rcImage.right) 460 rcSizing.right = rcImage.right; 461 break; 462 case WMSZ_TOP: 463 case WMSZ_BOTTOM: 464 default: 465 break; 466 } 467 468 // Vertically 469 switch (wParam) 470 { 471 case WMSZ_BOTTOM: 472 case WMSZ_BOTTOMLEFT: 473 case WMSZ_BOTTOMRIGHT: 474 if (rcSizing.bottom > rcImage.bottom) 475 rcSizing.bottom = rcImage.bottom; 476 break; 477 case WMSZ_TOP: 478 case WMSZ_TOPLEFT: 479 case WMSZ_TOPRIGHT: 480 if (rcSizing.top < rcImage.top) 481 rcSizing.top = rcImage.top; 482 break; 483 case WMSZ_LEFT: 484 case WMSZ_RIGHT: 485 default: 486 break; 487 } 488 489 *prcSizing = rcSizing; 490 Invalidate(TRUE); 491 return TRUE; 492 } 493 494 LRESULT CTextEditWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 495 { 496 return ::SendMessage(GetParent(), nMsg, wParam, lParam); 497 } 498 499 LRESULT CTextEditWindow::OnCut(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 500 { 501 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 502 Invalidate(TRUE); // Redraw 503 return ret; 504 } 505 506 LRESULT CTextEditWindow::OnPaste(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 507 { 508 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 509 FixEditPos(NULL); 510 return ret; 511 } 512 513 LRESULT CTextEditWindow::OnClear(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 514 { 515 LRESULT ret = DefWindowProc(nMsg, wParam, lParam); 516 Invalidate(TRUE); // Redraw 517 return ret; 518 } 519