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 if (image != NULL) 528 { 529 pchFileTitle = PathFindFileNameW(node->FileName); 530 StringCbPrintfW(szTitleBuf, sizeof(szTitleBuf), 531 L"%ls%ls%ls", szResStr, L" - ", pchFileTitle); 532 SetWindowTextW(hwnd, szTitleBuf); 533 } 534 else 535 { 536 SetWindowTextW(hwnd, szResStr); 537 } 538 539 EnableToolBarButtons(image != NULL); 540 } 541 542 static SHIMGVW_FILENODE* 543 pBuildFileList(LPWSTR szFirstFile) 544 { 545 HANDLE hFindHandle; 546 WCHAR *extension; 547 WCHAR szSearchPath[MAX_PATH]; 548 WCHAR szSearchMask[MAX_PATH]; 549 WCHAR szFileTypes[MAX_PATH]; 550 WIN32_FIND_DATAW findData; 551 SHIMGVW_FILENODE *currentNode = NULL; 552 SHIMGVW_FILENODE *root = NULL; 553 SHIMGVW_FILENODE *conductor = NULL; 554 ImageCodecInfo *codecInfo; 555 UINT num; 556 UINT size; 557 UINT j; 558 559 StringCbCopyW(szSearchPath, sizeof(szSearchPath), szFirstFile); 560 PathRemoveFileSpecW(szSearchPath); 561 562 GdipGetImageDecodersSize(&num, &size); 563 codecInfo = malloc(size); 564 if (!codecInfo) 565 { 566 DPRINT1("malloc() failed in pLoadFileList()\n"); 567 return NULL; 568 } 569 570 GdipGetImageDecoders(num, size, codecInfo); 571 572 root = malloc(sizeof(SHIMGVW_FILENODE)); 573 if (!root) 574 { 575 DPRINT1("malloc() failed in pLoadFileList()\n"); 576 free(codecInfo); 577 return NULL; 578 } 579 580 conductor = root; 581 582 for (j = 0; j < num; ++j) 583 { 584 StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension); 585 586 extension = wcstok(szFileTypes, L";"); 587 while (extension != NULL) 588 { 589 PathCombineW(szSearchMask, szSearchPath, extension); 590 591 hFindHandle = FindFirstFileW(szSearchMask, &findData); 592 if (hFindHandle != INVALID_HANDLE_VALUE) 593 { 594 do 595 { 596 PathCombineW(conductor->FileName, szSearchPath, findData.cFileName); 597 598 // compare the name of the requested file with the one currently found. 599 // if the name matches, the current node is returned by the function. 600 if (wcscmp(szFirstFile, conductor->FileName) == 0) 601 { 602 currentNode = conductor; 603 } 604 605 conductor->Next = malloc(sizeof(SHIMGVW_FILENODE)); 606 607 // if malloc fails, make circular what we have and return it 608 if (!conductor->Next) 609 { 610 DPRINT1("malloc() failed in pLoadFileList()\n"); 611 612 conductor->Next = root; 613 root->Prev = conductor; 614 615 FindClose(hFindHandle); 616 free(codecInfo); 617 return conductor; 618 } 619 620 conductor->Next->Prev = conductor; 621 conductor = conductor->Next; 622 } 623 while (FindNextFileW(hFindHandle, &findData) != 0); 624 625 FindClose(hFindHandle); 626 } 627 628 extension = wcstok(NULL, L";"); 629 } 630 } 631 632 // we now have a node too much in the list. In case the requested file was not found, 633 // we use this node to store the name of it, otherwise we free it. 634 if (currentNode == NULL) 635 { 636 StringCchCopyW(conductor->FileName, MAX_PATH, szFirstFile); 637 currentNode = conductor; 638 } 639 else 640 { 641 conductor = conductor->Prev; 642 free(conductor->Next); 643 } 644 645 // link the last node with the first one to make the list circular 646 conductor->Next = root; 647 root->Prev = conductor; 648 conductor = currentNode; 649 650 free(codecInfo); 651 652 return conductor; 653 } 654 655 static VOID 656 pFreeFileList(SHIMGVW_FILENODE *root) 657 { 658 SHIMGVW_FILENODE *conductor; 659 660 if (!root) 661 return; 662 663 root->Prev->Next = NULL; 664 root->Prev = NULL; 665 666 while (root) 667 { 668 conductor = root; 669 root = conductor->Next; 670 free(conductor); 671 } 672 } 673 674 static VOID 675 ImageView_UpdateWindow(HWND hwnd) 676 { 677 InvalidateRect(hwnd, NULL, FALSE); 678 UpdateWindow(hwnd); 679 } 680 681 static HBRUSH CreateCheckerBoardBrush(HDC hdc) 682 { 683 static const CHAR pattern[] = 684 "\x28\x00\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x01\x00\x04\x00\x00\x00" 685 "\x00\x00\x80\x00\x00\x00\x23\x2E\x00\x00\x23\x2E\x00\x00\x10\x00\x00\x00" 686 "\x00\x00\x00\x00\x99\x99\x99\x00\xCC\xCC\xCC\x00\x00\x00\x00\x00\x00\x00" 687 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 688 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 689 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11" 690 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00" 691 "\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 692 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 693 "\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 694 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 695 "\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11" 696 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11"; 697 698 return CreateDIBPatternBrushPt(pattern, DIB_RGB_COLORS); 699 } 700 701 static VOID 702 ImageView_DrawImage(HWND hwnd) 703 { 704 GpGraphics *graphics; 705 UINT ImageWidth, ImageHeight; 706 INT ZoomedWidth, ZoomedHeight, x, y; 707 PAINTSTRUCT ps; 708 RECT rect, margin; 709 HDC hdc; 710 HBRUSH white; 711 HGDIOBJ hbrOld; 712 UINT uFlags; 713 WCHAR szText[128]; 714 HGDIOBJ hFontOld; 715 716 hdc = BeginPaint(hwnd, &ps); 717 if (!hdc) 718 { 719 DPRINT1("BeginPaint() failed\n"); 720 return; 721 } 722 723 GdipCreateFromHDC(hdc, &graphics); 724 if (!graphics) 725 { 726 DPRINT1("GdipCreateFromHDC() failed\n"); 727 return; 728 } 729 730 GetClientRect(hwnd, &rect); 731 white = GetStockObject(WHITE_BRUSH); 732 733 if (image == NULL) 734 { 735 FillRect(hdc, &rect, white); 736 737 LoadStringW(hInstance, IDS_NOPREVIEW, szText, _countof(szText)); 738 739 SetTextColor(hdc, RGB(0, 0, 0)); 740 SetBkMode(hdc, TRANSPARENT); 741 742 hFontOld = SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT)); 743 DrawTextW(hdc, szText, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER | 744 DT_NOPREFIX); 745 SelectObject(hdc, hFontOld); 746 } 747 else 748 { 749 GdipGetImageWidth(image, &ImageWidth); 750 GdipGetImageHeight(image, &ImageHeight); 751 752 ZoomedWidth = (ImageWidth * ZoomPercents) / 100; 753 ZoomedHeight = (ImageHeight * ZoomPercents) / 100; 754 755 x = (rect.right - ZoomedWidth) / 2; 756 y = (rect.bottom - ZoomedHeight) / 2; 757 758 // Fill top part 759 margin = rect; 760 margin.bottom = y - 1; 761 FillRect(hdc, &margin, white); 762 // Fill bottom part 763 margin.top = y + ZoomedHeight + 1; 764 margin.bottom = rect.bottom; 765 FillRect(hdc, &margin, white); 766 // Fill left part 767 margin.top = y - 1; 768 margin.bottom = y + ZoomedHeight + 1; 769 margin.right = x - 1; 770 FillRect(hdc, &margin, white); 771 // Fill right part 772 margin.left = x + ZoomedWidth + 1; 773 margin.right = rect.right; 774 FillRect(hdc, &margin, white); 775 776 DPRINT("x = %d, y = %d, ImageWidth = %u, ImageHeight = %u\n"); 777 DPRINT("rect.right = %ld, rect.bottom = %ld\n", rect.right, rect.bottom); 778 DPRINT("ZoomPercents = %d, ZoomedWidth = %d, ZoomedHeight = %d\n", 779 ZoomPercents, ZoomedWidth, ZoomedWidth); 780 781 if (ZoomPercents % 100 == 0) 782 { 783 GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); 784 GdipSetSmoothingMode(graphics, SmoothingModeNone); 785 } 786 else 787 { 788 GdipSetInterpolationMode(graphics, InterpolationModeHighQualityBilinear); 789 GdipSetSmoothingMode(graphics, SmoothingModeHighQuality); 790 } 791 792 uFlags = 0; 793 GdipGetImageFlags(image, &uFlags); 794 795 if (uFlags & (ImageFlagsHasAlpha | ImageFlagsHasTranslucent)) 796 { 797 HBRUSH hbr = CreateCheckerBoardBrush(hdc); 798 hbrOld = SelectObject(hdc, hbr); 799 Rectangle(hdc, x - 1, y - 1, x + ZoomedWidth + 1, y + ZoomedHeight + 1); 800 SelectObject(hdc, hbrOld); 801 DeleteObject(hbr); 802 } 803 else 804 { 805 hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 806 Rectangle(hdc, x - 1, y - 1, x + ZoomedWidth + 1, y + ZoomedHeight + 1); 807 SelectObject(hdc, hbrOld); 808 } 809 810 GdipDrawImageRectI(graphics, image, x, y, ZoomedWidth, ZoomedHeight); 811 } 812 GdipDeleteGraphics(graphics); 813 EndPaint(hwnd, &ps); 814 } 815 816 static BOOL 817 ImageView_LoadSettings(VOID) 818 { 819 HKEY hKey; 820 DWORD dwSize; 821 LONG nError; 822 823 nError = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\ReactOS\\shimgvw", 0, KEY_READ, &hKey); 824 if (nError) 825 return FALSE; 826 827 dwSize = sizeof(shiSettings); 828 nError = RegQueryValueExW(hKey, L"Settings", NULL, NULL, (LPBYTE)&shiSettings, &dwSize); 829 RegCloseKey(hKey); 830 831 return !nError; 832 } 833 834 static VOID 835 ImageView_SaveSettings(HWND hwnd) 836 { 837 WINDOWPLACEMENT wp; 838 HKEY hKey; 839 840 ShowWindow(hwnd, SW_HIDE); 841 wp.length = sizeof(WINDOWPLACEMENT); 842 GetWindowPlacement(hwnd, &wp); 843 844 shiSettings.Left = wp.rcNormalPosition.left; 845 shiSettings.Top = wp.rcNormalPosition.top; 846 shiSettings.Right = wp.rcNormalPosition.right; 847 shiSettings.Bottom = wp.rcNormalPosition.bottom; 848 shiSettings.Maximized = (IsZoomed(hwnd) || (wp.flags & WPF_RESTORETOMAXIMIZED)); 849 850 if (RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\ReactOS\\shimgvw"), 0, NULL, 851 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) == ERROR_SUCCESS) 852 { 853 RegSetValueEx(hKey, _T("Settings"), 0, REG_BINARY, (LPBYTE)&shiSettings, sizeof(SHIMGVW_SETTINGS)); 854 RegCloseKey(hKey); 855 } 856 } 857 858 static BOOL 859 ImageView_CreateToolBar(HWND hwnd) 860 { 861 hToolBar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, 862 WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | CCS_BOTTOM | TBSTYLE_TOOLTIPS, 863 0, 0, 0, 0, hwnd, 864 0, hInstance, NULL); 865 if (hToolBar != NULL) 866 { 867 HIMAGELIST hImageList; 868 869 SendMessageW(hToolBar, TB_SETEXTENDEDSTYLE, 870 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); 871 872 SendMessageW(hToolBar, TB_BUTTONSTRUCTSIZE, 873 sizeof(Buttons[0]), 0); 874 875 hImageList = ImageList_Create(TB_IMAGE_WIDTH, TB_IMAGE_HEIGHT, ILC_MASK | ILC_COLOR24, 1, 1); 876 if (hImageList == NULL) return FALSE; 877 878 for (UINT n = 0; n < _countof(BtnConfig); n++) 879 { 880 ImageList_AddMasked(hImageList, LoadImageW(hInstance, MAKEINTRESOURCEW(BtnConfig[n].idb), IMAGE_BITMAP, 881 TB_IMAGE_WIDTH, TB_IMAGE_HEIGHT, LR_DEFAULTCOLOR), RGB(255, 255, 255)); 882 } 883 884 ImageList_Destroy((HIMAGELIST)SendMessageW(hToolBar, TB_SETIMAGELIST, 885 0, (LPARAM)hImageList)); 886 887 SendMessageW(hToolBar, TB_ADDBUTTONS, _countof(Buttons), (LPARAM)Buttons); 888 889 return TRUE; 890 } 891 892 return FALSE; 893 } 894 895 static void ImageView_OnTimer(HWND hwnd) 896 { 897 DWORD dwDelay; 898 899 KillTimer(hwnd, ANIME_TIMER_ID); 900 InvalidateRect(hwnd, NULL, FALSE); 901 902 if (Anime_Step(&dwDelay)) 903 { 904 SetTimer(hwnd, ANIME_TIMER_ID, dwDelay, NULL); 905 } 906 } 907 908 LRESULT CALLBACK 909 ImageView_DispWndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 910 { 911 switch (Message) 912 { 913 case WM_PAINT: 914 { 915 ImageView_DrawImage(hwnd); 916 return 0L; 917 } 918 case WM_TIMER: 919 { 920 if (wParam == ANIME_TIMER_ID) 921 { 922 ImageView_OnTimer(hwnd); 923 return 0; 924 } 925 break; 926 } 927 } 928 return CallWindowProcW(PrevProc, hwnd, Message, wParam, lParam); 929 } 930 931 static VOID 932 ImageView_InitControls(HWND hwnd) 933 { 934 MoveWindow(hwnd, shiSettings.Left, shiSettings.Top, 935 shiSettings.Right - shiSettings.Left, 936 shiSettings.Bottom - shiSettings.Top, TRUE); 937 938 if (shiSettings.Maximized) ShowWindow(hwnd, SW_MAXIMIZE); 939 940 hDispWnd = CreateWindowExW(WS_EX_CLIENTEDGE, WC_STATIC, L"", 941 WS_CHILD | WS_VISIBLE, 942 0, 0, 0, 0, hwnd, NULL, hInstance, NULL); 943 944 SetClassLongPtr(hDispWnd, GCL_STYLE, CS_HREDRAW | CS_VREDRAW); 945 PrevProc = (WNDPROC) SetWindowLongPtr(hDispWnd, GWLP_WNDPROC, (LPARAM) ImageView_DispWndProc); 946 947 ImageView_CreateToolBar(hwnd); 948 } 949 950 static VOID 951 ImageView_OnMouseWheel(HWND hwnd, INT x, INT y, INT zDelta, UINT fwKeys) 952 { 953 if (zDelta != 0) 954 { 955 ZoomInOrOut(zDelta > 0); 956 } 957 } 958 959 static VOID 960 ImageView_OnSize(HWND hwnd, UINT state, INT cx, INT cy) 961 { 962 RECT rc; 963 964 SendMessageW(hToolBar, TB_AUTOSIZE, 0, 0); 965 966 GetWindowRect(hToolBar, &rc); 967 968 MoveWindow(hDispWnd, 0, 0, cx, cy - (rc.bottom - rc.top), TRUE); 969 970 /* is it maximized or restored? */ 971 if (state == SIZE_MAXIMIZED || state == SIZE_RESTORED) 972 { 973 /* reset zoom */ 974 ResetZoom(); 975 } 976 } 977 978 static LRESULT 979 ImageView_Delete(HWND hwnd) 980 { 981 DPRINT1("ImageView_Delete: unimplemented.\n"); 982 return 0; 983 } 984 985 static LRESULT 986 ImageView_Modify(HWND hwnd) 987 { 988 int nChars = GetFullPathNameW(currentFile->FileName, 0, NULL, NULL); 989 LPWSTR pszPathName; 990 SHELLEXECUTEINFOW sei; 991 992 if (!nChars) 993 { 994 DPRINT1("ImageView_Modify: failed to get full path name.\n"); 995 return 1; 996 } 997 998 pszPathName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, nChars * sizeof(WCHAR)); 999 if (pszPathName == NULL) 1000 { 1001 DPRINT1("HeapAlloc() failed in ImageView_Modify()\n"); 1002 return 1; 1003 } 1004 1005 GetFullPathNameW(currentFile->FileName, nChars, pszPathName, NULL); 1006 1007 sei.cbSize = sizeof(sei); 1008 sei.fMask = 0; 1009 sei.hwnd = NULL; 1010 sei.lpVerb = L"edit"; 1011 sei.lpFile = pszPathName; 1012 sei.lpParameters = NULL; 1013 sei.lpDirectory = NULL; 1014 sei.nShow = SW_SHOWNORMAL; 1015 sei.hInstApp = NULL; 1016 1017 if (!ShellExecuteExW(&sei)) 1018 { 1019 DPRINT1("ImageView_Modify: ShellExecuteExW() failed with code %08X\n", (int)GetLastError()); 1020 } 1021 1022 HeapFree(GetProcessHeap(), 0, pszPathName); 1023 1024 return 0; 1025 } 1026 1027 LRESULT CALLBACK 1028 ImageView_WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 1029 { 1030 switch (Message) 1031 { 1032 case WM_CREATE: 1033 { 1034 ImageView_InitControls(hwnd); 1035 return 0L; 1036 } 1037 1038 case WM_COMMAND: 1039 { 1040 switch (LOWORD(wParam)) 1041 { 1042 case IDC_PREV_PIC: 1043 currentFile = currentFile->Prev; 1044 pLoadImageFromNode(currentFile, hwnd); 1045 break; 1046 1047 case IDC_NEXT_PIC: 1048 currentFile = currentFile->Next; 1049 pLoadImageFromNode(currentFile, hwnd); 1050 break; 1051 1052 case IDC_BEST_FIT: 1053 DPRINT1("IDC_BEST_FIT unimplemented\n"); 1054 break; 1055 1056 case IDC_REAL_SIZE: 1057 UpdateZoom(100); 1058 return 0; 1059 1060 case IDC_SLIDE_SHOW: 1061 DPRINT1("IDC_SLIDE_SHOW unimplemented\n"); 1062 break; 1063 1064 case IDC_ZOOM_IN: 1065 ZoomInOrOut(TRUE); 1066 break; 1067 1068 case IDC_ZOOM_OUT: 1069 ZoomInOrOut(FALSE); 1070 break; 1071 1072 case IDC_SAVEAS: 1073 pSaveImageAs(hwnd); 1074 break; 1075 1076 case IDC_PRINT: 1077 pPrintImage(hwnd); 1078 break; 1079 1080 case IDC_ROT_CLOCKW: 1081 if (image) 1082 { 1083 GdipImageRotateFlip(image, Rotate270FlipNone); 1084 ImageView_UpdateWindow(hwnd); 1085 } 1086 break; 1087 1088 case IDC_ROT_COUNCW: 1089 if (image) 1090 { 1091 GdipImageRotateFlip(image, Rotate90FlipNone); 1092 ImageView_UpdateWindow(hwnd); 1093 } 1094 break; 1095 1096 case IDC_DELETE: 1097 return ImageView_Delete(hwnd); 1098 1099 case IDC_MODIFY: 1100 return ImageView_Modify(hwnd); 1101 } 1102 } 1103 break; 1104 1105 case WM_MOUSEWHEEL: 1106 ImageView_OnMouseWheel(hwnd, 1107 GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 1108 (SHORT)HIWORD(wParam), (UINT)LOWORD(wParam)); 1109 break; 1110 1111 case WM_NOTIFY: 1112 { 1113 LPNMHDR pnmhdr = (LPNMHDR)lParam; 1114 1115 switch (pnmhdr->code) 1116 { 1117 case TTN_GETDISPINFO: 1118 { 1119 LPTOOLTIPTEXTW lpttt; 1120 1121 lpttt = (LPTOOLTIPTEXTW)lParam; 1122 lpttt->hinst = hInstance; 1123 1124 lpttt->lpszText = MAKEINTRESOURCEW(BtnConfig[lpttt->hdr.idFrom - IDC_TOOL_BASE].ids); 1125 return 0; 1126 } 1127 } 1128 break; 1129 } 1130 case WM_SIZING: 1131 { 1132 LPRECT pRect = (LPRECT)lParam; 1133 if (pRect->right-pRect->left < 350) 1134 pRect->right = pRect->left + 350; 1135 1136 if (pRect->bottom-pRect->top < 290) 1137 pRect->bottom = pRect->top + 290; 1138 return TRUE; 1139 } 1140 case WM_SIZE: 1141 { 1142 ImageView_OnSize(hwnd, (UINT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 1143 return 0; 1144 } 1145 case WM_DESTROY: 1146 { 1147 ImageView_SaveSettings(hwnd); 1148 SetWindowLongPtr(hDispWnd, GWLP_WNDPROC, (LPARAM) PrevProc); 1149 PostQuitMessage(0); 1150 break; 1151 } 1152 } 1153 1154 return DefWindowProcW(hwnd, Message, wParam, lParam); 1155 } 1156 1157 LONG WINAPI 1158 ImageView_CreateWindow(HWND hwnd, LPCWSTR szFileName) 1159 { 1160 struct GdiplusStartupInput gdiplusStartupInput; 1161 ULONG_PTR gdiplusToken; 1162 WNDCLASSW WndClass = {0}; 1163 WCHAR szBuf[512]; 1164 WCHAR szInitialFile[MAX_PATH]; 1165 HWND hMainWnd; 1166 MSG msg; 1167 HACCEL hKbdAccel; 1168 HRESULT hComRes; 1169 INITCOMMONCONTROLSEX Icc = { .dwSize = sizeof(Icc), .dwICC = ICC_WIN95_CLASSES }; 1170 1171 InitCommonControlsEx(&Icc); 1172 1173 hComRes = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 1174 if (hComRes != S_OK && hComRes != S_FALSE) 1175 { 1176 DPRINT1("Warning, CoInitializeEx failed with code=%08X\n", (int)hComRes); 1177 } 1178 1179 if (!ImageView_LoadSettings()) 1180 { 1181 shiSettings.Maximized = FALSE; 1182 shiSettings.Left = 0; 1183 shiSettings.Top = 0; 1184 shiSettings.Right = 520; 1185 shiSettings.Bottom = 400; 1186 } 1187 1188 // Initialize GDI+ 1189 gdiplusStartupInput.GdiplusVersion = 1; 1190 gdiplusStartupInput.DebugEventCallback = NULL; 1191 gdiplusStartupInput.SuppressBackgroundThread = FALSE; 1192 gdiplusStartupInput.SuppressExternalCodecs = FALSE; 1193 1194 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 1195 pLoadImage(szFileName); 1196 1197 // Create the window 1198 WndClass.lpszClassName = L"shimgvw_window"; 1199 WndClass.lpfnWndProc = ImageView_WndProc; 1200 WndClass.hInstance = hInstance; 1201 WndClass.style = CS_HREDRAW | CS_VREDRAW; 1202 WndClass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_APP_ICON)); 1203 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 1204 WndClass.hbrBackground = NULL; /* less flicker */ 1205 1206 if (!RegisterClassW(&WndClass)) return -1; 1207 1208 LoadStringW(hInstance, IDS_APPTITLE, szBuf, _countof(szBuf)); 1209 hMainWnd = CreateWindowExW(0, L"shimgvw_window", szBuf, 1210 WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CAPTION, 1211 CW_USEDEFAULT, CW_USEDEFAULT, 1212 0, 0, NULL, NULL, hInstance, NULL); 1213 1214 // make sure the path has no quotes on it 1215 StringCbCopyW(szInitialFile, sizeof(szInitialFile), szFileName); 1216 PathUnquoteSpacesW(szInitialFile); 1217 1218 currentFile = pBuildFileList(szInitialFile); 1219 if (currentFile) 1220 { 1221 pLoadImageFromNode(currentFile, hMainWnd); 1222 } 1223 1224 /* Create accelerator table for keystrokes */ 1225 hKbdAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(IDR_ACCELERATOR)); 1226 1227 // Show it 1228 ShowWindow(hMainWnd, SW_SHOW); 1229 UpdateWindow(hMainWnd); 1230 1231 // Message Loop 1232 for (;;) 1233 { 1234 if (GetMessageW(&msg, NULL, 0, 0) <= 0) 1235 break; 1236 1237 if (!TranslateAcceleratorW(hMainWnd, hKbdAccel, &msg)) 1238 { 1239 TranslateMessage(&msg); 1240 DispatchMessageW(&msg); 1241 } 1242 } 1243 1244 /* Destroy accelerator table */ 1245 DestroyAcceleratorTable(hKbdAccel); 1246 1247 pFreeFileList(currentFile); 1248 1249 if (image) 1250 { 1251 GdipDisposeImage(image); 1252 image = NULL; 1253 } 1254 1255 Anime_FreeInfo(); 1256 1257 GdiplusShutdown(gdiplusToken); 1258 1259 /* Release COM resources */ 1260 if (SUCCEEDED(hComRes)) 1261 CoUninitialize(); 1262 1263 return -1; 1264 } 1265 1266 VOID WINAPI 1267 ImageView_FullscreenW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1268 { 1269 ImageView_CreateWindow(hwnd, path); 1270 } 1271 1272 VOID WINAPI 1273 ImageView_Fullscreen(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1274 { 1275 ImageView_CreateWindow(hwnd, path); 1276 } 1277 1278 VOID WINAPI 1279 ImageView_FullscreenA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1280 { 1281 WCHAR szFile[MAX_PATH]; 1282 1283 if (MultiByteToWideChar(CP_ACP, 0, path, -1, szFile, _countof(szFile))) 1284 { 1285 ImageView_CreateWindow(hwnd, szFile); 1286 } 1287 } 1288 1289 VOID WINAPI 1290 ImageView_PrintTo(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1291 { 1292 DPRINT("ImageView_PrintTo() not implemented\n"); 1293 } 1294 1295 VOID WINAPI 1296 ImageView_PrintToA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1297 { 1298 DPRINT("ImageView_PrintToA() not implemented\n"); 1299 } 1300 1301 VOID WINAPI 1302 ImageView_PrintToW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1303 { 1304 DPRINT("ImageView_PrintToW() not implemented\n"); 1305 } 1306 1307 BOOL WINAPI 1308 DllMain(IN HINSTANCE hinstDLL, 1309 IN DWORD dwReason, 1310 IN LPVOID lpvReserved) 1311 { 1312 switch (dwReason) 1313 { 1314 case DLL_PROCESS_ATTACH: 1315 case DLL_THREAD_ATTACH: 1316 hInstance = hinstDLL; 1317 break; 1318 } 1319 1320 return TRUE; 1321 } 1322