1 /* 2 * PROJECT: ReactOS Picture and Fax Viewer 3 * LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0) 4 * PURPOSE: Image file browsing and manipulation 5 * COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org) 6 * Copyright 2018-2023 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 7 * Copyright 2025 Whindmar Saksit <whindsaks@proton.me> 8 */ 9 10 #include "shimgvw.h" 11 #include <windowsx.h> 12 #include <commctrl.h> 13 #include <commdlg.h> 14 #include <shlobj.h> 15 #include <shellapi.h> 16 17 EXTERN_C PCWSTR GetExtraExtensionsGdipList(VOID); 18 EXTERN_C HRESULT LoadImageFromPath(LPCWSTR Path, GpImage** ppImage); 19 20 /* Toolbar image size */ 21 #define TB_IMAGE_WIDTH 16 22 #define TB_IMAGE_HEIGHT 16 23 24 /* Slide show timer */ 25 #define SLIDESHOW_TIMER_ID 0xFACE 26 #define SLIDESHOW_TIMER_INTERVAL 5000 /* 5 seconds */ 27 #define HIDECURSOR_TIMER_ID 0xBABE 28 #define HIDECURSOR_TIMER_TIMEOUT 3000 29 30 HINSTANCE g_hInstance = NULL; 31 HWND g_hMainWnd = NULL; 32 HWND g_hwndFullscreen = NULL; 33 SHIMGVW_FILENODE * g_pCurrentFile = NULL; 34 GpImage * g_pImage = NULL; 35 SHIMGVW_SETTINGS g_Settings; 36 37 static const UINT s_ZoomSteps[] = 38 { 39 5, 10, 25, 50, 100, 200, 300, 500, 1000, 2000, 4000 40 }; 41 42 #define MIN_ZOOM s_ZoomSteps[0] 43 #define MAX_ZOOM s_ZoomSteps[_countof(s_ZoomSteps) - 1] 44 45 /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */ 46 #define DEFINE_BTN_INFO(_name) \ 47 { TBICON_##_name, IDC_##_name, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 } 48 49 #define DEFINE_BTN_SEPARATOR \ 50 { -1, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0 } 51 52 /* ToolBar Buttons */ 53 static const TBBUTTON s_Buttons[] = 54 { 55 DEFINE_BTN_INFO(PREV_PIC), 56 DEFINE_BTN_INFO(NEXT_PIC), 57 DEFINE_BTN_SEPARATOR, 58 DEFINE_BTN_INFO(BEST_FIT), 59 DEFINE_BTN_INFO(REAL_SIZE), 60 DEFINE_BTN_INFO(SLIDE_SHOW), 61 DEFINE_BTN_SEPARATOR, 62 DEFINE_BTN_INFO(ZOOM_IN), 63 DEFINE_BTN_INFO(ZOOM_OUT), 64 DEFINE_BTN_SEPARATOR, 65 DEFINE_BTN_INFO(ROT_CLOCKW), 66 DEFINE_BTN_INFO(ROT_COUNCW), 67 DEFINE_BTN_SEPARATOR, 68 DEFINE_BTN_INFO(ROT_CWSAVE), 69 DEFINE_BTN_INFO(ROT_CCWSAVE), 70 DEFINE_BTN_SEPARATOR, 71 DEFINE_BTN_INFO(DELETE), 72 DEFINE_BTN_INFO(PRINT), 73 DEFINE_BTN_INFO(SAVEAS), 74 DEFINE_BTN_INFO(MODIFY), 75 DEFINE_BTN_SEPARATOR, 76 DEFINE_BTN_INFO(HELP_TOC) 77 }; 78 79 /* ToolBar Button configuration */ 80 typedef struct 81 { 82 DWORD idb; /* Index to bitmap */ 83 DWORD ids; /* Index to tooltip */ 84 } TB_BUTTON_CONFIG; 85 86 #define DEFINE_BTN_CONFIG(_name) { IDB_##_name, IDS_TOOLTIP_##_name } 87 88 static const TB_BUTTON_CONFIG s_ButtonConfig[] = 89 { 90 DEFINE_BTN_CONFIG(PREV_PIC), 91 DEFINE_BTN_CONFIG(NEXT_PIC), 92 DEFINE_BTN_CONFIG(BEST_FIT), 93 DEFINE_BTN_CONFIG(REAL_SIZE), 94 DEFINE_BTN_CONFIG(SLIDE_SHOW), 95 DEFINE_BTN_CONFIG(ZOOM_IN), 96 DEFINE_BTN_CONFIG(ZOOM_OUT), 97 DEFINE_BTN_CONFIG(ROT_CLOCKW), 98 DEFINE_BTN_CONFIG(ROT_COUNCW), 99 DEFINE_BTN_CONFIG(ROT_CWSAVE), 100 DEFINE_BTN_CONFIG(ROT_CCWSAVE), 101 DEFINE_BTN_CONFIG(DELETE), 102 DEFINE_BTN_CONFIG(PRINT), 103 DEFINE_BTN_CONFIG(SAVEAS), 104 DEFINE_BTN_CONFIG(MODIFY), 105 DEFINE_BTN_CONFIG(HELP_TOC), 106 }; 107 108 typedef struct tagPREVIEW_DATA 109 { 110 HWND m_hwnd; 111 HWND m_hwndZoom; 112 HWND m_hwndToolBar; 113 INT m_nZoomPercents; 114 ANIME m_Anime; /* Animation */ 115 INT m_xScrollOffset; 116 INT m_yScrollOffset; 117 UINT m_nMouseDownMsg; 118 UINT m_nTimerInterval; 119 BOOL m_bHideCursor; 120 POINT m_ptOrigin; 121 WCHAR m_szFile[MAX_PATH]; 122 } PREVIEW_DATA, *PPREVIEW_DATA; 123 124 static VOID Preview_ToggleSlideShowEx(PPREVIEW_DATA pData, BOOL StartTimer); 125 126 static inline PPREVIEW_DATA 127 Preview_GetData(HWND hwnd) 128 { 129 return (PPREVIEW_DATA)GetWindowLongPtrW(hwnd, GWLP_USERDATA); 130 } 131 132 static inline BOOL 133 Preview_IsMainWnd(HWND hwnd) 134 { 135 return hwnd == g_hMainWnd; 136 } 137 138 static VOID 139 Preview_RestartTimer(HWND hwnd) 140 { 141 if (!Preview_IsMainWnd(hwnd)) 142 { 143 PPREVIEW_DATA pData = Preview_GetData(hwnd); 144 KillTimer(hwnd, SLIDESHOW_TIMER_ID); 145 if (pData->m_nTimerInterval) 146 SetTimer(hwnd, SLIDESHOW_TIMER_ID, pData->m_nTimerInterval, NULL); 147 } 148 } 149 150 static VOID 151 Preview_ChangeSlideShowTimer(PPREVIEW_DATA pData, BOOL bSlower) 152 { 153 BOOL IsFullscreen = !Preview_IsMainWnd(pData->m_hwnd); 154 enum { mintime = 1000, maxtime = SLIDESHOW_TIMER_INTERVAL * 3, step = 1000 }; 155 UINT interval = pData->m_nTimerInterval ? pData->m_nTimerInterval : SLIDESHOW_TIMER_INTERVAL; 156 if (IsFullscreen) 157 { 158 interval = bSlower ? min(interval + step, maxtime) : max(interval - step, mintime); 159 if (pData->m_nTimerInterval != interval) 160 { 161 pData->m_nTimerInterval = interval; 162 Preview_RestartTimer(pData->m_hwnd); 163 } 164 } 165 } 166 167 static VOID 168 ZoomWnd_UpdateScroll(PPREVIEW_DATA pData, BOOL bResetPos) 169 { 170 HWND hwnd = pData->m_hwndZoom; 171 RECT rcClient; 172 UINT ImageWidth, ImageHeight, ZoomedWidth, ZoomedHeight; 173 SCROLLINFO si; 174 BOOL bShowHorz, bShowVert; 175 176 if (bResetPos) 177 pData->m_xScrollOffset = pData->m_yScrollOffset = 0; 178 179 if (!g_pImage) 180 { 181 ShowScrollBar(hwnd, SB_BOTH, FALSE); 182 InvalidateRect(hwnd, NULL, TRUE); 183 return; 184 } 185 186 GdipGetImageWidth(g_pImage, &ImageWidth); 187 GdipGetImageHeight(g_pImage, &ImageHeight); 188 189 ZoomedWidth = (ImageWidth * pData->m_nZoomPercents) / 100; 190 ZoomedHeight = (ImageHeight * pData->m_nZoomPercents) / 100; 191 192 GetClientRect(hwnd, &rcClient); 193 194 bShowHorz = (rcClient.right < ZoomedWidth); 195 bShowVert = (rcClient.bottom < ZoomedHeight); 196 ShowScrollBar(hwnd, SB_HORZ, bShowHorz); 197 ShowScrollBar(hwnd, SB_VERT, bShowVert); 198 199 GetClientRect(hwnd, &rcClient); 200 201 ZeroMemory(&si, sizeof(si)); 202 si.cbSize = sizeof(si); 203 si.fMask = SIF_ALL; 204 205 if (bShowHorz) 206 { 207 GetScrollInfo(hwnd, SB_HORZ, &si); 208 si.nPage = rcClient.right; 209 si.nMin = 0; 210 si.nMax = ZoomedWidth; 211 si.nPos = (ZoomedWidth - rcClient.right) / 2 + pData->m_xScrollOffset; 212 si.nPos = max(min(si.nPos, si.nMax - (INT)si.nPage), si.nMin); 213 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); 214 pData->m_xScrollOffset = si.nPos - (ZoomedWidth - rcClient.right) / 2; 215 } 216 else 217 { 218 pData->m_xScrollOffset = 0; 219 } 220 221 if (bShowVert) 222 { 223 GetScrollInfo(hwnd, SB_VERT, &si); 224 si.nPage = rcClient.bottom; 225 si.nMin = 0; 226 si.nMax = ZoomedHeight; 227 si.nPos = (ZoomedHeight - rcClient.bottom) / 2 + pData->m_yScrollOffset; 228 si.nPos = max(min(si.nPos, si.nMax - (INT)si.nPage), si.nMin); 229 SetScrollInfo(hwnd, SB_VERT, &si, TRUE); 230 pData->m_yScrollOffset = si.nPos - (ZoomedHeight - rcClient.bottom) / 2; 231 } 232 else 233 { 234 pData->m_yScrollOffset = 0; 235 } 236 237 InvalidateRect(hwnd, NULL, TRUE); 238 } 239 240 static VOID 241 Preview_UpdateZoom(PPREVIEW_DATA pData, UINT NewZoom, BOOL bEnableBestFit, BOOL bEnableRealSize) 242 { 243 BOOL bEnableZoomIn, bEnableZoomOut; 244 HWND hToolBar = pData->m_hwndToolBar; 245 246 pData->m_nZoomPercents = NewZoom; 247 248 /* Check if a zoom button of the toolbar must be grayed */ 249 bEnableZoomIn = (NewZoom < MAX_ZOOM); 250 bEnableZoomOut = (NewZoom > MIN_ZOOM); 251 252 /* Update toolbar buttons */ 253 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_BEST_FIT, bEnableBestFit); 254 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_REAL_SIZE, NewZoom != 100); 255 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_ZOOM_IN, bEnableZoomIn); 256 SendMessageW(hToolBar, TB_ENABLEBUTTON, IDC_ZOOM_OUT, bEnableZoomOut); 257 258 /* Redraw the display window */ 259 InvalidateRect(pData->m_hwndZoom, NULL, TRUE); 260 261 /* Restart timer if necessary */ 262 Preview_RestartTimer(pData->m_hwnd); 263 264 /* Update scroll info */ 265 ZoomWnd_UpdateScroll(pData, FALSE); 266 } 267 268 static VOID 269 Preview_ZoomInOrOut(PPREVIEW_DATA pData, BOOL bZoomIn) 270 { 271 UINT i, NewZoom; 272 273 if (g_pImage == NULL) 274 return; 275 276 if (bZoomIn) /* zoom in */ 277 { 278 /* find next step */ 279 for (i = 0; i < _countof(s_ZoomSteps); ++i) 280 { 281 if (pData->m_nZoomPercents < s_ZoomSteps[i]) 282 break; 283 } 284 NewZoom = ((i >= _countof(s_ZoomSteps)) ? MAX_ZOOM : s_ZoomSteps[i]); 285 } 286 else /* zoom out */ 287 { 288 /* find previous step */ 289 for (i = _countof(s_ZoomSteps); i > 0; ) 290 { 291 --i; 292 if (s_ZoomSteps[i] < pData->m_nZoomPercents) 293 break; 294 } 295 NewZoom = ((i < 0) ? MIN_ZOOM : s_ZoomSteps[i]); 296 } 297 298 /* Update toolbar and refresh screen */ 299 Preview_UpdateZoom(pData, NewZoom, TRUE, TRUE); 300 } 301 302 static VOID 303 Preview_ResetZoom(PPREVIEW_DATA pData) 304 { 305 RECT Rect; 306 UINT ImageWidth, ImageHeight, NewZoom; 307 308 if (g_pImage == NULL) 309 return; 310 311 /* get disp window size and image size */ 312 GetClientRect(pData->m_hwndZoom, &Rect); 313 GdipGetImageWidth(g_pImage, &ImageWidth); 314 GdipGetImageHeight(g_pImage, &ImageHeight); 315 316 /* compare two aspect rates. same as 317 (ImageHeight / ImageWidth < Rect.bottom / Rect.right) in real */ 318 if (ImageHeight * Rect.right < Rect.bottom * ImageWidth) 319 { 320 if (Rect.right < ImageWidth) 321 { 322 /* it's large, shrink it */ 323 NewZoom = (Rect.right * 100) / ImageWidth; 324 } 325 else 326 { 327 /* it's small. show as original size */ 328 NewZoom = 100; 329 } 330 } 331 else 332 { 333 if (Rect.bottom < ImageHeight) 334 { 335 /* it's large, shrink it */ 336 NewZoom = (Rect.bottom * 100) / ImageHeight; 337 } 338 else 339 { 340 /* it's small. show as original size */ 341 NewZoom = 100; 342 } 343 } 344 345 Preview_UpdateZoom(pData, NewZoom, FALSE, TRUE); 346 } 347 348 static VOID 349 Preview_UpdateTitle(PPREVIEW_DATA pData, LPCWSTR FileName) 350 { 351 WCHAR szText[MAX_PATH + 100]; 352 LPWSTR pchFileTitle; 353 354 LoadStringW(g_hInstance, IDS_APPTITLE, szText, _countof(szText)); 355 356 pchFileTitle = PathFindFileNameW(FileName); 357 if (pchFileTitle && *pchFileTitle) 358 { 359 StringCchCatW(szText, _countof(szText), L" - "); 360 StringCchCatW(szText, _countof(szText), pchFileTitle); 361 } 362 363 SetWindowTextW(pData->m_hwnd, szText); 364 } 365 366 static VOID 367 Preview_pFreeImage(PPREVIEW_DATA pData) 368 { 369 Anime_FreeInfo(&pData->m_Anime); 370 371 if (g_pImage) 372 { 373 GdipDisposeImage(g_pImage); 374 g_pImage = NULL; 375 } 376 377 pData->m_szFile[0] = UNICODE_NULL; 378 } 379 380 static VOID 381 Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) 382 { 383 HRESULT hr; 384 Preview_pFreeImage(pData); 385 InvalidateRect(pData->m_hwnd, NULL, FALSE); /* Schedule redraw in case we change to "No preview" */ 386 387 hr = LoadImageFromPath(szOpenFileName, &g_pImage); 388 if (FAILED(hr)) 389 { 390 DPRINT1("GdipLoadImageFromStream() failed, %d\n", hr); 391 Preview_pFreeImage(pData); 392 Preview_UpdateTitle(pData, NULL); 393 return; 394 } 395 396 Anime_LoadInfo(&pData->m_Anime); 397 398 GetFullPathNameW(szOpenFileName, _countof(pData->m_szFile), pData->m_szFile, NULL); 399 SHAddToRecentDocs(SHARD_PATHW, pData->m_szFile); 400 401 /* Reset zoom and redraw display */ 402 Preview_ResetZoom(pData); 403 404 Preview_UpdateTitle(pData, szOpenFileName); 405 } 406 407 static VOID 408 Preview_pLoadImageFromNode(PPREVIEW_DATA pData, SHIMGVW_FILENODE *pNode) 409 { 410 Preview_pLoadImage(pData, (pNode ? pNode->FileName : NULL)); 411 } 412 413 static BOOL 414 Preview_pSaveImage(PPREVIEW_DATA pData, LPCWSTR pszFile) 415 { 416 ImageCodecInfo *codecInfo; 417 GUID rawFormat; 418 UINT j, num, nFilterIndex, size; 419 BOOL ret = FALSE; 420 421 if (g_pImage == NULL) 422 return FALSE; 423 424 GdipGetImageEncodersSize(&num, &size); 425 codecInfo = QuickAlloc(size, FALSE); 426 if (!codecInfo) 427 { 428 DPRINT1("QuickAlloc() failed in pSaveImage()\n"); 429 return FALSE; 430 } 431 GdipGetImageEncoders(num, size, codecInfo); 432 433 GdipGetImageRawFormat(g_pImage, &rawFormat); 434 if (IsEqualGUID(&rawFormat, &ImageFormatMemoryBMP)) 435 rawFormat = ImageFormatBMP; 436 437 nFilterIndex = 0; 438 for (j = 0; j < num; ++j) 439 { 440 if (IsEqualGUID(&rawFormat, &codecInfo[j].FormatID)) 441 { 442 nFilterIndex = j + 1; 443 break; 444 } 445 } 446 447 Anime_Pause(&pData->m_Anime); 448 449 ret = (nFilterIndex > 0) && 450 (GdipSaveImageToFile(g_pImage, pszFile, &codecInfo[nFilterIndex - 1].Clsid, NULL) == Ok); 451 if (!ret) 452 DPRINT1("GdipSaveImageToFile() failed\n"); 453 454 Anime_Start(&pData->m_Anime, 0); 455 456 QuickFree(codecInfo); 457 return ret; 458 } 459 460 static VOID 461 Preview_pSaveImageAs(PPREVIEW_DATA pData) 462 { 463 OPENFILENAMEW sfn; 464 ImageCodecInfo *codecInfo; 465 WCHAR szSaveFileName[MAX_PATH]; 466 WCHAR *szFilterMask; 467 GUID rawFormat; 468 UINT num, size, j; 469 size_t sizeRemain; 470 WCHAR *c; 471 HWND hwnd = pData->m_hwnd; 472 473 if (g_pImage == NULL) 474 return; 475 476 GdipGetImageEncodersSize(&num, &size); 477 codecInfo = QuickAlloc(size, FALSE); 478 if (!codecInfo) 479 { 480 DPRINT1("QuickAlloc() failed in pSaveImageAs()\n"); 481 return; 482 } 483 484 GdipGetImageEncoders(num, size, codecInfo); 485 486 GdipGetImageRawFormat(g_pImage, &rawFormat); 487 if (IsEqualGUID(&rawFormat, &ImageFormatMemoryBMP)) 488 rawFormat = ImageFormatBMP; 489 490 sizeRemain = 0; 491 for (j = 0; j < num; ++j) 492 { 493 // 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. 494 sizeRemain = sizeRemain + (((wcslen(codecInfo[j].FormatDescription) + (wcslen(codecInfo[j].FilenameExtension) * 2) + 5) * sizeof(WCHAR))); 495 } 496 497 /* Add two more chars for the last terminator */ 498 sizeRemain += (sizeof(WCHAR) * 2); 499 500 szFilterMask = QuickAlloc(sizeRemain, FALSE); 501 if (!szFilterMask) 502 { 503 DPRINT1("cannot allocate memory for filter mask in pSaveImageAs()"); 504 QuickFree(codecInfo); 505 return; 506 } 507 508 ZeroMemory(szSaveFileName, sizeof(szSaveFileName)); 509 ZeroMemory(szFilterMask, sizeRemain); 510 ZeroMemory(&sfn, sizeof(sfn)); 511 sfn.lStructSize = sizeof(sfn); 512 sfn.hwndOwner = hwnd; 513 sfn.lpstrFile = szSaveFileName; 514 sfn.lpstrFilter = szFilterMask; 515 sfn.nMaxFile = _countof(szSaveFileName); 516 sfn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; 517 sfn.lpstrDefExt = L"png"; 518 519 c = szFilterMask; 520 521 for (j = 0; j < num; ++j) 522 { 523 StringCbPrintfExW(c, sizeRemain, &c, &sizeRemain, 0, L"%ls (%ls)", codecInfo[j].FormatDescription, codecInfo[j].FilenameExtension); 524 525 /* Skip the NULL character */ 526 c++; 527 sizeRemain -= sizeof(*c); 528 529 StringCbPrintfExW(c, sizeRemain, &c, &sizeRemain, 0, L"%ls", codecInfo[j].FilenameExtension); 530 531 /* Skip the NULL character */ 532 c++; 533 sizeRemain -= sizeof(*c); 534 535 if (IsEqualGUID(&rawFormat, &codecInfo[j].FormatID)) 536 { 537 sfn.nFilterIndex = j + 1; 538 } 539 } 540 541 if (GetSaveFileNameW(&sfn) && sfn.nFilterIndex > 0) 542 { 543 Anime_Pause(&pData->m_Anime); 544 545 if (GdipSaveImageToFile(g_pImage, szSaveFileName, &codecInfo[sfn.nFilterIndex - 1].Clsid, NULL) != Ok) 546 { 547 DPRINT1("GdipSaveImageToFile() failed\n"); 548 } 549 550 Anime_Start(&pData->m_Anime, 0); 551 } 552 553 QuickFree(szFilterMask); 554 QuickFree(codecInfo); 555 } 556 557 static VOID 558 Preview_pPrintImage(PPREVIEW_DATA pData) 559 { 560 /* FIXME */ 561 } 562 563 static VOID 564 Preview_UpdateUI(PPREVIEW_DATA pData) 565 { 566 BOOL bEnable = (g_pImage != NULL); 567 PostMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_SAVEAS, bEnable); 568 PostMessageW(pData->m_hwndToolBar, TB_ENABLEBUTTON, IDC_PRINT, bEnable); 569 } 570 571 static VOID 572 Preview_UpdateImage(PPREVIEW_DATA pData) 573 { 574 if (!Preview_IsMainWnd(pData->m_hwnd)) 575 Preview_ResetZoom(pData); 576 577 ZoomWnd_UpdateScroll(pData, TRUE); 578 } 579 580 static SHIMGVW_FILENODE* 581 pBuildFileList(LPCWSTR szFirstFile) 582 { 583 HANDLE hFindHandle; 584 WCHAR *extension, *buffer; 585 WCHAR szSearchPath[MAX_PATH]; 586 WCHAR szSearchMask[MAX_PATH]; 587 WCHAR szFileTypes[MAX_PATH]; 588 WIN32_FIND_DATAW findData; 589 SHIMGVW_FILENODE *currentNode = NULL; 590 SHIMGVW_FILENODE *root = NULL; 591 SHIMGVW_FILENODE *conductor = NULL; 592 ImageCodecInfo *codecInfo; 593 UINT num = 0, size = 0, ExtraSize = 0; 594 UINT j; 595 596 const PCWSTR ExtraExtensions = GetExtraExtensionsGdipList(); 597 const UINT ExtraCount = ExtraExtensions[0] ? 1 : 0; 598 if (ExtraCount) 599 ExtraSize += sizeof(*codecInfo) + (wcslen(ExtraExtensions) + 1) * sizeof(WCHAR); 600 601 StringCbCopyW(szSearchPath, sizeof(szSearchPath), szFirstFile); 602 PathRemoveFileSpecW(szSearchPath); 603 604 GdipGetImageDecodersSize(&num, &size); 605 codecInfo = QuickAlloc(size + ExtraSize, FALSE); 606 if (!codecInfo) 607 { 608 DPRINT1("QuickAlloc() failed in pLoadFileList()\n"); 609 return NULL; 610 } 611 612 GdipGetImageDecoders(num, size, codecInfo); 613 buffer = (PWSTR)((UINT_PTR)codecInfo + size + (sizeof(*codecInfo) * ExtraCount)); 614 if (ExtraCount) 615 codecInfo[num].FilenameExtension = wcscpy(buffer, ExtraExtensions); 616 num += ExtraCount; 617 618 root = QuickAlloc(sizeof(SHIMGVW_FILENODE), FALSE); 619 if (!root) 620 { 621 DPRINT1("QuickAlloc() failed in pLoadFileList()\n"); 622 QuickFree(codecInfo); 623 return NULL; 624 } 625 626 conductor = root; 627 628 for (j = 0; j < num; ++j) 629 { 630 // FIXME: Parse each FilenameExtension list to bypass szFileTypes limit 631 StringCbCopyW(szFileTypes, sizeof(szFileTypes), codecInfo[j].FilenameExtension); 632 633 extension = wcstok(szFileTypes, L";"); 634 while (extension != NULL) 635 { 636 PathCombineW(szSearchMask, szSearchPath, extension); 637 638 hFindHandle = FindFirstFileW(szSearchMask, &findData); 639 if (hFindHandle != INVALID_HANDLE_VALUE) 640 { 641 do 642 { 643 PathCombineW(conductor->FileName, szSearchPath, findData.cFileName); 644 645 // compare the name of the requested file with the one currently found. 646 // if the name matches, the current node is returned by the function. 647 if (_wcsicmp(szFirstFile, conductor->FileName) == 0) 648 { 649 currentNode = conductor; 650 } 651 652 conductor->Next = QuickAlloc(sizeof(SHIMGVW_FILENODE), FALSE); 653 654 // if QuickAlloc fails, make circular what we have and return it 655 if (!conductor->Next) 656 { 657 DPRINT1("QuickAlloc() failed in pLoadFileList()\n"); 658 659 conductor->Next = root; 660 root->Prev = conductor; 661 662 FindClose(hFindHandle); 663 QuickFree(codecInfo); 664 return conductor; 665 } 666 667 conductor->Next->Prev = conductor; 668 conductor = conductor->Next; 669 } 670 while (FindNextFileW(hFindHandle, &findData) != 0); 671 672 FindClose(hFindHandle); 673 } 674 675 extension = wcstok(NULL, L";"); 676 } 677 } 678 679 // we now have a node too much in the list. In case the requested file was not found, 680 // we use this node to store the name of it, otherwise we free it. 681 if (currentNode == NULL) 682 { 683 StringCchCopyW(conductor->FileName, MAX_PATH, szFirstFile); 684 currentNode = conductor; 685 } 686 else 687 { 688 conductor = conductor->Prev; 689 QuickFree(conductor->Next); 690 } 691 692 // link the last node with the first one to make the list circular 693 conductor->Next = root; 694 root->Prev = conductor; 695 conductor = currentNode; 696 697 QuickFree(codecInfo); 698 699 return conductor; 700 } 701 702 static VOID 703 pFreeFileList(SHIMGVW_FILENODE *root) 704 { 705 SHIMGVW_FILENODE *conductor; 706 707 if (!root) 708 return; 709 710 root->Prev->Next = NULL; 711 root->Prev = NULL; 712 713 while (root) 714 { 715 conductor = root; 716 root = conductor->Next; 717 QuickFree(conductor); 718 } 719 } 720 721 static HBRUSH CreateCheckerBoardBrush(VOID) 722 { 723 static const CHAR pattern[] = 724 "\x28\x00\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x01\x00\x04\x00\x00\x00" 725 "\x00\x00\x80\x00\x00\x00\x23\x2E\x00\x00\x23\x2E\x00\x00\x10\x00\x00\x00" 726 "\x00\x00\x00\x00\x99\x99\x99\x00\xCC\xCC\xCC\x00\x00\x00\x00\x00\x00\x00" 727 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 728 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 729 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11" 730 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00" 731 "\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 732 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 733 "\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00" 734 "\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11" 735 "\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11" 736 "\x00\x00\x00\x00\x11\x11\x11\x11\x00\x00\x00\x00\x11\x11\x11\x11"; 737 738 return CreateDIBPatternBrushPt(pattern, DIB_RGB_COLORS); 739 } 740 741 static VOID 742 ZoomWnd_OnDraw( 743 PPREVIEW_DATA pData, 744 HDC hdc, 745 LPRECT prcPaint, 746 LPRECT prcClient) 747 { 748 GpGraphics *graphics; 749 INT ZoomedWidth, ZoomedHeight; 750 RECT rect, rcClient = *prcClient; 751 HDC hdcMem; 752 HBRUSH hBrush; 753 HPEN hPen; 754 HGDIOBJ hbrOld, hbmOld, hPenOld; 755 UINT uFlags; 756 HBITMAP hbmMem; 757 SIZE paintSize = { prcPaint->right - prcPaint->left, prcPaint->bottom - prcPaint->top }; 758 COLORREF color0, color1; 759 GpImageAttributes *imageAttributes; 760 761 /* We use a memory bitmap to reduce flickering */ 762 hdcMem = CreateCompatibleDC(hdc); 763 hbmMem = CreateCompatibleBitmap(hdc, paintSize.cx, paintSize.cy); 764 hbmOld = SelectObject(hdcMem, hbmMem); 765 766 /* Choose colors */ 767 if (Preview_IsMainWnd(pData->m_hwnd)) 768 { 769 color0 = GetSysColor(COLOR_WINDOW); 770 color1 = GetSysColor(COLOR_WINDOWTEXT); 771 } 772 else 773 { 774 color0 = RGB(0, 0, 0); 775 color1 = RGB(255, 255, 255); 776 } 777 778 hBrush = CreateSolidBrush(color0); 779 SetBkColor(hdcMem, color0); 780 781 hPen = CreatePen(PS_SOLID, 1, color1); 782 SetTextColor(hdcMem, color1); 783 784 /* Fill background */ 785 SetRect(&rect, 0, 0, paintSize.cx, paintSize.cy); 786 FillRect(hdcMem, &rect, hBrush); 787 788 DeleteObject(hBrush); 789 790 if (g_pImage == NULL) 791 { 792 WCHAR szText[128]; 793 LoadStringW(g_hInstance, IDS_NOPREVIEW, szText, _countof(szText)); 794 795 SelectObject(hdcMem, GetStockFont(DEFAULT_GUI_FONT)); 796 OffsetRect(&rcClient, -prcPaint->left, -prcPaint->top); 797 DrawTextW(hdcMem, szText, -1, &rcClient, DT_SINGLELINE | DT_CENTER | DT_VCENTER | 798 DT_NOPREFIX); 799 } 800 else 801 { 802 UINT ImageWidth, ImageHeight; 803 804 GdipGetImageWidth(g_pImage, &ImageWidth); 805 GdipGetImageHeight(g_pImage, &ImageHeight); 806 807 ZoomedWidth = (ImageWidth * pData->m_nZoomPercents) / 100; 808 ZoomedHeight = (ImageHeight * pData->m_nZoomPercents) / 100; 809 810 GdipCreateFromHDC(hdcMem, &graphics); 811 if (!graphics) 812 { 813 DPRINT1("error: GdipCreateFromHDC\n"); 814 return; 815 } 816 817 GdipGetImageFlags(g_pImage, &uFlags); 818 819 if (pData->m_nZoomPercents % 100 == 0) 820 { 821 GdipSetInterpolationMode(graphics, InterpolationModeNearestNeighbor); 822 GdipSetSmoothingMode(graphics, SmoothingModeNone); 823 } 824 else 825 { 826 GdipSetInterpolationMode(graphics, InterpolationModeHighQualityBilinear); 827 GdipSetSmoothingMode(graphics, SmoothingModeHighQuality); 828 } 829 830 rect.left = (rcClient.right - ZoomedWidth ) / 2; 831 rect.top = (rcClient.bottom - ZoomedHeight) / 2; 832 rect.right = rect.left + ZoomedWidth; 833 rect.bottom = rect.top + ZoomedHeight; 834 OffsetRect(&rect, 835 -prcPaint->left - pData->m_xScrollOffset, 836 -prcPaint->top - pData->m_yScrollOffset); 837 838 InflateRect(&rect, +1, +1); /* Add Rectangle() pen width */ 839 840 /* Draw a rectangle. Fill by checker board if necessary */ 841 if (uFlags & (ImageFlagsHasAlpha | ImageFlagsHasTranslucent)) 842 hbrOld = SelectObject(hdcMem, CreateCheckerBoardBrush()); 843 else 844 hbrOld = SelectObject(hdcMem, GetStockBrush(NULL_BRUSH)); 845 hPenOld = SelectObject(hdcMem, hPen); 846 Rectangle(hdcMem, rect.left, rect.top, rect.right, rect.bottom); 847 DeleteObject(SelectObject(hdcMem, hbrOld)); 848 DeleteObject(SelectObject(hdcMem, hPenOld)); 849 850 InflateRect(&rect, -1, -1); /* Subtract Rectangle() pen width */ 851 852 /* Image attributes are required to draw image correctly */ 853 GdipCreateImageAttributes(&imageAttributes); 854 GdipSetImageAttributesWrapMode(imageAttributes, WrapModeTile, 855 GetBkColor(hdcMem) | 0xFF000000, TRUE); 856 857 /* Draw image. -0.5f is used for interpolation */ 858 GdipDrawImageRectRect(graphics, g_pImage, 859 rect.left, rect.top, 860 rect.right - rect.left, rect.bottom - rect.top, 861 -0.5f, -0.5f, ImageWidth, ImageHeight, 862 UnitPixel, imageAttributes, NULL, NULL); 863 864 GdipDisposeImageAttributes(imageAttributes); 865 GdipDeleteGraphics(graphics); 866 } 867 868 BitBlt(hdc, prcPaint->left, prcPaint->top, paintSize.cx, paintSize.cy, hdcMem, 0, 0, SRCCOPY); 869 DeleteObject(SelectObject(hdcMem, hbmOld)); 870 DeleteDC(hdcMem); 871 } 872 873 static VOID 874 ZoomWnd_OnPaint(PPREVIEW_DATA pData, HWND hwnd) 875 { 876 PAINTSTRUCT ps; 877 HDC hDC; 878 RECT rcClient; 879 880 hDC = BeginPaint(hwnd, &ps); 881 if (hDC) 882 { 883 GetClientRect(hwnd, &rcClient); 884 ZoomWnd_OnDraw(pData, hDC, &ps.rcPaint, &rcClient); 885 EndPaint(hwnd, &ps); 886 } 887 } 888 889 static VOID 890 ImageView_ResetSettings(VOID) 891 { 892 g_Settings.Maximized = FALSE; 893 g_Settings.X = CW_USEDEFAULT; 894 g_Settings.Y = CW_USEDEFAULT; 895 g_Settings.Width = 520; 896 g_Settings.Height = 400; 897 } 898 899 static BOOL 900 ImageView_LoadSettings(VOID) 901 { 902 HKEY hKey; 903 DWORD dwSize; 904 LSTATUS nError; 905 906 nError = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\ReactOS\\shimgvw", 0, KEY_READ, &hKey); 907 if (nError != ERROR_SUCCESS) 908 return FALSE; 909 910 dwSize = sizeof(g_Settings); 911 nError = RegQueryValueExW(hKey, L"Settings", NULL, NULL, (LPBYTE)&g_Settings, &dwSize); 912 RegCloseKey(hKey); 913 914 return ((nError == ERROR_SUCCESS) && (dwSize == sizeof(g_Settings))); 915 } 916 917 static VOID 918 ImageView_SaveSettings(VOID) 919 { 920 HKEY hKey; 921 LSTATUS nError; 922 923 nError = RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\ReactOS\\shimgvw", 924 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); 925 if (nError != ERROR_SUCCESS) 926 return; 927 928 RegSetValueExW(hKey, L"Settings", 0, REG_BINARY, (LPBYTE)&g_Settings, sizeof(g_Settings)); 929 RegCloseKey(hKey); 930 } 931 932 static BOOL 933 Preview_CreateToolBar(PPREVIEW_DATA pData) 934 { 935 HWND hwndToolBar; 936 HIMAGELIST hImageList, hOldImageList; 937 DWORD style = WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS; 938 939 if (!Preview_IsMainWnd(pData->m_hwnd)) 940 return TRUE; /* FIXME */ 941 942 style |= CCS_BOTTOM; 943 hwndToolBar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style, 944 0, 0, 0, 0, pData->m_hwnd, NULL, g_hInstance, NULL); 945 if (!hwndToolBar) 946 return FALSE; 947 948 pData->m_hwndToolBar = hwndToolBar; 949 950 SendMessageW(hwndToolBar, TB_BUTTONSTRUCTSIZE, sizeof(s_Buttons[0]), 0); 951 SendMessageW(hwndToolBar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); 952 953 hImageList = ImageList_Create(TB_IMAGE_WIDTH, TB_IMAGE_HEIGHT, ILC_MASK | ILC_COLOR24, 1, 1); 954 if (hImageList == NULL) 955 return FALSE; 956 957 for (UINT n = 0; n < _countof(s_ButtonConfig); n++) 958 { 959 HBITMAP hBitmap = LoadBitmapW(g_hInstance, MAKEINTRESOURCEW(s_ButtonConfig[n].idb)); 960 ImageList_AddMasked(hImageList, hBitmap, RGB(255, 255, 255)); 961 DeleteObject(hBitmap); 962 } 963 964 hOldImageList = (HIMAGELIST)SendMessageW(hwndToolBar, TB_SETIMAGELIST, 0, (LPARAM)hImageList); 965 ImageList_Destroy(hOldImageList); 966 967 SendMessageW(hwndToolBar, TB_ADDBUTTONS, _countof(s_Buttons), (LPARAM)s_Buttons); 968 969 return TRUE; 970 } 971 972 static VOID 973 Preview_EndSlideShow(HWND hwnd) 974 { 975 if (Preview_IsMainWnd(hwnd)) 976 return; 977 978 KillTimer(hwnd, SLIDESHOW_TIMER_ID); 979 ShowWindow(g_hMainWnd, SW_SHOW); 980 ShowWindow(hwnd, SW_HIDE); 981 Preview_ResetZoom(Preview_GetData(g_hMainWnd)); 982 } 983 984 static VOID 985 GenerateSetCursor(HWND hwnd, UINT uMsg) 986 { 987 SendMessage(hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG(HTCLIENT, uMsg)); 988 } 989 990 static VOID 991 ZoomWnd_StopHideCursor(PPREVIEW_DATA pData) 992 { 993 pData->m_bHideCursor = FALSE; 994 KillTimer(pData->m_hwndZoom, HIDECURSOR_TIMER_ID); 995 } 996 997 static VOID 998 ZoomWnd_OnButtonDown(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 999 { 1000 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1001 HWND hParent = GetParent(hwnd); 1002 if ((uMsg == WM_LBUTTONDOWN) || (uMsg == WM_RBUTTONDOWN)) 1003 { 1004 if (!Preview_IsMainWnd(hParent)) 1005 Preview_EndSlideShow(hParent); 1006 return; 1007 } 1008 1009 ZoomWnd_StopHideCursor(pData); 1010 pData->m_nMouseDownMsg = uMsg; 1011 pData->m_ptOrigin.x = GET_X_LPARAM(lParam); 1012 pData->m_ptOrigin.y = GET_Y_LPARAM(lParam); 1013 SetCapture(hwnd); 1014 SetCursor(LoadCursorW(g_hInstance, MAKEINTRESOURCEW(IDC_HANDDRAG))); 1015 } 1016 1017 static VOID 1018 ZoomWnd_OnMouseMove(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1019 { 1020 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1021 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 1022 1023 if (!Preview_IsMainWnd(pData->m_hwnd)) 1024 { 1025 ZoomWnd_StopHideCursor(pData); 1026 if (!pData->m_nMouseDownMsg) 1027 SetTimer(hwnd, HIDECURSOR_TIMER_ID, HIDECURSOR_TIMER_TIMEOUT, NULL); 1028 } 1029 1030 if (pData->m_nMouseDownMsg == WM_MBUTTONDOWN) 1031 { 1032 INT x = GetScrollPos(hwnd, SB_HORZ) - (pt.x - pData->m_ptOrigin.x); 1033 INT y = GetScrollPos(hwnd, SB_VERT) - (pt.y - pData->m_ptOrigin.y); 1034 SendMessageW(hwnd, WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0); 1035 SendMessageW(hwnd, WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, y), 0); 1036 pData->m_ptOrigin = pt; 1037 } 1038 } 1039 1040 static BOOL 1041 ZoomWnd_OnSetCursor(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1042 { 1043 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1044 if (pData->m_nMouseDownMsg == WM_MBUTTONDOWN) 1045 { 1046 SetCursor(LoadCursorW(g_hInstance, MAKEINTRESOURCEW(IDC_HANDDRAG))); 1047 return TRUE; 1048 } 1049 1050 if (pData->m_bHideCursor) 1051 { 1052 SetCursor(NULL); /* Hide cursor in fullscreen */ 1053 return TRUE; 1054 } 1055 return FALSE; 1056 } 1057 1058 static VOID 1059 ZoomWnd_OnButtonUp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1060 { 1061 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1062 BOOL wasdrag = pData->m_nMouseDownMsg == WM_MBUTTONDOWN; 1063 1064 pData->m_nMouseDownMsg = 0; 1065 if (wasdrag) 1066 GenerateSetCursor(hwnd, uMsg); /* Reset to default cursor */ 1067 ReleaseCapture(); 1068 1069 if (!Preview_IsMainWnd(pData->m_hwnd)) 1070 SetTimer(hwnd, HIDECURSOR_TIMER_ID, HIDECURSOR_TIMER_TIMEOUT, NULL); 1071 } 1072 1073 static VOID 1074 ZoomWnd_OnHVScroll(PPREVIEW_DATA pData, HWND hwnd, WPARAM wParam, BOOL bVertical) 1075 { 1076 UINT ImageWidth, ImageHeight, ZoomedWidth, ZoomedHeight; 1077 RECT rcClient; 1078 UINT nBar = (bVertical ? SB_VERT : SB_HORZ); 1079 SCROLLINFO si = { sizeof(si), SIF_ALL }; 1080 GetScrollInfo(hwnd, nBar, &si); 1081 1082 if (!g_pImage) 1083 return; 1084 1085 if (bVertical) 1086 { 1087 if (!(GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_VSCROLL)) 1088 return; 1089 } 1090 else 1091 { 1092 if (!(GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_HSCROLL)) 1093 return; 1094 } 1095 1096 switch (LOWORD(wParam)) 1097 { 1098 case SB_THUMBTRACK: 1099 case SB_THUMBPOSITION: 1100 si.nPos = (SHORT)HIWORD(wParam); 1101 break; 1102 case SB_LINELEFT: 1103 si.nPos -= 48; 1104 break; 1105 case SB_LINERIGHT: 1106 si.nPos += 48; 1107 break; 1108 case SB_PAGELEFT: 1109 si.nPos -= si.nPage; 1110 break; 1111 case SB_PAGERIGHT: 1112 si.nPos += si.nPage; 1113 break; 1114 } 1115 1116 si.fMask = SIF_POS; 1117 SetScrollInfo(hwnd, nBar, &si, TRUE); 1118 GetScrollInfo(hwnd, nBar, &si); 1119 1120 GetClientRect(hwnd, &rcClient); 1121 1122 if (bVertical) 1123 { 1124 GdipGetImageHeight(g_pImage, &ImageHeight); 1125 ZoomedHeight = (ImageHeight * pData->m_nZoomPercents) / 100; 1126 pData->m_yScrollOffset = si.nPos - (ZoomedHeight - rcClient.bottom) / 2; 1127 } 1128 else 1129 { 1130 GdipGetImageWidth(g_pImage, &ImageWidth); 1131 ZoomedWidth = (ImageWidth * pData->m_nZoomPercents) / 100; 1132 pData->m_xScrollOffset = si.nPos - (ZoomedWidth - rcClient.right) / 2; 1133 } 1134 1135 InvalidateRect(hwnd, NULL, TRUE); 1136 } 1137 1138 static VOID 1139 ZoomWnd_OnMouseWheel(HWND hwnd, INT x, INT y, INT zDelta, UINT fwKeys) 1140 { 1141 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1142 if (zDelta == 0) 1143 return; 1144 1145 if (GetKeyState(VK_CONTROL) < 0) 1146 { 1147 Preview_ZoomInOrOut(pData, zDelta > 0); 1148 } 1149 else if (GetKeyState(VK_SHIFT) < 0) 1150 { 1151 if (zDelta > 0) 1152 SendMessageW(hwnd, WM_HSCROLL, SB_LINELEFT, 0); 1153 else 1154 SendMessageW(hwnd, WM_HSCROLL, SB_LINERIGHT, 0); 1155 } 1156 else 1157 { 1158 if (zDelta > 0) 1159 SendMessageW(hwnd, WM_VSCROLL, SB_LINEUP, 0); 1160 else 1161 SendMessageW(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); 1162 } 1163 } 1164 1165 LRESULT CALLBACK 1166 ZoomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1167 { 1168 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1169 switch (uMsg) 1170 { 1171 case WM_LBUTTONDOWN: 1172 case WM_MBUTTONDOWN: 1173 case WM_RBUTTONDOWN: 1174 { 1175 ZoomWnd_OnButtonDown(hwnd, uMsg, wParam, lParam); 1176 break; 1177 } 1178 case WM_MOUSEMOVE: 1179 { 1180 ZoomWnd_OnMouseMove(hwnd, uMsg, wParam, lParam); 1181 break; 1182 } 1183 case WM_SETCURSOR: 1184 { 1185 if (!ZoomWnd_OnSetCursor(hwnd, uMsg, wParam, lParam)) 1186 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 1187 } 1188 case WM_LBUTTONUP: 1189 case WM_MBUTTONUP: 1190 case WM_RBUTTONUP: 1191 { 1192 ZoomWnd_OnButtonUp(hwnd, uMsg, wParam, lParam); 1193 goto doDefault; 1194 } 1195 case WM_LBUTTONDBLCLK: 1196 { 1197 if (Preview_IsMainWnd(pData->m_hwnd)) 1198 Preview_ToggleSlideShowEx(pData, FALSE); 1199 break; 1200 } 1201 case WM_PAINT: 1202 { 1203 ZoomWnd_OnPaint(pData, hwnd); 1204 break; 1205 } 1206 case WM_MOUSEWHEEL: 1207 { 1208 ZoomWnd_OnMouseWheel(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 1209 (SHORT)HIWORD(wParam), (UINT)LOWORD(wParam)); 1210 break; 1211 } 1212 case WM_CONTEXTMENU: 1213 if (Preview_IsMainWnd(pData->m_hwnd)) 1214 DoShellContextMenuOnFile(hwnd, pData->m_szFile, lParam); 1215 break; 1216 case WM_HSCROLL: 1217 case WM_VSCROLL: 1218 ZoomWnd_OnHVScroll(pData, hwnd, wParam, uMsg == WM_VSCROLL); 1219 break; 1220 case WM_TIMER: 1221 { 1222 if (wParam == HIDECURSOR_TIMER_ID) 1223 { 1224 ZoomWnd_StopHideCursor(pData); 1225 if (IsWindowVisible(hwnd)) 1226 { 1227 pData->m_bHideCursor = TRUE; 1228 GenerateSetCursor(hwnd, uMsg); 1229 } 1230 } 1231 if (Anime_OnTimer(&pData->m_Anime, wParam)) 1232 { 1233 InvalidateRect(hwnd, NULL, FALSE); 1234 } 1235 break; 1236 } 1237 default: doDefault: 1238 { 1239 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 1240 } 1241 } 1242 return 0; 1243 } 1244 1245 static BOOL 1246 Preview_OnCreate(HWND hwnd, LPCREATESTRUCT pCS) 1247 { 1248 DWORD exstyle = 0; 1249 HWND hwndZoom; 1250 PPREVIEW_DATA pData = QuickAlloc(sizeof(PREVIEW_DATA), TRUE); 1251 pData->m_hwnd = hwnd; 1252 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pData); 1253 1254 DragAcceptFiles(hwnd, TRUE); 1255 1256 if (g_hMainWnd == NULL) 1257 { 1258 g_hMainWnd = hwnd; 1259 exstyle |= WS_EX_CLIENTEDGE; 1260 } 1261 else if (g_hwndFullscreen == NULL) 1262 { 1263 g_hwndFullscreen = hwnd; 1264 } 1265 else 1266 { 1267 return FALSE; 1268 } 1269 1270 hwndZoom = CreateWindowExW(exstyle, WC_ZOOM, NULL, WS_CHILD | WS_VISIBLE, 1271 0, 0, 0, 0, hwnd, NULL, g_hInstance, NULL); 1272 if (!hwndZoom) 1273 { 1274 QuickFree(pData); 1275 return FALSE; 1276 } 1277 1278 pData->m_hwndZoom = hwndZoom; 1279 SetWindowLongPtrW(hwndZoom, GWLP_USERDATA, (LONG_PTR)pData); 1280 Anime_SetTimerWnd(&pData->m_Anime, pData->m_hwndZoom); 1281 1282 if (!Preview_CreateToolBar(pData)) 1283 { 1284 QuickFree(pData); 1285 return FALSE; 1286 } 1287 1288 if (pCS && pCS->lpCreateParams) 1289 { 1290 LPCWSTR pszFileName = (LPCWSTR)pCS->lpCreateParams; 1291 WCHAR szFile[MAX_PATH]; 1292 1293 /* Make sure the path has no quotes on it */ 1294 StringCchCopyW(szFile, _countof(szFile), pszFileName); 1295 PathUnquoteSpacesW(szFile); 1296 1297 g_pCurrentFile = pBuildFileList(szFile); 1298 Preview_pLoadImageFromNode(pData, g_pCurrentFile); 1299 Preview_UpdateImage(pData); 1300 Preview_UpdateUI(pData); 1301 } 1302 1303 return TRUE; 1304 } 1305 1306 static VOID 1307 Preview_OnMoveSize(HWND hwnd) 1308 { 1309 WINDOWPLACEMENT wp; 1310 RECT *prc; 1311 1312 if (IsIconic(hwnd) || !Preview_IsMainWnd(hwnd)) 1313 return; 1314 1315 wp.length = sizeof(WINDOWPLACEMENT); 1316 GetWindowPlacement(hwnd, &wp); 1317 1318 /* Remember window position and size */ 1319 prc = &wp.rcNormalPosition; 1320 g_Settings.X = prc->left; 1321 g_Settings.Y = prc->top; 1322 g_Settings.Width = prc->right - prc->left; 1323 g_Settings.Height = prc->bottom - prc->top; 1324 g_Settings.Maximized = IsZoomed(hwnd); 1325 } 1326 1327 static VOID 1328 Preview_OnSize(HWND hwnd) 1329 { 1330 RECT rc, rcClient; 1331 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1332 HWND hToolBar = pData->m_hwndToolBar; 1333 INT cx, cy; 1334 1335 /* We want 32-bit values. Don't use WM_SIZE lParam */ 1336 GetClientRect(hwnd, &rcClient); 1337 cx = rcClient.right; 1338 cy = rcClient.bottom; 1339 1340 if (Preview_IsMainWnd(pData->m_hwnd)) 1341 { 1342 SendMessageW(hToolBar, TB_AUTOSIZE, 0, 0); 1343 GetWindowRect(hToolBar, &rc); 1344 1345 MoveWindow(pData->m_hwndZoom, 0, 0, cx, cy - (rc.bottom - rc.top), TRUE); 1346 1347 if (pData->m_nZoomPercents > 100) 1348 ZoomWnd_UpdateScroll(pData, FALSE); 1349 else if (!IsIconic(hwnd)) /* Is it not minimized? */ 1350 Preview_ResetZoom(pData); 1351 1352 Preview_OnMoveSize(hwnd); 1353 } 1354 else 1355 { 1356 MoveWindow(pData->m_hwndZoom, 0, 0, cx, cy, TRUE); 1357 } 1358 } 1359 1360 static VOID 1361 Preview_Delete(PPREVIEW_DATA pData) 1362 { 1363 WCHAR szCurFile[MAX_PATH + 1], szNextFile[MAX_PATH]; 1364 HWND hwnd = pData->m_hwnd; 1365 SHFILEOPSTRUCTW FileOp = { hwnd, FO_DELETE }; 1366 1367 if (!pData->m_szFile[0]) 1368 return; 1369 1370 /* FileOp.pFrom must be double-null-terminated */ 1371 GetFullPathNameW(pData->m_szFile, _countof(szCurFile) - 1, szCurFile, NULL); 1372 szCurFile[_countof(szCurFile) - 2] = UNICODE_NULL; /* Avoid buffer overrun */ 1373 szCurFile[lstrlenW(szCurFile) + 1] = UNICODE_NULL; 1374 1375 szNextFile[0] = UNICODE_NULL; 1376 if (g_pCurrentFile) 1377 { 1378 GetFullPathNameW(g_pCurrentFile->Next->FileName, _countof(szNextFile), szNextFile, NULL); 1379 szNextFile[_countof(szNextFile) - 1] = UNICODE_NULL; /* Avoid buffer overrun */ 1380 } 1381 1382 /* Confirm file deletion and delete if allowed */ 1383 FileOp.pFrom = szCurFile; 1384 FileOp.fFlags = FOF_ALLOWUNDO; 1385 if (SHFileOperationW(&FileOp) != 0) 1386 { 1387 DPRINT("Preview_Delete: SHFileOperationW() failed or canceled\n"); 1388 return; 1389 } 1390 1391 /* Reload the file list and go next file */ 1392 pFreeFileList(g_pCurrentFile); 1393 g_pCurrentFile = pBuildFileList(szNextFile); 1394 Preview_pLoadImageFromNode(pData, g_pCurrentFile); 1395 } 1396 1397 static VOID 1398 Preview_Edit(HWND hwnd) 1399 { 1400 SHELLEXECUTEINFOW sei; 1401 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1402 1403 if (!pData->m_szFile[0]) 1404 return; 1405 1406 ZeroMemory(&sei, sizeof(sei)); 1407 sei.cbSize = sizeof(sei); 1408 sei.lpVerb = L"edit"; 1409 sei.lpFile = pData->m_szFile; 1410 sei.nShow = SW_SHOWNORMAL; 1411 if (!ShellExecuteExW(&sei)) 1412 { 1413 DPRINT1("Preview_Edit: ShellExecuteExW() failed with code %ld\n", GetLastError()); 1414 } 1415 else 1416 { 1417 // Destroy the window to quit the application 1418 DestroyWindow(hwnd); 1419 } 1420 } 1421 1422 static VOID 1423 Preview_ToggleSlideShowEx(PPREVIEW_DATA pData, BOOL StartTimer) 1424 { 1425 if (!IsWindow(g_hwndFullscreen)) 1426 { 1427 DWORD style = WS_POPUP | WS_CLIPSIBLINGS, exstyle = WS_EX_TOPMOST; 1428 WCHAR szTitle[256]; 1429 LoadStringW(g_hInstance, IDS_APPTITLE, szTitle, _countof(szTitle)); 1430 g_hwndFullscreen = CreateWindowExW(exstyle, WC_PREVIEW, szTitle, style, 1431 0, 0, 0, 0, NULL, NULL, g_hInstance, NULL); 1432 } 1433 1434 if (IsWindowVisible(g_hwndFullscreen)) 1435 { 1436 Preview_EndSlideShow(g_hwndFullscreen); 1437 } 1438 else 1439 { 1440 PPREVIEW_DATA pSlideData = Preview_GetData(g_hwndFullscreen); 1441 pSlideData->m_nTimerInterval = StartTimer ? SLIDESHOW_TIMER_INTERVAL : 0; 1442 ShowWindow(g_hwndFullscreen, SW_SHOWMAXIMIZED); 1443 ShowWindow(g_hMainWnd, SW_HIDE); 1444 Preview_ResetZoom(pSlideData); 1445 Preview_RestartTimer(g_hwndFullscreen); 1446 PostMessage(pSlideData->m_hwndZoom, WM_MOUSEMOVE, 0, 0); /* Start hide cursor */ 1447 } 1448 } 1449 1450 static inline VOID 1451 Preview_ToggleSlideShow(PPREVIEW_DATA pData) 1452 { 1453 Preview_ToggleSlideShowEx(pData, TRUE); 1454 } 1455 1456 static VOID 1457 Preview_GoNextPic(PPREVIEW_DATA pData, BOOL bNext) 1458 { 1459 Preview_RestartTimer(pData->m_hwnd); 1460 if (g_pCurrentFile) 1461 { 1462 if (bNext) 1463 g_pCurrentFile = g_pCurrentFile->Next; 1464 else 1465 g_pCurrentFile = g_pCurrentFile->Prev; 1466 Preview_pLoadImageFromNode(pData, g_pCurrentFile); 1467 Preview_UpdateImage(pData); 1468 Preview_UpdateUI(pData); 1469 } 1470 } 1471 1472 static VOID 1473 Preview_OnCommand(HWND hwnd, UINT nCommandID) 1474 { 1475 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1476 1477 switch (nCommandID) 1478 { 1479 case IDC_PREV_PIC: 1480 Preview_GoNextPic(pData, FALSE); 1481 break; 1482 1483 case IDC_NEXT_PIC: 1484 Preview_GoNextPic(pData, TRUE); 1485 break; 1486 1487 case IDC_BEST_FIT: 1488 Preview_ResetZoom(pData); 1489 break; 1490 1491 case IDC_REAL_SIZE: 1492 Preview_UpdateZoom(pData, 100, TRUE, FALSE); 1493 break; 1494 1495 case IDC_SLIDE_SHOW: 1496 Preview_ToggleSlideShow(pData); 1497 break; 1498 1499 case IDC_ZOOM_IN: 1500 Preview_ZoomInOrOut(pData, TRUE); 1501 break; 1502 1503 case IDC_ZOOM_OUT: 1504 Preview_ZoomInOrOut(pData, FALSE); 1505 break; 1506 1507 case IDC_ENDSLIDESHOW: 1508 Preview_EndSlideShow(hwnd); 1509 break; 1510 1511 case IDC_TOGGLEFULLSCREEN: 1512 Preview_ToggleSlideShowEx(pData, FALSE); 1513 break; 1514 1515 case IDC_INCTIMER: 1516 case IDC_DECTIMER: 1517 Preview_ChangeSlideShowTimer(pData, nCommandID == IDC_INCTIMER); 1518 break; 1519 1520 default: 1521 break; 1522 } 1523 1524 if (!Preview_IsMainWnd(hwnd)) 1525 return; 1526 1527 // The following commands are for main window only: 1528 switch (nCommandID) 1529 { 1530 case IDC_SAVEAS: 1531 Preview_pSaveImageAs(pData); 1532 break; 1533 1534 case IDC_PRINT: 1535 Preview_pPrintImage(pData); 1536 break; 1537 1538 case IDC_ROT_CLOCKW: 1539 if (g_pImage) 1540 { 1541 GdipImageRotateFlip(g_pImage, Rotate270FlipNone); 1542 Preview_UpdateImage(pData); 1543 } 1544 break; 1545 1546 case IDC_ROT_COUNCW: 1547 if (g_pImage) 1548 { 1549 GdipImageRotateFlip(g_pImage, Rotate90FlipNone); 1550 Preview_UpdateImage(pData); 1551 } 1552 break; 1553 1554 case IDC_ROT_CWSAVE: 1555 if (g_pImage) 1556 { 1557 GdipImageRotateFlip(g_pImage, Rotate270FlipNone); 1558 Preview_pSaveImage(pData, pData->m_szFile); 1559 Preview_UpdateImage(pData); 1560 } 1561 break; 1562 1563 case IDC_ROT_CCWSAVE: 1564 if (g_pImage) 1565 { 1566 GdipImageRotateFlip(g_pImage, Rotate90FlipNone); 1567 Preview_pSaveImage(pData, pData->m_szFile); 1568 Preview_UpdateImage(pData); 1569 } 1570 break; 1571 1572 case IDC_DELETE: 1573 Preview_Delete(pData); 1574 Preview_UpdateImage(pData); 1575 Preview_UpdateUI(pData); 1576 break; 1577 1578 case IDC_MODIFY: 1579 Preview_Edit(hwnd); 1580 break; 1581 1582 case IDC_HELP_TOC: 1583 DisplayHelp(hwnd); 1584 break; 1585 1586 default: 1587 break; 1588 } 1589 } 1590 1591 static LRESULT 1592 Preview_OnNotify(HWND hwnd, LPNMHDR pnmhdr) 1593 { 1594 switch (pnmhdr->code) 1595 { 1596 case TTN_GETDISPINFOW: 1597 { 1598 LPTOOLTIPTEXTW lpttt = (LPTOOLTIPTEXTW)pnmhdr; 1599 lpttt->hinst = g_hInstance; 1600 lpttt->lpszText = MAKEINTRESOURCEW(s_ButtonConfig[lpttt->hdr.idFrom - IDC_TOOL_BASE].ids); 1601 break; 1602 } 1603 } 1604 return 0; 1605 } 1606 1607 static VOID 1608 Preview_OnDestroy(HWND hwnd) 1609 { 1610 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1611 1612 KillTimer(hwnd, SLIDESHOW_TIMER_ID); 1613 KillTimer(hwnd, HIDECURSOR_TIMER_ID); 1614 1615 pFreeFileList(g_pCurrentFile); 1616 g_pCurrentFile = NULL; 1617 1618 Preview_pFreeImage(pData); 1619 1620 SetWindowLongPtrW(pData->m_hwndZoom, GWLP_USERDATA, 0); 1621 DestroyWindow(pData->m_hwndZoom); 1622 pData->m_hwndZoom = NULL; 1623 1624 DestroyWindow(pData->m_hwndToolBar); 1625 pData->m_hwndToolBar = NULL; 1626 1627 SetWindowLongPtrW(pData->m_hwnd, GWLP_USERDATA, 0); 1628 QuickFree(pData); 1629 1630 PostQuitMessage(0); 1631 } 1632 1633 static VOID 1634 Preview_OnDropFiles(HWND hwnd, HDROP hDrop) 1635 { 1636 WCHAR szFile[MAX_PATH]; 1637 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1638 1639 DragQueryFileW(hDrop, 0, szFile, _countof(szFile)); 1640 1641 pFreeFileList(g_pCurrentFile); 1642 g_pCurrentFile = pBuildFileList(szFile); 1643 Preview_pLoadImageFromNode(pData, g_pCurrentFile); 1644 1645 DragFinish(hDrop); 1646 } 1647 1648 LRESULT CALLBACK 1649 PreviewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1650 { 1651 switch (uMsg) 1652 { 1653 case WM_CREATE: 1654 { 1655 if (!Preview_OnCreate(hwnd, (LPCREATESTRUCT)lParam)) 1656 return -1; 1657 break; 1658 } 1659 case WM_COMMAND: 1660 { 1661 Preview_OnCommand(hwnd, LOWORD(wParam)); 1662 break; 1663 } 1664 case WM_NOTIFY: 1665 { 1666 return Preview_OnNotify(hwnd, (LPNMHDR)lParam); 1667 } 1668 case WM_GETMINMAXINFO: 1669 { 1670 MINMAXINFO *pMMI = (MINMAXINFO*)lParam; 1671 pMMI->ptMinTrackSize.x = 350; 1672 pMMI->ptMinTrackSize.y = 290; 1673 break; 1674 } 1675 case WM_MOVE: 1676 { 1677 Preview_OnMoveSize(hwnd); 1678 break; 1679 } 1680 case WM_SIZE: 1681 { 1682 Preview_OnSize(hwnd); 1683 break; 1684 } 1685 case WM_DROPFILES: 1686 { 1687 Preview_OnDropFiles(hwnd, (HDROP)wParam); 1688 break; 1689 } 1690 case WM_SYSCOLORCHANGE: 1691 { 1692 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1693 InvalidateRect(pData->m_hwnd, NULL, TRUE); 1694 InvalidateRect(pData->m_hwndZoom, NULL, TRUE); 1695 break; 1696 } 1697 case WM_DESTROY: 1698 { 1699 Preview_OnDestroy(hwnd); 1700 break; 1701 } 1702 case WM_CONTEXTMENU: 1703 { 1704 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1705 if ((int)lParam == -1) 1706 return ZoomWndProc(pData->m_hwndZoom, uMsg, wParam, lParam); 1707 break; 1708 } 1709 case WM_TIMER: 1710 { 1711 if (wParam == SLIDESHOW_TIMER_ID) 1712 { 1713 PPREVIEW_DATA pData = Preview_GetData(hwnd); 1714 Preview_GoNextPic(pData, TRUE); 1715 } 1716 break; 1717 } 1718 default: 1719 { 1720 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 1721 } 1722 } 1723 1724 return 0; 1725 } 1726 1727 LONG 1728 ImageView_Main(HWND hwnd, LPCWSTR szFileName) 1729 { 1730 struct GdiplusStartupInput gdiplusStartupInput; 1731 ULONG_PTR gdiplusToken; 1732 WNDCLASSW WndClass; 1733 WCHAR szTitle[256]; 1734 HWND hMainWnd; 1735 MSG msg; 1736 HACCEL hAccel; 1737 HRESULT hrCoInit; 1738 INITCOMMONCONTROLSEX Icc = { .dwSize = sizeof(Icc), .dwICC = ICC_WIN95_CLASSES }; 1739 1740 InitCommonControlsEx(&Icc); 1741 1742 /* Initialize COM */ 1743 hrCoInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); 1744 if (FAILED(hrCoInit)) 1745 DPRINT1("Warning, CoInitializeEx failed with code=%08X\n", (int)hrCoInit); 1746 1747 if (!ImageView_LoadSettings()) 1748 ImageView_ResetSettings(); 1749 1750 /* Initialize GDI+ */ 1751 ZeroMemory(&gdiplusStartupInput, sizeof(gdiplusStartupInput)); 1752 gdiplusStartupInput.GdiplusVersion = 1; 1753 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 1754 1755 /* Register window classes */ 1756 ZeroMemory(&WndClass, sizeof(WndClass)); 1757 WndClass.lpszClassName = WC_PREVIEW; 1758 WndClass.lpfnWndProc = PreviewWndProc; 1759 WndClass.hInstance = g_hInstance; 1760 WndClass.style = CS_HREDRAW | CS_VREDRAW; 1761 WndClass.hIcon = LoadIconW(g_hInstance, MAKEINTRESOURCEW(IDI_APP_ICON)); 1762 WndClass.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); 1763 WndClass.hbrBackground = GetStockBrush(NULL_BRUSH); /* less flicker */ 1764 if (!RegisterClassW(&WndClass)) 1765 return -1; 1766 WndClass.lpszClassName = WC_ZOOM; 1767 WndClass.lpfnWndProc = ZoomWndProc; 1768 WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; 1769 WndClass.hbrBackground = GetStockBrush(NULL_BRUSH); /* less flicker */ 1770 if (!RegisterClassW(&WndClass)) 1771 return -1; 1772 1773 /* Create the main window */ 1774 LoadStringW(g_hInstance, IDS_APPTITLE, szTitle, _countof(szTitle)); 1775 hMainWnd = CreateWindowExW(WS_EX_WINDOWEDGE, WC_PREVIEW, szTitle, 1776 WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, 1777 g_Settings.X, g_Settings.Y, g_Settings.Width, g_Settings.Height, 1778 NULL, NULL, g_hInstance, (LPVOID)szFileName); 1779 1780 /* Create accelerator table for keystrokes */ 1781 hAccel = LoadAcceleratorsW(g_hInstance, MAKEINTRESOURCEW(IDR_ACCELERATOR)); 1782 1783 /* Show the main window now */ 1784 if (g_Settings.Maximized) 1785 ShowWindow(hMainWnd, SW_SHOWMAXIMIZED); 1786 else 1787 ShowWindow(hMainWnd, SW_SHOWNORMAL); 1788 1789 UpdateWindow(hMainWnd); 1790 1791 /* Message Loop */ 1792 while (GetMessageW(&msg, NULL, 0, 0) > 0) 1793 { 1794 const HWND hwndFull = g_hwndFullscreen; 1795 if (IsWindowVisible(hwndFull) && TranslateAcceleratorW(hwndFull, hAccel, &msg)) 1796 continue; 1797 if (TranslateAcceleratorW(hMainWnd, hAccel, &msg)) 1798 continue; 1799 1800 TranslateMessage(&msg); 1801 DispatchMessageW(&msg); 1802 } 1803 1804 /* Destroy accelerator table */ 1805 DestroyAcceleratorTable(hAccel); 1806 1807 ImageView_SaveSettings(); 1808 1809 GdiplusShutdown(gdiplusToken); 1810 1811 /* Release COM resources */ 1812 if (SUCCEEDED(hrCoInit)) 1813 CoUninitialize(); 1814 1815 return 0; 1816 } 1817 1818 VOID WINAPI 1819 ImageView_FullscreenW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1820 { 1821 ImageView_Main(hwnd, path); 1822 } 1823 1824 VOID WINAPI 1825 ImageView_Fullscreen(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1826 { 1827 ImageView_Main(hwnd, path); 1828 } 1829 1830 VOID WINAPI 1831 ImageView_FullscreenA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1832 { 1833 WCHAR szFile[MAX_PATH]; 1834 1835 if (MultiByteToWideChar(CP_ACP, 0, path, -1, szFile, _countof(szFile))) 1836 { 1837 ImageView_Main(hwnd, szFile); 1838 } 1839 } 1840 1841 VOID WINAPI 1842 ImageView_PrintTo(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1843 { 1844 DPRINT("ImageView_PrintTo() not implemented\n"); 1845 } 1846 1847 VOID WINAPI 1848 ImageView_PrintToA(HWND hwnd, HINSTANCE hInst, LPCSTR path, int nShow) 1849 { 1850 DPRINT("ImageView_PrintToA() not implemented\n"); 1851 } 1852 1853 VOID WINAPI 1854 ImageView_PrintToW(HWND hwnd, HINSTANCE hInst, LPCWSTR path, int nShow) 1855 { 1856 DPRINT("ImageView_PrintToW() not implemented\n"); 1857 } 1858 1859 BOOL WINAPI 1860 DllMain(IN HINSTANCE hinstDLL, 1861 IN DWORD dwReason, 1862 IN LPVOID lpvReserved) 1863 { 1864 switch (dwReason) 1865 { 1866 case DLL_PROCESS_ATTACH: 1867 g_hInstance = hinstDLL; 1868 break; 1869 } 1870 1871 return TRUE; 1872 } 1873