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