1 /* 2 * PROJECT: ReactOS Character Map 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/applications/charmap/map.c 5 * PURPOSE: class implementation for painting glyph region 6 * COPYRIGHT: Copyright 2007 Ged Murphy <gedmurphy@reactos.org> 7 * 8 */ 9 10 #include "precomp.h" 11 12 #include <stdlib.h> 13 14 static const WCHAR szMapWndClass[] = L"FontMapWnd"; 15 static const WCHAR szLrgCellWndClass[] = L"LrgCellWnd"; 16 17 #define MAX_ROWS (0xFFFF / XCELLS) + 1 - YCELLS 18 19 20 static 21 VOID 22 SetGrid(PMAP infoPtr) 23 { 24 INT x, y; 25 26 for (y = 0; y < YCELLS; y++) 27 for (x = 0; x < XCELLS; x++) 28 { 29 infoPtr->Cells[y][x].CellExt.left = x * infoPtr->CellSize.cx + 1; 30 infoPtr->Cells[y][x].CellExt.top = y * infoPtr->CellSize.cy + 1; 31 infoPtr->Cells[y][x].CellExt.right = (x + 1) * infoPtr->CellSize.cx + 2; 32 infoPtr->Cells[y][x].CellExt.bottom = (y + 1) * infoPtr->CellSize.cy + 2; 33 34 CopyRect(&infoPtr->Cells[y][x].CellInt, 35 &infoPtr->Cells[y][x].CellExt); 36 37 InflateRect(&infoPtr->Cells[y][x].CellInt, 38 -1, 39 -1); 40 } 41 } 42 43 static 44 VOID 45 DrawActiveCell(PMAP infoPtr, 46 HDC hdc) 47 { 48 Rectangle(hdc, 49 infoPtr->pActiveCell->CellInt.left, 50 infoPtr->pActiveCell->CellInt.top, 51 infoPtr->pActiveCell->CellInt.right, 52 infoPtr->pActiveCell->CellInt.bottom); 53 54 } 55 56 57 static 58 VOID 59 DrawGrid(PMAP infoPtr, 60 PAINTSTRUCT *ps) 61 { 62 INT x, y; 63 RECT rc; 64 PCELL Cell; 65 66 for (y = 0; y < YCELLS; y++) 67 for (x = 0; x < XCELLS; x++) 68 { 69 Cell = &infoPtr->Cells[y][x]; 70 71 if (!IntersectRect(&rc, 72 &ps->rcPaint, 73 &Cell->CellExt)) 74 { 75 continue; 76 } 77 78 Rectangle(ps->hdc, 79 Cell->CellExt.left, 80 Cell->CellExt.top, 81 Cell->CellExt.right, 82 Cell->CellExt.bottom); 83 84 if (infoPtr->pActiveCell == Cell) 85 { 86 DrawActiveCell(infoPtr, ps->hdc); 87 } 88 } 89 } 90 91 92 static 93 VOID 94 FillGrid(PMAP infoPtr, 95 PAINTSTRUCT *ps) 96 { 97 HFONT hOldFont; 98 WCHAR ch; 99 INT x, y; 100 RECT rc; 101 PCELL Cell; 102 INT i, added; 103 104 hOldFont = SelectObject(ps->hdc, 105 infoPtr->hFont); 106 107 i = XCELLS * infoPtr->iYStart; 108 109 added = 0; 110 111 for (y = 0; y < YCELLS; y++) 112 for (x = 0; x < XCELLS; x++) 113 { 114 ch = (WCHAR)infoPtr->ValidGlyphs[i]; 115 116 Cell = &infoPtr->Cells[y][x]; 117 118 if (IntersectRect(&rc, 119 &ps->rcPaint, 120 &Cell->CellExt)) 121 { 122 Cell->ch = ch; 123 124 DrawTextW(ps->hdc, 125 &ch, 126 1, 127 &Cell->CellInt, 128 DT_CENTER | DT_VCENTER | DT_SINGLELINE); 129 130 added++; 131 } 132 133 i++; 134 ch = (WCHAR)i; 135 } 136 SelectObject(ps->hdc, 137 hOldFont); 138 } 139 140 141 static 142 BOOL 143 CreateLargeCell(PMAP infoPtr) 144 { 145 RECT rLarge; 146 147 CopyRect(&rLarge, 148 &infoPtr->pActiveCell->CellExt); 149 150 MapWindowPoints(infoPtr->hMapWnd, 151 infoPtr->hParent, 152 (VOID*)&rLarge, 153 2); 154 155 InflateRect(&rLarge, 156 XLARGE - XCELLS, 157 YLARGE - YCELLS); 158 159 infoPtr->hLrgWnd = CreateWindowExW(0, 160 szLrgCellWndClass, 161 NULL, 162 WS_CHILDWINDOW | WS_VISIBLE, 163 rLarge.left, 164 rLarge.top, 165 rLarge.right - rLarge.left, 166 rLarge.bottom - rLarge.top, 167 infoPtr->hParent, 168 NULL, 169 hInstance, 170 infoPtr); 171 if (!infoPtr->hLrgWnd) 172 return FALSE; 173 174 return TRUE; 175 } 176 177 178 static 179 VOID 180 MoveLargeCell(PMAP infoPtr) 181 { 182 RECT rLarge; 183 184 CopyRect(&rLarge, 185 &infoPtr->pActiveCell->CellExt); 186 187 MapWindowPoints(infoPtr->hMapWnd, 188 infoPtr->hParent, 189 (VOID*)&rLarge, 190 2); 191 192 InflateRect(&rLarge, 193 XLARGE - XCELLS, 194 YLARGE - YCELLS); 195 196 MoveWindow(infoPtr->hLrgWnd, 197 rLarge.left, 198 rLarge.top, 199 rLarge.right - rLarge.left, 200 rLarge.bottom - rLarge.top, 201 TRUE); 202 203 InvalidateRect(infoPtr->hLrgWnd, 204 NULL, 205 TRUE); 206 } 207 208 209 static 210 VOID 211 SetFont(PMAP infoPtr, 212 LPWSTR lpFontName) 213 { 214 HDC hdc; 215 WCHAR ch[MAX_GLYPHS]; 216 WORD out[MAX_GLYPHS]; 217 DWORD i, j; 218 219 /* Destroy Zoom window, since it was created with older font */ 220 DestroyWindow(infoPtr->hLrgWnd); 221 infoPtr->hLrgWnd = NULL; 222 223 if (infoPtr->hFont) 224 DeleteObject(infoPtr->hFont); 225 226 ZeroMemory(&infoPtr->CurrentFont, 227 sizeof(LOGFONTW)); 228 229 hdc = GetDC(infoPtr->hMapWnd); 230 infoPtr->CurrentFont.lfHeight = GetDeviceCaps(hdc, LOGPIXELSY) / 5; 231 232 infoPtr->CurrentFont.lfCharSet = DEFAULT_CHARSET; 233 wcsncpy(infoPtr->CurrentFont.lfFaceName, 234 lpFontName, 235 sizeof(infoPtr->CurrentFont.lfFaceName) / sizeof(infoPtr->CurrentFont.lfFaceName[0])); 236 237 infoPtr->hFont = CreateFontIndirectW(&infoPtr->CurrentFont); 238 239 InvalidateRect(infoPtr->hMapWnd, 240 NULL, 241 TRUE); 242 243 infoPtr->pActiveCell = &infoPtr->Cells[0][0]; 244 245 // Get all the valid glyphs in this font 246 247 SelectObject(hdc, infoPtr->hFont); 248 249 for (i = 0; i < MAX_GLYPHS; i++) 250 ch[i] = (WCHAR)i; 251 252 if (GetGlyphIndicesW(hdc, 253 ch, 254 MAX_GLYPHS, 255 out, 256 GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) 257 { 258 j = 0; 259 for (i = ' ' + 1; i < MAX_GLYPHS; i++) 260 { 261 if (out[i] != 0xffff) 262 { 263 infoPtr->ValidGlyphs[j] = ch[i]; 264 j++; 265 } 266 } 267 infoPtr->NumValidGlyphs = j; 268 } 269 270 ReleaseDC(infoPtr->hMapWnd, hdc); 271 272 infoPtr->NumRows = infoPtr->NumValidGlyphs / XCELLS; 273 if (infoPtr->NumValidGlyphs % XCELLS) 274 infoPtr->NumRows += 1; 275 infoPtr->NumRows = (infoPtr->NumRows > YCELLS) ? infoPtr->NumRows - YCELLS : 0; 276 277 SetScrollRange(infoPtr->hMapWnd, SB_VERT, 0, infoPtr->NumRows, FALSE); 278 SetScrollPos(infoPtr->hMapWnd, SB_VERT, 0, TRUE); 279 infoPtr->iYStart = 0; 280 } 281 282 283 static 284 LRESULT 285 NotifyParentOfSelection(PMAP infoPtr, 286 UINT code, 287 WCHAR ch) 288 { 289 LRESULT Ret = 0; 290 291 if (infoPtr->hParent != NULL) 292 { 293 DWORD dwIdc = GetWindowLongPtr(infoPtr->hMapWnd, GWLP_ID); 294 /* 295 * Push directly into the event queue instead of waiting 296 * the parent to be unlocked. 297 * High word of LPARAM is still available for future needs... 298 */ 299 Ret = PostMessage(infoPtr->hParent, 300 WM_COMMAND, 301 MAKELPARAM((WORD)dwIdc, (WORD)code), 302 (LPARAM)LOWORD(ch)); 303 } 304 305 return Ret; 306 } 307 308 309 static 310 VOID 311 OnClick(PMAP infoPtr, 312 WORD ptx, 313 WORD pty) 314 { 315 INT x, y; 316 317 x = ptx / max(1, infoPtr->CellSize.cx); 318 y = pty / max(1, infoPtr->CellSize.cy); 319 320 /* if the cell is not already active */ 321 if (!infoPtr->Cells[y][x].bActive) 322 { 323 /* set previous active cell to inactive */ 324 if (infoPtr->pActiveCell) 325 { 326 /* invalidate normal cells, required when 327 * moving a small active cell via keyboard */ 328 if (!infoPtr->pActiveCell->bLarge) 329 { 330 InvalidateRect(infoPtr->hMapWnd, 331 &infoPtr->pActiveCell->CellInt, 332 TRUE); 333 } 334 335 infoPtr->pActiveCell->bActive = FALSE; 336 infoPtr->pActiveCell->bLarge = FALSE; 337 } 338 339 /* set new cell to active */ 340 infoPtr->pActiveCell = &infoPtr->Cells[y][x]; 341 infoPtr->pActiveCell->bActive = TRUE; 342 infoPtr->pActiveCell->bLarge = TRUE; 343 if (infoPtr->hLrgWnd) 344 MoveLargeCell(infoPtr); 345 else 346 CreateLargeCell(infoPtr); 347 } 348 } 349 350 351 static 352 BOOL 353 MapOnCreate(PMAP infoPtr, 354 HWND hwnd, 355 HWND hParent) 356 { 357 RECT rc; 358 BOOL Ret = FALSE; 359 360 infoPtr = HeapAlloc(GetProcessHeap(), 361 0, 362 sizeof(MAP)); 363 if (infoPtr) 364 { 365 SetLastError(0); 366 SetWindowLongPtrW(hwnd, 367 0, 368 (DWORD_PTR)infoPtr); 369 if (GetLastError() == 0) 370 { 371 ZeroMemory(infoPtr, 372 sizeof(MAP)); 373 374 infoPtr->hMapWnd = hwnd; 375 infoPtr->hParent = hParent; 376 377 GetClientRect(hwnd, &rc); 378 infoPtr->ClientSize.cx = rc.right; 379 infoPtr->ClientSize.cy = rc.bottom; 380 infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS; 381 infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS; 382 383 infoPtr->pActiveCell = NULL; 384 385 SetGrid(infoPtr); 386 387 SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE); 388 389 Ret = TRUE; 390 } 391 } 392 393 return Ret; 394 } 395 396 397 static 398 VOID 399 OnVScroll(PMAP infoPtr, 400 INT Value, 401 INT Pos) 402 { 403 INT iYDiff, iOldYStart = infoPtr->iYStart; 404 405 switch (Value) 406 { 407 case SB_LINEUP: 408 infoPtr->iYStart -= 1; 409 break; 410 411 case SB_LINEDOWN: 412 infoPtr->iYStart += 1; 413 break; 414 415 case SB_PAGEUP: 416 infoPtr->iYStart -= YCELLS; 417 break; 418 419 case SB_PAGEDOWN: 420 infoPtr->iYStart += YCELLS; 421 break; 422 423 case SB_THUMBTRACK: 424 infoPtr->iYStart = Pos; 425 break; 426 427 default: 428 break; 429 } 430 431 infoPtr->iYStart = max(0, infoPtr->iYStart); 432 infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows); 433 434 iYDiff = iOldYStart - infoPtr->iYStart; 435 if (iYDiff) 436 { 437 if (infoPtr->hLrgWnd != NULL) 438 { 439 ShowWindow(infoPtr->hLrgWnd, SW_HIDE); 440 } 441 442 SetScrollPos(infoPtr->hMapWnd, 443 SB_VERT, 444 infoPtr->iYStart, 445 TRUE); 446 447 if (abs(iYDiff) < YCELLS) 448 { 449 RECT rect; 450 GetClientRect(infoPtr->hMapWnd, &rect); 451 rect.top += 2; 452 rect.bottom -= 2; 453 ScrollWindowEx(infoPtr->hMapWnd, 454 0, 455 iYDiff * infoPtr->CellSize.cy, 456 &rect, 457 &rect, 458 NULL, 459 NULL, 460 SW_INVALIDATE); 461 } 462 else 463 { 464 InvalidateRect(infoPtr->hMapWnd, 465 NULL, 466 TRUE); 467 } 468 469 if (infoPtr->hLrgWnd != NULL) 470 { 471 ShowWindow(infoPtr->hLrgWnd, SW_SHOW); 472 } 473 } 474 } 475 476 477 static 478 VOID 479 OnPaint(PMAP infoPtr, 480 WPARAM wParam) 481 { 482 PAINTSTRUCT ps; 483 HDC hdc; 484 485 486 if (wParam != 0) 487 { 488 if (!GetUpdateRect(infoPtr->hMapWnd, 489 &ps.rcPaint, 490 TRUE)) 491 { 492 return; 493 } 494 ps.hdc = (HDC)wParam; 495 } 496 else 497 { 498 hdc = BeginPaint(infoPtr->hMapWnd, 499 &ps); 500 if (hdc == NULL) 501 { 502 return; 503 } 504 } 505 506 DrawGrid(infoPtr, &ps); 507 508 FillGrid(infoPtr, &ps); 509 510 if (wParam == 0) 511 { 512 EndPaint(infoPtr->hMapWnd, 513 &ps); 514 } 515 } 516 517 518 LRESULT 519 CALLBACK 520 MapWndProc(HWND hwnd, 521 UINT uMsg, 522 WPARAM wParam, 523 LPARAM lParam) 524 { 525 PMAP infoPtr; 526 LRESULT Ret = 0; 527 528 infoPtr = (PMAP)GetWindowLongPtrW(hwnd, 529 0); 530 531 switch (uMsg) 532 { 533 case WM_CREATE: 534 { 535 if (!MapOnCreate(infoPtr, 536 hwnd, 537 ((LPCREATESTRUCTW)lParam)->hwndParent)) 538 { 539 return (LRESULT)-1; 540 } 541 542 break; 543 } 544 545 case WM_LBUTTONDOWN: 546 { 547 OnClick(infoPtr, 548 LOWORD(lParam), 549 HIWORD(lParam)); 550 551 break; 552 } 553 554 case WM_MOUSEMOVE: 555 { 556 if (wParam & MK_LBUTTON) 557 { 558 OnClick(infoPtr, 559 LOWORD(lParam), 560 HIWORD(lParam)); 561 } 562 break; 563 } 564 565 case WM_LBUTTONDBLCLK: 566 { 567 NotifyParentOfSelection(infoPtr, 568 FM_SETCHAR, 569 infoPtr->pActiveCell->ch); 570 571 if (infoPtr->pActiveCell->bLarge) 572 { 573 DestroyWindow(infoPtr->hLrgWnd); 574 infoPtr->hLrgWnd = NULL; 575 } 576 577 infoPtr->pActiveCell->bLarge = FALSE; 578 579 break; 580 } 581 582 case WM_VSCROLL: 583 { 584 OnVScroll(infoPtr, 585 LOWORD(wParam), 586 HIWORD(wParam)); 587 588 break; 589 } 590 591 case FM_SETFONT: 592 SetFont(infoPtr, (LPWSTR)lParam); 593 break; 594 595 case FM_GETCHAR: 596 { 597 if (!infoPtr->pActiveCell) return 0; 598 return infoPtr->pActiveCell->ch; 599 } 600 601 case FM_GETHFONT: 602 return (LRESULT)infoPtr->hFont; 603 604 case WM_PAINT: 605 { 606 OnPaint(infoPtr, 607 wParam); 608 break; 609 } 610 611 case WM_DESTROY: 612 { 613 DeleteObject(infoPtr->hFont); 614 HeapFree(GetProcessHeap(), 615 0, 616 infoPtr); 617 SetWindowLongPtrW(hwnd, 618 0, 619 (DWORD_PTR)NULL); 620 break; 621 } 622 623 default: 624 { 625 Ret = DefWindowProcW(hwnd, 626 uMsg, 627 wParam, 628 lParam); 629 break; 630 } 631 } 632 633 return Ret; 634 } 635 636 637 BOOL 638 RegisterMapClasses(HINSTANCE hInstance) 639 { 640 WNDCLASSW wc = {0}; 641 642 wc.style = CS_DBLCLKS; 643 wc.lpfnWndProc = MapWndProc; 644 wc.cbWndExtra = sizeof(PMAP); 645 wc.hInstance = hInstance; 646 wc.hCursor = LoadCursorW(NULL, 647 (LPWSTR)IDC_ARROW); 648 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 649 wc.lpszClassName = szMapWndClass; 650 651 if (RegisterClassW(&wc)) 652 { 653 wc.lpfnWndProc = LrgCellWndProc; 654 wc.cbWndExtra = 0; 655 wc.lpszClassName = szLrgCellWndClass; 656 657 return RegisterClassW(&wc) != 0; 658 } 659 660 return FALSE; 661 } 662 663 VOID 664 UnregisterMapClasses(HINSTANCE hInstance) 665 { 666 UnregisterClassW(szMapWndClass, 667 hInstance); 668 669 UnregisterClassW(szLrgCellWndClass, 670 hInstance); 671 } 672