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