Sync TortoiseIDiff with TortoiseSVN
[TortoiseGit.git] / src / TortoiseIDiff / PicWindow.cpp
blob4c45caf2e8a1679d976341848c7e922f3e6fa5bd
1 // TortoiseIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2012 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "shellapi.h"
21 #include "commctrl.h"
22 #include "PicWindow.h"
23 #include "math.h"
24 #include "SysInfo.h"
26 #pragma comment(lib, "Msimg32.lib")
27 #pragma comment(lib, "shell32.lib")
29 bool CPicWindow::RegisterAndCreateWindow(HWND hParent)
31 WNDCLASSEX wcx;
33 // Fill in the window class structure with default parameters
34 wcx.cbSize = sizeof(WNDCLASSEX);
35 wcx.style = CS_HREDRAW | CS_VREDRAW;
36 wcx.lpfnWndProc = CWindow::stWinMsgHandler;
37 wcx.cbClsExtra = 0;
38 wcx.cbWndExtra = 0;
39 wcx.hInstance = hResource;
40 wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
41 wcx.lpszClassName = _T("TortoiseGitIDiffPicWindow");
42 wcx.hIcon = LoadIcon(hResource, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
43 wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
44 wcx.lpszMenuName = MAKEINTRESOURCE(IDC_TORTOISEIDIFF);
45 wcx.hIconSm = LoadIcon(wcx.hInstance, MAKEINTRESOURCE(IDI_TORTOISEIDIFF));
46 RegisterWindow(&wcx);
47 if (CreateEx(WS_EX_ACCEPTFILES | WS_EX_CLIENTEDGE, WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE, hParent))
49 ShowWindow(m_hwnd, SW_SHOW);
50 UpdateWindow(m_hwnd);
51 CreateButtons();
52 return true;
54 return false;
57 void CPicWindow::PositionTrackBar()
59 RECT rc;
60 GetClientRect(&rc);
61 HWND slider = m_AlphaSlider.GetWindow();
62 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
64 MoveWindow(slider, 0, rc.top-4+SLIDER_WIDTH, SLIDER_WIDTH, rc.bottom-rc.top-SLIDER_WIDTH+8, true);
65 ShowWindow(slider, SW_SHOW);
66 MoveWindow(hwndAlphaToggleBtn, 0, rc.top-4, SLIDER_WIDTH, SLIDER_WIDTH, true);
67 ShowWindow(hwndAlphaToggleBtn, SW_SHOW);
69 else
71 ShowWindow(slider, SW_HIDE);
72 ShowWindow(hwndAlphaToggleBtn, SW_HIDE);
76 LRESULT CALLBACK CPicWindow::WinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
78 TRACKMOUSEEVENT mevt;
79 switch (uMsg)
81 case WM_CREATE:
83 // create a slider control
84 CreateTrackbar(hwnd);
85 ShowWindow(m_AlphaSlider.GetWindow(), SW_HIDE);
86 //Create the tooltips
87 TOOLINFO ti;
88 RECT rect; // for client area coordinates
90 hwndTT = CreateWindowEx(WS_EX_TOPMOST,
91 TOOLTIPS_CLASS,
92 NULL,
93 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
94 CW_USEDEFAULT,
95 CW_USEDEFAULT,
96 CW_USEDEFAULT,
97 CW_USEDEFAULT,
98 hwnd,
99 NULL,
100 hResource,
101 NULL
104 SetWindowPos(hwndTT,
105 HWND_TOPMOST,
110 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
112 ::GetClientRect(hwnd, &rect);
114 ti.cbSize = sizeof(TOOLINFO);
115 ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
116 ti.hwnd = hwnd;
117 ti.hinst = hResource;
118 ti.uId = 0;
119 ti.lpszText = LPSTR_TEXTCALLBACK;
120 // ToolTip control will cover the whole window
121 ti.rect.left = rect.left;
122 ti.rect.top = rect.top;
123 ti.rect.right = rect.right;
124 ti.rect.bottom = rect.bottom;
126 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
127 SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 600);
128 nHSecondScrollPos = 0;
129 nVSecondScrollPos = 0;
131 break;
132 case WM_SETFOCUS:
133 case WM_KILLFOCUS:
134 InvalidateRect(*this, NULL, FALSE);
135 break;
136 case WM_ERASEBKGND:
137 return 1;
138 break;
139 case WM_PAINT:
140 Paint(hwnd);
141 break;
142 case WM_SIZE:
143 PositionTrackBar();
144 SetupScrollBars();
145 break;
146 case WM_VSCROLL:
147 if ((pSecondPic)&&((HWND)lParam == m_AlphaSlider.GetWindow()))
149 if (LOWORD(wParam) == TB_THUMBTRACK)
151 // while tracking, only redraw after 50 milliseconds
152 ::SetTimer(*this, TIMER_ALPHASLIDER, 50, NULL);
154 else
155 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0) / 16.0f);
157 else
159 UINT nPos = HIWORD(wParam);
160 bool bForceUpdate = false;
161 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
163 // Get true 32-bit scroll position
164 SCROLLINFO si;
165 si.cbSize = sizeof(SCROLLINFO);
166 si.fMask = SIF_TRACKPOS;
167 GetScrollInfo(*this, SB_VERT, &si);
168 nPos = si.nTrackPos;
169 bForceUpdate = true;
172 OnVScroll(LOWORD(wParam), nPos);
173 if (bLinkedPositions)
175 pTheOtherPic->OnVScroll(LOWORD(wParam), nPos);
176 if (bForceUpdate)
177 ::UpdateWindow(*pTheOtherPic);
180 break;
181 case WM_HSCROLL:
183 UINT nPos = HIWORD(wParam);
184 bool bForceUpdate = false;
185 if (LOWORD(wParam) == SB_THUMBTRACK || LOWORD(wParam) == SB_THUMBPOSITION)
187 // Get true 32-bit scroll position
188 SCROLLINFO si;
189 si.cbSize = sizeof(SCROLLINFO);
190 si.fMask = SIF_TRACKPOS;
191 GetScrollInfo(*this, SB_VERT, &si);
192 nPos = si.nTrackPos;
193 bForceUpdate = true;
196 OnHScroll(LOWORD(wParam), nPos);
197 if (bLinkedPositions)
199 pTheOtherPic->OnHScroll(LOWORD(wParam), nPos);
200 if (bForceUpdate)
201 ::UpdateWindow(*pTheOtherPic);
204 break;
205 case WM_MOUSEWHEEL:
207 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));
208 if (bFitSizes)
209 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam));
211 break;
212 case WM_MOUSEHWHEEL:
214 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));
215 if (bFitSizes)
216 pTheOtherPic->OnMouseWheel(GET_KEYSTATE_WPARAM(wParam)|MK_SHIFT, GET_WHEEL_DELTA_WPARAM(wParam));
218 break;
219 case WM_LBUTTONDOWN:
220 SetFocus(*this);
221 ptPanStart.x = GET_X_LPARAM(lParam);
222 ptPanStart.y = GET_Y_LPARAM(lParam);
223 startVScrollPos = nVScrollPos;
224 startHScrollPos = nHScrollPos;
225 startVSecondScrollPos = nVSecondScrollPos;
226 startHSecondScrollPos = nHSecondScrollPos;
227 SetCapture(*this);
228 break;
229 case WM_LBUTTONUP:
230 ReleaseCapture();
231 break;
232 case WM_MOUSELEAVE:
233 ptPanStart.x = -1;
234 ptPanStart.y = -1;
235 m_lastTTPos.x = 0;
236 m_lastTTPos.y = 0;
237 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
238 break;
239 case WM_MOUSEMOVE:
241 mevt.cbSize = sizeof(TRACKMOUSEEVENT);
242 mevt.dwFlags = TME_LEAVE;
243 mevt.dwHoverTime = HOVER_DEFAULT;
244 mevt.hwndTrack = *this;
245 ::TrackMouseEvent(&mevt);
246 POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};
247 if (pt.y < HEADER_HEIGHT)
249 ClientToScreen(*this, &pt);
250 if ((abs(m_lastTTPos.x - pt.x) > 20)||(abs(m_lastTTPos.y - pt.y) > 10))
252 m_lastTTPos = pt;
253 pt.x += 15;
254 pt.y += 15;
255 SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
256 TOOLINFO ti = {0};
257 ti.cbSize = sizeof(TOOLINFO);
258 ti.hwnd = *this;
259 ti.uId = 0;
260 SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
263 else
265 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
266 m_lastTTPos.x = 0;
267 m_lastTTPos.y = 0;
269 if ((wParam & MK_LBUTTON) &&
270 (ptPanStart.x >= 0) &&
271 (ptPanStart.y >= 0))
273 // pan the image
274 int xPos = GET_X_LPARAM(lParam);
275 int yPos = GET_Y_LPARAM(lParam);
277 if (wParam & MK_CONTROL)
279 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
280 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
282 else if (wParam & MK_SHIFT)
284 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
285 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
287 else
289 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
290 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
291 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
292 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
294 SetupScrollBars();
295 InvalidateRect(*this, NULL, TRUE);
296 UpdateWindow(*this);
297 if ((bLinkedPositions)&&((wParam & MK_SHIFT)==0))
299 pTheOtherPic->nHScrollPos = nHScrollPos;
300 pTheOtherPic->nVScrollPos = nVScrollPos;
301 pTheOtherPic->SetupScrollBars();
302 InvalidateRect(*pTheOtherPic, NULL, TRUE);
303 UpdateWindow(*pTheOtherPic);
307 break;
308 case WM_SETCURSOR:
310 // we show a hand cursor if the image can be dragged,
311 // and a hand-down cursor if the image is currently dragged
312 if ((*this == (HWND)wParam)&&(LOWORD(lParam)==HTCLIENT))
314 RECT rect;
315 GetClientRect(&rect);
316 LONG width = picture.m_Width;
317 LONG height = picture.m_Height;
318 if (pSecondPic)
320 width = max(width, pSecondPic->m_Width);
321 height = max(height, pSecondPic->m_Height);
324 if ((GetKeyState(VK_LBUTTON)&0x8000)||(HIWORD(lParam) == WM_LBUTTONDOWN))
326 SetCursor(curHandDown);
328 else
330 SetCursor(curHand);
332 return TRUE;
334 return DefWindowProc(hwnd, uMsg, wParam, lParam);
336 break;
337 case WM_DROPFILES:
339 HDROP hDrop = (HDROP)wParam;
340 TCHAR szFileName[MAX_PATH];
341 // we only use the first file dropped (if multiple files are dropped)
342 if (DragQueryFile(hDrop, 0, szFileName, _countof(szFileName)))
344 SetPic(szFileName, _T(""), bMainPic);
345 FitImageInWindow();
346 InvalidateRect(*this, NULL, TRUE);
349 break;
350 case WM_COMMAND:
352 switch (LOWORD(wParam))
354 case LEFTBUTTON_ID:
356 PrevImage();
357 if (bLinkedPositions)
358 pTheOtherPic->PrevImage();
359 return 0;
361 break;
362 case RIGHTBUTTON_ID:
364 NextImage();
365 if (bLinkedPositions)
366 pTheOtherPic->NextImage();
367 return 0;
369 break;
370 case PLAYBUTTON_ID:
372 bPlaying = !bPlaying;
373 Animate(bPlaying);
374 if (bLinkedPositions)
375 pTheOtherPic->Animate(bPlaying);
376 return 0;
378 break;
379 case ALPHATOGGLEBUTTON_ID:
381 WORD msg = HIWORD(wParam);
382 switch (msg)
384 case BN_DOUBLECLICKED:
386 SendMessage(hwndAlphaToggleBtn, BM_SETSTATE, 1, 0);
387 SetTimer(*this, ID_ALPHATOGGLETIMER, 1000, NULL);
389 break;
390 case BN_CLICKED:
391 KillTimer(*this, ID_ALPHATOGGLETIMER);
392 ToggleAlpha();
393 break;
395 return 0;
397 break;
398 case BLENDALPHA_ID:
400 m_blend = BLEND_ALPHA;
401 PositionTrackBar();
402 InvalidateRect(*this, NULL, TRUE);
404 break;
405 case BLENDXOR_ID:
407 m_blend = BLEND_XOR;
408 PositionTrackBar();
409 InvalidateRect(*this, NULL, TRUE);
411 break;
414 break;
415 case WM_TIMER:
417 switch (wParam)
419 case ID_ANIMATIONTIMER:
421 nCurrentFrame++;
422 if (nCurrentFrame > picture.GetNumberOfFrames(0))
423 nCurrentFrame = 1;
424 long delay = picture.SetActiveFrame(nCurrentFrame);
425 delay = max(100, delay);
426 SetTimer(*this, ID_ANIMATIONTIMER, delay, NULL);
427 InvalidateRect(*this, NULL, FALSE);
429 break;
430 case TIMER_ALPHASLIDER:
432 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0)/16.0f);
433 KillTimer(*this, TIMER_ALPHASLIDER);
435 break;
436 case ID_ALPHATOGGLETIMER:
438 ToggleAlpha();
440 break;
443 break;
444 case WM_NOTIFY:
446 LPNMHDR pNMHDR = (LPNMHDR)lParam;
447 if (pNMHDR->code == TTN_GETDISPINFO)
449 if (pNMHDR->hwndFrom == m_AlphaSlider.GetWindow())
451 LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT) lParam;
452 lpttt->hinst = hResource;
453 TCHAR stringbuf[MAX_PATH] = {0};
454 _stprintf_s(stringbuf, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
455 _tcscpy_s(lpttt->lpszText, 80, stringbuf);
457 else if (pNMHDR->idFrom == (UINT_PTR)hwndAlphaToggleBtn)
459 _stprintf_s(m_wszTip, (TCHAR const *)ResString(hResource, IDS_ALPHABUTTONTT), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
460 if (pNMHDR->code == TTN_NEEDTEXTW)
462 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
463 pTTTW->lpszText = m_wszTip;
465 else
467 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
468 pTTTA->lpszText = m_szTip;
469 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
472 else
474 BuildInfoString(m_wszTip, _countof(m_wszTip), true);
475 if (pNMHDR->code == TTN_NEEDTEXTW)
477 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
478 pTTTW->lpszText = m_wszTip;
480 else
482 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
483 pTTTA->lpszText = m_szTip;
484 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
489 break;
490 case WM_DESTROY:
491 DestroyIcon(hLeft);
492 DestroyIcon(hRight);
493 DestroyIcon(hPlay);
494 DestroyIcon(hStop);
495 bWindowClosed = TRUE;
496 break;
497 default:
498 return DefWindowProc(hwnd, uMsg, wParam, lParam);
501 return 0;
504 void CPicWindow::NextImage()
506 nCurrentDimension++;
507 if (nCurrentDimension > picture.GetNumberOfDimensions())
508 nCurrentDimension = picture.GetNumberOfDimensions();
509 nCurrentFrame++;
510 if (nCurrentFrame > picture.GetNumberOfFrames(0))
511 nCurrentFrame = picture.GetNumberOfFrames(0);
512 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
513 InvalidateRect(*this, NULL, FALSE);
514 PositionChildren();
517 void CPicWindow::PrevImage()
519 nCurrentDimension--;
520 if (nCurrentDimension < 1)
521 nCurrentDimension = 1;
522 nCurrentFrame--;
523 if (nCurrentFrame < 1)
524 nCurrentFrame = 1;
525 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
526 InvalidateRect(*this, NULL, FALSE);
527 PositionChildren();
530 void CPicWindow::Animate(bool bStart)
532 if (bStart)
534 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hStop);
535 SetTimer(*this, ID_ANIMATIONTIMER, 0, NULL);
537 else
539 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
540 KillTimer(*this, ID_ANIMATIONTIMER);
544 void CPicWindow::SetPic(tstring path, tstring title, bool bFirst)
546 bMainPic = bFirst;
547 picpath=path;pictitle=title;
548 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
549 bValid = picture.Load(picpath);
550 nDimensions = picture.GetNumberOfDimensions();
551 if (nDimensions)
552 nFrames = picture.GetNumberOfFrames(0);
553 if (bValid)
555 picscale = 1.0;
556 PositionChildren();
557 InvalidateRect(*this, NULL, FALSE);
561 void CPicWindow::DrawViewTitle(HDC hDC, RECT * rect)
563 HFONT hFont = NULL;
564 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"));
565 HFONT hFontOld = (HFONT)SelectObject(hDC, (HGDIOBJ)hFont);
567 RECT textrect;
568 textrect.left = rect->left;
569 textrect.top = rect->top;
570 textrect.right = rect->right;
571 textrect.bottom = rect->top + HEADER_HEIGHT;
572 if (HasMultipleImages())
573 textrect.bottom += HEADER_HEIGHT;
575 COLORREF crBk, crFg;
576 crBk = ::GetSysColor(COLOR_SCROLLBAR);
577 crFg = ::GetSysColor(COLOR_WINDOWTEXT);
578 SetBkColor(hDC, crBk);
579 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &textrect, NULL, 0, NULL);
581 if (GetFocus() == *this)
582 DrawEdge(hDC, &textrect, EDGE_BUMP, BF_RECT);
583 else
584 DrawEdge(hDC, &textrect, EDGE_ETCHED, BF_RECT);
586 SetTextColor(hDC, crFg);
588 // use the path if no title is set.
589 tstring * title = pictitle.empty() ? &picpath : &pictitle;
591 tstring realtitle = *title;
592 tstring imgnumstring;
594 if (HasMultipleImages())
596 TCHAR buf[MAX_PATH];
597 if (nFrames > 1)
598 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentFrame, nFrames);
599 else
600 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentDimension, nDimensions);
601 imgnumstring = buf;
604 SIZE stringsize;
605 if (GetTextExtentPoint32(hDC, realtitle.c_str(), (int)realtitle.size(), &stringsize))
607 int nStringLength = stringsize.cx;
608 int texttop = pSecondPic ? textrect.top + (HEADER_HEIGHT/2) - stringsize.cy : textrect.top + (HEADER_HEIGHT/2) - stringsize.cy/2;
609 ExtTextOut(hDC,
610 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
611 texttop,
612 ETO_CLIPPED,
613 &textrect,
614 realtitle.c_str(),
615 (UINT)realtitle.size(),
616 NULL);
617 if (pSecondPic)
619 realtitle = (pictitle2.empty() ? picpath2 : pictitle2);
620 ExtTextOut(hDC,
621 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
622 texttop + stringsize.cy,
623 ETO_CLIPPED,
624 &textrect,
625 realtitle.c_str(),
626 (UINT)realtitle.size(),
627 NULL);
630 if (HasMultipleImages())
632 if (GetTextExtentPoint32(hDC, imgnumstring.c_str(), (int)imgnumstring.size(), &stringsize))
634 int nStringLength = stringsize.cx;
636 ExtTextOut(hDC,
637 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
638 textrect.top + HEADER_HEIGHT + (HEADER_HEIGHT/2) - stringsize.cy/2,
639 ETO_CLIPPED,
640 &textrect,
641 imgnumstring.c_str(),
642 (UINT)imgnumstring.size(),
643 NULL);
646 SelectObject(hDC, (HGDIOBJ)hFontOld);
647 DeleteObject(hFont);
650 void CPicWindow::SetupScrollBars()
652 RECT rect;
653 GetClientRect(&rect);
655 SCROLLINFO si = {sizeof(si)};
657 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL;
659 double width = double(picture.m_Width)*picscale;
660 double height = double(picture.m_Height)*picscale;
661 if (pSecondPic && pTheOtherPic)
663 width = max(width, double(pSecondPic->m_Width)*pTheOtherPic->GetZoom());
664 height = max(height, double(pSecondPic->m_Height)*pTheOtherPic->GetZoom());
667 bool bShowHScrollBar = (nHScrollPos > 0); // left of pic is left of window
668 bShowHScrollBar = bShowHScrollBar || (width-nHScrollPos > rect.right); // right of pic is outside right of window
669 bShowHScrollBar = bShowHScrollBar || (width+nHScrollPos > rect.right); // right of pic is outside right of window
670 bool bShowVScrollBar = (nVScrollPos > 0); // top of pic is above window
671 bShowVScrollBar = bShowVScrollBar || (height-nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
672 bShowVScrollBar = bShowVScrollBar || (height+nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
674 // if the image is smaller than the window, we don't need the scrollbars
675 ShowScrollBar(*this, SB_HORZ, bShowHScrollBar);
676 ShowScrollBar(*this, SB_VERT, bShowVScrollBar);
678 if (bShowVScrollBar)
680 si.nPos = nVScrollPos;
681 si.nPage = rect.bottom-rect.top;
682 if (height < rect.bottom-rect.top)
684 if (nVScrollPos > 0)
686 si.nMin = 0;
687 si.nMax = rect.bottom+nVScrollPos-rect.top;
689 else
691 si.nMin = nVScrollPos;
692 si.nMax = int(height);
695 else
697 if (nVScrollPos > 0)
699 si.nMin = 0;
700 si.nMax = int(max(height, rect.bottom+nVScrollPos-rect.top));
702 else
704 si.nMin = 0;
705 si.nMax = int(height-nVScrollPos);
708 SetScrollInfo(*this, SB_VERT, &si, TRUE);
711 if (bShowHScrollBar)
713 si.nPos = nHScrollPos;
714 si.nPage = rect.right-rect.left;
715 if (width < rect.right-rect.left)
717 if (nHScrollPos > 0)
719 si.nMin = 0;
720 si.nMax = rect.right+nHScrollPos-rect.left;
722 else
724 si.nMin = nHScrollPos;
725 si.nMax = int(width);
728 else
730 if (nHScrollPos > 0)
732 si.nMin = 0;
733 si.nMax = int(max(width, rect.right+nHScrollPos-rect.left));
735 else
737 si.nMin = 0;
738 si.nMax = int(width-nHScrollPos);
741 SetScrollInfo(*this, SB_HORZ, &si, TRUE);
744 PositionChildren();
747 void CPicWindow::OnVScroll(UINT nSBCode, UINT nPos)
749 RECT rect;
750 GetClientRect(&rect);
752 switch (nSBCode)
754 case SB_BOTTOM:
755 nVScrollPos = LONG(double(picture.GetHeight())*picscale);
756 break;
757 case SB_TOP:
758 nVScrollPos = 0;
759 break;
760 case SB_LINEDOWN:
761 nVScrollPos++;
762 break;
763 case SB_LINEUP:
764 nVScrollPos--;
765 break;
766 case SB_PAGEDOWN:
767 nVScrollPos += (rect.bottom-rect.top);
768 break;
769 case SB_PAGEUP:
770 nVScrollPos -= (rect.bottom-rect.top);
771 break;
772 case SB_THUMBPOSITION:
773 nVScrollPos = nPos;
774 break;
775 case SB_THUMBTRACK:
776 nVScrollPos = nPos;
777 break;
778 default:
779 return;
781 LONG height = LONG(double(picture.GetHeight())*picscale);
782 if (pSecondPic)
784 height = max(height, LONG(double(pSecondPic->GetHeight())*picscale));
785 nVSecondScrollPos = nVScrollPos;
787 SetupScrollBars();
788 PositionChildren();
789 InvalidateRect(*this, NULL, TRUE);
792 void CPicWindow::OnHScroll(UINT nSBCode, UINT nPos)
794 RECT rect;
795 GetClientRect(&rect);
797 switch (nSBCode)
799 case SB_RIGHT:
800 nHScrollPos = LONG(double(picture.GetWidth())*picscale);
801 break;
802 case SB_LEFT:
803 nHScrollPos = 0;
804 break;
805 case SB_LINERIGHT:
806 nHScrollPos++;
807 break;
808 case SB_LINELEFT:
809 nHScrollPos--;
810 break;
811 case SB_PAGERIGHT:
812 nHScrollPos += (rect.right-rect.left);
813 break;
814 case SB_PAGELEFT:
815 nHScrollPos -= (rect.right-rect.left);
816 break;
817 case SB_THUMBPOSITION:
818 nHScrollPos = nPos;
819 break;
820 case SB_THUMBTRACK:
821 nHScrollPos = nPos;
822 break;
823 default:
824 return;
826 LONG width = LONG(double(picture.GetWidth())*picscale);
827 if (pSecondPic)
829 width = max(width, LONG(double(pSecondPic->GetWidth())*picscale));
830 nHSecondScrollPos = nHScrollPos;
832 SetupScrollBars();
833 PositionChildren();
834 InvalidateRect(*this, NULL, TRUE);
837 void CPicWindow::OnMouseWheel(short fwKeys, short zDelta)
839 RECT rect;
840 GetClientRect(&rect);
841 LONG width = long(double(picture.m_Width)*picscale);
842 LONG height = long(double(picture.m_Height)*picscale);
843 if (pSecondPic)
845 width = max(width, long(double(pSecondPic->m_Width)*picscale));
846 height = max(height, long(double(pSecondPic->m_Height)*picscale));
848 if ((fwKeys & MK_SHIFT)&&(fwKeys & MK_CONTROL)&&(pSecondPic))
850 // ctrl+shift+wheel: change the alpha channel
851 float a = blendAlpha;
852 a -= float(zDelta)/120.0f/4.0f;
853 if (a < 0.0f)
854 a = 0.0f;
855 else if (a > 1.0f)
856 a = 1.0f;
857 SetBlendAlpha(m_blend, a);
859 else if (fwKeys & MK_SHIFT)
861 // shift means scrolling sideways
862 OnHScroll(SB_THUMBPOSITION, GetHPos()-zDelta);
863 if ((bLinkedPositions)&&(pTheOtherPic))
865 pTheOtherPic->OnHScroll(SB_THUMBPOSITION, pTheOtherPic->GetHPos()-zDelta);
868 else if (fwKeys & MK_CONTROL)
870 // control means adjusting the scale factor
871 Zoom(zDelta>0, true);
872 if ((bFitSizes)&&(pTheOtherPic)&&(!bOverlap))
873 pTheOtherPic->Zoom(zDelta>0, true);
874 PositionChildren();
875 InvalidateRect(*this, NULL, FALSE);
876 SetWindowPos(*this, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOMOVE);
878 else
880 OnVScroll(SB_THUMBPOSITION, GetVPos()-zDelta);
881 if ((bLinkedPositions)&&(pTheOtherPic))
883 pTheOtherPic->OnVScroll(SB_THUMBPOSITION, pTheOtherPic->GetVPos()-zDelta);
888 void CPicWindow::GetClientRect(RECT * pRect)
890 ::GetClientRect(*this, pRect);
891 pRect->top += HEADER_HEIGHT;
892 if (HasMultipleImages())
894 pRect->top += HEADER_HEIGHT;
896 if (pSecondPic)
897 pRect->left += SLIDER_WIDTH;
900 void CPicWindow::GetClientRectWithScrollbars(RECT * pRect)
902 GetClientRect(pRect);
903 ::GetWindowRect(*this, pRect);
904 pRect->right = pRect->right-pRect->left;
905 pRect->bottom = pRect->bottom-pRect->top;
906 pRect->top = 0;
907 pRect->left = 0;
908 pRect->top += HEADER_HEIGHT;
909 if (HasMultipleImages())
911 pRect->top += HEADER_HEIGHT;
913 if (pSecondPic)
914 pRect->left += SLIDER_WIDTH;
918 void CPicWindow::SetZoom(double dZoom, bool centermouse)
920 // Set the interpolation mode depending on zoom
921 double oldPicscale = picscale;
922 double oldOtherPicscale = picscale;
923 if (dZoom < 1.0)
924 { // Zoomed out, use high quality bicubic
925 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
926 if (pSecondPic)
927 pSecondPic->SetInterpolationMode(InterpolationModeHighQualityBicubic);
929 else if (!((int)(dZoom*100.0)%100))
930 { // "Even" zoom sizes should be shown w-o any interpolation
931 picture.SetInterpolationMode(InterpolationModeNearestNeighbor);
932 if (pSecondPic)
933 pSecondPic->SetInterpolationMode(InterpolationModeNearestNeighbor);
935 else
936 { // Arbitrary zoomed in, use bilinear that is semi-smoothed
937 picture.SetInterpolationMode(InterpolationModeBilinear);
938 if (pSecondPic)
939 pSecondPic->SetInterpolationMode(InterpolationModeBilinear);
941 picscale = dZoom;
943 if ((bFitSizes)&&(bMainPic))
945 double width, height;
946 double zoomWidth, zoomHeight;
947 width = double(picture.m_Width)*dZoom;
948 height = double(picture.m_Height)*dZoom;
949 zoomWidth = width/double(pTheOtherPic->GetPic()->m_Width);
950 zoomHeight = height/double(pTheOtherPic->GetPic()->m_Height);
951 oldOtherPicscale = pTheOtherPic->GetZoom();
952 pTheOtherPic->SetZoom(min(zoomWidth, zoomHeight), false);
955 // adjust the scrollbar positions according to the new zoom and the
956 // mouse position: if possible, keep the pixel where the mouse pointer
957 // is at the same position after the zoom
958 POINT cpos;
959 DWORD ptW = GetMessagePos();
960 cpos.x = GET_X_LPARAM(ptW);
961 cpos.y = GET_Y_LPARAM(ptW);
962 ScreenToClient(*this, &cpos);
963 RECT clientrect;
964 GetClientRect(&clientrect);
965 if ((PtInRect(&clientrect, cpos))&&(centermouse))
967 // the mouse pointer is over our window
968 nHScrollPos = int(double(nHScrollPos + cpos.x)*(dZoom/oldPicscale))-cpos.x;
969 nVScrollPos = int(double(nVScrollPos + cpos.y)*(dZoom/oldPicscale))-cpos.y;
970 if (pTheOtherPic && bMainPic)
972 double otherzoom = pTheOtherPic->GetZoom();
973 nHSecondScrollPos = int(double(nHSecondScrollPos + cpos.x)*(otherzoom/oldOtherPicscale))-cpos.x;
974 nVSecondScrollPos = int(double(nVSecondScrollPos + cpos.y)*(otherzoom/oldOtherPicscale))-cpos.y;
977 else
979 nHScrollPos = int(double(nHScrollPos + ((clientrect.right-clientrect.left)/2))*(dZoom/oldPicscale))-((clientrect.right-clientrect.left)/2);
980 nVScrollPos = int(double(nVScrollPos + ((clientrect.bottom-clientrect.top)/2))*(dZoom/oldPicscale))-((clientrect.bottom-clientrect.top)/2);
981 if (pTheOtherPic && bMainPic)
983 double otherzoom = pTheOtherPic->GetZoom();
984 nHSecondScrollPos = int(double(nHSecondScrollPos + ((clientrect.right-clientrect.left)/2))*(otherzoom/oldOtherPicscale))-((clientrect.right-clientrect.left)/2);
985 nVSecondScrollPos = int(double(nVSecondScrollPos + ((clientrect.bottom-clientrect.top)/2))*(otherzoom/oldOtherPicscale))-((clientrect.bottom-clientrect.top)/2);
989 SetupScrollBars();
990 PositionChildren();
991 InvalidateRect(*this, NULL, TRUE);
994 void CPicWindow::Zoom(bool in, bool centermouse)
996 double zoomFactor;
998 // Find correct zoom factor and quantize picscale
999 if (!in && picscale <= 0.2)
1001 picscale = 0.1;
1002 zoomFactor = 0;
1004 else if ((in && picscale < 1.0) || (!in && picscale <= 1.0))
1006 picscale = 0.1 * RoundDouble(picscale/0.1, 0); // Quantize to 0.1
1007 zoomFactor = 0.1;
1009 else if ((in && picscale < 2.0) || (!in && picscale <= 2.0))
1011 picscale = 0.25 * RoundDouble(picscale/0.25, 0); // Quantize to 0.25
1012 zoomFactor = 0.25;
1014 else
1016 picscale = RoundDouble(picscale,0);
1017 zoomFactor = 1;
1020 // Set zoom
1021 if (in)
1023 if ((pSecondPic)&&(!bFitSizes))
1024 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()+zoomFactor, false);
1025 SetZoom(picscale+zoomFactor, centermouse);
1027 else
1029 if ((pSecondPic)&&(!bFitSizes))
1030 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()-zoomFactor, false);
1031 SetZoom(picscale-zoomFactor, centermouse);
1035 double CPicWindow::RoundDouble(double doValue, int nPrecision)
1037 static const double doBase = 10.0;
1038 double doComplete5, doComplete5i;
1040 doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));
1042 if (doValue < 0.0)
1044 doComplete5 -= 5.0;
1046 else
1048 doComplete5 += 5.0;
1051 doComplete5 /= doBase;
1052 modf(doComplete5, &doComplete5i);
1054 return doComplete5i / pow(doBase, (double) nPrecision);
1056 void CPicWindow::FitImageInWindow()
1058 RECT rect;
1060 GetClientRectWithScrollbars(&rect);
1062 if (rect.right-rect.left)
1064 double dZoom = 1.0;
1065 if (((rect.right - rect.left) > picture.m_Width+2)&&((rect.bottom - rect.top)> picture.m_Height+2))
1067 // image is smaller than the window
1068 dZoom = 1.0;
1070 else
1072 // image is bigger than the window
1073 double xscale = double(rect.right-rect.left-2)/double(picture.m_Width);
1074 double yscale = double(rect.bottom-rect.top-2)/double(picture.m_Height);
1075 dZoom = min(yscale, xscale);
1077 if (pSecondPic)
1079 if (((rect.right - rect.left) > pSecondPic->m_Width+2)&&((rect.bottom - rect.top)> pSecondPic->m_Height+2))
1081 // image is smaller than the window
1082 pTheOtherPic->SetZoom(min(1.0, dZoom), false);
1084 else
1086 // image is bigger than the window
1087 double xscale = double(rect.right-rect.left-2)/double(pSecondPic->m_Width);
1088 double yscale = double(rect.bottom-rect.top-2)/double(pSecondPic->m_Height);
1089 pTheOtherPic->SetZoom(min(yscale, xscale), false);
1091 nHSecondScrollPos = 0;
1092 nVSecondScrollPos = 0;
1094 SetZoom(dZoom, false);
1096 CenterImage();
1097 PositionChildren();
1098 InvalidateRect(*this, NULL, TRUE);
1101 void CPicWindow::CenterImage()
1103 RECT rect;
1104 GetClientRectWithScrollbars(&rect);
1105 double width = (double(picture.m_Width)*picscale) + 2.0;
1106 double height = (double(picture.m_Height)*picscale) + 2.0;
1107 if (pSecondPic)
1109 width = max(width, (double(pSecondPic->m_Width)*pTheOtherPic->GetZoom()) + 2.0);
1110 height = max(height, (double(pSecondPic->m_Height)*pTheOtherPic->GetZoom()) + 2.0);
1113 bool bPicWidthBigger = (int(width) > (rect.right-rect.left));
1114 bool bPicHeightBigger = (int(height) > (rect.bottom-rect.top));
1115 // set the scroll position so that the image is drawn centered in the window
1116 // if the window is bigger than the image
1117 if (!bPicWidthBigger)
1119 nHScrollPos = -((rect.right-rect.left+4)-int(width))/2;
1120 nHSecondScrollPos = nHScrollPos;
1122 if (!bPicHeightBigger)
1124 nVScrollPos = -((rect.bottom-rect.top+4)-int(height))/2;
1125 nVSecondScrollPos = nVScrollPos;
1127 SetupScrollBars();
1130 void CPicWindow::FitSizes(bool bFit)
1132 bFitSizes = bFit;
1134 SetZoom(GetZoom(), false);
1137 void CPicWindow::ShowPicWithBorder(HDC hdc, const RECT &bounds, CPicture &pic, double scale)
1139 ::SetBkColor(hdc, transparentColor);
1140 ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1142 RECT picrect;
1143 picrect.left = bounds.left - nHScrollPos;
1144 picrect.top = bounds.top - nVScrollPos;
1145 if ((!bLinkedPositions || bOverlap) && (pTheOtherPic) && (&pic != &picture))
1147 picrect.left = bounds.left - nHSecondScrollPos;
1148 picrect.top = bounds.top - nVSecondScrollPos;
1150 picrect.right = (picrect.left + LONG(double(pic.m_Width) * scale));
1151 picrect.bottom = (picrect.top + LONG(double(pic.m_Height) * scale));
1153 pic.Show(hdc, picrect);
1155 RECT border;
1156 border.left = picrect.left-1;
1157 border.top = picrect.top-1;
1158 border.right = picrect.right+1;
1159 border.bottom = picrect.bottom+1;
1161 HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));
1162 HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
1163 MoveToEx(hdc, border.left, border.top, NULL);
1164 LineTo(hdc, border.left, border.bottom);
1165 LineTo(hdc, border.right, border.bottom);
1166 LineTo(hdc, border.right, border.top);
1167 LineTo(hdc, border.left, border.top);
1168 SelectObject(hdc, hOldPen);
1169 DeleteObject(hPen);
1172 void CPicWindow::Paint(HWND hwnd)
1174 PAINTSTRUCT ps;
1175 HDC hdc;
1176 RECT rect, fullrect;
1178 GetUpdateRect(hwnd, &rect, FALSE);
1179 if (IsRectEmpty(&rect))
1180 return;
1182 ::GetClientRect(*this, &fullrect);
1183 hdc = BeginPaint(hwnd, &ps);
1185 // Exclude the alpha control and button
1186 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1187 ExcludeClipRect(hdc, 0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4);
1189 CMyMemDC memDC(hdc);
1190 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1192 // erase the place where the alpha slider would be
1193 ::SetBkColor(memDC, transparentColor);
1194 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1195 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1198 GetClientRect(&rect);
1199 if (bValid)
1201 ShowPicWithBorder(memDC, rect, picture, picscale);
1202 if (pSecondPic)
1204 HDC secondhdc = CreateCompatibleDC(hdc);
1205 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
1206 HBITMAP hOldBitmap = (HBITMAP)SelectObject(secondhdc, hBitmap);
1207 SetWindowOrgEx(secondhdc, rect.left, rect.top, NULL);
1209 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1211 // erase the place where the alpha slider would be
1212 ::SetBkColor(secondhdc, transparentColor);
1213 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1214 ::ExtTextOut(secondhdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1216 ShowPicWithBorder(secondhdc, rect, *pSecondPic, pTheOtherPic->GetZoom());
1218 if (m_blend == BLEND_ALPHA)
1220 BLENDFUNCTION blender;
1221 blender.AlphaFormat = 0;
1222 blender.BlendFlags = 0;
1223 blender.BlendOp = AC_SRC_OVER;
1224 blender.SourceConstantAlpha = (BYTE)(blendAlpha*255);
1225 AlphaBlend(memDC,
1226 rect.left,
1227 rect.top,
1228 rect.right-rect.left,
1229 rect.bottom-rect.top,
1230 secondhdc,
1231 rect.left,
1232 rect.top,
1233 rect.right-rect.left,
1234 rect.bottom-rect.top,
1235 blender);
1237 else if (m_blend == BLEND_XOR)
1239 BitBlt(memDC,
1240 rect.left,
1241 rect.top,
1242 rect.right-rect.left,
1243 rect.bottom-rect.top,
1244 secondhdc,
1245 rect.left,
1246 rect.top,
1247 //rect.right-rect.left,
1248 //rect.bottom-rect.top,
1249 SRCINVERT);
1250 InvertRect(memDC, &rect);
1252 SelectObject(secondhdc, hOldBitmap);
1253 DeleteObject(hBitmap);
1254 DeleteDC(secondhdc);
1256 int sliderwidth = 0;
1257 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1258 sliderwidth = SLIDER_WIDTH;
1259 m_inforect.left = rect.left+4+sliderwidth;
1260 m_inforect.top = rect.top;
1261 m_inforect.right = rect.right+sliderwidth;
1262 m_inforect.bottom = rect.bottom;
1264 SetBkColor(memDC, transparentColor);
1265 if (bShowInfo)
1267 TCHAR infostring[8192];
1268 BuildInfoString(infostring, _countof(infostring), false);
1269 // set the font
1270 NONCLIENTMETRICS metrics = {0};
1271 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1273 #if (WINVER >= 0x600)
1274 if (!SysInfo::Instance().IsVistaOrLater())
1276 metrics.cbSize -= sizeof(int); // subtract the size of the iPaddedBorderWidth member which is not available on XP
1278 #endif
1280 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1281 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1282 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1283 // find out how big the rectangle for the text has to be
1284 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER | DT_CALCRECT);
1286 // the text should be drawn with a four pixel offset to the window borders
1287 m_inforect.top = rect.bottom - (m_inforect.bottom-m_inforect.top) - 4;
1288 m_inforect.bottom = rect.bottom-4;
1290 // first draw an edge rectangle
1291 RECT edgerect;
1292 edgerect.left = m_inforect.left-4;
1293 edgerect.top = m_inforect.top-4;
1294 edgerect.right = m_inforect.right+4;
1295 edgerect.bottom = m_inforect.bottom+4;
1296 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &edgerect, NULL, 0, NULL);
1297 DrawEdge(memDC, &edgerect, EDGE_BUMP, BF_RECT | BF_SOFT);
1299 SetTextColor(memDC, GetSysColor(COLOR_WINDOWTEXT));
1300 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER);
1301 SelectObject(memDC, (HGDIOBJ)hFontOld);
1302 DeleteObject(hFont);
1305 else
1307 SetBkColor(memDC, ::GetSysColor(COLOR_WINDOW));
1308 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1309 SIZE stringsize;
1310 ResString str = ResString(hResource, IDS_INVALIDIMAGEINFO);
1312 // set the font
1313 NONCLIENTMETRICS metrics = {0};
1314 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1315 #if (WINVER >= 0x600)
1316 if (!SysInfo::Instance().IsVistaOrLater())
1318 metrics.cbSize -= sizeof(int); // subtract the size of the iPaddedBorderWidth member which is not available on XP
1320 #endif
1321 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1322 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1323 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1325 if (GetTextExtentPoint32(memDC, str, (int)_tcslen(str), &stringsize))
1327 int nStringLength = stringsize.cx;
1329 ExtTextOut(memDC,
1330 max(rect.left + ((rect.right-rect.left)-nStringLength)/2, 1),
1331 rect.top + ((rect.bottom-rect.top) - stringsize.cy)/2,
1332 ETO_CLIPPED,
1333 &rect,
1334 str,
1335 (UINT)_tcslen(str),
1336 NULL);
1338 SelectObject(memDC, (HGDIOBJ)hFontOld);
1339 DeleteObject(hFont);
1341 DrawViewTitle(memDC, &fullrect);
1343 EndPaint(hwnd, &ps);
1346 bool CPicWindow::CreateButtons()
1348 // Ensure that the common control DLL is loaded.
1349 INITCOMMONCONTROLSEX icex;
1350 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
1351 icex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
1352 InitCommonControlsEx(&icex);
1354 hwndLeftBtn = CreateWindowEx(0,
1355 _T("BUTTON"),
1356 (LPCTSTR)NULL,
1357 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1358 0, 0, 0, 0,
1359 (HWND)*this,
1360 (HMENU)LEFTBUTTON_ID,
1361 hResource,
1362 NULL);
1363 if (hwndLeftBtn == INVALID_HANDLE_VALUE)
1364 return false;
1365 hLeft = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_BACKWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1366 SendMessage(hwndLeftBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hLeft);
1367 hwndRightBtn = CreateWindowEx(0,
1368 _T("BUTTON"),
1369 (LPCTSTR)NULL,
1370 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1371 0, 0, 0, 0,
1372 *this,
1373 (HMENU)RIGHTBUTTON_ID,
1374 hResource,
1375 NULL);
1376 if (hwndRightBtn == INVALID_HANDLE_VALUE)
1377 return false;
1378 hRight = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_FORWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1379 SendMessage(hwndRightBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hRight);
1380 hwndPlayBtn = CreateWindowEx(0,
1381 _T("BUTTON"),
1382 (LPCTSTR)NULL,
1383 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1384 0, 0, 0, 0,
1385 *this,
1386 (HMENU)PLAYBUTTON_ID,
1387 hResource,
1388 NULL);
1389 if (hwndPlayBtn == INVALID_HANDLE_VALUE)
1390 return false;
1391 hPlay = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1392 hStop = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1393 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
1394 hwndAlphaToggleBtn = CreateWindowEx(0,
1395 _T("BUTTON"),
1396 (LPCTSTR)NULL,
1397 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT | BS_NOTIFY | BS_PUSHLIKE,
1398 0, 0, 0, 0,
1399 (HWND)*this,
1400 (HMENU)ALPHATOGGLEBUTTON_ID,
1401 hResource,
1402 NULL);
1403 if (hwndAlphaToggleBtn == INVALID_HANDLE_VALUE)
1404 return false;
1405 hAlphaToggle = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_ALPHATOGGLE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1406 SendMessage(hwndAlphaToggleBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hAlphaToggle);
1408 TOOLINFO ti = {0};
1409 ti.cbSize = sizeof(TOOLINFO);
1410 ti.uFlags = TTF_IDISHWND|TTF_SUBCLASS;
1411 ti.hwnd = *this;
1412 ti.hinst = hResource;
1413 ti.uId = (UINT_PTR)hwndAlphaToggleBtn;
1414 ti.lpszText = LPSTR_TEXTCALLBACK;
1415 // ToolTip control will cover the whole window
1416 ti.rect.left = 0;
1417 ti.rect.top = 0;
1418 ti.rect.right = 0;
1419 ti.rect.bottom = 0;
1420 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
1422 return true;
1425 void CPicWindow::PositionChildren()
1427 RECT rect;
1428 ::GetClientRect(*this, &rect);
1429 if (HasMultipleImages())
1431 SetWindowPos(hwndLeftBtn, HWND_TOP, rect.left+3, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1432 SetWindowPos(hwndRightBtn, HWND_TOP, rect.left+23, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1433 if (nFrames > 1)
1434 SetWindowPos(hwndPlayBtn, HWND_TOP, rect.left+43, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1435 else
1436 ShowWindow(hwndPlayBtn, SW_HIDE);
1438 else
1440 ShowWindow(hwndLeftBtn, SW_HIDE);
1441 ShowWindow(hwndRightBtn, SW_HIDE);
1442 ShowWindow(hwndPlayBtn, SW_HIDE);
1444 PositionTrackBar();
1447 bool CPicWindow::HasMultipleImages()
1449 return (((nDimensions > 1)||(nFrames > 1))&&(pSecondPic == NULL));
1452 void CPicWindow::CreateTrackbar(HWND hwndParent)
1454 HWND hwndTrack = CreateWindowEx(
1455 0, // no extended styles
1456 TRACKBAR_CLASS, // class name
1457 _T("Trackbar Control"), // title (caption)
1458 WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_TOOLTIPS | TBS_AUTOTICKS, // style
1459 10, 10, // position
1460 200, 30, // size
1461 hwndParent, // parent window
1462 (HMENU)TRACKBAR_ID, // control identifier
1463 hInst, // instance
1464 NULL // no WM_CREATE parameter
1467 SendMessage(hwndTrack, TBM_SETRANGE,
1468 (WPARAM) TRUE, // redraw flag
1469 (LPARAM) MAKELONG(0, 16)); // min. & max. positions
1470 SendMessage(hwndTrack, TBM_SETTIPSIDE,
1471 (WPARAM) TBTS_TOP,
1472 (LPARAM) 0);
1474 m_AlphaSlider.ConvertTrackbarToNice(hwndTrack);
1477 void CPicWindow::BuildInfoString(TCHAR * buf, int size, bool bTooltip)
1479 // Unfortunately, we need two different strings for the tooltip
1480 // and the info box. Because the tooltips use a different tab size
1481 // than ExtTextOut(), and to keep the output aligned we therefore
1482 // need two different strings.
1483 // Note: some translations could end up with two identical strings, but
1484 // in English we need two - even if we wouldn't need two in English, some
1485 // translation might then need two again.
1486 if (pSecondPic)
1488 _stprintf_s(buf, size,
1489 (TCHAR const *)ResString(hResource, bTooltip ? IDS_DUALIMAGEINFOTT : IDS_DUALIMAGEINFO),
1490 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1491 picture.m_Width, picture.m_Height,
1492 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1493 picture.m_ColorDepth,
1494 (UINT)(GetZoom()*100.0),
1495 pSecondPic->GetFileSizeAsText().c_str(), pSecondPic->GetFileSizeAsText(false).c_str(),
1496 pSecondPic->m_Width, pSecondPic->m_Height,
1497 pSecondPic->GetHorizontalResolution(), pSecondPic->GetVerticalResolution(),
1498 pSecondPic->m_ColorDepth,
1499 (UINT)(pTheOtherPic->GetZoom()*100.0));
1501 else
1503 _stprintf_s(buf, size,
1504 (TCHAR const *)ResString(hResource, bTooltip ? IDS_IMAGEINFOTT : IDS_IMAGEINFO),
1505 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1506 picture.m_Width, picture.m_Height,
1507 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1508 picture.m_ColorDepth,
1509 (UINT)(GetZoom()*100.0));