1 /* 2 * PROJECT: ReactOS Picture and Fax Viewer 3 * FILE: dll/win32/shimgvw/shimgvw.c 4 * PURPOSE: shimgvw.dll 5 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) 6 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 */ 8 9 #define WIN32_NO_STATUS 10 #define _INC_WINDOWS 11 #define COM_NO_WINDOWS_H 12 #define INITGUID 13 14 #include <stdarg.h> 15 16 #include <windef.h> 17 #include <winbase.h> 18 #include <winnls.h> 19 #include <winreg.h> 20 #include <wingdi.h> 21 #include <wincon.h> 22 #include <windowsx.h> 23 #include <objbase.h> 24 #include <commctrl.h> 25 #include <commdlg.h> 26 #include <gdiplus.h> 27 #include <tchar.h> 28 #include <shlobj.h> 29 #include <strsafe.h> 30 #include <shlwapi.h> 31 #include <shellapi.h> 32 33 #define NDEBUG 34 #include <debug.h> 35 36 #include "shimgvw.h" 37 38 HINSTANCE hInstance; 39 SHIMGVW_SETTINGS shiSettings; 40 SHIMGVW_FILENODE *currentFile; 41 GpImage *image = NULL; 42 WNDPROC PrevProc = NULL; 43 44 HWND hDispWnd, hToolBar; 45 46 /* zooming */ 47 UINT ZoomPercents = 100; 48 49 static const UINT ZoomSteps[] = 50 { 51 10, 25, 50, 100, 200, 400, 800, 1600 52 }; 53 54 #define MIN_ZOOM ZoomSteps[0] 55 #define MAX_ZOOM ZoomSteps[_countof(ZoomSteps)-1] 56 57 /* ToolBar Buttons */ 58 typedef struct { 59 DWORD idb; /* Index to bitmap */ 60 DWORD ids; /* Index to tooltip */ 61 } TB_BUTTON_CONFIG; 62 63 /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */ 64 #define DEFINE_BTN_INFO(_name) \ 65 { TBICON_##_name, IDC_##_name, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 } 66 67 #define DEFINE_BTN_SEPARATOR \ 68 { 15, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0 } 69 70 /* ToolBar Buttons */ 71 static const TBBUTTON Buttons[] = 72 { 73 DEFINE_BTN_INFO(PREV_PIC), 74 DEFINE_BTN_INFO(NEXT_PIC), 75 DEFINE_BTN_SEPARATOR, 76 DEFINE_BTN_INFO(BEST_FIT), 77 DEFINE_BTN_INFO(REAL_SIZE), 78 DEFINE_BTN_INFO(SLIDE_SHOW), 79 DEFINE_BTN_SEPARATOR, 80 DEFINE_BTN_INFO(ZOOM_IN), 81 DEFINE_BTN_INFO(ZOOM_OUT), 82 DEFINE_BTN_SEPARATOR, 83 DEFINE_BTN_INFO(ROT_CLOCKW), 84 DEFINE_BTN_INFO(ROT_COUNCW), 85 DEFINE_BTN_SEPARATOR, 86 DEFINE_BTN_INFO(DELETE), 87 DEFINE_BTN_INFO(PRINT), 88 DEFINE_BTN_INFO(SAVEAS), 89 DEFINE_BTN_INFO(MODIFY), 90 DEFINE_BTN_SEPARATOR, 91 DEFINE_BTN_INFO(HELP_TOC) 92 }; 93 94 #define DEFINE_BTN_CONFIG(_name) { IDB_##_name, IDS_TOOLTIP_##_name } 95 96 static const TB_BUTTON_CONFIG BtnConfig[] = 97 { 98 DEFINE_BTN_CONFIG(PREV_PIC), 99 DEFINE_BTN_CONFIG(NEXT_PIC), 100 DEFINE_BTN_CONFIG(BEST_FIT), 101 DEFINE_BTN_CONFIG(REAL_SIZE), 102 DEFINE_BTN_CONFIG(SLIDE_SHOW), 103 DEFINE_BTN_CONFIG(ZOOM_IN), 104 DEFINE_BTN_CONFIG(ZOOM_OUT), 105 DEFINE_BTN_CONFIG(ROT_CLOCKW), 106 DEFINE_BTN_CONFIG(ROT_COUNCW), 107 DEFINE_BTN_CONFIG(DELETE), 108 DEFINE_BTN_CONFIG(PRINT), 109 DEFINE_BTN_CONFIG(SAVEAS), 110 DEFINE_BTN_CONFIG(MODIFY), 111 DEFINE_BTN_CONFIG(HELP_TOC) 112 }; 113 114 /* animation */ 115 UINT m_nFrameIndex = 0; 116 UINT m_nFrameCount = 0; 117 UINT m_nLoopIndex = 0; 118 UINT m_nLoopCount = (UINT)-1; 119 PropertyItem *m_pDelayItem = NULL; 120 121 #define ANIME_TIMER_ID 9999 122 123 static void Anime_FreeInfo(void) 124 { 125 if (m_pDelayItem) 126 { 127 free(m_pDelayItem); 128 m_pDelayItem = NULL; 129 } 130 m_nFrameIndex = 0; 131 m_nFrameCount = 0; 132 m_nLoopIndex = 0; 133 m_nLoopCount = (UINT)-1; 134 } 135 136 static BOOL Anime_LoadInfo(void) 137 { 138 GUID *dims; 139 UINT nDimCount = 0; 140 UINT cbItem; 141 UINT result; 142 PropertyItem *pItem; 143 144 Anime_FreeInfo(); 145 KillTimer(hDispWnd, ANIME_TIMER_ID); 146 147 if (!image) 148 return FALSE; 149 150 GdipImageGetFrameDimensionsCount(image, &nDimCount); 151 if (nDimCount) 152 { 153 dims = (GUID *)calloc(nDimCount, sizeof(GUID)); 154 if (dims) 155 { 156 GdipImageGetFrameDimensionsList(image, dims, nDimCount); 157 GdipImageGetFrameCount(image, dims, &result); 158 m_nFrameCount = result; 159 free(dims); 160 } 161 } 162 163 result = 0; 164 GdipGetPropertyItemSize(image, PropertyTagFrameDelay, &result); 165 cbItem = result; 166 if (cbItem) 167 { 168 m_pDelayItem = (PropertyItem *)malloc(cbItem); 169 GdipGetPropertyItem(image, PropertyTagFrameDelay, cbItem, m_pDelayItem); 170 } 171 172 result = 0; 173 GdipGetPropertyItemSize(image, PropertyTagLoopCount, &result); 174 cbItem = result; 175 if (cbItem) 176 { 177 pItem = (PropertyItem *)malloc(cbItem); 178 if (pItem) 179 { 180 if (GdipGetPropertyItem(image, PropertyTagLoopCount, cbItem, pItem) == Ok) 181 { 182 m_nLoopCount = *(WORD *)pItem->value; 183 } 184 free(pItem); 185 } 186 } 187 188 if (m_pDelayItem) 189 { 190 SetTimer(hDispWnd, ANIME_TIMER_ID, 0, NULL); 191 } 192 193 return m_pDelayItem != NULL; 194 } 195 196 static void Anime_SetFrameIndex(UINT nFrameIndex) 197 { 198 if (nFrameIndex < m_nFrameCount) 199 { 200 GUID guid = FrameDimensionTime; 201 if (Ok != GdipImageSelectActiveFrame(image, &guid, nFrameIndex)) 202 { 203 guid = FrameDimensionPage; 204 GdipImageSelectActiveFrame(image, &guid, nFrameIndex); 205 } 206 } 207 m_nFrameIndex = nFrameIndex; 208 } 209 210 DWORD Anime_GetFrameDelay(UINT nFrameIndex) 211 { 212 if (nFrameIndex < m_nFrameCount && m_pDelayItem) 213 { 214 return ((DWORD *)m_pDelayItem->value)[m_nFrameIndex] * 10; 215 } 216 return 0; 217 } 218 219 BOOL Anime_Step(DWORD *pdwDelay) 220 { 221 *pdwDelay = INFINITE; 222 if (m_nLoopCount == (UINT)-1) 223 return FALSE; 224 225 if (m_nFrameIndex + 1 < m_nFrameCount) 226 { 227 *pdwDelay = Anime_GetFrameDelay(m_nFrameIndex); 228 Anime_SetFrameIndex(m_nFrameIndex); 229 ++m_nFrameIndex; 230 return TRUE; 231 } 232 233 if (m_nLoopCount == 0 || m_nLoopIndex < m_nLoopCount) 234 { 235 *pdwDelay = Anime_GetFrameDelay(m_nFrameIndex); 236 Anime_SetFrameIndex(m_nFrameIndex); 237 m_nFrameIndex = 0; 238 ++m_nLoopIndex; 239 return TRUE; 240 } 241 242 return FALSE; 243 } 244 245 static void UpdateZoom(UINT NewZoom) 246 { 247 BOOL bEnableZoomIn, bEnableZoomOut; 248 249 /* If zoom has not been changed, ignore it */ 250 if (ZoomPercents == NewZoom) 251 return; 252 253 ZoomPercents = NewZoom; 254 255 /* Check if a zoom button of the toolbar must be grayed */ 256 bEnableZoomIn = bEnableZoomOut = TRUE; 257 258 if (NewZoom >= MAX_ZOOM) 259 { 260 bEnableZoomIn = FALSE; 261 } 262 else if (NewZoom <= MIN_ZOOM) 263 { 264 bEnableZoomOut = FALSE; 265 } 266 267 /* Update the state of the zoom buttons */ 268 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_ZOOM_OUT, bEnableZoomOut); 269 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_ZOOM_IN, bEnableZoomIn); 270 271 /* Redraw the display window */ 272 InvalidateRect(hDispWnd, NULL, FALSE); 273 } 274 275 static void ZoomInOrOut(BOOL bZoomIn) 276 { 277 UINT i, NewZoom; 278 279 if (image == NULL) 280 return; 281 282 if (bZoomIn) /* zoom in */ 283 { 284 /* find next step */ 285 for (i = 0; i < _countof(ZoomSteps); ++i) 286 { 287 if (ZoomPercents < ZoomSteps[i]) 288 break; 289 } 290 if (i == _countof(ZoomSteps)) 291 NewZoom = MAX_ZOOM; 292 else 293 NewZoom = ZoomSteps[i]; 294 } 295 else /* zoom out */ 296 { 297 /* find previous step */ 298 for (i = _countof(ZoomSteps); i > 0; ) 299 { 300 --i; 301 if (ZoomSteps[i] < ZoomPercents) 302 break; 303 } 304 if (i < 0) 305 NewZoom = MIN_ZOOM; 306 else 307 NewZoom = ZoomSteps[i]; 308 } 309 310 /* Update toolbar and refresh screen */ 311 UpdateZoom(NewZoom); 312 } 313 314 static void ResetZoom(void) 315 { 316 RECT Rect; 317 UINT ImageWidth, ImageHeight, NewZoom; 318 319 if (image == NULL) 320 return; 321 322 /* get disp window size and image size */ 323 GetClientRect(hDispWnd, &Rect); 324 GdipGetImageWidth(image, &ImageWidth); 325 GdipGetImageHeight(image, &ImageHeight); 326 327 /* compare two aspect rates. same as 328 (ImageHeight / ImageWidth < Rect.bottom / Rect.right) in real */ 329 if (ImageHeight * Rect.right < Rect.bottom * ImageWidth) 330 { 331 if (Rect.right < ImageWidth) 332 { 333 /* it's large, shrink it */ 334 NewZoom = (Rect.right * 100) / ImageWidth; 335 } 336 else 337 { 338 /* it's small. show as original size */ 339 NewZoom = 100; 340 } 341 } 342 else 343 { 344 if (Rect.bottom < ImageHeight) 345 { 346 /* it's large, shrink it */ 347 NewZoom = (Rect.bottom * 100) / ImageHeight; 348 } 349 else 350 { 351 /* it's small. show as original size */ 352 NewZoom = 100; 353 } 354 } 355 356 UpdateZoom(NewZoom); 357 } 358 359 static void pLoadImage(LPCWSTR szOpenFileName) 360 { 361 /* check file presence */ 362 if (GetFileAttributesW(szOpenFileName) == 0xFFFFFFFF) 363 { 364 DPRINT1("File %s not found!\n", szOpenFileName); 365 return; 366 } 367 368 /* load now */ 369 GdipLoadImageFromFile(szOpenFileName, &image); 370 if (!image) 371 { 372 DPRINT1("GdipLoadImageFromFile() failed\n"); 373 return; 374 } 375 Anime_LoadInfo(); 376 377 if (szOpenFileName && szOpenFileName[0]) 378 SHAddToRecentDocs(SHARD_PATHW, szOpenFileName); 379 380 /* Reset zoom and redraw display */ 381 ResetZoom(); 382 } 383 384 static void pSaveImageAs(HWND hwnd) 385 { 386 OPENFILENAMEW sfn; 387 ImageCodecInfo *codecInfo; 388 WCHAR szSaveFileName[MAX_PATH]; 389 WCHAR *szFilterMask; 390 GUID rawFormat; 391 UINT num; 392 UINT size; 393 size_t sizeRemain; 394 UINT j; 395 WCHAR *c; 396 397 if (image == NULL) 398 return; 399 400 GdipGetImageEncodersSize(&num, &size); 401 codecInfo = malloc(size); 402 if (!codecInfo) 403 { 404 DPRINT1("malloc() failed in pSaveImageAs()\n"); 405 return; 406 } 407 408 GdipGetImageEncoders(num, size, codecInfo); 409 GdipGetImageRawFormat(image, &rawFormat); 410 411 sizeRemain = 0; 412 413 for (j = 0; j < num; ++j) 414 { 415 // Every pair needs space for the Description, twice the Extensions, 1 char for the space, 2 for the braces and 2 for the NULL terminators. 416 sizeRemain = sizeRemain + (((wcslen(codecInfo[j].FormatDescription) + (wcslen(codecInfo[j].FilenameExtension) * 2) + 5) * sizeof(WCHAR))); 417 } 418 419 /* Add two more chars for the last terminator */ 420 sizeRemain = sizeRemain + (sizeof(WCHAR) * 2); 421 422 szFilterMask = malloc(sizeRemain); 423 if (!szFilterMask) 424 { 425 DPRINT1("cannot allocate memory for filter mask in pSaveImageAs()"); 426 free(codecInfo); 427 return; 428 } 429 430 ZeroMemory(szSaveFileName, sizeof(szSaveFileName)); 431 ZeroMemory(szFilterMask, sizeRemain); 432 ZeroMemory(&sfn, sizeof(sfn)); 433 sfn.lStructSize = sizeof(sfn); 434 sfn.hwndOwner = hwnd; 435 sfn.hInstance = hInstance; 436 sfn.lpstrFile = szSaveFileName; 437 sfn.lpstrFilter = szFilterMask; 438 sfn.nMaxFile = MAX_PATH; 439 sfn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; 440 441 c = szFilterMask; 442 443 for (j = 0; j < num; ++j) 444 { 445 StringCbPrintfExW(c, sizeRemain, &c, &sizeRemain, 0, L"%ls (%ls)", codecInfo[j].FormatDescription, codecInfo[j].FilenameExtension); 446 447 /* Skip the NULL character */ 448 c++; 449 sizeRemain -= sizeof(*c); 450 451 StringCbPrintfExW(c, sizeRemain, &c, &sizeRemain, 0, L"%ls", codecInfo[j].FilenameExtension); 452 453 /* Skip the NULL character */ 454 c++; 455 sizeRemain -= sizeof(*c); 456 457 if (IsEqualGUID(&rawFormat, &codecInfo[j].FormatID) != FALSE) 458 { 459 sfn.nFilterIndex = j + 1; 460 } 461 } 462 463 if (GetSaveFileNameW(&sfn)) 464 { 465 if (m_pDelayItem) 466 { 467 /* save animation */ 468 KillTimer(hDispWnd, ANIME_TIMER_ID); 469 470 DPRINT1("FIXME: save animation\n"); 471 if (GdipSaveImageToFile(image, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok) 472 { 473 DPRINT1("GdipSaveImageToFile() failed\n"); 474 } 475 476 SetTimer(hDispWnd, ANIME_TIMER_ID, 0, NULL); 477 } 478 else 479 { 480 /* save non-animation */ 481 if (GdipSaveImageToFile(image, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok) 482 { 483 DPRINT1("GdipSaveImageToFile() failed\n"); 484 } 485 } 486 } 487 488 free(szFilterMask); 489 free(codecInfo); 490 } 491 492 static VOID 493 pPrintImage(HWND hwnd) 494 { 495 /* FIXME */ 496 } 497 498 static VOID 499 EnableToolBarButtons(BOOL bEnable) 500 { 501 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_SAVEAS, bEnable); 502 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_PRINT, bEnable); 503 } 504 505 static VOID 506 pLoadImageFromNode(SHIMGVW_FILENODE *node, HWND hwnd) 507 { 508 WCHAR szTitleBuf[800]; 509 WCHAR szResStr[512]; 510 LPWSTR pchFileTitle; 511 512 if (image) 513 { 514 GdipDisposeImage(image); 515 image = NULL; 516 } 517 518 if (node == NULL) 519 { 520 EnableToolBarButtons(FALSE); 521 return; 522 } 523 524 pLoadImage(node->FileName); 525 526 LoadStringW(hInstance, IDS_APPTITLE, szResStr, _countof(szResStr)); 527 528 pchFileTitle = PathFindFileNameW(node->FileName); 529 if (pchFileTitle && *pchFileTitle) 530 { 531 StringCbPrintfW(szTitleBuf, sizeof(szTitleBuf), 532 L"%ls%ls%ls", szResStr, L" - ", pchFileTitle); 533 SetWindowTextW(hwnd, szTitleBuf); 534 } 535 else 536 { 537 SetWindowTextW(hwnd, szResStr); 538 } 539 540 EnableToolBarButtons(image != NULL); 541 542 /* Redraw the display window */ 543 InvalidateRect(hwnd, NULL, FALSE); 544 } 545 546 static SHIMGVW_FILENODE* 547 pBuildFileList(LPWSTR szFirstFile) 548 { 549 HANDLE hFindHandle; 550 WCHAR *extension; 551 WCHAR szSearchPath[MAX_PATH]; 552 WCHAR szSearchMask[MAX_PATH]; 553 WCHAR szFileTypes[MAX_PATH]; 554 WIN32_FIND_DATAW findData; 555 SHIMGVW_FILENODE *currentNode = NULL; 556 SHIMGVW_FILENODE *root = NULL; 557 SHIMGVW_FILENODE *conductor = NULL; 558 ImageCodecInfo *codecInfo; 559 UINT num; 560 UINT size; 561 UINT j; 562 563 StringCbCopyW(szSearchPath, sizeof(szSearchPath), szFirstFile); 564 PathRemoveFileSpecW(szSearchPath); 565 566 GdipGetImageDecodersSize(&num, &size); 567 codecInfo = malloc(size); 568 if (!codecInfo) 569 { 570 DPRINT1("malloc() failed in pLoadFileList()\n"); 571 return NULL; 572 } 573 574 GdipGetImageDecoders(num, size, codecInfo); 575 576 root = malloc(sizeof(SHIMGVW_FILENODE)); 577 if (!root) 578 { 579 DPRINT1("malloc() failed in pLoadFileList()\n"); 580 free(codecInfo); 581 return NULL; 582 } 583 584 conductor = root; 585 586 for (j = 0; j < num; ++j) 587 { 588 StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension); 589 590 extension = wcstok(szFileTypes, L";"); 591 while (extension != NULL) 592 { 593 PathCombineW(szSearchMask, szSearchPath, extension); 594 595 hFindHandle = FindFirstFileW(szSearchMask, &findData); 596 if (hFindHandle != INVALID_HANDLE_VALUE) 597 { 598 do 599 { 600 PathCombineW(conductor->FileName, szSearchPath, findData.cFileName); 601 602 // compare the name of the requested file with the one currently found. 603 // if the name matches, the current node is returned by the function. 604 if (wcscmp(szFirstFile, conductor->FileName) == 0) 605 { 606 currentNode = conductor; 607 } 608 609 conductor->Next = malloc(sizeof(SHIMGVW_FILENODE)); 610 611 // if malloc fails, make circular what we have and return it 612 if (!conductor->Next) 613 { 614 DPRINT1("malloc() failed in pLoadFileList()\n"); 615 616 conductor->Next = root; 617 root->Prev = conductor; 618 619 FindClose(hFindHandle); 620 free(codecInfo); 621 return conductor; 622 } 623 624 conductor->Next->Prev = conductor; 625 conductor = conductor->Next; 626 } 627 while (FindNextFileW(hFindHandle, &findData) != 0); 628 629 FindClose(hFindHandle); 630 } 631 632 extension = wcstok(NULL, L";"); 633 } 634 } 635 636 // we now have a node too much in the list. In case the requested file was not found, 637 // we use this node to store the name of it, otherwise we free it. 638 if (currentNode == NULL) 639 { 640 StringCchCopyW(conductor->FileName, MAX_PATH, szFirstFile); 641 currentNode = conductor; 642 } 643 else 644 { 645 conductor = conductor->Prev; 646 free(conductor->Next); 647 } 648 649 // link the last node with the first one to make the list circular 650 conductor->Next = root; 651 root->Prev = conductor; 652 conductor = currentNode; 653 654 free(codecInfo); 655 656 return conductor; 657 } 658 659 static VOID 660 pFreeFileList(SHIMGVW_FILENODE *root) 661 { 662 SHIMGVW_FILENODE *conductor; 663 664 if (!root) 665 return; 666 667 root->Prev->Next = NULL; 668 root->Prev = NULL; 669 670 while (root) 671 { 672 conductor = root; 673 root = conductor->Next; 674 free(conductor); 675 } 676 } 677 678 static VOID 679 ImageView_UpdateWindow(HWND hwnd) 680 { 681 InvalidateRect(hwnd, NULL, FALSE); 682 UpdateWindow(hwnd); 683 } 684 685 static HBRUSH CreateCheckerBoardBrush(HDC hdc) 686 { 687 static const CHAR pattern[] = 688 "\x28\x00\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x01\x00\x04\x00\x00\x00" 689 "\x00\x00\x80\x00\x00\x00\x23\x2E\x00\x00\x23\x2E\x00\x00\x10\x00\x00\x00" 690 "\x00\x00\x00\x00\x99\x99\x99\x00\xCC\xCC\xCC\x00\x00\x00\x00\x00\x00\x00" 691 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 692 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 693 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11" 694 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00" 695 "\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 696 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 697 "\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 698 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 699 "\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11" 700 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11"; 701 702 return CreateDIBPatternBrushPt(pattern, DIB_RGB_COLORS); 703 } 704 705 static VOID 706 ImageView_DrawImage(HWND hwnd) 707 { 708 GpGraphics *graphics; 709 UINT ImageWidth, ImageHeight; 710 INT ZoomedWidth, ZoomedHeight, x, y; 711 PAINTSTRUCT ps; 712 RECT rect, margin; 713 HDC hdc; 714 HBRUSH white; 715 HGDIOBJ hbrOld; 716 UINT uFlags; 717 WCHAR szText[128]; 718 HGDIOBJ hFontOld; 719 720 hdc = BeginPaint(hwnd, &ps); 721 if (!hdc) 722 { 723 DPRINT1("BeginPaint() failed\n"); 724 return; 725 } 726 727 GdipCreateFromHDC(hdc, &graphics); 728 if (!graphics) 729 { 730 DPRINT1("GdipCreateFromHDC() failed\n"); 731 return; 732 } 733 734 GetClientRect(hwnd, &rect); 735 white = GetStockObject(WHITE_BRUSH); 736 737 if (image == NULL) 738 { 739 FillRect(hdc, &rect, white); 740 741 LoadStringW(hInstance, IDS_NOPREVIEW, szText, _countof(szText)); 742 743 SetTextColor(hdc, RGB(0, 0, 0)); 744 SetBkMode(hdc, TRANSPARENT); 745 746 hFontOld = SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); 747 DrawTextW(hdc, szText, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER | 748 DT_NOPREFIX); 749 SelectObject(hdc, hFontOld); 750 } 751 else 752 { 753 GdipGetImageWidth(image, &ImageWidth); 754 GdipGetImageHeight(image, &ImageHeight); 755 756 ZoomedWidth = (ImageWidth * ZoomPercents) / 100; 757 ZoomedHeight = (ImageHeight * ZoomPercents) / 100; 758 759 x = (rect.right - ZoomedWidth) / 2; 760 y = (rect.bottom - ZoomedHeight) / 2; 761 762 // Fill top part 763 margin = rect; 764 margin.bottom = y - 1; 765 FillRect(hdc, &margin, white); 766 // Fill bottom part 767 margin.top = y + ZoomedHeight + 1; 768 margin.bottom = rect.bottom; 769 FillRect(hdc, &margin, white); 770 // Fill left part 771 margin.top = y - 1; 772 margin.bottom = y + ZoomedHeight + 1; 773 margin.right = x - 1; 774 FillRect(hdc, &margin, white); 775 // Fill right part 776 margin.left = x + ZoomedWidth + 1; 777 margin.right = rect.right; 778 FillRect(hdc, &margin, white); 779 780 DPRINT("x = %d, y = %d, ImageWidth = %u, ImageHeight = %u\n"); 781 DPRINT("rect.right = %ld, rect.bottom = %ld\n", rect.right, rect.bottom); 782 DPRINT("ZoomPercents = %d, ZoomedWidth = %d, ZoomedHeight = %d\n", 783 ZoomPercents, ZoomedWidth, ZoomedWidth); 784 785 if (ZoomPercents % 100 == 0) 786 { 787 GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); 788 GdipSetSmoothingMode(graphics, SmoothingModeNone); 789 } 790 else 791 { 792 GdipSetInterpolationMode(graphics, InterpolationModeHighQualityBilinear); 793 GdipSetSmoothingMode(graphics, SmoothingModeHighQuality); 794 } 795 796 uFlags = 0; 797 GdipGetImageFlags(image, &uFlags); 798 799 if (uFlags & (ImageFlagsHasAlpha | ImageFlagsHasTranslucent)) 800 { 801 HBRUSH hbr = CreateCheckerBoardBrush(hdc); 802 hbrOld = SelectObject(hdc, hbr); 803 Rectangle(hdc, x - 1, y - 1, x + ZoomedWidth + 1, y + ZoomedHeight + 1); 804 SelectObject(hdc, hbrOld); 805 DeleteObject(hbr); 806 } 807 else 808 { 809 hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 810 Rectangle(hdc, x - 1, y - 1, x + ZoomedWidth + 1, y + ZoomedHeight + 1); 811 SelectObject(hdc, hbrOld); 812 } 813 814 GdipDrawImageRectI(graphics, image, x, y, ZoomedWidth, ZoomedHeight); 815 } 816 GdipDeleteGraphics(graphics); 817 EndPaint(hwnd, &ps); 818 } 819 820 static BOOL 821 ImageView_LoadSettings(VOID) 822 { 823 HKEY hKey; 824 DWORD dwSize; 825 LONG nError; 826 827 nError = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\ReactOS\\shimgvw", 0, KEY_READ, &hKey); 828 if (nError) 829 return FALSE; 830 831 dwSize = sizeof(shiSettings); 832 nError = RegQueryValueExW(hKey, L"Settings", NULL, NULL, (LPBYTE)&shiSettings, &dwSize); 833 RegCloseKey(hKey); 834 835 return !nError; 836 } 837 838 static VOID 839 ImageView_SaveSettings(HWND hwnd) 840 { 841 WINDOWPLACEMENT wp; 842 HKEY hKey; 843 844 ShowWindow(hwnd, SW_HIDE); 845 wp.length = sizeof(WINDOWPLACEMENT); 846 GetWindowPlacement(hwnd, &wp); 847 848 shiSettings.Left = wp.rcNormalPosition.left; 849 shiSettings.Top = wp.rcNormalPosition.top; 850 shiSettings.Right = wp.rcNormalPosition.right; 851 shiSettings.Bottom = wp.rcNormalPosition.bottom; 852 shiSettings.Maximized = (IsZoomed(hwnd) || (wp.flags & WPF_RESTORETOMAXIMIZED)); 853 854 if (RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\ReactOS\\shimgvw"), 0, NULL, 855 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) 856 { 857 RegSetValueEx(hKey, _T("Settings"), 0, REG_BINARY, (LPBYTE)&shiSettings, sizeof(SHIMGVW_SETTINGS)); 858 RegCloseKey(hKey); 859 } 860 } 861 862 static BOOL 863 ImageView_CreateToolBar(HWND hwnd) 864 { 865 hToolBar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, 866 WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_BOTTOM | TBSTYLE_TOOLTIPS, 867 0, 0, 0, 0, hwnd, 868 0, hInstance, NULL); 869 if (hToolBar != NULL) 870 { 871 HIMAGELIST hImageList; 872 873 SendMessageW(hToolBar, TB_SETEXTENDEDSTYLE, 874 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); 875 876 SendMessageW(hToolBar, TB_BUTTONSTRUCTSIZE, 877 sizeof(Buttons[0]), 0); 878 879 hImageList = ImageList_Create(TB_IMAGE_WIDTH, TB_IMAGE_HEIGHT, ILC_MASK | ILC_COLOR24, 1, 1); 880 if (hImageList == NULL) return FALSE; 881 882 for (UINT n = 0; n < _countof(BtnConfig); n++) 883 { 884 ImageList_AddMasked(hImageList, LoadImageW(hInstance, MAKEINTRESOURCEW(BtnConfig[n].idb), IMAGE_BITMAP, 885 TB_IMAGE_WIDTH, TB_IMAGE_HEIGHT, LR_DEFAULTCOLOR), RGB(255, 255, 255)); 886 } 887 888 ImageList_Destroy((HIMAGELIST)SendMessageW(hToolBar, TB_SETIMAGELIST, 889 0, (LPARAM)hImageList)); 890 891 SendMessageW(hToolBar, TB_ADDBUTTONS, _countof(Buttons), (LPARAM)Buttons); 892 893 return TRUE; 894 } 895 896 return FALSE; 897 } 898 899 static void ImageView_OnTimer(HWND hwnd) 900 { 901 DWORD dwDelay; 902 903 KillTimer(hwnd, ANIME_TIMER_ID); 904 InvalidateRect(hwnd, NULL, FALSE); 905 906 if (Anime_Step(&dwDelay)) 907 { 908 SetTimer(hwnd, ANIME_TIMER_ID, dwDelay, NULL); 909 } 910 } 911 912 LRESULT CALLBACK 913 ImageView_DispWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 914 { 915 switch (Message) 916 { 917 case WM_PAINT: 918 { 919 ImageView_DrawImage(hwnd); 920 return 0L; 921 } 922 case WM_TIMER: 923 { 924 if (wParam == ANIME_TIMER_ID) 925 { 926 ImageView_OnTimer(hwnd); 927 return 0; 928 } 929 break; 930 } 931 } 932 return CallWindowProcW(PrevProc, hwnd, Message, wParam, lParam); 933 } 934 935 static VOID 936 ImageView_InitControls(HWND hwnd) 937 { 938 MoveWindow(hwnd, shiSettings.Left, shiSettings.Top, 939 shiSettings.Right - shiSettings.Left, 940 shiSettings.Bottom - shiSettings.Top, TRUE); 941 942 if (shiSettings.Maximized) ShowWindow(hwnd, SW_MAXIMIZE); 943 944 hDispWnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_STATIC, L"", 945 WS_CHILD | WS_VISIBLE, 946 0, 0, 0, 0, hwnd, NULL, hInstance, NULL); 947 948 SetClassLongPtr(hDispWnd, GCL_STYLE, CS_HREDRAW | CS_VREDRAW); 949 PrevProc = (WNDPROC) SetWindowLongPtr(hDispWnd, GWLP_WNDPROC, (LPARAM) ImageView_DispWndProc); 950 951 ImageView_CreateToolBar(hwnd); 952 } 953 954 static VOID 955 ImageView_OnMouseWheel(HWND hwnd, INT x, INT y, INT zDelta, UINT fwKeys) 956 { 957 if (zDelta != 0) 958 { 959 ZoomInOrOut(zDelta > 0); 960 } 961 } 962 963 static VOID 964 ImageView_OnSize(HWND hwnd, UINT state, INT cx, INT cy) 965 { 966 RECT rc; 967 968 SendMessageW(hToolBar, TB_AUTOSIZE, 0, 0); 969 970 GetWindowRect(hToolBar, &rc); 971 972 MoveWindow(hDispWnd, 0, 0, cx, cy - (rc.bottom - rc.top), TRUE); 973 974 /* is it maximized or restored? */ 975 if (state == SIZE_MAXIMIZED || state == SIZE_RESTORED) 976 { 977 /* reset zoom */ 978 ResetZoom(); 979 } 980 } 981 982 static LRESULT 983 ImageView_Delete(HWND hwnd) 984 { 985 DPRINT1("ImageView_Delete: unimplemented.\n"); 986 return 0; 987 } 988 989 static LRESULT 990 ImageView_Modify(HWND hwnd) 991 { 992 int nChars = GetFullPathNameW(currentFile->FileName, 0, NULL, NULL); 993 LPWSTR pszPathName; 994 SHELLEXECUTEINFOW sei; 995 996 if (!nChars) 997 { 998 DPRINT1("ImageView_Modify: failed to get full path name.\n"); 999 return 1; 1000 } 1001 1002 pszPathName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, nChars * sizeof(WCHAR)); 1003 if (pszPathName == NULL) 1004 { 1005 DPRINT1("HeapAlloc() failed in ImageView_Modify()\n"); 1006 return 1; 1007 } 1008 1009 GetFullPathNameW(currentFile->FileName, nChars, pszPathName, NULL); 1010 1011 sei.cbSize = sizeof(sei); 1012 sei.fMask = 0; 1013 sei.hwnd = NULL; 1014 sei.lpVerb = L"edit"; 1015 sei.lpFile = pszPathName; 1016 sei.lpParameters = NULL; 1017 sei.lpDirectory = NULL; 1018 sei.nShow = SW_SHOWNORMAL; 1019 sei.hInstApp = NULL; 1020 1021 if (!ShellExecuteExW(&sei)) 1022 { 1023 DPRINT1("ImageView_Modify: ShellExecuteExW() failed with code %08X\n", (int)GetLastError()); 1024 } 1025 1026 HeapFree(GetProcessHeap(), 0, pszPathName); 1027 1028 return 0; 1029 } 1030 1031 LRESULT CALLBACK 1032 ImageView_WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 1033 { 1034 switch (Message) 1035 { 1036 case WM_CREATE: 1037 { 1038 ImageView_InitControls(hwnd); 1039 return 0L; 1040 } 1041 1042 case WM_COMMAND: 1043 { 1044 switch (LOWORD(wParam)) 1045 { 1046 case IDC_PREV_PIC: 1047 currentFile = currentFile->Prev; 1048 pLoadImageFromNode(currentFile, hwnd); 1049 break; 1050 1051 case IDC_NEXT_PIC: 1052 currentFile = currentFile->Next; 1053 pLoadImageFromNode(currentFile, hwnd); 1054 break; 1055 1056 case IDC_BEST_FIT: 1057 DPRINT1("IDC_BEST_FIT unimplemented\n"); 1058 break; 1059 1060 case IDC_REAL_SIZE: 1061 UpdateZoom(100); 1062 return 0; 1063 1064 case IDC_SLIDE_SHOW: 1065 DPRINT1("IDC_SLIDE_SHOW unimplemented\n"); 1066 break; 1067 1068 case IDC_ZOOM_IN: 1069 ZoomInOrOut(TRUE); 1070 break; 1071 1072 case IDC_ZOOM_OUT: 1073 ZoomInOrOut(FALSE); 1074 break; 1075 1076 case IDC_SAVEAS: 1077 pSaveImageAs(hwnd); 1078 break; 1079 1080 case IDC_PRINT: 1081 pPrintImage(hwnd); 1082 break; 1083 1084 case IDC_ROT_CLOCKW: 1085 if (image) 1086 { 1087 GdipImageRotateFlip(image, Rotate270FlipNone); 1088 ImageView_UpdateWindow(hwnd); 1089 } 1090 break; 1091 1092 case IDC_ROT_COUNCW: 1093 if (image) 1094 { 1095 GdipImageRotateFlip(image, Rotate90FlipNone); 1096 ImageView_UpdateWindow(hwnd); 1097 } 1098 break; 1099 1100 case IDC_DELETE: 1101 return ImageView_Delete(hwnd); 1102 1103 case IDC_MODIFY: 1104 return ImageView_Modify(hwnd); 1105 } 1106 } 1107 break; 1108 1109 case WM_MOUSEWHEEL: 1110 ImageView_OnMouseWheel(hwnd, 1111 GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 1112 (SHORT)HIWORD(wParam), (UINT)LOWORD(wParam)); 1113 break; 1114 1115 case WM_NOTIFY: 1116 { 1117 LPNMHDR pnmhdr = (LPNMHDR)lParam; 1118 1119 switch (pnmhdr->code) 1120 { 1121 case TTN_GETDISPINFO: 1122 { 1123 LPTOOLTIPTEXTW lpttt; 1124 1125 lpttt = (LPTOOLTIPTEXTW)lParam; 1126 lpttt->hinst = hInstance; 1127 1128 lpttt->lpszText = MAKEINTRESOURCEW(BtnConfig[lpttt->hdr.idFrom - IDC_TOOL_BASE].ids); 1129 return 0; 1130 } 1131 } 1132 break; 1133 } 1134 case WM_SIZING: 1135 { 1136 LPRECT pRect = (LPRECT)lParam; 1137 if (pRect->right-pRect->left < 350) 1138 pRect->right = pRect->left + 350; 1139 1140 if (pRect->bottom-pRect->top < 290) 1141 pRect->bottom = pRect->top + 290; 1142 return TRUE; 1143 } 1144 case WM_SIZE: 1145 { 1146 ImageView_OnSize(hwnd, (UINT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 1147 return 0; 1148 } 1149 case WM_DESTROY: 1150 { 1151 ImageView_SaveSettings(hwnd); 1152 SetWindowLongPtr(hDispWnd, GWLP_WNDPROC, (LPARAM) PrevProc); 1153 PostQuitMessage(0); 1154 break; 1155 } 1156 } 1157 1158 return DefWindowProcW(hwnd, Message, wParam, lParam); 1159 } 1160 1161 LONG WINAPI 1162 ImageView_CreateWindow(HWND hwnd, LPCWSTR szFileName) 1163 { 1164 struct GdiplusStartupInput gdiplusStartupInput; 1165 ULONG_PTR gdiplusToken; 1166 WNDCLASSW WndClass = {0}; 1167 WCHAR szBuf[512]; 1168 WCHAR szInitialFile[MAX_PATH]; 1169 HWND hMainWnd; 1170 MSG msg; 1171 HACCEL hKbdAccel; 1172 HRESULT hComRes; 1173 INITCOMMONCONTROLSEX Icc = { .dwSize = sizeof(Icc), .dwICC = ICC_WIN95_CLASSES }; 1174 1175 InitCommonControlsEx(&Icc); 1176 1177 hComRes = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 1178 if (hComRes != S_OK && hComRes != S_FALSE) 1179 { 1180 DPRINT1("Warning, CoInitializeEx failed with code=%08X\n", (int)hComRes); 1181 } 1182 1183 if (!ImageView_LoadSettings()) 1184 { 1185 shiSettings.Maximized = FALSE; 1186 shiSettings.Left = 0; 1187 shiSettings.Top = 0; 1188 shiSettings.Right = 520; 1189 shiSettings.Bottom = 400; 1190 } 1191 1192 // Initialize GDI+ 1193 gdiplusStartupInput.GdiplusVersion = 1; 1194 gdiplusStartupInput.DebugEventCallback = NULL; 1195 gdiplusStartupInput.SuppressBackgroundThread = FALSE; 1196 gdiplusStartupInput.SuppressExternalCodecs = FALSE; 1197 1198 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 1199 pLoadImage(szFileName); 1200 1201 // Create the window 1202 WndClass.lpszClassName = L"shimgvw_window"; 1203 WndClass.lpfnWndProc = ImageView_WndProc; 1204 WndClass.hInstance = hInstance; 1205 WndClass.style = CS_HREDRAW | CS_VREDRAW; 1206 WndClass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_APP_ICON)); 1207 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 1208 WndClass.hbrBackground = NULL; /* less flicker */ 1209 1210 if (!RegisterClassW(&WndClass)) return -1; 1211 1212 LoadStringW(hInstance, IDS_APPTITLE, szBuf, _countof(szBuf)); 1213 hMainWnd = CreateWindowExW(0, L"shimgvw_window", szBuf, 1214 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CAPTION, 1215 CW_USEDEFAULT, CW_USEDEFAULT, 1216 0, 0, NULL, NULL, hInstance, NULL); 1217 1218 // make sure the path has no quotes on it 1219 StringCbCopyW(szInitialFile, sizeof(szInitialFile), szFileName); 1220 PathUnquoteSpacesW(szInitialFile); 1221 1222 currentFile = pBuildFileList(szInitialFile); 1223 if (currentFile) 1224 { 1225 pLoadImageFromNode(currentFile, hMainWnd); 1226 } 1227 1228 /* Create accelerator table for keystrokes */ 1229 hKbdAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDR_ACCELERATOR)); 1230 1231 // Show it 1232 ShowWindow(hMainWnd, SW_SHOW); 1233 UpdateWindow(hMainWnd); 1234 1235 // Message Loop 1236 for (;;) 1237 { 1238 if (GetMessageW(&msg, NULL, 0, 0) <= 0) 1239 break; 1240 1241 if (!TranslateAcceleratorW(hMainWnd, hKbdAccel, &msg)) 1242 { 1243 TranslateMessage(&msg); 1244 DispatchMessageW(&msg); 1245 } 1246 } 1247 1248 /* Destroy accelerator table */ 1249 DestroyAcceleratorTable(hKbdAccel); 1250 1251 pFreeFileList(currentFile); 1252 1253 if (image) 1254 { 1255 GdipDisposeImage(image); 1256 image = NULL; 1257 } 1258 1259 Anime_FreeInfo(); 1260 1261 GdiplusShutdown(gdiplusToken); 1262 1263 /* Release COM resources */ 1264 if (SUCCEEDED(hComRes)) 1265 CoUninitialize(); 1266 1267 return -1; 1268 } 1269 1270 VOID WINAPI 1271 ImageView_FullscreenW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1272 { 1273 ImageView_CreateWindow(hwnd, path); 1274 } 1275 1276 VOID WINAPI 1277 ImageView_Fullscreen(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1278 { 1279 ImageView_CreateWindow(hwnd, path); 1280 } 1281 1282 VOID WINAPI 1283 ImageView_FullscreenA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1284 { 1285 WCHAR szFile[MAX_PATH]; 1286 1287 if (MultiByteToWideChar(CP_ACP, 0, path, -1, szFile, _countof(szFile))) 1288 { 1289 ImageView_CreateWindow(hwnd, szFile); 1290 } 1291 } 1292 1293 VOID WINAPI 1294 ImageView_PrintTo(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1295 { 1296 DPRINT("ImageView_PrintTo() not implemented\n"); 1297 } 1298 1299 VOID WINAPI 1300 ImageView_PrintToA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1301 { 1302 DPRINT("ImageView_PrintToA() not implemented\n"); 1303 } 1304 1305 VOID WINAPI 1306 ImageView_PrintToW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1307 { 1308 DPRINT("ImageView_PrintToW() not implemented\n"); 1309 } 1310 1311 BOOL WINAPI 1312 DllMain(IN HINSTANCE hinstDLL, 1313 IN DWORD dwReason, 1314 IN LPVOID lpvReserved) 1315 { 1316 switch (dwReason) 1317 { 1318 case DLL_PROCESS_ATTACH: 1319 case DLL_THREAD_ATTACH: 1320 hInstance = hinstDLL; 1321 break; 1322 } 1323 1324 return TRUE; 1325 } 1326