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