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 = 0; 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 POINT pt; 316 INT x, y; 317 318 pt.x = ptx; 319 pt.y = pty; 320 321 for (x = 0; x < XCELLS; x++) 322 for (y = 0; y < YCELLS; y++) 323 { 324 if (PtInRect(&infoPtr->Cells[y][x].CellInt, 325 pt)) 326 { 327 /* if the cell is not already active */ 328 if (!infoPtr->Cells[y][x].bActive) 329 { 330 /* set previous active cell to inactive */ 331 if (infoPtr->pActiveCell) 332 { 333 /* invalidate normal cells, required when 334 * moving a small active cell via keyboard */ 335 if (!infoPtr->pActiveCell->bLarge) 336 { 337 InvalidateRect(infoPtr->hMapWnd, 338 &infoPtr->pActiveCell->CellInt, 339 TRUE); 340 } 341 342 infoPtr->pActiveCell->bActive = FALSE; 343 infoPtr->pActiveCell->bLarge = FALSE; 344 } 345 346 /* set new cell to active */ 347 infoPtr->pActiveCell = &infoPtr->Cells[y][x]; 348 infoPtr->pActiveCell->bActive = TRUE; 349 infoPtr->pActiveCell->bLarge = TRUE; 350 if (infoPtr->hLrgWnd) 351 MoveLargeCell(infoPtr); 352 else 353 CreateLargeCell(infoPtr); 354 } 355 else 356 { 357 /* flick between large and small */ 358 if (infoPtr->pActiveCell->bLarge) 359 { 360 DestroyWindow(infoPtr->hLrgWnd); 361 infoPtr->hLrgWnd = NULL; 362 } 363 else 364 { 365 CreateLargeCell(infoPtr); 366 } 367 368 infoPtr->pActiveCell->bLarge = (infoPtr->pActiveCell->bLarge) ? FALSE : TRUE; 369 } 370 371 break; 372 } 373 } 374 } 375 376 377 static 378 BOOL 379 MapOnCreate(PMAP infoPtr, 380 HWND hwnd, 381 HWND hParent) 382 { 383 RECT rc; 384 BOOL Ret = FALSE; 385 386 infoPtr = HeapAlloc(GetProcessHeap(), 387 0, 388 sizeof(MAP)); 389 if (infoPtr) 390 { 391 SetLastError(0); 392 SetWindowLongPtrW(hwnd, 393 0, 394 (DWORD_PTR)infoPtr); 395 if (GetLastError() == 0) 396 { 397 ZeroMemory(infoPtr, 398 sizeof(MAP)); 399 400 infoPtr->hMapWnd = hwnd; 401 infoPtr->hParent = hParent; 402 403 GetClientRect(hwnd, &rc); 404 infoPtr->ClientSize.cx = rc.right; 405 infoPtr->ClientSize.cy = rc.bottom; 406 infoPtr->CellSize.cx = infoPtr->ClientSize.cx / XCELLS; 407 infoPtr->CellSize.cy = infoPtr->ClientSize.cy / YCELLS; 408 409 infoPtr->pActiveCell = NULL; 410 411 SetGrid(infoPtr); 412 413 SetScrollPos(infoPtr->hParent, SB_VERT, 0, TRUE); 414 415 Ret = TRUE; 416 } 417 } 418 419 return Ret; 420 } 421 422 423 static 424 VOID 425 OnVScroll(PMAP infoPtr, 426 INT Value, 427 INT Pos) 428 { 429 INT iYDiff, iOldYStart = infoPtr->iYStart; 430 431 switch (Value) 432 { 433 case SB_LINEUP: 434 infoPtr->iYStart -= 1; 435 break; 436 437 case SB_LINEDOWN: 438 infoPtr->iYStart += 1; 439 break; 440 441 case SB_PAGEUP: 442 infoPtr->iYStart -= YCELLS; 443 break; 444 445 case SB_PAGEDOWN: 446 infoPtr->iYStart += YCELLS; 447 break; 448 449 case SB_THUMBTRACK: 450 infoPtr->iYStart = Pos; 451 break; 452 453 default: 454 break; 455 } 456 457 infoPtr->iYStart = max(0, infoPtr->iYStart); 458 infoPtr->iYStart = min(infoPtr->iYStart, infoPtr->NumRows); 459 460 iYDiff = iOldYStart - infoPtr->iYStart; 461 if (iYDiff) 462 { 463 if (infoPtr->hLrgWnd != NULL) 464 { 465 ShowWindow(infoPtr->hLrgWnd, SW_HIDE); 466 } 467 468 SetScrollPos(infoPtr->hMapWnd, 469 SB_VERT, 470 infoPtr->iYStart, 471 TRUE); 472 473 if (abs(iYDiff) < YCELLS) 474 { 475 RECT rect; 476 GetClientRect(infoPtr->hMapWnd, &rect); 477 rect.top += 2; 478 rect.bottom -= 2; 479 ScrollWindowEx(infoPtr->hMapWnd, 480 0, 481 iYDiff * infoPtr->CellSize.cy, 482 &rect, 483 &rect, 484 NULL, 485 NULL, 486 SW_INVALIDATE); 487 } 488 else 489 { 490 InvalidateRect(infoPtr->hMapWnd, 491 NULL, 492 TRUE); 493 } 494 495 if (infoPtr->hLrgWnd != NULL) 496 { 497 ShowWindow(infoPtr->hLrgWnd, SW_SHOW); 498 } 499 } 500 } 501 502 503 static 504 VOID 505 OnPaint(PMAP infoPtr, 506 WPARAM wParam) 507 { 508 PAINTSTRUCT ps; 509 HDC hdc; 510 511 512 if (wParam != 0) 513 { 514 if (!GetUpdateRect(infoPtr->hMapWnd, 515 &ps.rcPaint, 516 TRUE)) 517 { 518 return; 519 } 520 ps.hdc = (HDC)wParam; 521 } 522 else 523 { 524 hdc = BeginPaint(infoPtr->hMapWnd, 525 &ps); 526 if (hdc == NULL) 527 { 528 return; 529 } 530 } 531 532 DrawGrid(infoPtr, &ps); 533 534 FillGrid(infoPtr, &ps); 535 536 if (wParam == 0) 537 { 538 EndPaint(infoPtr->hMapWnd, 539 &ps); 540 } 541 } 542 543 544 LRESULT 545 CALLBACK 546 MapWndProc(HWND hwnd, 547 UINT uMsg, 548 WPARAM wParam, 549 LPARAM lParam) 550 { 551 PMAP infoPtr; 552 LRESULT Ret = 0; 553 554 infoPtr = (PMAP)GetWindowLongPtrW(hwnd, 555 0); 556 557 switch (uMsg) 558 { 559 case WM_CREATE: 560 { 561 if (!MapOnCreate(infoPtr, 562 hwnd, 563 ((LPCREATESTRUCTW)lParam)->hwndParent)) 564 { 565 return (LRESULT)-1; 566 } 567 568 break; 569 } 570 571 case WM_LBUTTONDOWN: 572 { 573 OnClick(infoPtr, 574 LOWORD(lParam), 575 HIWORD(lParam)); 576 577 break; 578 } 579 580 case WM_LBUTTONDBLCLK: 581 { 582 NotifyParentOfSelection(infoPtr, 583 FM_SETCHAR, 584 infoPtr->pActiveCell->ch); 585 586 587 break; 588 } 589 590 case WM_VSCROLL: 591 { 592 OnVScroll(infoPtr, 593 LOWORD(wParam), 594 HIWORD(wParam)); 595 596 break; 597 } 598 599 case FM_SETFONT: 600 SetFont(infoPtr, (LPWSTR)lParam); 601 break; 602 603 case FM_GETCHAR: 604 { 605 if (!infoPtr->pActiveCell) return 0; 606 return infoPtr->pActiveCell->ch; 607 } 608 609 case FM_GETHFONT: 610 return (LRESULT)infoPtr->hFont; 611 612 case WM_PAINT: 613 { 614 OnPaint(infoPtr, 615 wParam); 616 break; 617 } 618 619 case WM_DESTROY: 620 { 621 DeleteObject(infoPtr->hFont); 622 HeapFree(GetProcessHeap(), 623 0, 624 infoPtr); 625 SetWindowLongPtrW(hwnd, 626 0, 627 (DWORD_PTR)NULL); 628 break; 629 } 630 631 default: 632 { 633 Ret = DefWindowProcW(hwnd, 634 uMsg, 635 wParam, 636 lParam); 637 break; 638 } 639 } 640 641 return Ret; 642 } 643 644 645 BOOL 646 RegisterMapClasses(HINSTANCE hInstance) 647 { 648 WNDCLASSW wc = {0}; 649 650 wc.style = CS_DBLCLKS; 651 wc.lpfnWndProc = MapWndProc; 652 wc.cbWndExtra = sizeof(PMAP); 653 wc.hInstance = hInstance; 654 wc.hCursor = LoadCursorW(NULL, 655 (LPWSTR)IDC_ARROW); 656 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 657 wc.lpszClassName = szMapWndClass; 658 659 if (RegisterClassW(&wc)) 660 { 661 wc.lpfnWndProc = LrgCellWndProc; 662 wc.cbWndExtra = 0; 663 wc.lpszClassName = szLrgCellWndClass; 664 665 return RegisterClassW(&wc) != 0; 666 } 667 668 return FALSE; 669 } 670 671 VOID 672 UnregisterMapClasses(HINSTANCE hInstance) 673 { 674 UnregisterClassW(szMapWndClass, 675 hInstance); 676 677 UnregisterClassW(szLrgCellWndClass, 678 hInstance); 679 } 680