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