OSDN Git Service

Fixed issue #112: (8) Low hanging GUI fruits during Commit and Push
[tortoisegit/TortoiseGitJp.git] / src / TortoiseIDiff / PicWindow.cpp
1 // TortoiseIDiff - an image diff viewer in TortoiseSVN\r
2 \r
3 // Copyright (C) 2006-2009 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "StdAfx.h"\r
20 #include "shellapi.h"\r
21 #include "commctrl.h"\r
22 #include "PicWindow.h"\r
23 #include "math.h"\r
24 \r
25 #pragma comment(lib, "Msimg32.lib")\r
26 #pragma comment(lib, "shell32.lib")\r
27 \r
28 bool CPicWindow::RegisterAndCreateWindow(HWND hParent)\r
29 {\r
30         WNDCLASSEX wcx; \r
31 \r
32         // Fill in the window class structure with default parameters \r
33         wcx.cbSize = sizeof(WNDCLASSEX);\r
34         wcx.style = CS_HREDRAW | CS_VREDRAW;\r
35         wcx.lpfnWndProc = CWindow::stWinMsgHandler;\r
36         wcx.cbClsExtra = 0;\r
37         wcx.cbWndExtra = 0;\r
38         wcx.hInstance = hResource;\r
39         wcx.hCursor = LoadCursor(NULL, IDC_ARROW);\r
40         wcx.lpszClassName = _T("TortoiseIDiffPicWindow");\r
41         wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));\r
42         wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);\r
43         wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEIDIFF);\r
44         wcx.hIconSm     = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));\r
45         RegisterWindow(&wcx);\r
46         if (CreateEx(WS_EX_ACCEPTFILES | WS_EX_CLIENTEDGE, WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, hParent))\r
47         {\r
48                 ShowWindow(m_hwnd, SW_SHOW);\r
49                 UpdateWindow(m_hwnd);\r
50                 CreateButtons();\r
51                 return true;\r
52         }\r
53         return false;\r
54 }\r
55 \r
56 void CPicWindow::PositionTrackBar()\r
57 {\r
58         RECT rc;\r
59         GetClientRect(&rc);\r
60         HWND slider = m_AlphaSlider.GetWindow();\r
61         if ((pSecondPic)&&(m_blend == BLEND_ALPHA))\r
62         {\r
63                 MoveWindow(slider, 0, rc.top-4+SLIDER_WIDTH, SLIDER_WIDTH, rc.bottom-rc.top-SLIDER_WIDTH+8, true);\r
64                 ShowWindow(slider, SW_SHOW);\r
65                 MoveWindow(hwndAlphaToggleBtn, 0, rc.top-4, SLIDER_WIDTH, SLIDER_WIDTH, true);\r
66                 ShowWindow(hwndAlphaToggleBtn, SW_SHOW);\r
67         }\r
68         else\r
69         {\r
70                 ShowWindow(slider, SW_HIDE);\r
71                 ShowWindow(hwndAlphaToggleBtn, SW_HIDE);\r
72         }\r
73 }\r
74 \r
75 LRESULT CALLBACK CPicWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
76 {\r
77         TRACKMOUSEEVENT mevt;\r
78         switch (uMsg)\r
79         {\r
80         case WM_CREATE:\r
81                 {\r
82                         // create a slider control\r
83                          CreateTrackbar(hwnd);\r
84                         ShowWindow(m_AlphaSlider.GetWindow(), SW_HIDE);\r
85                         //Create the tooltips\r
86                         TOOLINFO ti;\r
87                         RECT rect;                  // for client area coordinates\r
88 \r
89                         hwndTT = CreateWindowEx(WS_EX_TOPMOST,\r
90                                 TOOLTIPS_CLASS,\r
91                                 NULL,\r
92                                 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,                \r
93                                 CW_USEDEFAULT,\r
94                                 CW_USEDEFAULT,\r
95                                 CW_USEDEFAULT,\r
96                                 CW_USEDEFAULT,\r
97                                 hwnd,\r
98                                 NULL,\r
99                                 hResource,\r
100                                 NULL\r
101                                 );\r
102 \r
103                         SetWindowPos(hwndTT,\r
104                                 HWND_TOPMOST,\r
105                                 0,\r
106                                 0,\r
107                                 0,\r
108                                 0,\r
109                                 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);\r
110 \r
111                         ::GetClientRect(hwnd, &rect);\r
112 \r
113                         ti.cbSize = sizeof(TOOLINFO);\r
114                         ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;\r
115                         ti.hwnd = hwnd;\r
116                         ti.hinst = hResource;\r
117                         ti.uId = 0;\r
118                         ti.lpszText = LPSTR_TEXTCALLBACK;\r
119                         // ToolTip control will cover the whole window\r
120                         ti.rect.left = rect.left;    \r
121                         ti.rect.top = rect.top;\r
122                         ti.rect.right = rect.right;\r
123                         ti.rect.bottom = rect.bottom;\r
124 \r
125                         SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti); \r
126                         SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 600);\r
127                         nHSecondScrollPos = 0;\r
128                         nVSecondScrollPos = 0;\r
129                 }\r
130                 break;\r
131         case WM_SETFOCUS:\r
132         case WM_KILLFOCUS:\r
133                 InvalidateRect(*this, NULL, FALSE);\r
134                 break;\r
135         case WM_ERASEBKGND:\r
136                 return 1;\r
137                 break;\r
138         case WM_PAINT:\r
139                 Paint(hwnd);\r
140                 break;\r
141         case WM_SIZE:\r
142                 PositionTrackBar();\r
143                 SetupScrollBars();\r
144                 break;\r
145         case WM_VSCROLL:\r
146                 if ((pSecondPic)&&((HWND)lParam == m_AlphaSlider.GetWindow()))\r
147                 {\r
148                         if (LOWORD(wParam) == TB_THUMBTRACK)\r
149                         {\r
150                                 // while tracking, only redraw after 50 milliseconds\r
151                                 ::SetTimer(*this, TIMER_ALPHASLIDER, 50, NULL);\r
152                         }\r
153                         else\r
154                                 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0) / 16.0f);\r
155                 }\r
156                 else\r
157                 {\r
158                         UINT nPos = HIWORD(wParam);\r
159                         bool bForceUpdate = false;\r
160                         if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)\r
161                         {\r
162                                 // Get true 32-bit scroll position\r
163                                 SCROLLINFO si;\r
164                                 si.cbSize = sizeof(SCROLLINFO);\r
165                                 si.fMask = SIF_TRACKPOS;\r
166                                 GetScrollInfo(*this, SB_VERT, &si);\r
167                                 nPos = si.nTrackPos;\r
168                                 bForceUpdate = true;\r
169                         }\r
170 \r
171                         OnVScroll(LOWORD(wParam), nPos);\r
172                         if (bLinkedPositions)\r
173                         {\r
174                                 pTheOtherPic->OnVScroll(LOWORD(wParam), nPos);\r
175                                 if (bForceUpdate)\r
176                                         ::UpdateWindow(*pTheOtherPic);\r
177                         }\r
178                 }\r
179                 break;\r
180         case WM_HSCROLL:\r
181                 {\r
182                         UINT nPos = HIWORD(wParam);\r
183                         bool bForceUpdate = false;\r
184                         if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)\r
185                         {\r
186                                 // Get true 32-bit scroll position\r
187                                 SCROLLINFO si;\r
188                                 si.cbSize = sizeof(SCROLLINFO);\r
189                                 si.fMask = SIF_TRACKPOS;\r
190                                 GetScrollInfo(*this, SB_VERT, &si);\r
191                                 nPos = si.nTrackPos;\r
192                                 bForceUpdate = true;\r
193                         }\r
194 \r
195                         OnHScroll(LOWORD(wParam), nPos);\r
196                         if (bLinkedPositions)\r
197                         {\r
198                                 pTheOtherPic->OnHScroll(LOWORD(wParam), nPos);\r
199                                 if (bForceUpdate)\r
200                                         ::UpdateWindow(*pTheOtherPic);\r
201                         }\r
202                 }\r
203                 break;\r
204         case WM_MOUSEWHEEL:\r
205                 {\r
206                         OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));\r
207                         if (bFitSizes)\r
208                                 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));\r
209                 }\r
210                 break;\r
211         case WM_MOUSEHWHEEL:\r
212                 {\r
213                         OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));\r
214                         if (bFitSizes)\r
215                                 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));\r
216                 }\r
217                 break;\r
218         case WM_LBUTTONDOWN:\r
219                 SetFocus(*this);\r
220                 ptPanStart.x = GET_X_LPARAM(lParam);\r
221                 ptPanStart.y = GET_Y_LPARAM(lParam);\r
222                 startVScrollPos = nVScrollPos;\r
223                 startHScrollPos = nHScrollPos;\r
224                 startVSecondScrollPos = nVSecondScrollPos;\r
225                 startHSecondScrollPos = nHSecondScrollPos;\r
226                 SetCapture(*this);\r
227                 break;\r
228         case WM_LBUTTONUP:\r
229                 ReleaseCapture();\r
230                 break;\r
231         case WM_MOUSELEAVE:\r
232                 m_lastTTPos.x = 0;\r
233                 m_lastTTPos.y = 0;\r
234                 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);\r
235                 break;\r
236         case WM_MOUSEMOVE:\r
237                 {\r
238                         mevt.cbSize = sizeof(TRACKMOUSEEVENT);\r
239                         mevt.dwFlags = TME_LEAVE;\r
240                         mevt.dwHoverTime = HOVER_DEFAULT;\r
241                         mevt.hwndTrack = *this;\r
242                         ::TrackMouseEvent(&mevt);\r
243                         POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};\r
244                         if (pt.y < HEADER_HEIGHT)\r
245                         {\r
246                                 ClientToScreen(*this, &pt);\r
247                                 if ((abs(m_lastTTPos.x - pt.x) > 20)||(abs(m_lastTTPos.y - pt.y) > 10))\r
248                                 {\r
249                                         m_lastTTPos = pt;\r
250                                         pt.x += 15;\r
251                                         pt.y += 15;\r
252                                         SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));\r
253                                         TOOLINFO ti = {0};\r
254                                         ti.cbSize = sizeof(TOOLINFO);\r
255                                         ti.hwnd = *this;\r
256                                         ti.uId = 0;\r
257                                         SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);\r
258                                 }\r
259                         }\r
260                         else\r
261                         {\r
262                                 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);\r
263                                 m_lastTTPos.x = 0;\r
264                                 m_lastTTPos.y = 0;\r
265                         }\r
266                         if (wParam & MK_LBUTTON)\r
267                         {\r
268                                 // pan the image\r
269                                 int xPos = GET_X_LPARAM(lParam); \r
270                                 int yPos = GET_Y_LPARAM(lParam);\r
271                                 if (wParam & MK_CONTROL)\r
272                                 {\r
273                                         nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);\r
274                                         nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);\r
275                                 }\r
276                                 else\r
277                                 {\r
278                                         nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);\r
279                                         nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);\r
280                                 }\r
281                                 SetupScrollBars();\r
282                                 InvalidateRect(*this, NULL, TRUE);\r
283                                 UpdateWindow(*this);\r
284                                 if (bLinkedPositions)\r
285                                 {\r
286                                         pTheOtherPic->nHScrollPos = nHScrollPos;\r
287                                         pTheOtherPic->nVScrollPos = nVScrollPos;\r
288                                         pTheOtherPic->SetupScrollBars();\r
289                                         InvalidateRect(*pTheOtherPic, NULL, TRUE);\r
290                                         UpdateWindow(*pTheOtherPic);\r
291                                 }\r
292                         }\r
293                 }\r
294                 break;\r
295         case WM_SETCURSOR:\r
296                 {\r
297                         // we show a hand cursor if the image can be dragged,\r
298                         // and a hand-down cursor if the image is currently dragged\r
299                         if ((*this == (HWND)wParam)&&(LOWORD(lParam)==HTCLIENT))\r
300                         {\r
301                                 RECT rect;\r
302                                 GetClientRect(&rect);\r
303                                 LONG width = picture.m_Width;\r
304                                 LONG height = picture.m_Height;\r
305                                 if (pSecondPic)\r
306                                 {\r
307                                         width = max(width, pSecondPic->m_Width);\r
308                                         height = max(height, pSecondPic->m_Height);\r
309                                 }\r
310 \r
311                                 if ((GetKeyState(VK_LBUTTON)&0x8000)||(HIWORD(lParam) == WM_LBUTTONDOWN))\r
312                                 {\r
313                                         SetCursor(curHandDown);\r
314                                 }\r
315                                 else\r
316                                 {\r
317                                         SetCursor(curHand);\r
318                                 }\r
319                                 return TRUE;\r
320                         }\r
321                         return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
322                 }\r
323                 break;\r
324         case WM_DROPFILES:\r
325                 {\r
326                         HDROP hDrop = (HDROP)wParam;            \r
327                         TCHAR szFileName[MAX_PATH];\r
328                         // we only use the first file dropped (if multiple files are dropped)\r
329                         DragQueryFile(hDrop, 0, szFileName, sizeof(szFileName)/sizeof(TCHAR));\r
330                         SetPic(szFileName, _T(""), bMainPic);\r
331                         FitImageInWindow();\r
332                         InvalidateRect(*this, NULL, TRUE);\r
333                 }\r
334                 break;\r
335         case WM_COMMAND:\r
336                 {\r
337                         switch (LOWORD(wParam))\r
338                         {\r
339                         case LEFTBUTTON_ID:\r
340                                 {\r
341                                         PrevImage();\r
342                                         if (bLinkedPositions)\r
343                                                 pTheOtherPic->PrevImage();\r
344                                         return 0;\r
345                                 }\r
346                                 break;\r
347                         case RIGHTBUTTON_ID:\r
348                                 {\r
349                                         NextImage();\r
350                                         if (bLinkedPositions)\r
351                                                 pTheOtherPic->NextImage();\r
352                                         return 0;\r
353                                 }\r
354                                 break;\r
355                         case PLAYBUTTON_ID:\r
356                                 {\r
357                                         bPlaying = !bPlaying;\r
358                                         Animate(bPlaying);\r
359                                         if (bLinkedPositions)\r
360                                                 pTheOtherPic->Animate(bPlaying);\r
361                                         return 0;\r
362                                 }\r
363                                 break;\r
364                         case ALPHATOGGLEBUTTON_ID:\r
365                                 {\r
366                                         WORD msg = HIWORD(wParam);\r
367                                         switch (msg)\r
368                                         {\r
369                                         case BN_DOUBLECLICKED:\r
370                                                 {\r
371                                                         SendMessage(hwndAlphaToggleBtn, BM_SETSTATE, 1, 0);\r
372                                                         SetTimer(*this, ID_ALPHATOGGLETIMER, 1000, NULL);\r
373                                                 }\r
374                                                 break;\r
375                                         case BN_CLICKED:\r
376                                                 KillTimer(*this, ID_ALPHATOGGLETIMER);\r
377                                                 ToggleAlpha();\r
378                                                 break;\r
379                                         }\r
380                                         return 0;\r
381                                 }\r
382                                 break;\r
383                         case BLENDALPHA_ID:\r
384                                 {\r
385                                         m_blend = BLEND_ALPHA;\r
386                                         PositionTrackBar();\r
387                                         InvalidateRect(*this, NULL, TRUE);\r
388                                 }\r
389                                 break;\r
390                         case BLENDXOR_ID:\r
391                                 {\r
392                                         m_blend = BLEND_XOR;\r
393                                         PositionTrackBar();\r
394                                         InvalidateRect(*this, NULL, TRUE);\r
395                                 }\r
396                                 break;\r
397                         }\r
398                 }\r
399                 break;\r
400         case WM_TIMER:\r
401                 {\r
402                         switch (wParam)\r
403                         {\r
404                         case ID_ANIMATIONTIMER:\r
405                                 {\r
406                                         nCurrentFrame++;\r
407                                         if (nCurrentFrame > picture.GetNumberOfFrames(0))\r
408                                                 nCurrentFrame = 1;\r
409                                         long delay = picture.SetActiveFrame(nCurrentFrame);\r
410                                         delay = max(100, delay);\r
411                                         SetTimer(*this, ID_ANIMATIONTIMER, delay, NULL);\r
412                                         InvalidateRect(*this, NULL, FALSE);\r
413                                 }\r
414                                 break;\r
415                         case TIMER_ALPHASLIDER:\r
416                                 {\r
417                                         SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0)/16.0f);\r
418                                         KillTimer(*this, TIMER_ALPHASLIDER);\r
419                                 }\r
420                                 break;\r
421                         case ID_ALPHATOGGLETIMER:\r
422                                 {\r
423                                         ToggleAlpha();\r
424                                 }\r
425                                 break;\r
426                         }\r
427                 }\r
428                 break;\r
429         case WM_NOTIFY:\r
430                 {\r
431                         LPNMHDR pNMHDR = (LPNMHDR)lParam;\r
432                         if (pNMHDR->code == TTN_GETDISPINFO)\r
433                         {\r
434                                 if ((HWND)wParam == m_AlphaSlider.GetWindow())\r
435                                 {\r
436                                         LPTOOLTIPTEXT lpttt; \r
437 \r
438                                         lpttt = (LPTOOLTIPTEXT) lParam; \r
439                                         lpttt->hinst = hResource; \r
440                                         TCHAR stringbuf[MAX_PATH] = {0};\r
441                                         _stprintf_s(stringbuf, MAX_PATH, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));\r
442                                         lpttt->lpszText = stringbuf;\r
443                                 }\r
444                                 else\r
445                                 {\r
446                                         NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;\r
447                                         NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;\r
448                                         BuildInfoString(m_wszTip, sizeof(m_wszTip)/sizeof(TCHAR), true);\r
449                                         if (pNMHDR->code == TTN_NEEDTEXTW)\r
450                                         {\r
451                                                 pTTTW->lpszText = m_wszTip;\r
452                                         }\r
453                                         else\r
454                                         {\r
455                                                 pTTTA->lpszText = m_szTip;\r
456                                                 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);\r
457                                         }\r
458                                 }\r
459                         }\r
460                 }\r
461                 break;\r
462         case WM_DESTROY:\r
463                 DestroyIcon(hLeft);\r
464                 DestroyIcon(hRight);\r
465                 DestroyIcon(hPlay);\r
466                 DestroyIcon(hStop);\r
467                 bWindowClosed = TRUE;\r
468                 break;\r
469         default:\r
470                 return DefWindowProc(hwnd, uMsg, wParam, lParam);\r
471         }\r
472 \r
473         return 0;\r
474 };\r
475 \r
476 void CPicWindow::NextImage()\r
477 {\r
478         nCurrentDimension++;\r
479         if (nCurrentDimension > picture.GetNumberOfDimensions())\r
480                 nCurrentDimension = picture.GetNumberOfDimensions();\r
481         nCurrentFrame++;\r
482         if (nCurrentFrame > picture.GetNumberOfFrames(0))\r
483                 nCurrentFrame = picture.GetNumberOfFrames(0);\r
484         picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);\r
485         InvalidateRect(*this, NULL, FALSE);\r
486         PositionChildren();\r
487 }\r
488 \r
489 void CPicWindow::PrevImage()\r
490 {\r
491         nCurrentDimension--;\r
492         if (nCurrentDimension < 1)\r
493                 nCurrentDimension = 1;\r
494         nCurrentFrame--;\r
495         if (nCurrentFrame < 1)\r
496                 nCurrentFrame = 1;\r
497         picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);\r
498         InvalidateRect(*this, NULL, FALSE);\r
499         PositionChildren();\r
500 }\r
501 \r
502 void CPicWindow::Animate(bool bStart)\r
503 {\r
504         if (bStart)\r
505         {\r
506                 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hStop);\r
507                 SetTimer(*this, ID_ANIMATIONTIMER, 0, NULL);\r
508         }\r
509         else\r
510         {\r
511                 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);\r
512                 KillTimer(*this, ID_ANIMATIONTIMER);\r
513         }\r
514 }\r
515 \r
516 void CPicWindow::SetPic(tstring path, tstring title, bool bFirst)\r
517 {\r
518         bMainPic = bFirst;\r
519         picpath=path;pictitle=title;\r
520         picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);\r
521         bValid = picture.Load(picpath);\r
522         nDimensions = picture.GetNumberOfDimensions();\r
523         if (nDimensions)\r
524                 nFrames = picture.GetNumberOfFrames(0);\r
525         if (bValid)\r
526         {\r
527                 picscale = 1.0;\r
528                 PositionChildren();\r
529                 InvalidateRect(*this, NULL, FALSE);\r
530         }\r
531 }\r
532 \r
533 void CPicWindow::DrawViewTitle(HDC hDC, RECT * rect)\r
534 {\r
535         HFONT hFont = NULL;\r
536         hFont = CreateFont(-MulDiv(pSecondPic ? 8 : 10, GetDeviceCaps(hDC, LOGPIXELSY), 72), 0, 0, 0, FW_DONTCARE, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, _T("MS Shell Dlg"));\r
537         HFONT hFontOld = (HFONT)SelectObject(hDC, (HGDIOBJ)hFont);\r
538 \r
539         RECT textrect;\r
540         textrect.left = rect->left;\r
541         textrect.top = rect->top;\r
542         textrect.right = rect->right;\r
543         textrect.bottom = rect->top + HEADER_HEIGHT;\r
544         if (HasMultipleImages())\r
545                 textrect.bottom += HEADER_HEIGHT;\r
546 \r
547         COLORREF crBk, crFg;\r
548         crBk = ::GetSysColor(COLOR_SCROLLBAR);\r
549         crFg = ::GetSysColor(COLOR_WINDOWTEXT);\r
550         SetBkColor(hDC, crBk);\r
551         ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &textrect, NULL, 0, NULL);\r
552 \r
553         if (GetFocus() == *this)\r
554                 DrawEdge(hDC, &textrect, EDGE_BUMP, BF_RECT);\r
555         else\r
556                 DrawEdge(hDC, &textrect, EDGE_ETCHED, BF_RECT);\r
557 \r
558         SetTextColor(hDC, crFg);\r
559 \r
560         // use the path if no title is set.\r
561         tstring * title = pictitle.empty() ? &picpath : &pictitle;\r
562 \r
563         tstring realtitle = *title;\r
564         tstring imgnumstring;\r
565 \r
566         if (HasMultipleImages())\r
567         {\r
568                 TCHAR buf[MAX_PATH];\r
569                 if (nFrames > 1)\r
570                         _stprintf_s(buf, sizeof(buf)/sizeof(TCHAR), (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentFrame, nFrames);\r
571                 else\r
572                         _stprintf_s(buf, sizeof(buf)/sizeof(TCHAR), (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentDimension, nDimensions);\r
573                 imgnumstring = buf;\r
574         }\r
575 \r
576         SIZE stringsize;\r
577         if (GetTextExtentPoint32(hDC, realtitle.c_str(), (int)realtitle.size(), &stringsize))\r
578         {\r
579                 int nStringLength = stringsize.cx;\r
580                 int texttop = pSecondPic ? textrect.top + (HEADER_HEIGHT/2) - stringsize.cy : textrect.top + (HEADER_HEIGHT/2) - stringsize.cy/2;\r
581                 ExtTextOut(hDC, \r
582                         max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),\r
583                         texttop,\r
584                         ETO_CLIPPED,\r
585                         &textrect,\r
586                         realtitle.c_str(),\r
587                         (UINT)realtitle.size(),\r
588                         NULL);\r
589                 if (pSecondPic)\r
590                 {\r
591                         realtitle = (pictitle2.empty() ? picpath2 : pictitle2);\r
592                         ExtTextOut(hDC, \r
593                                 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),\r
594                                 texttop + stringsize.cy,\r
595                                 ETO_CLIPPED,\r
596                                 &textrect,\r
597                                 realtitle.c_str(),\r
598                                 (UINT)realtitle.size(),\r
599                                 NULL);\r
600                 }\r
601         }\r
602         if (HasMultipleImages())\r
603         {\r
604                 if (GetTextExtentPoint32(hDC, imgnumstring.c_str(), (int)imgnumstring.size(), &stringsize))\r
605                 {\r
606                         int nStringLength = stringsize.cx;\r
607 \r
608                         ExtTextOut(hDC, \r
609                                 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),\r
610                                 textrect.top + HEADER_HEIGHT + (HEADER_HEIGHT/2) - stringsize.cy/2,\r
611                                 ETO_CLIPPED,\r
612                                 &textrect,\r
613                                 imgnumstring.c_str(),\r
614                                 (UINT)imgnumstring.size(),\r
615                                 NULL);\r
616                 }\r
617         }\r
618         SelectObject(hDC, (HGDIOBJ)hFontOld);\r
619         DeleteObject(hFont);\r
620 }\r
621 \r
622 void CPicWindow::SetupScrollBars()\r
623 {\r
624         RECT rect;\r
625         GetClientRect(&rect);\r
626 \r
627         SCROLLINFO si = {sizeof(si)};\r
628 \r
629         si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL;\r
630 \r
631         double width = double(picture.m_Width)*picscale;\r
632         double height = double(picture.m_Height)*picscale;\r
633         if (pSecondPic)\r
634         {\r
635                 width = max(width, double(pSecondPic->m_Width)*pTheOtherPic->GetZoom());\r
636                 height = max(height, double(pSecondPic->m_Height)*pTheOtherPic->GetZoom());\r
637         }\r
638 \r
639         bool bShowHScrollBar = (nHScrollPos > 0); // left of pic is left of window\r
640         bShowHScrollBar      = bShowHScrollBar || (width-nHScrollPos > rect.right); // right of pic is outside right of window\r
641         bShowHScrollBar      = bShowHScrollBar || (width+nHScrollPos > rect.right); // right of pic is outside right of window\r
642         bool bShowVScrollBar = (nVScrollPos > 0); // top of pic is above window\r
643         bShowVScrollBar      = bShowVScrollBar || (height-nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window\r
644         bShowVScrollBar      = bShowVScrollBar || (height+nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window\r
645 \r
646         // if the image is smaller than the window, we don't need the scrollbars\r
647         ShowScrollBar(*this, SB_HORZ, bShowHScrollBar);\r
648         ShowScrollBar(*this, SB_VERT, bShowVScrollBar);\r
649 \r
650         if (bShowVScrollBar)\r
651         {\r
652                 si.nPos  = nVScrollPos;\r
653                 si.nPage = rect.bottom-rect.top;\r
654                 if (height < rect.bottom-rect.top)\r
655                 {\r
656                         if (nVScrollPos > 0)\r
657                         {\r
658                                 si.nMin  = 0;\r
659                                 si.nMax  = rect.bottom+nVScrollPos-rect.top;\r
660                         }\r
661                         else\r
662                         {\r
663                                 si.nMin  = nVScrollPos;\r
664                                 si.nMax  = int(height);\r
665                         }\r
666                 }\r
667                 else\r
668                 {\r
669                         if (nVScrollPos > 0)\r
670                         {\r
671                                 si.nMin  = 0;\r
672                                 si.nMax  = int(max(height, rect.bottom+nVScrollPos-rect.top));\r
673                         }\r
674                         else\r
675                         {\r
676                                 si.nMin  = 0;\r
677                                 si.nMax  = int(height-nVScrollPos);\r
678                         }\r
679                 }\r
680                 SetScrollInfo(*this, SB_VERT, &si, TRUE);\r
681         }\r
682 \r
683         if (bShowHScrollBar)\r
684         {\r
685                 si.nPos  = nHScrollPos;\r
686                 si.nPage = rect.right-rect.left;\r
687                 if (width < rect.right-rect.left)\r
688                 {\r
689                         if (nHScrollPos > 0)\r
690                         {\r
691                                 si.nMin  = 0;\r
692                                 si.nMax  = rect.right+nHScrollPos-rect.left;\r
693                         }\r
694                         else\r
695                         {\r
696                                 si.nMin  = nHScrollPos;\r
697                                 si.nMax  = int(width);\r
698                         }\r
699                 }\r
700                 else\r
701                 {\r
702                         if (nHScrollPos > 0)\r
703                         {\r
704                                 si.nMin  = 0;\r
705                                 si.nMax  = int(max(width, rect.right+nHScrollPos-rect.left));\r
706                         }\r
707                         else\r
708                         {\r
709                                 si.nMin  = 0;\r
710                                 si.nMax  = int(width-nHScrollPos);\r
711                         }\r
712                 }\r
713                 SetScrollInfo(*this, SB_HORZ, &si, TRUE);\r
714         }\r
715 \r
716         PositionChildren();\r
717 }\r
718 \r
719 void CPicWindow::OnVScroll(UINT nSBCode, UINT nPos)\r
720 {\r
721         RECT rect;\r
722         GetClientRect(&rect);\r
723 \r
724         switch (nSBCode)\r
725         {\r
726         case SB_BOTTOM:\r
727                 nVScrollPos = LONG(double(picture.GetHeight())*picscale);;\r
728                 break;\r
729         case SB_TOP:\r
730                 nVScrollPos = 0;\r
731                 break;\r
732         case SB_LINEDOWN:\r
733                 nVScrollPos++;\r
734                 break;\r
735         case SB_LINEUP:\r
736                 nVScrollPos--;\r
737                 break;\r
738         case SB_PAGEDOWN:\r
739                 nVScrollPos += (rect.bottom-rect.top);\r
740                 break;\r
741         case SB_PAGEUP:\r
742                 nVScrollPos -= (rect.bottom-rect.top);\r
743                 break;\r
744         case SB_THUMBPOSITION:\r
745                 nVScrollPos = nPos;\r
746                 break;\r
747         case SB_THUMBTRACK:\r
748                 nVScrollPos = nPos;\r
749                 break;\r
750         default:\r
751                 return;\r
752         }\r
753         LONG height = LONG(double(picture.GetHeight())*picscale);\r
754         if (pSecondPic)\r
755         {\r
756                 height = max(height, LONG(double(pSecondPic->GetHeight())*picscale));\r
757         }\r
758         SetupScrollBars();\r
759         PositionChildren();\r
760         InvalidateRect(*this, NULL, TRUE);\r
761 }\r
762 \r
763 void CPicWindow::OnHScroll(UINT nSBCode, UINT nPos)\r
764 {\r
765         RECT rect;\r
766         GetClientRect(&rect);\r
767 \r
768         switch (nSBCode)\r
769         {\r
770         case SB_RIGHT:\r
771                 nHScrollPos = LONG(double(picture.GetWidth())*picscale);\r
772                 break;\r
773         case SB_LEFT:\r
774                 nHScrollPos = 0;\r
775                 break;\r
776         case SB_LINERIGHT:\r
777                 nHScrollPos++;\r
778                 break;\r
779         case SB_LINELEFT:\r
780                 nHScrollPos--;\r
781                 break;\r
782         case SB_PAGERIGHT:\r
783                 nHScrollPos += (rect.right-rect.left);\r
784                 break;\r
785         case SB_PAGELEFT:\r
786                 nHScrollPos -= (rect.right-rect.left);\r
787                 break;\r
788         case SB_THUMBPOSITION:\r
789                 nHScrollPos = nPos;\r
790                 break;\r
791         case SB_THUMBTRACK:\r
792                 nHScrollPos = nPos;\r
793                 break;\r
794         default:\r
795                 return;\r
796         }\r
797         LONG width = LONG(double(picture.GetWidth())*picscale);\r
798         if (pSecondPic)\r
799         {\r
800                 width = max(width, LONG(double(pSecondPic->GetWidth())*picscale));\r
801         }\r
802         SetupScrollBars();\r
803         PositionChildren();\r
804         InvalidateRect(*this, NULL, TRUE);\r
805 }\r
806 \r
807 void CPicWindow::OnMouseWheel(short fwKeys, short zDelta)\r
808 {\r
809         RECT rect;\r
810         GetClientRect(&rect);\r
811         LONG width = long(double(picture.m_Width)*picscale);\r
812         LONG height = long(double(picture.m_Height)*picscale);\r
813         if (pSecondPic)\r
814         {\r
815                 width = max(width, long(double(pSecondPic->m_Width)*picscale));\r
816                 height = max(height, long(double(pSecondPic->m_Height)*picscale));\r
817         }\r
818         if ((fwKeys & MK_SHIFT)&&(fwKeys & MK_CONTROL)&&(pSecondPic))\r
819         {\r
820                 // ctrl+shift+wheel: change the alpha channel\r
821                 float a = blendAlpha;\r
822                 a -= float(zDelta)/120.0f/4.0f;\r
823                 if (a < 0.0f)\r
824                         a = 0.0f;\r
825                 else if (a > 1.0f)\r
826                         a = 1.0f;\r
827                 SetBlendAlpha(m_blend, a);\r
828         }\r
829         else if (fwKeys & MK_SHIFT)\r
830         {\r
831                 // shift means scrolling sideways\r
832                 OnHScroll(SB_THUMBPOSITION, GetHPos()-zDelta);\r
833                 if ((bLinkedPositions)&&(pTheOtherPic))\r
834                 {\r
835                         pTheOtherPic->OnHScroll(SB_THUMBPOSITION, pTheOtherPic->GetHPos()-zDelta);\r
836                 }\r
837         }\r
838         else if (fwKeys & MK_CONTROL)\r
839         {\r
840                 // control means adjusting the scale factor\r
841                 Zoom(zDelta>0, true);\r
842                 if ((!bFitSizes)&&(pTheOtherPic))\r
843                         pTheOtherPic->Zoom(zDelta>0, true);\r
844                 PositionChildren();\r
845                 InvalidateRect(*this, NULL, FALSE);\r
846                 SetWindowPos(*this, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOMOVE);\r
847         }\r
848         else\r
849         {\r
850                 OnVScroll(SB_THUMBPOSITION, GetVPos()-zDelta);\r
851                 if ((bLinkedPositions)&&(pTheOtherPic))\r
852                 {\r
853                         pTheOtherPic->OnVScroll(SB_THUMBPOSITION, pTheOtherPic->GetVPos()-zDelta);\r
854                 }\r
855         }\r
856 }\r
857 \r
858 void CPicWindow::GetClientRect(RECT * pRect)\r
859 {\r
860         ::GetClientRect(*this, pRect);\r
861         pRect->top += HEADER_HEIGHT;\r
862         if (HasMultipleImages())\r
863         {\r
864                 pRect->top += HEADER_HEIGHT;\r
865         }\r
866         if (pSecondPic)\r
867                 pRect->left += SLIDER_WIDTH;\r
868 }\r
869 \r
870 void CPicWindow::GetClientRectWithScrollbars(RECT * pRect)\r
871 {\r
872         GetClientRect(pRect);\r
873         ::GetWindowRect(*this, pRect);\r
874         pRect->right = pRect->right-pRect->left;\r
875         pRect->bottom = pRect->bottom-pRect->top;\r
876         pRect->top = 0;\r
877         pRect->left = 0;\r
878         pRect->top += HEADER_HEIGHT;\r
879         if (HasMultipleImages())\r
880         {\r
881                 pRect->top += HEADER_HEIGHT;\r
882         }\r
883         if (pSecondPic)\r
884                 pRect->left += SLIDER_WIDTH;\r
885 };\r
886 \r
887 \r
888 void CPicWindow::SetZoom(double dZoom, bool centermouse)\r
889 {\r
890         // Set the interpolation mode depending on zoom\r
891         double oldPicscale = picscale;\r
892         if (dZoom < 1.0)\r
893         {       // Zoomed out, use high quality bicubic\r
894                 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);\r
895                 if (pSecondPic)\r
896                         pSecondPic->SetInterpolationMode(InterpolationModeHighQualityBicubic);\r
897         }\r
898         else if (!((int)(dZoom*100.0)%100))\r
899         {       // "Even" zoom sizes should be shown w-o any interpolation\r
900                 picture.SetInterpolationMode(InterpolationModeNearestNeighbor);\r
901                 if (pSecondPic)\r
902                         pSecondPic->SetInterpolationMode(InterpolationModeNearestNeighbor);\r
903         }\r
904         else\r
905         {       // Arbitrary zoomed in, use bilinear that is semi-smoothed\r
906                 picture.SetInterpolationMode(InterpolationModeBilinear);\r
907                 if (pSecondPic)\r
908                         pSecondPic->SetInterpolationMode(InterpolationModeBilinear);\r
909         }\r
910         picscale = dZoom;\r
911 \r
912         if ((bFitSizes)&&(bMainPic))\r
913         {\r
914                 double width, height;\r
915                 double zoomWidth, zoomHeight;\r
916                 width = double(picture.m_Width)*dZoom;\r
917                 height = double(picture.m_Height)*dZoom;\r
918                 zoomWidth = width/double(pTheOtherPic->GetPic()->m_Width);\r
919                 zoomHeight = height/double(pTheOtherPic->GetPic()->m_Height);\r
920                 pTheOtherPic->SetZoom(min(zoomWidth, zoomHeight), centermouse);\r
921         }\r
922 \r
923         // adjust the scrollbar positions according to the new zoom and the\r
924         // mouse position: if possible, keep the pixel where the mouse pointer\r
925         // is at the same position after the zoom\r
926         POINT cpos;\r
927         DWORD ptW = GetMessagePos();\r
928         cpos.x = GET_X_LPARAM(ptW);\r
929         cpos.y = GET_Y_LPARAM(ptW);\r
930         ScreenToClient(*this, &cpos);\r
931         RECT clientrect;\r
932         GetClientRect(&clientrect);\r
933         if ((PtInRect(&clientrect, cpos))&&(centermouse))\r
934         {\r
935                 // the mouse pointer is over our window\r
936                 nHScrollPos = int(double(nHScrollPos + cpos.x)*(dZoom/oldPicscale))-cpos.x;\r
937                 nVScrollPos = int(double(nVScrollPos + cpos.y)*(dZoom/oldPicscale))-cpos.y;\r
938         }\r
939         else\r
940         {\r
941                 nHScrollPos = int(double(nHScrollPos + ((clientrect.right-clientrect.left)/2))*(dZoom/oldPicscale))-((clientrect.right-clientrect.left)/2);\r
942                 nVScrollPos = int(double(nVScrollPos + ((clientrect.bottom-clientrect.top)/2))*(dZoom/oldPicscale))-((clientrect.bottom-clientrect.top)/2);\r
943         }\r
944 \r
945         SetupScrollBars();\r
946         PositionChildren();\r
947         InvalidateRect(*this, NULL, TRUE);\r
948 }\r
949 \r
950 void CPicWindow::Zoom(bool in, bool centermouse)\r
951 {\r
952         double zoomFactor;\r
953 \r
954         // Find correct zoom factor and quantize picscale\r
955         if (!in && picscale <= 0.2)\r
956         {\r
957                 picscale = 0.1;\r
958                 zoomFactor = 0;\r
959         }\r
960         else if ((in && picscale < 1.0) || (!in && picscale <= 1.0))\r
961         {\r
962                 picscale = 0.1 * RoundDouble(picscale/0.1, 0);  // Quantize to 0.1\r
963                 zoomFactor = 0.1;\r
964         }\r
965         else if ((in && picscale < 2.0) || (!in && picscale <= 2.0))\r
966         {\r
967                 picscale = 0.25 * RoundDouble(picscale/0.25, 0);        // Quantize to 0.25\r
968                 zoomFactor = 0.25;\r
969         }\r
970         else\r
971         {\r
972                 picscale = RoundDouble(picscale,0);\r
973                 zoomFactor = 1;\r
974         }\r
975 \r
976         // Set zoom\r
977         if (in)\r
978         {\r
979                 if ((pSecondPic)&&(!bFitSizes))\r
980                         pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()+zoomFactor, false);\r
981                 SetZoom(picscale+zoomFactor, centermouse);\r
982         }\r
983         else\r
984         {\r
985                 if ((pSecondPic)&&(!bFitSizes))\r
986                         pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()-zoomFactor, false);\r
987                 SetZoom(picscale-zoomFactor, centermouse);\r
988         }\r
989 }\r
990 \r
991 double CPicWindow::RoundDouble(double doValue, int nPrecision)\r
992 {\r
993         static const double doBase = 10.0;\r
994         double doComplete5, doComplete5i;\r
995 \r
996         doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));\r
997 \r
998         if (doValue < 0.0)\r
999         {\r
1000                 doComplete5 -= 5.0;\r
1001         }\r
1002         else\r
1003         {\r
1004                 doComplete5 += 5.0;\r
1005         }\r
1006 \r
1007         doComplete5 /= doBase;\r
1008         modf(doComplete5, &doComplete5i);\r
1009 \r
1010         return doComplete5i / pow(doBase, (double) nPrecision);\r
1011 }\r
1012 void CPicWindow::FitImageInWindow()\r
1013 {\r
1014         RECT rect;\r
1015         double dZoom = 1.0;\r
1016 \r
1017         GetClientRectWithScrollbars(&rect);\r
1018 \r
1019         if (rect.right-rect.left)\r
1020         {\r
1021                 if (((rect.right - rect.left) > picture.m_Width+2)&&((rect.bottom - rect.top)> picture.m_Height+2))\r
1022                 {\r
1023                         // image is smaller than the window\r
1024                         dZoom = 1.0;\r
1025                 }\r
1026                 else\r
1027                 {\r
1028                         // image is bigger than the window\r
1029                         double xscale = double(rect.right-rect.left-2)/double(picture.m_Width);\r
1030                         double yscale = double(rect.bottom-rect.top-2)/double(picture.m_Height);\r
1031                         dZoom = min(yscale, xscale);\r
1032                 }\r
1033                 if (pSecondPic)\r
1034                 {\r
1035                         if (((rect.right - rect.left) > pSecondPic->m_Width+2)&&((rect.bottom - rect.top)> pSecondPic->m_Height+2))\r
1036                         {\r
1037                                 // image is smaller than the window\r
1038                                 pTheOtherPic->SetZoom(min(1.0, dZoom), false);\r
1039                         }\r
1040                         else\r
1041                         {\r
1042                                 // image is bigger than the window\r
1043                                 double xscale = double(rect.right-rect.left-2)/double(pSecondPic->m_Width);\r
1044                                 double yscale = double(rect.bottom-rect.top-2)/double(pSecondPic->m_Height);\r
1045                                 pTheOtherPic->SetZoom(min(yscale, xscale), false);\r
1046                         }\r
1047                         nHSecondScrollPos = 0;\r
1048                         nVSecondScrollPos = 0;\r
1049                 }\r
1050                 SetZoom(dZoom, false);\r
1051         }\r
1052         CenterImage();\r
1053         PositionChildren();\r
1054         InvalidateRect(*this, NULL, TRUE);\r
1055 }\r
1056 \r
1057 void CPicWindow::CenterImage()\r
1058 {\r
1059         RECT rect;\r
1060         GetClientRectWithScrollbars(&rect);\r
1061         double width = (double(picture.m_Width)*picscale) + 2.0;\r
1062         double height = (double(picture.m_Height)*picscale) + 2.0;\r
1063         if (pSecondPic)\r
1064         {\r
1065                 width = max(width, (double(pSecondPic->m_Width)*pTheOtherPic->GetZoom()) + 2.0);\r
1066                 height = max(height, (double(pSecondPic->m_Height)*pTheOtherPic->GetZoom()) + 2.0);\r
1067         }\r
1068 \r
1069         bool bPicWidthBigger = (int(width) > (rect.right-rect.left));\r
1070         bool bPicHeightBigger = (int(height) > (rect.bottom-rect.top));\r
1071         // set the scroll position so that the image is drawn centered in the window\r
1072         // if the window is bigger than the image\r
1073         if (!bPicWidthBigger)\r
1074         {\r
1075                 nHScrollPos = -((rect.right-rect.left+4)-int(width))/2;\r
1076         }\r
1077         if (!bPicHeightBigger)\r
1078         {\r
1079                 nVScrollPos = -((rect.bottom-rect.top+4)-int(height))/2;\r
1080         }\r
1081         SetupScrollBars();\r
1082 }\r
1083 \r
1084 void CPicWindow::FitSizes(bool bFit)\r
1085 {\r
1086         bFitSizes = bFit;\r
1087 \r
1088         if (bFitSizes)\r
1089         {\r
1090                 nHSecondScrollPos = 0;\r
1091                 nVSecondScrollPos = 0;\r
1092         }\r
1093         SetZoom(GetZoom(), false);\r
1094 }\r
1095 \r
1096 void CPicWindow::ShowPicWithBorder(HDC hdc, const RECT &bounds, CPicture &pic, double scale)\r
1097 {\r
1098         ::SetBkColor(hdc, transparentColor);\r
1099         ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);\r
1100 \r
1101         RECT picrect;\r
1102         picrect.left =  bounds.left - nHScrollPos;\r
1103         picrect.top = bounds.top - nVScrollPos;\r
1104         if (!bLinkedPositions && (pTheOtherPic) && (&pic != &picture))\r
1105         {\r
1106                 picrect.left = bounds.left - nHSecondScrollPos;\r
1107                 picrect.top  = bounds.top - nVSecondScrollPos;\r
1108         }\r
1109         picrect.right = (picrect.left + LONG(double(pic.m_Width) * scale));\r
1110         picrect.bottom = (picrect.top + LONG(double(pic.m_Height) * scale));\r
1111 \r
1112         pic.Show(hdc, picrect);\r
1113 \r
1114         RECT border;\r
1115         border.left = picrect.left-1;\r
1116         border.top = picrect.top-1;\r
1117         border.right = picrect.right+1;\r
1118         border.bottom = picrect.bottom+1;\r
1119 \r
1120         HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));\r
1121         HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);\r
1122         MoveToEx(hdc, border.left, border.top, NULL);\r
1123         LineTo(hdc, border.left, border.bottom);\r
1124         LineTo(hdc, border.right, border.bottom);\r
1125         LineTo(hdc, border.right, border.top);\r
1126         LineTo(hdc, border.left, border.top);\r
1127         SelectObject(hdc, hOldPen);\r
1128         DeleteObject(hPen);\r
1129 }\r
1130 \r
1131 void CPicWindow::Paint(HWND hwnd)\r
1132 {\r
1133         PAINTSTRUCT ps;\r
1134         HDC hdc;\r
1135         RECT rect, fullrect;\r
1136 \r
1137         GetUpdateRect(hwnd, &rect, FALSE);\r
1138         if (IsRectEmpty(&rect))\r
1139                 return;\r
1140 \r
1141         ::GetClientRect(*this, &fullrect);\r
1142         hdc = BeginPaint(hwnd, &ps);\r
1143         {\r
1144                 // Exclude the alpha control and button\r
1145                 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))\r
1146                         ExcludeClipRect(hdc, 0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4);\r
1147 \r
1148                 CMyMemDC memDC(hdc);\r
1149                 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))\r
1150                 {\r
1151                         // erase the place where the alpha slider would be\r
1152                         ::SetBkColor(memDC, transparentColor);\r
1153                         RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};\r
1154                         ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);\r
1155                 }\r
1156 \r
1157                 GetClientRect(&rect);\r
1158                 if (bValid)\r
1159                 {\r
1160                         ShowPicWithBorder(memDC, rect, picture, picscale);\r
1161                         if (pSecondPic)\r
1162                         {\r
1163                                 HDC secondhdc = CreateCompatibleDC(hdc);\r
1164                                 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);\r
1165                                 HBITMAP hOldBitmap = (HBITMAP)SelectObject(secondhdc, hBitmap);\r
1166                                 SetWindowOrgEx(secondhdc, rect.left, rect.top, NULL);\r
1167 \r
1168                                 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))\r
1169                                 {\r
1170                                         // erase the place where the alpha slider would be\r
1171                                         ::SetBkColor(secondhdc, transparentColor);\r
1172                                         RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};\r
1173                                         ::ExtTextOut(secondhdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);\r
1174                                 }\r
1175                                 ShowPicWithBorder(secondhdc, rect, *pSecondPic, pTheOtherPic->GetZoom());\r
1176 \r
1177                                 if (m_blend == BLEND_ALPHA)\r
1178                                 {\r
1179                                         BLENDFUNCTION blender;\r
1180                                         blender.AlphaFormat = 0;\r
1181                                         blender.BlendFlags = 0;\r
1182                                         blender.BlendOp = AC_SRC_OVER;\r
1183                                         blender.SourceConstantAlpha = (BYTE)(blendAlpha*255);\r
1184                                         AlphaBlend(memDC, \r
1185                                                 rect.left,\r
1186                                                 rect.top,\r
1187                                                 rect.right-rect.left,\r
1188                                                 rect.bottom-rect.top,\r
1189                                                 secondhdc,\r
1190                                                 rect.left,\r
1191                                                 rect.top,\r
1192                                                 rect.right-rect.left,\r
1193                                                 rect.bottom-rect.top,\r
1194                                                 blender);\r
1195                                 }\r
1196                                 else if (m_blend == BLEND_XOR)\r
1197                                 {\r
1198                                         BitBlt(memDC, \r
1199                                                 rect.left,\r
1200                                                 rect.top,\r
1201                                                 rect.right-rect.left,\r
1202                                                 rect.bottom-rect.top,\r
1203                                                 secondhdc,\r
1204                                                 rect.left,\r
1205                                                 rect.top,\r
1206                                                 //rect.right-rect.left,\r
1207                                                 //rect.bottom-rect.top,\r
1208                                                 SRCINVERT);\r
1209                                         InvertRect(memDC, &rect);\r
1210                                 }\r
1211                                 SelectObject(secondhdc, hOldBitmap);\r
1212                                 DeleteObject(hBitmap);\r
1213                                 DeleteDC(secondhdc);\r
1214                         }\r
1215                         int sliderwidth = 0;\r
1216                         if ((pSecondPic)&&(m_blend == BLEND_ALPHA))\r
1217                                 sliderwidth = SLIDER_WIDTH;\r
1218                         m_inforect.left = rect.left+4+sliderwidth;\r
1219                         m_inforect.top = rect.top;\r
1220                         m_inforect.right = rect.right+sliderwidth;\r
1221                         m_inforect.bottom = rect.bottom;\r
1222 \r
1223                         SetBkColor(memDC, transparentColor);\r
1224                         if (bShowInfo)\r
1225                         {\r
1226                                 TCHAR infostring[8192];\r
1227                                 BuildInfoString(infostring, sizeof(infostring)/sizeof(TCHAR), false);\r
1228                                 // set the font\r
1229                                 NONCLIENTMETRICS metrics = {0};\r
1230                                 metrics.cbSize = sizeof(NONCLIENTMETRICS);\r
1231                                 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);\r
1232                                 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);\r
1233                                 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);\r
1234                                 // find out how big the rectangle for the text has to be\r
1235                                 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER | DT_CALCRECT);\r
1236 \r
1237                                 // the text should be drawn with a four pixel offset to the window borders\r
1238                                 m_inforect.top = rect.bottom - (m_inforect.bottom-m_inforect.top) - 4;\r
1239                                 m_inforect.bottom = rect.bottom-4;\r
1240 \r
1241                                 // first draw an edge rectangle\r
1242                                 RECT edgerect;\r
1243                                 edgerect.left = m_inforect.left-4;\r
1244                                 edgerect.top = m_inforect.top-4;\r
1245                                 edgerect.right = m_inforect.right+4;\r
1246                                 edgerect.bottom = m_inforect.bottom+4;\r
1247                                 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &edgerect, NULL, 0, NULL);\r
1248                                 DrawEdge(memDC, &edgerect, EDGE_BUMP, BF_RECT | BF_SOFT);\r
1249 \r
1250                                 SetTextColor(memDC, GetSysColor(COLOR_WINDOWTEXT));\r
1251                                 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER);\r
1252                                 SelectObject(memDC, (HGDIOBJ)hFontOld);\r
1253                                 DeleteObject(hFont);\r
1254                         }\r
1255                 }\r
1256                 else\r
1257                 {\r
1258                         SetBkColor(memDC, ::GetSysColor(COLOR_WINDOW));\r
1259                         ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);\r
1260                         SIZE stringsize;\r
1261                         ResString str = ResString(hResource, IDS_INVALIDIMAGEINFO);\r
1262                         if (GetTextExtentPoint32(memDC, str, (int)_tcslen(str), &stringsize))\r
1263                         {\r
1264                                 int nStringLength = stringsize.cx;\r
1265 \r
1266                                 ExtTextOut(memDC, \r
1267                                         max(rect.left + ((rect.right-rect.left)-nStringLength)/2, 1),\r
1268                                         rect.top + ((rect.bottom-rect.top) - stringsize.cy)/2,\r
1269                                         ETO_CLIPPED,\r
1270                                         &rect,\r
1271                                         str,\r
1272                                         (UINT)_tcslen(str),\r
1273                                         NULL);\r
1274                         }\r
1275                 }\r
1276                 DrawViewTitle(memDC, &fullrect);\r
1277         }\r
1278         EndPaint(hwnd, &ps);\r
1279 }\r
1280 \r
1281 bool CPicWindow::CreateButtons()\r
1282 {\r
1283         // Ensure that the common control DLL is loaded. \r
1284         INITCOMMONCONTROLSEX icex;\r
1285         icex.dwSize = sizeof(INITCOMMONCONTROLSEX);\r
1286         icex.dwICC  = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;\r
1287         InitCommonControlsEx(&icex);\r
1288 \r
1289         hwndLeftBtn = CreateWindowEx(0, \r
1290                                                                 _T("BUTTON"), \r
1291                                                                 (LPCTSTR)NULL,\r
1292                                                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT, \r
1293                                                                 0, 0, 0, 0, \r
1294                                                                 (HWND)*this,\r
1295                                                                 (HMENU)LEFTBUTTON_ID,\r
1296                                                                 hResource, \r
1297                                                                 NULL);\r
1298         if (hwndLeftBtn == INVALID_HANDLE_VALUE)\r
1299                 return false;\r
1300         hLeft = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_BACKWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);\r
1301         SendMessage(hwndLeftBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hLeft);\r
1302         hwndRightBtn = CreateWindowEx(0, \r
1303                                                                 _T("BUTTON"), \r
1304                                                                 (LPCTSTR)NULL,\r
1305                                                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT, \r
1306                                                                 0, 0, 0, 0, \r
1307                                                                 *this,\r
1308                                                                 (HMENU)RIGHTBUTTON_ID,\r
1309                                                                 hResource, \r
1310                                                                 NULL);\r
1311         if (hwndRightBtn == INVALID_HANDLE_VALUE)\r
1312                 return false;\r
1313         hRight = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_FORWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);\r
1314         SendMessage(hwndRightBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hRight);\r
1315         hwndPlayBtn = CreateWindowEx(0, \r
1316                                                                 _T("BUTTON"), \r
1317                                                                 (LPCTSTR)NULL,\r
1318                                                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT, \r
1319                                                                 0, 0, 0, 0, \r
1320                                                                 *this,\r
1321                                                                 (HMENU)PLAYBUTTON_ID,\r
1322                                                                 hResource, \r
1323                                                                 NULL);\r
1324         if (hwndPlayBtn == INVALID_HANDLE_VALUE)\r
1325                 return false;\r
1326         hPlay = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);\r
1327         hStop = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);\r
1328         SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);\r
1329         hwndAlphaToggleBtn = CreateWindowEx(0, \r
1330                                                                 _T("BUTTON"), \r
1331                                                                 (LPCTSTR)NULL,\r
1332                                                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT | BS_NOTIFY | BS_PUSHLIKE, \r
1333                                                                 0, 0, 0, 0, \r
1334                                                                 (HWND)*this,\r
1335                                                                 (HMENU)ALPHATOGGLEBUTTON_ID,\r
1336                                                                 hResource, \r
1337                                                                 NULL);\r
1338         if (hwndAlphaToggleBtn == INVALID_HANDLE_VALUE)\r
1339                 return false;\r
1340         hAlphaToggle = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_ALPHATOGGLE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);\r
1341         SendMessage(hwndAlphaToggleBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hAlphaToggle);\r
1342 \r
1343         return true;\r
1344 }\r
1345 \r
1346 void CPicWindow::PositionChildren()\r
1347 {\r
1348         RECT rect;\r
1349         ::GetClientRect(*this, &rect);\r
1350         if (HasMultipleImages())\r
1351         {\r
1352                 SetWindowPos(hwndLeftBtn, HWND_TOP, rect.left+3, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);\r
1353                 SetWindowPos(hwndRightBtn, HWND_TOP, rect.left+23, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);\r
1354                 if (nFrames > 1)\r
1355                         SetWindowPos(hwndPlayBtn, HWND_TOP, rect.left+43, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);\r
1356                 else\r
1357                         ShowWindow(hwndPlayBtn, SW_HIDE);\r
1358         }\r
1359         else\r
1360         {\r
1361                 ShowWindow(hwndLeftBtn, SW_HIDE);\r
1362                 ShowWindow(hwndRightBtn, SW_HIDE);\r
1363                 ShowWindow(hwndPlayBtn, SW_HIDE);\r
1364         }\r
1365         PositionTrackBar();\r
1366 }\r
1367 \r
1368 bool CPicWindow::HasMultipleImages()\r
1369 {\r
1370         return (((nDimensions > 1)||(nFrames > 1))&&(pSecondPic == NULL));\r
1371 }\r
1372 \r
1373 void CPicWindow::CreateTrackbar(HWND hwndParent)\r
1374\r
1375         HWND hwndTrack = CreateWindowEx( \r
1376                 0,                                                                      // no extended styles \r
1377                 TRACKBAR_CLASS,                                         // class name \r
1378                 _T("Trackbar Control"),                         // title (caption) \r
1379                 WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_TOOLTIPS | TBS_AUTOTICKS,                                // style \r
1380                 10, 10,                                                         // position \r
1381                 200, 30,                                                        // size \r
1382                 hwndParent,                                                     // parent window \r
1383                 (HMENU)TRACKBAR_ID,                                     // control identifier \r
1384                 hInst,                                                          // instance \r
1385                 NULL                                                            // no WM_CREATE parameter \r
1386                 );\r
1387 \r
1388         SendMessage(hwndTrack, TBM_SETRANGE, \r
1389                 (WPARAM) TRUE,                                  // redraw flag \r
1390                 (LPARAM) MAKELONG(0, 16));              // min. & max. positions \r
1391         SendMessage(hwndTrack, TBM_SETTIPSIDE, \r
1392                 (WPARAM) TBTS_TOP,\r
1393                 (LPARAM) 0);\r
1394 \r
1395         m_AlphaSlider.ConvertTrackbarToNice(hwndTrack);\r
1396 }\r
1397 \r
1398 void CPicWindow::BuildInfoString(TCHAR * buf, int size, bool bTooltip)\r
1399 {\r
1400         // Unfortunately, we need two different strings for the tooltip\r
1401         // and the info box. Because the tooltips use a different tab size\r
1402         // than ExtTextOut(), and to keep the output aligned we therefore\r
1403         // need two different strings.\r
1404         // Note: some translations could end up with two identical strings, but\r
1405         // in English we need two - even if we wouldn't need two in English, some\r
1406         // translation might then need two again.\r
1407         if (pSecondPic)\r
1408         {\r
1409                 _stprintf_s(buf, size, \r
1410                         (TCHAR const *)ResString(hResource, bTooltip ? IDS_DUALIMAGEINFOTT : IDS_DUALIMAGEINFO),\r
1411                         picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),\r
1412                         picture.m_Width, picture.m_Height,\r
1413                         picture.GetHorizontalResolution(), picture.GetVerticalResolution(),\r
1414                         picture.m_ColorDepth,\r
1415                         (UINT)(GetZoom()*100.0),\r
1416                         pSecondPic->GetFileSizeAsText().c_str(), pSecondPic->GetFileSizeAsText(false).c_str(),\r
1417                         pSecondPic->m_Width, pSecondPic->m_Height,\r
1418                         pSecondPic->GetHorizontalResolution(), pSecondPic->GetVerticalResolution(),\r
1419                         pSecondPic->m_ColorDepth,\r
1420                         (UINT)(pTheOtherPic->GetZoom()*100.0));\r
1421         }\r
1422         else\r
1423         {\r
1424                 _stprintf_s(buf, size, \r
1425                         (TCHAR const *)ResString(hResource, bTooltip ? IDS_IMAGEINFOTT : IDS_IMAGEINFO),\r
1426                         picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),\r
1427                         picture.m_Width, picture.m_Height,\r
1428                         picture.GetHorizontalResolution(), picture.GetVerticalResolution(),\r
1429                         picture.m_ColorDepth,\r
1430                         (UINT)(GetZoom()*100.0));\r
1431         }\r
1432 }\r