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