also scroll to selected revision
[TortoiseGit.git] / src / TortoiseIDiff / PicWindow.cpp
blobdc81718ee1aa348996c646e65c6749d08348c24d
1 // TortoiseIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2011 - 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("TortoiseIDiffPicWindow");
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 m_lastTTPos.x = 0;
234 m_lastTTPos.y = 0;
235 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
236 break;
237 case WM_MOUSEMOVE:
239 mevt.cbSize = sizeof(TRACKMOUSEEVENT);
240 mevt.dwFlags = TME_LEAVE;
241 mevt.dwHoverTime = HOVER_DEFAULT;
242 mevt.hwndTrack = *this;
243 ::TrackMouseEvent(&mevt);
244 POINT pt = {((int)(short)LOWORD(lParam)), ((int)(short)HIWORD(lParam))};
245 if (pt.y < HEADER_HEIGHT)
247 ClientToScreen(*this, &pt);
248 if ((abs(m_lastTTPos.x - pt.x) > 20)||(abs(m_lastTTPos.y - pt.y) > 10))
250 m_lastTTPos = pt;
251 pt.x += 15;
252 pt.y += 15;
253 SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(pt.x, pt.y));
254 TOOLINFO ti = {0};
255 ti.cbSize = sizeof(TOOLINFO);
256 ti.hwnd = *this;
257 ti.uId = 0;
258 SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
261 else
263 SendMessage(hwndTT, TTM_TRACKACTIVATE, FALSE, 0);
264 m_lastTTPos.x = 0;
265 m_lastTTPos.y = 0;
267 if (wParam & MK_LBUTTON)
269 // pan the image
270 int xPos = GET_X_LPARAM(lParam);
271 int yPos = GET_Y_LPARAM(lParam);
273 if (wParam & MK_CONTROL)
275 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
276 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
278 else if (wParam & MK_SHIFT)
280 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
281 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
283 else
285 nHSecondScrollPos = startHSecondScrollPos + (ptPanStart.x - xPos);
286 nVSecondScrollPos = startVSecondScrollPos + (ptPanStart.y - yPos);
287 nHScrollPos = startHScrollPos + (ptPanStart.x - xPos);
288 nVScrollPos = startVScrollPos + (ptPanStart.y - yPos);
290 SetupScrollBars();
291 InvalidateRect(*this, NULL, TRUE);
292 UpdateWindow(*this);
293 if ((bLinkedPositions)&&((wParam & MK_SHIFT)==0))
295 pTheOtherPic->nHScrollPos = nHScrollPos;
296 pTheOtherPic->nVScrollPos = nVScrollPos;
297 pTheOtherPic->SetupScrollBars();
298 InvalidateRect(*pTheOtherPic, NULL, TRUE);
299 UpdateWindow(*pTheOtherPic);
303 break;
304 case WM_SETCURSOR:
306 // we show a hand cursor if the image can be dragged,
307 // and a hand-down cursor if the image is currently dragged
308 if ((*this == (HWND)wParam)&&(LOWORD(lParam)==HTCLIENT))
310 RECT rect;
311 GetClientRect(&rect);
312 LONG width = picture.m_Width;
313 LONG height = picture.m_Height;
314 if (pSecondPic)
316 width = max(width, pSecondPic->m_Width);
317 height = max(height, pSecondPic->m_Height);
320 if ((GetKeyState(VK_LBUTTON)&0x8000)||(HIWORD(lParam) == WM_LBUTTONDOWN))
322 SetCursor(curHandDown);
324 else
326 SetCursor(curHand);
328 return TRUE;
330 return DefWindowProc(hwnd, uMsg, wParam, lParam);
332 break;
333 case WM_DROPFILES:
335 HDROP hDrop = (HDROP)wParam;
336 TCHAR szFileName[MAX_PATH];
337 // we only use the first file dropped (if multiple files are dropped)
338 if (DragQueryFile(hDrop, 0, szFileName, _countof(szFileName)))
340 SetPic(szFileName, _T(""), bMainPic);
341 FitImageInWindow();
342 InvalidateRect(*this, NULL, TRUE);
345 break;
346 case WM_COMMAND:
348 switch (LOWORD(wParam))
350 case LEFTBUTTON_ID:
352 PrevImage();
353 if (bLinkedPositions)
354 pTheOtherPic->PrevImage();
355 return 0;
357 break;
358 case RIGHTBUTTON_ID:
360 NextImage();
361 if (bLinkedPositions)
362 pTheOtherPic->NextImage();
363 return 0;
365 break;
366 case PLAYBUTTON_ID:
368 bPlaying = !bPlaying;
369 Animate(bPlaying);
370 if (bLinkedPositions)
371 pTheOtherPic->Animate(bPlaying);
372 return 0;
374 break;
375 case ALPHATOGGLEBUTTON_ID:
377 WORD msg = HIWORD(wParam);
378 switch (msg)
380 case BN_DOUBLECLICKED:
382 SendMessage(hwndAlphaToggleBtn, BM_SETSTATE, 1, 0);
383 SetTimer(*this, ID_ALPHATOGGLETIMER, 1000, NULL);
385 break;
386 case BN_CLICKED:
387 KillTimer(*this, ID_ALPHATOGGLETIMER);
388 ToggleAlpha();
389 break;
391 return 0;
393 break;
394 case BLENDALPHA_ID:
396 m_blend = BLEND_ALPHA;
397 PositionTrackBar();
398 InvalidateRect(*this, NULL, TRUE);
400 break;
401 case BLENDXOR_ID:
403 m_blend = BLEND_XOR;
404 PositionTrackBar();
405 InvalidateRect(*this, NULL, TRUE);
407 break;
410 break;
411 case WM_TIMER:
413 switch (wParam)
415 case ID_ANIMATIONTIMER:
417 nCurrentFrame++;
418 if (nCurrentFrame > picture.GetNumberOfFrames(0))
419 nCurrentFrame = 1;
420 long delay = picture.SetActiveFrame(nCurrentFrame);
421 delay = max(100, delay);
422 SetTimer(*this, ID_ANIMATIONTIMER, delay, NULL);
423 InvalidateRect(*this, NULL, FALSE);
425 break;
426 case TIMER_ALPHASLIDER:
428 SetBlendAlpha(m_blend, SendMessage(m_AlphaSlider.GetWindow(), TBM_GETPOS, 0, 0)/16.0f);
429 KillTimer(*this, TIMER_ALPHASLIDER);
431 break;
432 case ID_ALPHATOGGLETIMER:
434 ToggleAlpha();
436 break;
439 break;
440 case WM_NOTIFY:
442 LPNMHDR pNMHDR = (LPNMHDR)lParam;
443 if (pNMHDR->code == TTN_GETDISPINFO)
445 if ((HWND)wParam == m_AlphaSlider.GetWindow())
447 LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT) lParam;
448 lpttt->hinst = hResource;
449 TCHAR stringbuf[MAX_PATH] = {0};
450 _stprintf_s(stringbuf, _T("%i%% alpha"), (int)(SendMessage(m_AlphaSlider.GetWindow(),TBM_GETPOS,0,0)/16.0f*100.0f));
451 _tcscpy_s(lpttt->lpszText, 80, stringbuf);
453 else
455 BuildInfoString(m_wszTip, _countof(m_wszTip), true);
456 if (pNMHDR->code == TTN_NEEDTEXTW)
458 NMTTDISPINFOW* pTTTW = (NMTTDISPINFOW*)pNMHDR;
459 pTTTW->lpszText = m_wszTip;
461 else
463 NMTTDISPINFOA* pTTTA = (NMTTDISPINFOA*)pNMHDR;
464 pTTTA->lpszText = m_szTip;
465 ::WideCharToMultiByte(CP_ACP, 0, m_wszTip, -1, m_szTip, 8192, NULL, NULL);
470 break;
471 case WM_DESTROY:
472 DestroyIcon(hLeft);
473 DestroyIcon(hRight);
474 DestroyIcon(hPlay);
475 DestroyIcon(hStop);
476 bWindowClosed = TRUE;
477 break;
478 default:
479 return DefWindowProc(hwnd, uMsg, wParam, lParam);
482 return 0;
485 void CPicWindow::NextImage()
487 nCurrentDimension++;
488 if (nCurrentDimension > picture.GetNumberOfDimensions())
489 nCurrentDimension = picture.GetNumberOfDimensions();
490 nCurrentFrame++;
491 if (nCurrentFrame > picture.GetNumberOfFrames(0))
492 nCurrentFrame = picture.GetNumberOfFrames(0);
493 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
494 InvalidateRect(*this, NULL, FALSE);
495 PositionChildren();
498 void CPicWindow::PrevImage()
500 nCurrentDimension--;
501 if (nCurrentDimension < 1)
502 nCurrentDimension = 1;
503 nCurrentFrame--;
504 if (nCurrentFrame < 1)
505 nCurrentFrame = 1;
506 picture.SetActiveFrame(nCurrentFrame >= nCurrentDimension ? nCurrentFrame : nCurrentDimension);
507 InvalidateRect(*this, NULL, FALSE);
508 PositionChildren();
511 void CPicWindow::Animate(bool bStart)
513 if (bStart)
515 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hStop);
516 SetTimer(*this, ID_ANIMATIONTIMER, 0, NULL);
518 else
520 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
521 KillTimer(*this, ID_ANIMATIONTIMER);
525 void CPicWindow::SetPic(tstring path, tstring title, bool bFirst)
527 bMainPic = bFirst;
528 picpath=path;pictitle=title;
529 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
530 bValid = picture.Load(picpath);
531 nDimensions = picture.GetNumberOfDimensions();
532 if (nDimensions)
533 nFrames = picture.GetNumberOfFrames(0);
534 if (bValid)
536 picscale = 1.0;
537 PositionChildren();
538 InvalidateRect(*this, NULL, FALSE);
542 void CPicWindow::DrawViewTitle(HDC hDC, RECT * rect)
544 HFONT hFont = NULL;
545 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"));
546 HFONT hFontOld = (HFONT)SelectObject(hDC, (HGDIOBJ)hFont);
548 RECT textrect;
549 textrect.left = rect->left;
550 textrect.top = rect->top;
551 textrect.right = rect->right;
552 textrect.bottom = rect->top + HEADER_HEIGHT;
553 if (HasMultipleImages())
554 textrect.bottom += HEADER_HEIGHT;
556 COLORREF crBk, crFg;
557 crBk = ::GetSysColor(COLOR_SCROLLBAR);
558 crFg = ::GetSysColor(COLOR_WINDOWTEXT);
559 SetBkColor(hDC, crBk);
560 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &textrect, NULL, 0, NULL);
562 if (GetFocus() == *this)
563 DrawEdge(hDC, &textrect, EDGE_BUMP, BF_RECT);
564 else
565 DrawEdge(hDC, &textrect, EDGE_ETCHED, BF_RECT);
567 SetTextColor(hDC, crFg);
569 // use the path if no title is set.
570 tstring * title = pictitle.empty() ? &picpath : &pictitle;
572 tstring realtitle = *title;
573 tstring imgnumstring;
575 if (HasMultipleImages())
577 TCHAR buf[MAX_PATH];
578 if (nFrames > 1)
579 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentFrame, nFrames);
580 else
581 _stprintf_s(buf, (const TCHAR *)ResString(hResource, IDS_DIMENSIONSANDFRAMES), nCurrentDimension, nDimensions);
582 imgnumstring = buf;
585 SIZE stringsize;
586 if (GetTextExtentPoint32(hDC, realtitle.c_str(), (int)realtitle.size(), &stringsize))
588 int nStringLength = stringsize.cx;
589 int texttop = pSecondPic ? textrect.top + (HEADER_HEIGHT/2) - stringsize.cy : textrect.top + (HEADER_HEIGHT/2) - stringsize.cy/2;
590 ExtTextOut(hDC,
591 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
592 texttop,
593 ETO_CLIPPED,
594 &textrect,
595 realtitle.c_str(),
596 (UINT)realtitle.size(),
597 NULL);
598 if (pSecondPic)
600 realtitle = (pictitle2.empty() ? picpath2 : pictitle2);
601 ExtTextOut(hDC,
602 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
603 texttop + stringsize.cy,
604 ETO_CLIPPED,
605 &textrect,
606 realtitle.c_str(),
607 (UINT)realtitle.size(),
608 NULL);
611 if (HasMultipleImages())
613 if (GetTextExtentPoint32(hDC, imgnumstring.c_str(), (int)imgnumstring.size(), &stringsize))
615 int nStringLength = stringsize.cx;
617 ExtTextOut(hDC,
618 max(textrect.left + ((textrect.right-textrect.left)-nStringLength)/2, 1),
619 textrect.top + HEADER_HEIGHT + (HEADER_HEIGHT/2) - stringsize.cy/2,
620 ETO_CLIPPED,
621 &textrect,
622 imgnumstring.c_str(),
623 (UINT)imgnumstring.size(),
624 NULL);
627 SelectObject(hDC, (HGDIOBJ)hFontOld);
628 DeleteObject(hFont);
631 void CPicWindow::SetupScrollBars()
633 RECT rect;
634 GetClientRect(&rect);
636 SCROLLINFO si = {sizeof(si)};
638 si.fMask = SIF_POS | SIF_PAGE | SIF_RANGE | SIF_DISABLENOSCROLL;
640 double width = double(picture.m_Width)*picscale;
641 double height = double(picture.m_Height)*picscale;
642 if (pSecondPic)
644 width = max(width, double(pSecondPic->m_Width)*pTheOtherPic->GetZoom());
645 height = max(height, double(pSecondPic->m_Height)*pTheOtherPic->GetZoom());
648 bool bShowHScrollBar = (nHScrollPos > 0); // left of pic is left of window
649 bShowHScrollBar = bShowHScrollBar || (width-nHScrollPos > rect.right); // right of pic is outside right of window
650 bShowHScrollBar = bShowHScrollBar || (width+nHScrollPos > rect.right); // right of pic is outside right of window
651 bool bShowVScrollBar = (nVScrollPos > 0); // top of pic is above window
652 bShowVScrollBar = bShowVScrollBar || (height-nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
653 bShowVScrollBar = bShowVScrollBar || (height+nVScrollPos+rect.top > rect.bottom); // bottom of pic is below window
655 // if the image is smaller than the window, we don't need the scrollbars
656 ShowScrollBar(*this, SB_HORZ, bShowHScrollBar);
657 ShowScrollBar(*this, SB_VERT, bShowVScrollBar);
659 if (bShowVScrollBar)
661 si.nPos = nVScrollPos;
662 si.nPage = rect.bottom-rect.top;
663 if (height < rect.bottom-rect.top)
665 if (nVScrollPos > 0)
667 si.nMin = 0;
668 si.nMax = rect.bottom+nVScrollPos-rect.top;
670 else
672 si.nMin = nVScrollPos;
673 si.nMax = int(height);
676 else
678 if (nVScrollPos > 0)
680 si.nMin = 0;
681 si.nMax = int(max(height, rect.bottom+nVScrollPos-rect.top));
683 else
685 si.nMin = 0;
686 si.nMax = int(height-nVScrollPos);
689 SetScrollInfo(*this, SB_VERT, &si, TRUE);
692 if (bShowHScrollBar)
694 si.nPos = nHScrollPos;
695 si.nPage = rect.right-rect.left;
696 if (width < rect.right-rect.left)
698 if (nHScrollPos > 0)
700 si.nMin = 0;
701 si.nMax = rect.right+nHScrollPos-rect.left;
703 else
705 si.nMin = nHScrollPos;
706 si.nMax = int(width);
709 else
711 if (nHScrollPos > 0)
713 si.nMin = 0;
714 si.nMax = int(max(width, rect.right+nHScrollPos-rect.left));
716 else
718 si.nMin = 0;
719 si.nMax = int(width-nHScrollPos);
722 SetScrollInfo(*this, SB_HORZ, &si, TRUE);
725 PositionChildren();
728 void CPicWindow::OnVScroll(UINT nSBCode, UINT nPos)
730 RECT rect;
731 GetClientRect(&rect);
733 switch (nSBCode)
735 case SB_BOTTOM:
736 nVScrollPos = LONG(double(picture.GetHeight())*picscale);
737 break;
738 case SB_TOP:
739 nVScrollPos = 0;
740 break;
741 case SB_LINEDOWN:
742 nVScrollPos++;
743 break;
744 case SB_LINEUP:
745 nVScrollPos--;
746 break;
747 case SB_PAGEDOWN:
748 nVScrollPos += (rect.bottom-rect.top);
749 break;
750 case SB_PAGEUP:
751 nVScrollPos -= (rect.bottom-rect.top);
752 break;
753 case SB_THUMBPOSITION:
754 nVScrollPos = nPos;
755 break;
756 case SB_THUMBTRACK:
757 nVScrollPos = nPos;
758 break;
759 default:
760 return;
762 LONG height = LONG(double(picture.GetHeight())*picscale);
763 if (pSecondPic)
765 height = max(height, LONG(double(pSecondPic->GetHeight())*picscale));
767 SetupScrollBars();
768 PositionChildren();
769 InvalidateRect(*this, NULL, TRUE);
772 void CPicWindow::OnHScroll(UINT nSBCode, UINT nPos)
774 RECT rect;
775 GetClientRect(&rect);
777 switch (nSBCode)
779 case SB_RIGHT:
780 nHScrollPos = LONG(double(picture.GetWidth())*picscale);
781 break;
782 case SB_LEFT:
783 nHScrollPos = 0;
784 break;
785 case SB_LINERIGHT:
786 nHScrollPos++;
787 break;
788 case SB_LINELEFT:
789 nHScrollPos--;
790 break;
791 case SB_PAGERIGHT:
792 nHScrollPos += (rect.right-rect.left);
793 break;
794 case SB_PAGELEFT:
795 nHScrollPos -= (rect.right-rect.left);
796 break;
797 case SB_THUMBPOSITION:
798 nHScrollPos = nPos;
799 break;
800 case SB_THUMBTRACK:
801 nHScrollPos = nPos;
802 break;
803 default:
804 return;
806 LONG width = LONG(double(picture.GetWidth())*picscale);
807 if (pSecondPic)
809 width = max(width, LONG(double(pSecondPic->GetWidth())*picscale));
811 SetupScrollBars();
812 PositionChildren();
813 InvalidateRect(*this, NULL, TRUE);
816 void CPicWindow::OnMouseWheel(short fwKeys, short zDelta)
818 RECT rect;
819 GetClientRect(&rect);
820 LONG width = long(double(picture.m_Width)*picscale);
821 LONG height = long(double(picture.m_Height)*picscale);
822 if (pSecondPic)
824 width = max(width, long(double(pSecondPic->m_Width)*picscale));
825 height = max(height, long(double(pSecondPic->m_Height)*picscale));
827 if ((fwKeys & MK_SHIFT)&&(fwKeys & MK_CONTROL)&&(pSecondPic))
829 // ctrl+shift+wheel: change the alpha channel
830 float a = blendAlpha;
831 a -= float(zDelta)/120.0f/4.0f;
832 if (a < 0.0f)
833 a = 0.0f;
834 else if (a > 1.0f)
835 a = 1.0f;
836 SetBlendAlpha(m_blend, a);
838 else if (fwKeys & MK_SHIFT)
840 // shift means scrolling sideways
841 OnHScroll(SB_THUMBPOSITION, GetHPos()-zDelta);
842 if ((bLinkedPositions)&&(pTheOtherPic))
844 pTheOtherPic->OnHScroll(SB_THUMBPOSITION, pTheOtherPic->GetHPos()-zDelta);
847 else if (fwKeys & MK_CONTROL)
849 // control means adjusting the scale factor
850 Zoom(zDelta>0, true);
851 if ((!bFitSizes)&&(pTheOtherPic)&&(!bOverlap))
852 pTheOtherPic->Zoom(zDelta>0, true);
853 PositionChildren();
854 InvalidateRect(*this, NULL, FALSE);
855 SetWindowPos(*this, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOREPOSITION|SWP_NOMOVE);
857 else
859 OnVScroll(SB_THUMBPOSITION, GetVPos()-zDelta);
860 if ((bLinkedPositions)&&(pTheOtherPic))
862 pTheOtherPic->OnVScroll(SB_THUMBPOSITION, pTheOtherPic->GetVPos()-zDelta);
867 void CPicWindow::GetClientRect(RECT * pRect)
869 ::GetClientRect(*this, pRect);
870 pRect->top += HEADER_HEIGHT;
871 if (HasMultipleImages())
873 pRect->top += HEADER_HEIGHT;
875 if (pSecondPic)
876 pRect->left += SLIDER_WIDTH;
879 void CPicWindow::GetClientRectWithScrollbars(RECT * pRect)
881 GetClientRect(pRect);
882 ::GetWindowRect(*this, pRect);
883 pRect->right = pRect->right-pRect->left;
884 pRect->bottom = pRect->bottom-pRect->top;
885 pRect->top = 0;
886 pRect->left = 0;
887 pRect->top += HEADER_HEIGHT;
888 if (HasMultipleImages())
890 pRect->top += HEADER_HEIGHT;
892 if (pSecondPic)
893 pRect->left += SLIDER_WIDTH;
897 void CPicWindow::SetZoom(double dZoom, bool centermouse)
899 // Set the interpolation mode depending on zoom
900 double oldPicscale = picscale;
901 if (dZoom < 1.0)
902 { // Zoomed out, use high quality bicubic
903 picture.SetInterpolationMode(InterpolationModeHighQualityBicubic);
904 if (pSecondPic)
905 pSecondPic->SetInterpolationMode(InterpolationModeHighQualityBicubic);
907 else if (!((int)(dZoom*100.0)%100))
908 { // "Even" zoom sizes should be shown w-o any interpolation
909 picture.SetInterpolationMode(InterpolationModeNearestNeighbor);
910 if (pSecondPic)
911 pSecondPic->SetInterpolationMode(InterpolationModeNearestNeighbor);
913 else
914 { // Arbitrary zoomed in, use bilinear that is semi-smoothed
915 picture.SetInterpolationMode(InterpolationModeBilinear);
916 if (pSecondPic)
917 pSecondPic->SetInterpolationMode(InterpolationModeBilinear);
919 picscale = dZoom;
921 if ((bFitSizes)&&(bMainPic))
923 double width, height;
924 double zoomWidth, zoomHeight;
925 width = double(picture.m_Width)*dZoom;
926 height = double(picture.m_Height)*dZoom;
927 zoomWidth = width/double(pTheOtherPic->GetPic()->m_Width);
928 zoomHeight = height/double(pTheOtherPic->GetPic()->m_Height);
929 pTheOtherPic->SetZoom(min(zoomWidth, zoomHeight), centermouse);
932 // adjust the scrollbar positions according to the new zoom and the
933 // mouse position: if possible, keep the pixel where the mouse pointer
934 // is at the same position after the zoom
935 POINT cpos;
936 DWORD ptW = GetMessagePos();
937 cpos.x = GET_X_LPARAM(ptW);
938 cpos.y = GET_Y_LPARAM(ptW);
939 ScreenToClient(*this, &cpos);
940 RECT clientrect;
941 GetClientRect(&clientrect);
942 if ((PtInRect(&clientrect, cpos))&&(centermouse))
944 // the mouse pointer is over our window
945 nHScrollPos = int(double(nHScrollPos + cpos.x)*(dZoom/oldPicscale))-cpos.x;
946 nVScrollPos = int(double(nVScrollPos + cpos.y)*(dZoom/oldPicscale))-cpos.y;
948 else
950 nHScrollPos = int(double(nHScrollPos + ((clientrect.right-clientrect.left)/2))*(dZoom/oldPicscale))-((clientrect.right-clientrect.left)/2);
951 nVScrollPos = int(double(nVScrollPos + ((clientrect.bottom-clientrect.top)/2))*(dZoom/oldPicscale))-((clientrect.bottom-clientrect.top)/2);
954 SetupScrollBars();
955 PositionChildren();
956 InvalidateRect(*this, NULL, TRUE);
959 void CPicWindow::Zoom(bool in, bool centermouse)
961 double zoomFactor;
963 // Find correct zoom factor and quantize picscale
964 if (!in && picscale <= 0.2)
966 picscale = 0.1;
967 zoomFactor = 0;
969 else if ((in && picscale < 1.0) || (!in && picscale <= 1.0))
971 picscale = 0.1 * RoundDouble(picscale/0.1, 0); // Quantize to 0.1
972 zoomFactor = 0.1;
974 else if ((in && picscale < 2.0) || (!in && picscale <= 2.0))
976 picscale = 0.25 * RoundDouble(picscale/0.25, 0); // Quantize to 0.25
977 zoomFactor = 0.25;
979 else
981 picscale = RoundDouble(picscale,0);
982 zoomFactor = 1;
985 // Set zoom
986 if (in)
988 if ((pSecondPic)&&(!bFitSizes))
989 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()+zoomFactor, false);
990 SetZoom(picscale+zoomFactor, centermouse);
992 else
994 if ((pSecondPic)&&(!bFitSizes))
995 pTheOtherPic->SetZoom(pTheOtherPic->GetZoom()-zoomFactor, false);
996 SetZoom(picscale-zoomFactor, centermouse);
1000 double CPicWindow::RoundDouble(double doValue, int nPrecision)
1002 static const double doBase = 10.0;
1003 double doComplete5, doComplete5i;
1005 doComplete5 = doValue * pow(doBase, (double) (nPrecision + 1));
1007 if (doValue < 0.0)
1009 doComplete5 -= 5.0;
1011 else
1013 doComplete5 += 5.0;
1016 doComplete5 /= doBase;
1017 modf(doComplete5, &doComplete5i);
1019 return doComplete5i / pow(doBase, (double) nPrecision);
1021 void CPicWindow::FitImageInWindow()
1023 RECT rect;
1025 GetClientRectWithScrollbars(&rect);
1027 if (rect.right-rect.left)
1029 double dZoom = 1.0;
1030 if (((rect.right - rect.left) > picture.m_Width+2)&&((rect.bottom - rect.top)> picture.m_Height+2))
1032 // image is smaller than the window
1033 dZoom = 1.0;
1035 else
1037 // image is bigger than the window
1038 double xscale = double(rect.right-rect.left-2)/double(picture.m_Width);
1039 double yscale = double(rect.bottom-rect.top-2)/double(picture.m_Height);
1040 dZoom = min(yscale, xscale);
1042 if (pSecondPic)
1044 if (((rect.right - rect.left) > pSecondPic->m_Width+2)&&((rect.bottom - rect.top)> pSecondPic->m_Height+2))
1046 // image is smaller than the window
1047 pTheOtherPic->SetZoom(min(1.0, dZoom), false);
1049 else
1051 // image is bigger than the window
1052 double xscale = double(rect.right-rect.left-2)/double(pSecondPic->m_Width);
1053 double yscale = double(rect.bottom-rect.top-2)/double(pSecondPic->m_Height);
1054 pTheOtherPic->SetZoom(min(yscale, xscale), false);
1056 nHSecondScrollPos = 0;
1057 nVSecondScrollPos = 0;
1059 SetZoom(dZoom, false);
1061 CenterImage();
1062 PositionChildren();
1063 InvalidateRect(*this, NULL, TRUE);
1066 void CPicWindow::CenterImage()
1068 RECT rect;
1069 GetClientRectWithScrollbars(&rect);
1070 double width = (double(picture.m_Width)*picscale) + 2.0;
1071 double height = (double(picture.m_Height)*picscale) + 2.0;
1072 if (pSecondPic)
1074 width = max(width, (double(pSecondPic->m_Width)*pTheOtherPic->GetZoom()) + 2.0);
1075 height = max(height, (double(pSecondPic->m_Height)*pTheOtherPic->GetZoom()) + 2.0);
1078 bool bPicWidthBigger = (int(width) > (rect.right-rect.left));
1079 bool bPicHeightBigger = (int(height) > (rect.bottom-rect.top));
1080 // set the scroll position so that the image is drawn centered in the window
1081 // if the window is bigger than the image
1082 if (!bPicWidthBigger)
1084 nHScrollPos = -((rect.right-rect.left+4)-int(width))/2;
1086 if (!bPicHeightBigger)
1088 nVScrollPos = -((rect.bottom-rect.top+4)-int(height))/2;
1090 SetupScrollBars();
1093 void CPicWindow::FitSizes(bool bFit)
1095 bFitSizes = bFit;
1097 if (bFitSizes)
1099 nHSecondScrollPos = 0;
1100 nVSecondScrollPos = 0;
1102 SetZoom(GetZoom(), false);
1105 void CPicWindow::ShowPicWithBorder(HDC hdc, const RECT &bounds, CPicture &pic, double scale)
1107 ::SetBkColor(hdc, transparentColor);
1108 ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1110 RECT picrect;
1111 picrect.left = bounds.left - nHScrollPos;
1112 picrect.top = bounds.top - nVScrollPos;
1113 if ((!bLinkedPositions || bOverlap) && (pTheOtherPic) && (&pic != &picture))
1115 picrect.left = bounds.left - nHSecondScrollPos;
1116 picrect.top = bounds.top - nVSecondScrollPos;
1118 picrect.right = (picrect.left + LONG(double(pic.m_Width) * scale));
1119 picrect.bottom = (picrect.top + LONG(double(pic.m_Height) * scale));
1121 pic.Show(hdc, picrect);
1123 RECT border;
1124 border.left = picrect.left-1;
1125 border.top = picrect.top-1;
1126 border.right = picrect.right+1;
1127 border.bottom = picrect.bottom+1;
1129 HPEN hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));
1130 HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
1131 MoveToEx(hdc, border.left, border.top, NULL);
1132 LineTo(hdc, border.left, border.bottom);
1133 LineTo(hdc, border.right, border.bottom);
1134 LineTo(hdc, border.right, border.top);
1135 LineTo(hdc, border.left, border.top);
1136 SelectObject(hdc, hOldPen);
1137 DeleteObject(hPen);
1140 void CPicWindow::Paint(HWND hwnd)
1142 PAINTSTRUCT ps;
1143 HDC hdc;
1144 RECT rect, fullrect;
1146 GetUpdateRect(hwnd, &rect, FALSE);
1147 if (IsRectEmpty(&rect))
1148 return;
1150 ::GetClientRect(*this, &fullrect);
1151 hdc = BeginPaint(hwnd, &ps);
1153 // Exclude the alpha control and button
1154 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1155 ExcludeClipRect(hdc, 0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4);
1157 CMyMemDC memDC(hdc);
1158 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1160 // erase the place where the alpha slider would be
1161 ::SetBkColor(memDC, transparentColor);
1162 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1163 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1166 GetClientRect(&rect);
1167 if (bValid)
1169 ShowPicWithBorder(memDC, rect, picture, picscale);
1170 if (pSecondPic)
1172 HDC secondhdc = CreateCompatibleDC(hdc);
1173 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top);
1174 HBITMAP hOldBitmap = (HBITMAP)SelectObject(secondhdc, hBitmap);
1175 SetWindowOrgEx(secondhdc, rect.left, rect.top, NULL);
1177 if ((pSecondPic)&&(m_blend != BLEND_ALPHA))
1179 // erase the place where the alpha slider would be
1180 ::SetBkColor(secondhdc, transparentColor);
1181 RECT bounds = {0, m_inforect.top-4, SLIDER_WIDTH, m_inforect.bottom+4};
1182 ::ExtTextOut(secondhdc, 0, 0, ETO_OPAQUE, &bounds, NULL, 0, NULL);
1184 ShowPicWithBorder(secondhdc, rect, *pSecondPic, pTheOtherPic->GetZoom());
1186 if (m_blend == BLEND_ALPHA)
1188 BLENDFUNCTION blender;
1189 blender.AlphaFormat = 0;
1190 blender.BlendFlags = 0;
1191 blender.BlendOp = AC_SRC_OVER;
1192 blender.SourceConstantAlpha = (BYTE)(blendAlpha*255);
1193 AlphaBlend(memDC,
1194 rect.left,
1195 rect.top,
1196 rect.right-rect.left,
1197 rect.bottom-rect.top,
1198 secondhdc,
1199 rect.left,
1200 rect.top,
1201 rect.right-rect.left,
1202 rect.bottom-rect.top,
1203 blender);
1205 else if (m_blend == BLEND_XOR)
1207 BitBlt(memDC,
1208 rect.left,
1209 rect.top,
1210 rect.right-rect.left,
1211 rect.bottom-rect.top,
1212 secondhdc,
1213 rect.left,
1214 rect.top,
1215 //rect.right-rect.left,
1216 //rect.bottom-rect.top,
1217 SRCINVERT);
1218 InvertRect(memDC, &rect);
1220 SelectObject(secondhdc, hOldBitmap);
1221 DeleteObject(hBitmap);
1222 DeleteDC(secondhdc);
1224 int sliderwidth = 0;
1225 if ((pSecondPic)&&(m_blend == BLEND_ALPHA))
1226 sliderwidth = SLIDER_WIDTH;
1227 m_inforect.left = rect.left+4+sliderwidth;
1228 m_inforect.top = rect.top;
1229 m_inforect.right = rect.right+sliderwidth;
1230 m_inforect.bottom = rect.bottom;
1232 SetBkColor(memDC, transparentColor);
1233 if (bShowInfo)
1235 TCHAR infostring[8192];
1236 BuildInfoString(infostring, _countof(infostring), false);
1237 // set the font
1238 NONCLIENTMETRICS metrics = {0};
1239 metrics.cbSize = sizeof(NONCLIENTMETRICS);
1241 #if (WINVER >= 0x600)
1242 if (!SysInfo::Instance().IsVistaOrLater())
1244 metrics.cbSize -= sizeof(int); // subtract the size of the iPaddedBorderWidth member which is not available on XP
1246 #endif
1248 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
1249 HFONT hFont = CreateFontIndirect(&metrics.lfStatusFont);
1250 HFONT hFontOld = (HFONT)SelectObject(memDC, (HGDIOBJ)hFont);
1251 // find out how big the rectangle for the text has to be
1252 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER | DT_CALCRECT);
1254 // the text should be drawn with a four pixel offset to the window borders
1255 m_inforect.top = rect.bottom - (m_inforect.bottom-m_inforect.top) - 4;
1256 m_inforect.bottom = rect.bottom-4;
1258 // first draw an edge rectangle
1259 RECT edgerect;
1260 edgerect.left = m_inforect.left-4;
1261 edgerect.top = m_inforect.top-4;
1262 edgerect.right = m_inforect.right+4;
1263 edgerect.bottom = m_inforect.bottom+4;
1264 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &edgerect, NULL, 0, NULL);
1265 DrawEdge(memDC, &edgerect, EDGE_BUMP, BF_RECT | BF_SOFT);
1267 SetTextColor(memDC, GetSysColor(COLOR_WINDOWTEXT));
1268 DrawText(memDC, infostring, -1, &m_inforect, DT_EDITCONTROL | DT_EXPANDTABS | DT_LEFT | DT_VCENTER);
1269 SelectObject(memDC, (HGDIOBJ)hFontOld);
1270 DeleteObject(hFont);
1273 else
1275 SetBkColor(memDC, ::GetSysColor(COLOR_WINDOW));
1276 ::ExtTextOut(memDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1277 SIZE stringsize;
1278 ResString str = ResString(hResource, IDS_INVALIDIMAGEINFO);
1279 if (GetTextExtentPoint32(memDC, str, (int)_tcslen(str), &stringsize))
1281 int nStringLength = stringsize.cx;
1283 ExtTextOut(memDC,
1284 max(rect.left + ((rect.right-rect.left)-nStringLength)/2, 1),
1285 rect.top + ((rect.bottom-rect.top) - stringsize.cy)/2,
1286 ETO_CLIPPED,
1287 &rect,
1288 str,
1289 (UINT)_tcslen(str),
1290 NULL);
1293 DrawViewTitle(memDC, &fullrect);
1295 EndPaint(hwnd, &ps);
1298 bool CPicWindow::CreateButtons()
1300 // Ensure that the common control DLL is loaded.
1301 INITCOMMONCONTROLSEX icex;
1302 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
1303 icex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
1304 InitCommonControlsEx(&icex);
1306 hwndLeftBtn = CreateWindowEx(0,
1307 _T("BUTTON"),
1308 (LPCTSTR)NULL,
1309 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1310 0, 0, 0, 0,
1311 (HWND)*this,
1312 (HMENU)LEFTBUTTON_ID,
1313 hResource,
1314 NULL);
1315 if (hwndLeftBtn == INVALID_HANDLE_VALUE)
1316 return false;
1317 hLeft = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_BACKWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1318 SendMessage(hwndLeftBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hLeft);
1319 hwndRightBtn = CreateWindowEx(0,
1320 _T("BUTTON"),
1321 (LPCTSTR)NULL,
1322 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1323 0, 0, 0, 0,
1324 *this,
1325 (HMENU)RIGHTBUTTON_ID,
1326 hResource,
1327 NULL);
1328 if (hwndRightBtn == INVALID_HANDLE_VALUE)
1329 return false;
1330 hRight = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_FORWARD), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1331 SendMessage(hwndRightBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hRight);
1332 hwndPlayBtn = CreateWindowEx(0,
1333 _T("BUTTON"),
1334 (LPCTSTR)NULL,
1335 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT,
1336 0, 0, 0, 0,
1337 *this,
1338 (HMENU)PLAYBUTTON_ID,
1339 hResource,
1340 NULL);
1341 if (hwndPlayBtn == INVALID_HANDLE_VALUE)
1342 return false;
1343 hPlay = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1344 hStop = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1345 SendMessage(hwndPlayBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hPlay);
1346 hwndAlphaToggleBtn = CreateWindowEx(0,
1347 _T("BUTTON"),
1348 (LPCTSTR)NULL,
1349 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_ICON | BS_FLAT | BS_NOTIFY | BS_PUSHLIKE,
1350 0, 0, 0, 0,
1351 (HWND)*this,
1352 (HMENU)ALPHATOGGLEBUTTON_ID,
1353 hResource,
1354 NULL);
1355 if (hwndAlphaToggleBtn == INVALID_HANDLE_VALUE)
1356 return false;
1357 hAlphaToggle = (HICON)LoadImage(hResource, MAKEINTRESOURCE(IDI_ALPHATOGGLE), IMAGE_ICON, 16, 16, LR_LOADTRANSPARENT);
1358 SendMessage(hwndAlphaToggleBtn, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hAlphaToggle);
1360 return true;
1363 void CPicWindow::PositionChildren()
1365 RECT rect;
1366 ::GetClientRect(*this, &rect);
1367 if (HasMultipleImages())
1369 SetWindowPos(hwndLeftBtn, HWND_TOP, rect.left+3, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1370 SetWindowPos(hwndRightBtn, HWND_TOP, rect.left+23, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1371 if (nFrames > 1)
1372 SetWindowPos(hwndPlayBtn, HWND_TOP, rect.left+43, rect.top + HEADER_HEIGHT + (HEADER_HEIGHT-16)/2, 16, 16, SWP_FRAMECHANGED|SWP_SHOWWINDOW);
1373 else
1374 ShowWindow(hwndPlayBtn, SW_HIDE);
1376 else
1378 ShowWindow(hwndLeftBtn, SW_HIDE);
1379 ShowWindow(hwndRightBtn, SW_HIDE);
1380 ShowWindow(hwndPlayBtn, SW_HIDE);
1382 PositionTrackBar();
1385 bool CPicWindow::HasMultipleImages()
1387 return (((nDimensions > 1)||(nFrames > 1))&&(pSecondPic == NULL));
1390 void CPicWindow::CreateTrackbar(HWND hwndParent)
1392 HWND hwndTrack = CreateWindowEx(
1393 0, // no extended styles
1394 TRACKBAR_CLASS, // class name
1395 _T("Trackbar Control"), // title (caption)
1396 WS_CHILD | WS_VISIBLE | TBS_VERT | TBS_TOOLTIPS | TBS_AUTOTICKS, // style
1397 10, 10, // position
1398 200, 30, // size
1399 hwndParent, // parent window
1400 (HMENU)TRACKBAR_ID, // control identifier
1401 hInst, // instance
1402 NULL // no WM_CREATE parameter
1405 SendMessage(hwndTrack, TBM_SETRANGE,
1406 (WPARAM) TRUE, // redraw flag
1407 (LPARAM) MAKELONG(0, 16)); // min. & max. positions
1408 SendMessage(hwndTrack, TBM_SETTIPSIDE,
1409 (WPARAM) TBTS_TOP,
1410 (LPARAM) 0);
1412 m_AlphaSlider.ConvertTrackbarToNice(hwndTrack);
1415 void CPicWindow::BuildInfoString(TCHAR * buf, int size, bool bTooltip)
1417 // Unfortunately, we need two different strings for the tooltip
1418 // and the info box. Because the tooltips use a different tab size
1419 // than ExtTextOut(), and to keep the output aligned we therefore
1420 // need two different strings.
1421 // Note: some translations could end up with two identical strings, but
1422 // in English we need two - even if we wouldn't need two in English, some
1423 // translation might then need two again.
1424 if (pSecondPic)
1426 _stprintf_s(buf, size,
1427 (TCHAR const *)ResString(hResource, bTooltip ? IDS_DUALIMAGEINFOTT : IDS_DUALIMAGEINFO),
1428 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1429 picture.m_Width, picture.m_Height,
1430 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1431 picture.m_ColorDepth,
1432 (UINT)(GetZoom()*100.0),
1433 pSecondPic->GetFileSizeAsText().c_str(), pSecondPic->GetFileSizeAsText(false).c_str(),
1434 pSecondPic->m_Width, pSecondPic->m_Height,
1435 pSecondPic->GetHorizontalResolution(), pSecondPic->GetVerticalResolution(),
1436 pSecondPic->m_ColorDepth,
1437 (UINT)(pTheOtherPic->GetZoom()*100.0));
1439 else
1441 _stprintf_s(buf, size,
1442 (TCHAR const *)ResString(hResource, bTooltip ? IDS_IMAGEINFOTT : IDS_IMAGEINFO),
1443 picture.GetFileSizeAsText().c_str(), picture.GetFileSizeAsText(false).c_str(),
1444 picture.m_Width, picture.m_Height,
1445 picture.GetHorizontalResolution(), picture.GetVerticalResolution(),
1446 picture.m_ColorDepth,
1447 (UINT)(GetZoom()*100.0));