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 } 512