1
// TortoiseGitIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2006-2013, 2018 - TortoiseSVN
4 // Copyright (C) 2016, 2018 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "PicWindow.h"
26 #include "../Utils/DPIAware.h"
27 #include "../Utils/LoadIconEx.h"
29 #pragma comment(lib, "Msimg32.lib")
30 #pragma comment(lib, "shell32.lib")
32 bool CPicWindow::RegisterAndCreateWindow(HWND hParent
)
36 // Fill in the window class structure with default parameters
37 wcx
.cbSize
= sizeof(WNDCLASSEX
);
38 wcx
.style
= CS_HREDRAW
| CS_VREDRAW
;
39 wcx
.lpfnWndProc
= CWindow::stWinMsgHandler
;
42 wcx
.hInstance
= hResource
;
43 wcx
.hCursor
= LoadCursor(nullptr, IDC_ARROW
);
44 wcx
.lpszClassName
= L
"TortoiseGitIDiffPicWindow";
45 wcx
.hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
), GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
46 wcx
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+1);
47 wcx
.lpszMenuName
= MAKEINTRESOURCE(IDC_TORTOISEIDIFF
);
48 wcx
.hIconSm
= LoadIconEx(wcx
.hInstance
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
));
50 if (CreateEx(WS_EX_ACCEPTFILES
| WS_EX_CLIENTEDGE
, WS_CHILD
| WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
, hParent
))
52 ShowWindow(m_hwnd
, SW_SHOW
);
60 void CPicWindow::PositionTrackBar()
62 const auto slider_width
= CDPIAware::Instance().ScaleX(SLIDER_WIDTH
);
65 HWND slider
= m_AlphaSlider
.GetWindow();
66 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
68 MoveWindow(slider
, 0, rc
.top
- CDPIAware::Instance().ScaleY(4) + slider_width
, slider_width
, rc
.bottom
- rc
.top
- slider_width
+ CDPIAware::Instance().ScaleX(8), true);
69 ShowWindow(slider
, SW_SHOW
);
70 MoveWindow(hwndAlphaToggleBtn
, 0, rc
.top
- CDPIAware::Instance().ScaleY(4), slider_width
, slider_width
, true);
71 ShowWindow(hwndAlphaToggleBtn
, SW_SHOW
);
75 ShowWindow(slider
, SW_HIDE
);
76 ShowWindow(hwndAlphaToggleBtn
, SW_HIDE
);
80 LRESULT CALLBACK
CPicWindow::WinMsgHandler(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
87 // create a slider control
89 ShowWindow(m_AlphaSlider
.GetWindow(), SW_HIDE
);
92 RECT rect
; // for client area coordinates
94 hwndTT
= CreateWindowEx(0,
97 WS_POPUP
| TTS_NOPREFIX
| TTS_ALWAYSTIP
,
114 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
116 ::GetClientRect(hwnd
, &rect
);
118 ti
.cbSize
= sizeof(TOOLINFO
);
119 ti
.uFlags
= TTF_TRACK
| TTF_ABSOLUTE
;
121 ti
.hinst
= hResource
;
123 ti
.lpszText
= LPSTR_TEXTCALLBACK
;
124 // ToolTip control will cover the whole window
125 ti
.rect
.left
= rect
.left
;
126 ti
.rect
.top
= rect
.top
;
127 ti
.rect
.right
= rect
.right
;
128 ti
.rect
.bottom
= rect
.bottom
;
130 SendMessage(hwndTT
, TTM_ADDTOOL
, 0, (LPARAM
) (LPTOOLINFO
) &ti
);
131 SendMessage(hwndTT
, TTM_SETMAXTIPWIDTH
, 0, 600);
132 nHSecondScrollPos
= 0;
133 nVSecondScrollPos
= 0;
138 InvalidateRect(*this, nullptr, FALSE
);
151 if ((pSecondPic
)&&((HWND
)lParam
== m_AlphaSlider
.GetWindow()))
153 if (LOWORD(wParam
) == TB_THUMBTRACK
)
155 // while tracking, only redraw after 50 milliseconds
156 ::SetTimer(*this, TIMER_ALPHASLIDER
, 50, nullptr);
159 SetBlendAlpha(m_blend
, SendMessage(m_AlphaSlider
.GetWindow(), TBM_GETPOS
, 0, 0) / 16.0f
);
163 UINT nPos
= HIWORD(wParam
);
164 bool bForceUpdate
= false;
165 if (LOWORD(wParam
) == SB_THUMBTRACK
|| LOWORD(wParam
) == SB_THUMBPOSITION
)
167 // Get true 32-bit scroll position
169 si
.cbSize
= sizeof(SCROLLINFO
);
170 si
.fMask
= SIF_TRACKPOS
;
171 GetScrollInfo(*this, SB_VERT
, &si
);
176 OnVScroll(LOWORD(wParam
), nPos
);
177 if (bLinkedPositions
&& pTheOtherPic
)
179 pTheOtherPic
->OnVScroll(LOWORD(wParam
), nPos
);
181 ::UpdateWindow(*pTheOtherPic
);
187 UINT nPos
= HIWORD(wParam
);
188 bool bForceUpdate
= false;
189 if (LOWORD(wParam
) == SB_THUMBTRACK
|| LOWORD(wParam
) == SB_THUMBPOSITION
)
191 // Get true 32-bit scroll position
193 si
.cbSize
= sizeof(SCROLLINFO
);
194 si
.fMask
= SIF_TRACKPOS
;
195 GetScrollInfo(*this, SB_VERT
, &si
);
200 OnHScroll(LOWORD(wParam
), nPos
);
201 if (bLinkedPositions
&& pTheOtherPic
)
203 pTheOtherPic
->OnHScroll(LOWORD(wParam
), nPos
);
205 ::UpdateWindow(*pTheOtherPic
);
211 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
216 OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
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
;
233 InvalidateRect(*this, nullptr, FALSE
);
240 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, FALSE
, 0);
244 mevt
.cbSize
= sizeof(TRACKMOUSEEVENT
);
245 mevt
.dwFlags
= TME_LEAVE
;
246 mevt
.dwHoverTime
= HOVER_DEFAULT
;
247 mevt
.hwndTrack
= *this;
248 ::TrackMouseEvent(&mevt
);
249 POINT pt
= {((int)(short)LOWORD(lParam
)), ((int)(short)HIWORD(lParam
))};
250 if (pt
.y
< CDPIAware::Instance().ScaleY(HEADER_HEIGHT
))
252 ClientToScreen(*this, &pt
);
253 if ((abs(m_lastTTPos
.x
- pt
.x
) > 20)||(abs(m_lastTTPos
.y
- pt
.y
) > 10))
258 SendMessage(hwndTT
, TTM_TRACKPOSITION
, 0, MAKELONG(pt
.x
, pt
.y
));
260 ti
.cbSize
= sizeof(TOOLINFO
);
263 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, TRUE
, (LPARAM
)&ti
);
268 SendMessage(hwndTT
, TTM_TRACKACTIVATE
, FALSE
, 0);
272 if ((wParam
& MK_LBUTTON
) &&
273 (ptPanStart
.x
>= 0) &&
277 int xPos
= GET_X_LPARAM(lParam
);
278 int yPos
= GET_Y_LPARAM(lParam
);
280 if (wParam
& MK_CONTROL
)
282 nHSecondScrollPos
= startHSecondScrollPos
+ (ptPanStart
.x
- xPos
);
283 nVSecondScrollPos
= startVSecondScrollPos
+ (ptPanStart
.y
- yPos
);
285 else if (wParam
& MK_SHIFT
)
287 nHScrollPos
= startHScrollPos
+ (ptPanStart
.x
- xPos
);
288 nVScrollPos
= startVScrollPos
+ (ptPanStart
.y
- yPos
);
292 nHSecondScrollPos
= startHSecondScrollPos
+ (ptPanStart
.x
- xPos
);
293 nVSecondScrollPos
= startVSecondScrollPos
+ (ptPanStart
.y
- yPos
);
294 nHScrollPos
= startHScrollPos
+ (ptPanStart
.x
- xPos
);
295 nVScrollPos
= startVScrollPos
+ (ptPanStart
.y
- yPos
);
296 if (!bLinkedPositions
&& pTheOtherPic
)
298 // snap to the other picture borders
299 if (abs(nVScrollPos
-pTheOtherPic
->nVScrollPos
) < 10)
300 nVScrollPos
= pTheOtherPic
->nVScrollPos
;
301 if (abs(nHScrollPos
-pTheOtherPic
->nHScrollPos
) < 10)
302 nHScrollPos
= pTheOtherPic
->nHScrollPos
;
306 InvalidateRect(*this, nullptr, TRUE
);
308 if (pTheOtherPic
&& (bLinkedPositions
) && ((wParam
& MK_SHIFT
)==0))
310 pTheOtherPic
->nHScrollPos
= nHScrollPos
;
311 pTheOtherPic
->nVScrollPos
= nVScrollPos
;
312 pTheOtherPic
->SetupScrollBars();
313 InvalidateRect(*pTheOtherPic
, nullptr, TRUE
);
314 UpdateWindow(*pTheOtherPic
);
321 // we show a hand cursor if the image can be dragged,
322 // and a hand-down cursor if the image is currently dragged
323 if ((*this == (HWND
)wParam
)&&(LOWORD(lParam
)==HTCLIENT
))
326 GetClientRect(&rect
);
327 LONG width
= picture
.m_Width
;
328 LONG height
= picture
.m_Height
;
331 width
= max(width
, pSecondPic
->m_Width
);
332 height
= max(height
, pSecondPic
->m_Height
);
335 if ((GetKeyState(VK_LBUTTON
)&0x8000)||(HIWORD(lParam
) == WM_LBUTTONDOWN
))
337 SetCursor(curHandDown
);
345 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
350 HDROP hDrop
= (HDROP
)wParam
;
351 TCHAR szFileName
[MAX_PATH
] = {0};
352 // we only use the first file dropped (if multiple files are dropped)
353 if (DragQueryFile(hDrop
, 0, szFileName
, _countof(szFileName
)))
355 SetPic(szFileName
, L
"", bMainPic
);
357 InvalidateRect(*this, nullptr, TRUE
);
363 switch (LOWORD(wParam
))
368 if (bLinkedPositions
&& pTheOtherPic
)
369 pTheOtherPic
->PrevImage();
376 if (bLinkedPositions
&& pTheOtherPic
)
377 pTheOtherPic
->NextImage();
383 bPlaying
= !bPlaying
;
385 if (bLinkedPositions
&& pTheOtherPic
)
386 pTheOtherPic
->Animate(bPlaying
);
390 case ALPHATOGGLEBUTTON_ID
:
392 WORD msg
= HIWORD(wParam
);
395 case BN_DOUBLECLICKED
:
397 SendMessage(hwndAlphaToggleBtn
, BM_SETSTATE
, 1, 0);
398 SetTimer(*this, ID_ALPHATOGGLETIMER
, 1000, nullptr);
402 KillTimer(*this, ID_ALPHATOGGLETIMER
);
411 m_blend
= BLEND_ALPHA
;
413 InvalidateRect(*this, nullptr, TRUE
);
420 InvalidateRect(*this, nullptr, TRUE
);
423 case SELECTBUTTON_ID
:
425 SendMessage(GetParent(m_hwnd
), WM_COMMAND
, MAKEWPARAM(SELECTBUTTON_ID
, SELECTBUTTON_ID
), (LPARAM
)m_hwnd
);
435 case ID_ANIMATIONTIMER
:
438 if (nCurrentFrame
> picture
.GetNumberOfFrames(0))
440 long delay
= picture
.SetActiveFrame(nCurrentFrame
);
441 delay
= max(100, delay
);
442 SetTimer(*this, ID_ANIMATIONTIMER
, delay
, nullptr);
443 InvalidateRect(*this, nullptr, FALSE
);
446 case TIMER_ALPHASLIDER
:
448 SetBlendAlpha(m_blend
, SendMessage(m_AlphaSlider
.GetWindow(), TBM_GETPOS
, 0, 0)/16.0f
);
449 KillTimer(*this, TIMER_ALPHASLIDER
);
452 case ID_ALPHATOGGLETIMER
:
462 LPNMHDR pNMHDR
= (LPNMHDR
)lParam
;
463 if (pNMHDR
->code
== TTN_GETDISPINFO
)
465 if (pNMHDR
->hwndFrom
== m_AlphaSlider
.GetWindow())
467 LPTOOLTIPTEXT lpttt
= (LPTOOLTIPTEXT
) lParam
;
468 lpttt
->hinst
= hResource
;
469 TCHAR stringbuf
[MAX_PATH
] = {0};
470 swprintf_s(stringbuf
, L
"%i%% alpha", (int)(SendMessage(m_AlphaSlider
.GetWindow(),TBM_GETPOS
, 0, 0) / 16.0f
* 100.0f
));
471 wcscpy_s(lpttt
->lpszText
, 80, stringbuf
);
473 else if (pNMHDR
->idFrom
== (UINT_PTR
)hwndAlphaToggleBtn
)
475 swprintf_s(m_wszTip
, (TCHAR
const*)ResString(hResource
, IDS_ALPHABUTTONTT
), (int)(SendMessage(m_AlphaSlider
.GetWindow(),TBM_GETPOS
, 0, 0) / 16.0f
* 100.0f
));
476 if (pNMHDR
->code
== TTN_NEEDTEXTW
)
478 NMTTDISPINFOW
* pTTTW
= (NMTTDISPINFOW
*)pNMHDR
;
479 pTTTW
->lpszText
= m_wszTip
;
483 NMTTDISPINFOA
* pTTTA
= (NMTTDISPINFOA
*)pNMHDR
;
484 pTTTA
->lpszText
= m_szTip
;
485 ::WideCharToMultiByte(CP_ACP
, 0, m_wszTip
, -1, m_szTip
, 8192, nullptr, nullptr);
490 BuildInfoString(m_wszTip
, _countof(m_wszTip
), true);
491 if (pNMHDR
->code
== TTN_NEEDTEXTW
)
493 NMTTDISPINFOW
* pTTTW
= (NMTTDISPINFOW
*)pNMHDR
;
494 pTTTW
->lpszText
= m_wszTip
;
498 NMTTDISPINFOA
* pTTTA
= (NMTTDISPINFOA
*)pNMHDR
;
499 pTTTA
->lpszText
= m_szTip
;
500 ::WideCharToMultiByte(CP_ACP
, 0, m_wszTip
, -1, m_szTip
, 8192, nullptr, nullptr);
511 bWindowClosed
= TRUE
;
514 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
520 void CPicWindow::NextImage()
523 if (nCurrentDimension
> picture
.GetNumberOfDimensions())
524 nCurrentDimension
= picture
.GetNumberOfDimensions();
526 if (nCurrentFrame
> picture
.GetNumberOfFrames(0))
527 nCurrentFrame
= picture
.GetNumberOfFrames(0);
528 picture
.SetActiveFrame(nCurrentFrame
>= nCurrentDimension
? nCurrentFrame
: nCurrentDimension
);
529 InvalidateRect(*this, nullptr, FALSE
);
533 void CPicWindow::PrevImage()
536 if (nCurrentDimension
< 1)
537 nCurrentDimension
= 1;
539 if (nCurrentFrame
< 1)
541 picture
.SetActiveFrame(nCurrentFrame
>= nCurrentDimension
? nCurrentFrame
: nCurrentDimension
);
542 InvalidateRect(*this, nullptr, FALSE
);
546 void CPicWindow::Animate(bool bStart
)
550 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hStop
);
551 SetTimer(*this, ID_ANIMATIONTIMER
, 0, nullptr);
555 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hPlay
);
556 KillTimer(*this, ID_ANIMATIONTIMER
);
560 void CPicWindow::SetPic(const tstring
& path
, const tstring
& title
, bool bFirst
)
563 picpath
=path
;pictitle
=title
;
564 picture
.SetInterpolationMode(InterpolationModeHighQualityBicubic
);
565 bValid
= picture
.Load(picpath
);
566 nDimensions
= picture
.GetNumberOfDimensions();
568 nFrames
= picture
.GetNumberOfFrames(0);
573 InvalidateRect(*this, nullptr, FALSE
);
577 void CPicWindow::DrawViewTitle(HDC hDC
, RECT
* rect
)
579 const auto header_height
= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
580 auto hFont
= CreateFont(-CDPIAware::Instance().PointsToPixelsY(pSecondPic
? 8 : 10), 0, 0, 0, FW_DONTCARE
, false, false, false, ANSI_CHARSET
, OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
, CLEARTYPE_QUALITY
, DEFAULT_PITCH
, L
"MS Shell Dlg");
581 HFONT hFontOld
= (HFONT
)SelectObject(hDC
, (HGDIOBJ
)hFont
);
584 textrect
.left
= rect
->left
;
585 textrect
.top
= rect
->top
;
586 textrect
.right
= rect
->right
;
587 textrect
.bottom
= rect
->top
+ header_height
;
588 if (HasMultipleImages())
589 textrect
.bottom
+= header_height
;
592 crBk
= ::GetSysColor(COLOR_SCROLLBAR
);
593 crFg
= ::GetSysColor(COLOR_WINDOWTEXT
);
594 SetBkColor(hDC
, crBk
);
595 ::ExtTextOut(hDC
, 0, 0, ETO_OPAQUE
, &textrect
, nullptr, 0, nullptr);
597 if (GetFocus() == *this)
598 DrawEdge(hDC
, &textrect
, EDGE_BUMP
, BF_RECT
);
600 DrawEdge(hDC
, &textrect
, EDGE_ETCHED
, BF_RECT
);
602 SetTextColor(hDC
, crFg
);
604 // use the path if no title is set.
605 tstring
* title
= pictitle
.empty() ? &picpath
: &pictitle
;
607 tstring realtitle
= *title
;
608 tstring imgnumstring
;
610 if (HasMultipleImages())
612 TCHAR buf
[MAX_PATH
] = {0};
614 swprintf_s(buf
, (const TCHAR
*)ResString(hResource
, IDS_DIMENSIONSANDFRAMES
), nCurrentFrame
, nFrames
);
616 swprintf_s(buf
, (const TCHAR
*)ResString(hResource
, IDS_DIMENSIONSANDFRAMES
), nCurrentDimension
, nDimensions
);
621 if (GetTextExtentPoint32(hDC
, realtitle
.c_str(), (int)realtitle
.size(), &stringsize
))
623 int nStringLength
= stringsize
.cx
;
624 int texttop
= pSecondPic
? textrect
.top
+ (header_height
/2) - stringsize
.cy
: textrect
.top
+ (header_height
/2) - stringsize
.cy
/2;
626 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
631 (UINT
)realtitle
.size(),
635 realtitle
= (pictitle2
.empty() ? picpath2
: pictitle2
);
637 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
638 texttop
+ stringsize
.cy
,
642 (UINT
)realtitle
.size(),
646 if (HasMultipleImages())
648 if (GetTextExtentPoint32(hDC
, imgnumstring
.c_str(), (int)imgnumstring
.size(), &stringsize
))
650 int nStringLength
= stringsize
.cx
;
653 max(textrect
.left
+ ((textrect
.right
-textrect
.left
)-nStringLength
)/2, 1),
654 textrect
.top
+ header_height
+ (header_height
/2) - stringsize
.cy
/2,
657 imgnumstring
.c_str(),
658 (UINT
)imgnumstring
.size(),
662 SelectObject(hDC
, (HGDIOBJ
)hFontOld
);
666 void CPicWindow::SetupScrollBars()
669 GetClientRect(&rect
);
671 SCROLLINFO si
= {sizeof(si
)};
673 si
.fMask
= SIF_POS
| SIF_PAGE
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
675 long width
= picture
.m_Width
*picscale
/100;
676 long height
= picture
.m_Height
*picscale
/100;
677 if (pSecondPic
&& pTheOtherPic
)
679 width
= max(width
, pSecondPic
->m_Width
*pTheOtherPic
->GetZoom()/100);
680 height
= max(height
, pSecondPic
->m_Height
*pTheOtherPic
->GetZoom()/100);
683 bool bShowHScrollBar
= (nHScrollPos
> 0); // left of pic is left of window
684 bShowHScrollBar
= bShowHScrollBar
|| (width
-nHScrollPos
> rect
.right
); // right of pic is outside right of window
685 bShowHScrollBar
= bShowHScrollBar
|| (width
+nHScrollPos
> rect
.right
); // right of pic is outside right of window
686 bool bShowVScrollBar
= (nVScrollPos
> 0); // top of pic is above window
687 bShowVScrollBar
= bShowVScrollBar
|| (height
-nVScrollPos
+rect
.top
> rect
.bottom
); // bottom of pic is below window
688 bShowVScrollBar
= bShowVScrollBar
|| (height
+nVScrollPos
+rect
.top
> rect
.bottom
); // bottom of pic is below window
690 // if the image is smaller than the window, we don't need the scrollbars
691 ShowScrollBar(*this, SB_HORZ
, bShowHScrollBar
);
692 ShowScrollBar(*this, SB_VERT
, bShowVScrollBar
);
696 si
.nPos
= nVScrollPos
;
697 si
.nPage
= rect
.bottom
-rect
.top
;
698 if (height
< rect
.bottom
-rect
.top
)
703 si
.nMax
= rect
.bottom
+nVScrollPos
-rect
.top
;
707 si
.nMin
= nVScrollPos
;
708 si
.nMax
= int(height
);
716 si
.nMax
= int(max(height
, rect
.bottom
+nVScrollPos
-rect
.top
));
721 si
.nMax
= int(height
-nVScrollPos
);
724 SetScrollInfo(*this, SB_VERT
, &si
, TRUE
);
729 si
.nPos
= nHScrollPos
;
730 si
.nPage
= rect
.right
-rect
.left
;
731 if (width
< rect
.right
-rect
.left
)
736 si
.nMax
= rect
.right
+nHScrollPos
-rect
.left
;
740 si
.nMin
= nHScrollPos
;
741 si
.nMax
= int(width
);
749 si
.nMax
= int(max(width
, rect
.right
+nHScrollPos
-rect
.left
));
754 si
.nMax
= int(width
-nHScrollPos
);
757 SetScrollInfo(*this, SB_HORZ
, &si
, TRUE
);
763 void CPicWindow::OnVScroll(UINT nSBCode
, UINT nPos
)
766 GetClientRect(&rect
);
771 nVScrollPos
= LONG(picture
.GetHeight()*picscale
/100);
783 nVScrollPos
+= (rect
.bottom
-rect
.top
);
786 nVScrollPos
-= (rect
.bottom
-rect
.top
);
788 case SB_THUMBPOSITION
:
797 LONG height
= LONG(picture
.GetHeight()*picscale
/100);
800 height
= max(height
, LONG(pSecondPic
->GetHeight()*picscale
/100));
801 nVSecondScrollPos
= nVScrollPos
;
805 InvalidateRect(*this, nullptr, TRUE
);
808 void CPicWindow::OnHScroll(UINT nSBCode
, UINT nPos
)
811 GetClientRect(&rect
);
816 nHScrollPos
= LONG(picture
.GetWidth()*picscale
/100);
828 nHScrollPos
+= (rect
.right
-rect
.left
);
831 nHScrollPos
-= (rect
.right
-rect
.left
);
833 case SB_THUMBPOSITION
:
842 LONG width
= LONG(picture
.GetWidth()*picscale
/100);
845 width
= max(width
, LONG(pSecondPic
->GetWidth()*picscale
/100));
846 nHSecondScrollPos
= nHScrollPos
;
850 InvalidateRect(*this, nullptr, TRUE
);
853 void CPicWindow::OnMouseWheel(short fwKeys
, short zDelta
)
856 GetClientRect(&rect
);
857 LONG width
= long(picture
.m_Width
*picscale
/100);
858 LONG height
= long(picture
.m_Height
*picscale
/100);
861 width
= max(width
, long(pSecondPic
->m_Width
*picscale
/100));
862 height
= max(height
, long(pSecondPic
->m_Height
*picscale
/100));
864 if ((fwKeys
& MK_SHIFT
)&&(fwKeys
& MK_CONTROL
)&&(pSecondPic
))
866 // ctrl+shift+wheel: change the alpha channel
867 float a
= blendAlpha
;
868 a
-= float(zDelta
)/120.0f
/4.0f
;
873 SetBlendAlpha(m_blend
, a
);
875 else if (fwKeys
& MK_SHIFT
)
877 // shift means scrolling sideways
878 OnHScroll(SB_THUMBPOSITION
, GetHPos()-zDelta
);
879 if ((bLinkedPositions
)&&(pTheOtherPic
))
881 pTheOtherPic
->OnHScroll(SB_THUMBPOSITION
, pTheOtherPic
->GetHPos()-zDelta
);
884 else if (fwKeys
& MK_CONTROL
)
886 // control means adjusting the scale factor
887 Zoom(zDelta
>0, true);
889 InvalidateRect(*this, nullptr, FALSE
);
890 SetWindowPos(*this, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOSIZE
| SWP_NOREPOSITION
| SWP_NOMOVE
);
892 if ((bLinkedPositions
|| bOverlap
) && pTheOtherPic
)
894 pTheOtherPic
->nHScrollPos
= nHScrollPos
;
895 pTheOtherPic
->nVScrollPos
= nVScrollPos
;
896 pTheOtherPic
->SetupScrollBars();
897 InvalidateRect(*pTheOtherPic
, nullptr, TRUE
);
898 UpdateWindow(*pTheOtherPic
);
903 OnVScroll(SB_THUMBPOSITION
, GetVPos()-zDelta
);
904 if ((bLinkedPositions
)&&(pTheOtherPic
))
906 pTheOtherPic
->OnVScroll(SB_THUMBPOSITION
, pTheOtherPic
->GetVPos()-zDelta
);
911 void CPicWindow::GetClientRect(RECT
* pRect
)
913 ::GetClientRect(*this, pRect
);
914 pRect
->top
+= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
915 if (HasMultipleImages())
917 pRect
->top
+= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
920 pRect
->left
+= CDPIAware::Instance().ScaleX(SLIDER_WIDTH
);
923 void CPicWindow::GetClientRectWithScrollbars(RECT
* pRect
)
925 GetClientRect(pRect
);
926 ::GetWindowRect(*this, pRect
);
927 pRect
->right
= pRect
->right
-pRect
->left
;
928 pRect
->bottom
= pRect
->bottom
-pRect
->top
;
931 pRect
->top
+= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
932 if (HasMultipleImages())
934 pRect
->top
+= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
937 pRect
->left
+= CDPIAware::Instance().ScaleX(SLIDER_WIDTH
);
941 void CPicWindow::SetZoom(int Zoom
, bool centermouse
, bool inzoom
)
943 // Set the interpolation mode depending on zoom
944 int oldPicscale
= picscale
;
945 int oldOtherPicscale
= picscale
;
947 picture
.SetInterpolationMode(InterpolationModeNearestNeighbor
);
949 pSecondPic
->SetInterpolationMode(InterpolationModeNearestNeighbor
);
951 if ((oldPicscale
== 0) || (Zoom
== 0))
956 if (pTheOtherPic
&& !inzoom
)
960 pTheOtherPic
->SetZoom(Zoom
, centermouse
, true);
965 pTheOtherPic
->SetZoomToHeight(picture
.m_Height
*Zoom
/100);
970 pTheOtherPic
->SetZoomToWidth(picture
.m_Width
*Zoom
/100);
974 // adjust the scrollbar positions according to the new zoom and the
975 // mouse position: if possible, keep the pixel where the mouse pointer
976 // is at the same position after the zoom
980 DWORD ptW
= GetMessagePos();
981 cpos
.x
= GET_X_LPARAM(ptW
);
982 cpos
.y
= GET_Y_LPARAM(ptW
);
983 ScreenToClient(*this, &cpos
);
985 GetClientRect(&clientrect
);
986 if ((PtInRect(&clientrect
, cpos
))&&(centermouse
))
988 // the mouse pointer is over our window
989 nHScrollPos
= (nHScrollPos
+ cpos
.x
)*Zoom
/oldPicscale
-cpos
.x
;
990 nVScrollPos
= (nVScrollPos
+ cpos
.y
)*Zoom
/oldPicscale
-cpos
.y
;
991 if (pTheOtherPic
&& bMainPic
)
993 int otherzoom
= pTheOtherPic
->GetZoom();
994 nHSecondScrollPos
= (nHSecondScrollPos
+ cpos
.x
)*otherzoom
/oldOtherPicscale
-cpos
.x
;
995 nVSecondScrollPos
= (nVSecondScrollPos
+ cpos
.y
)*otherzoom
/oldOtherPicscale
-cpos
.y
;
1000 nHScrollPos
= (nHScrollPos
+ ((clientrect
.right
-clientrect
.left
)/2))*Zoom
/oldPicscale
-((clientrect
.right
-clientrect
.left
)/2);
1001 nVScrollPos
= (nVScrollPos
+ ((clientrect
.bottom
-clientrect
.top
)/2))*Zoom
/oldPicscale
-((clientrect
.bottom
-clientrect
.top
)/2);
1002 if (pTheOtherPic
&& bMainPic
)
1004 int otherzoom
= pTheOtherPic
->GetZoom();
1005 nHSecondScrollPos
= (nHSecondScrollPos
+ ((clientrect
.right
-clientrect
.left
)/2))*otherzoom
/oldOtherPicscale
-((clientrect
.right
-clientrect
.left
)/2);
1006 nVSecondScrollPos
= (nVSecondScrollPos
+ ((clientrect
.bottom
-clientrect
.top
)/2))*otherzoom
/oldOtherPicscale
-((clientrect
.bottom
-clientrect
.top
)/2);
1013 InvalidateRect(*this, nullptr, TRUE
);
1016 void CPicWindow::Zoom(bool in
, bool centermouse
)
1020 // Find correct zoom factor and quantize picscale
1029 if (!in
&& picscale
<= 20)
1034 else if ((in
&& picscale
< 100) || (!in
&& picscale
<= 100))
1038 else if ((in
&& picscale
< 200) || (!in
&& picscale
<= 200))
1050 SetZoom(picscale
+zoomFactor
, centermouse
);
1054 SetZoom(picscale
-zoomFactor
, centermouse
);
1058 void CPicWindow::FitImageInWindow()
1062 GetClientRectWithScrollbars(&rect
);
1064 const auto border
= CDPIAware::Instance().ScaleX(2);
1065 if (rect
.right
-rect
.left
)
1068 if (((rect
.right
- rect
.left
) > picture
.m_Width
+ border
) && ((rect
.bottom
- rect
.top
) > picture
.m_Height
+ border
))
1070 // image is smaller than the window
1075 // image is bigger than the window
1076 int xscale
= (rect
.right
- rect
.left
- border
) * 100 / picture
.m_Width
;
1077 int yscale
= (rect
.bottom
- rect
.top
- border
) * 100 / picture
.m_Height
;
1078 Zoom
= min(yscale
, xscale
);
1082 if (((rect
.right
- rect
.left
) > pSecondPic
->m_Width
+ border
) && ((rect
.bottom
- rect
.top
) > pSecondPic
->m_Height
+ border
))
1084 // image is smaller than the window
1086 pTheOtherPic
->SetZoom(min(100, Zoom
), false);
1090 // image is bigger than the window
1091 int xscale
= (rect
.right
- rect
.left
- border
) * 100 / pSecondPic
->m_Width
;
1092 int yscale
= (rect
.bottom
- rect
.top
- border
) * 100 / pSecondPic
->m_Height
;
1094 pTheOtherPic
->SetZoom(min(yscale
, xscale
), false);
1096 nHSecondScrollPos
= 0;
1097 nVSecondScrollPos
= 0;
1099 SetZoom(Zoom
, false);
1103 InvalidateRect(*this, nullptr, TRUE
);
1106 void CPicWindow::CenterImage()
1109 GetClientRectWithScrollbars(&rect
);
1110 const auto border
= CDPIAware::Instance().ScaleX(2);
1111 long width
= picture
.m_Width
*picscale
/ 100 + border
;
1112 long height
= picture
.m_Height
*picscale
/ 100 + border
;
1113 if (pSecondPic
&& pTheOtherPic
)
1115 width
= max(width
, pSecondPic
->m_Width
*pTheOtherPic
->GetZoom() / 100 + border
);
1116 height
= max(height
, pSecondPic
->m_Height
*pTheOtherPic
->GetZoom() / 100 + border
);
1119 bool bPicWidthBigger
= (int(width
) > (rect
.right
-rect
.left
));
1120 bool bPicHeightBigger
= (int(height
) > (rect
.bottom
-rect
.top
));
1121 // set the scroll position so that the image is drawn centered in the window
1122 // if the window is bigger than the image
1123 if (!bPicWidthBigger
)
1125 nHScrollPos
= -((rect
.right
-rect
.left
+4)-int(width
))/2;
1126 nHSecondScrollPos
= nHScrollPos
;
1128 if (!bPicHeightBigger
)
1130 nVScrollPos
= -((rect
.bottom
-rect
.top
+4)-int(height
))/2;
1131 nVSecondScrollPos
= nVScrollPos
;
1136 void CPicWindow::FitWidths(bool bFit
)
1140 SetZoom(GetZoom(), false);
1143 void CPicWindow::FitHeights(bool bFit
)
1147 SetZoom(GetZoom(), false);
1150 void CPicWindow::ShowPicWithBorder(HDC hdc
, const RECT
&bounds
, CPicture
&pic
, int scale
)
1152 ::SetBkColor(hdc
, transparentColor
);
1153 ::ExtTextOut(hdc
, 0, 0, ETO_OPAQUE
, &bounds
, nullptr, 0, nullptr);
1156 picrect
.left
= bounds
.left
- nHScrollPos
;
1157 picrect
.top
= bounds
.top
- nVScrollPos
;
1158 if ((!bLinkedPositions
|| bOverlap
) && (pTheOtherPic
) && (&pic
!= &picture
))
1160 picrect
.left
= bounds
.left
- nHSecondScrollPos
;
1161 picrect
.top
= bounds
.top
- nVSecondScrollPos
;
1163 picrect
.right
= (picrect
.left
+ pic
.m_Width
* scale
/ 100);
1164 picrect
.bottom
= (picrect
.top
+ pic
.m_Height
* scale
/ 100);
1166 if (bFitWidths
&& m_linkedWidth
)
1167 picrect
.right
= picrect
.left
+ m_linkedWidth
;
1168 if (bFitHeights
&& m_linkedHeight
)
1169 picrect
.bottom
= picrect
.top
+ m_linkedHeight
;
1171 pic
.Show(hdc
, picrect
);
1173 const auto bordersize
= CDPIAware::Instance().ScaleX(1);
1176 border
.left
= picrect
.left
- bordersize
;
1177 border
.top
= picrect
.top
- bordersize
;
1178 border
.right
= picrect
.right
+ bordersize
;
1179 border
.bottom
= picrect
.bottom
+ bordersize
;
1181 HPEN hPen
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
));
1182 HPEN hOldPen
= (HPEN
)SelectObject(hdc
, hPen
);
1183 MoveToEx(hdc
, border
.left
, border
.top
, nullptr);
1184 LineTo(hdc
, border
.left
, border
.bottom
);
1185 LineTo(hdc
, border
.right
, border
.bottom
);
1186 LineTo(hdc
, border
.right
, border
.top
);
1187 LineTo(hdc
, border
.left
, border
.top
);
1188 SelectObject(hdc
, hOldPen
);
1192 void CPicWindow::Paint(HWND hwnd
)
1196 RECT rect
, fullrect
;
1198 GetUpdateRect(hwnd
, &rect
, FALSE
);
1199 if (IsRectEmpty(&rect
))
1202 const auto slider_width
= CDPIAware::Instance().ScaleX(SLIDER_WIDTH
);
1203 const auto border
= CDPIAware::Instance().ScaleX(4);
1204 ::GetClientRect(*this, &fullrect
);
1205 hdc
= BeginPaint(hwnd
, &ps
);
1207 // Exclude the alpha control and button
1208 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
1209 ExcludeClipRect(hdc
, 0, m_inforect
.top
- border
, slider_width
, m_inforect
.bottom
+ border
);
1211 CMyMemDC
memDC(hdc
);
1212 if ((pSecondPic
)&&(m_blend
!= BLEND_ALPHA
))
1214 // erase the place where the alpha slider would be
1215 ::SetBkColor(memDC
, transparentColor
);
1216 RECT bounds
= { 0, m_inforect
.top
- border
, slider_width
, m_inforect
.bottom
+ border
};
1217 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &bounds
, nullptr, 0, nullptr);
1220 GetClientRect(&rect
);
1223 ShowPicWithBorder(memDC
, rect
, picture
, picscale
);
1226 HDC secondhdc
= CreateCompatibleDC(hdc
);
1227 HBITMAP hBitmap
= CreateCompatibleBitmap(hdc
, rect
.right
- rect
.left
, rect
.bottom
- rect
.top
);
1228 HBITMAP hOldBitmap
= (HBITMAP
)SelectObject(secondhdc
, hBitmap
);
1229 SetWindowOrgEx(secondhdc
, rect
.left
, rect
.top
, nullptr);
1231 if ((pSecondPic
)&&(m_blend
!= BLEND_ALPHA
))
1233 // erase the place where the alpha slider would be
1234 ::SetBkColor(secondhdc
, transparentColor
);
1235 RECT bounds
= { 0, m_inforect
.top
- border
, slider_width
, m_inforect
.bottom
+ border
};
1236 ::ExtTextOut(secondhdc
, 0, 0, ETO_OPAQUE
, &bounds
, nullptr, 0, nullptr);
1239 ShowPicWithBorder(secondhdc
, rect
, *pSecondPic
, pTheOtherPic
->GetZoom());
1241 if (m_blend
== BLEND_ALPHA
)
1243 BLENDFUNCTION blender
;
1244 blender
.AlphaFormat
= 0;
1245 blender
.BlendFlags
= 0;
1246 blender
.BlendOp
= AC_SRC_OVER
;
1247 blender
.SourceConstantAlpha
= (BYTE
)(blendAlpha
*255);
1251 rect
.right
-rect
.left
,
1252 rect
.bottom
-rect
.top
,
1256 rect
.right
-rect
.left
,
1257 rect
.bottom
-rect
.top
,
1260 else if (m_blend
== BLEND_XOR
)
1265 rect
.right
-rect
.left
,
1266 rect
.bottom
-rect
.top
,
1270 //rect.right-rect.left,
1271 //rect.bottom-rect.top,
1273 InvertRect(memDC
, &rect
);
1275 SelectObject(secondhdc
, hOldBitmap
);
1276 DeleteObject(hBitmap
);
1277 DeleteDC(secondhdc
);
1279 else if (bDragging
&& pTheOtherPic
&& !bLinkedPositions
)
1281 // when dragging, show lines indicating the position of the other image
1282 HPEN hPen
= CreatePen(PS_SOLID
, 1, GetSysColor(/*COLOR_ACTIVEBORDER*/COLOR_HIGHLIGHT
));
1283 HPEN hOldPen
= (HPEN
)SelectObject(memDC
, hPen
);
1284 int xpos
= rect
.left
- pTheOtherPic
->nHScrollPos
- 1;
1285 MoveToEx(memDC
, xpos
, rect
.top
, nullptr);
1286 LineTo(memDC
, xpos
, rect
.bottom
);
1287 xpos
= rect
.left
- pTheOtherPic
->nHScrollPos
+ pTheOtherPic
->picture
.m_Width
*pTheOtherPic
->GetZoom()/100 + 1;
1288 if (bFitWidths
&& m_linkedWidth
)
1289 xpos
= rect
.left
+ pTheOtherPic
->m_linkedWidth
+ 1;
1290 MoveToEx(memDC
, xpos
, rect
.top
, nullptr);
1291 LineTo(memDC
, xpos
, rect
.bottom
);
1293 int ypos
= rect
.top
- pTheOtherPic
->nVScrollPos
- 1;
1294 MoveToEx(memDC
, rect
.left
, ypos
, nullptr);
1295 LineTo(memDC
, rect
.right
, ypos
);
1296 ypos
= rect
.top
- pTheOtherPic
->nVScrollPos
+ pTheOtherPic
->picture
.m_Height
*pTheOtherPic
->GetZoom()/100 + 1;
1297 if (bFitHeights
&& m_linkedHeight
)
1298 ypos
= rect
.top
- pTheOtherPic
->m_linkedHeight
+ 1;
1299 MoveToEx(memDC
, rect
.left
, ypos
, nullptr);
1300 LineTo(memDC
, rect
.right
, ypos
);
1302 SelectObject(memDC
, hOldPen
);
1306 int sliderwidth
= 0;
1307 if ((pSecondPic
)&&(m_blend
== BLEND_ALPHA
))
1308 sliderwidth
= slider_width
;
1309 m_inforect
.left
= rect
.left
+ border
+ sliderwidth
;
1310 m_inforect
.top
= rect
.top
;
1311 m_inforect
.right
= rect
.right
+sliderwidth
;
1312 m_inforect
.bottom
= rect
.bottom
;
1314 SetBkColor(memDC
, transparentColor
);
1317 auto infostring
= std::make_unique
<TCHAR
[]>(8192);
1318 BuildInfoString(infostring
.get(), 8192, false);
1320 NONCLIENTMETRICS metrics
= {0};
1321 metrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
1322 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, 0, &metrics
, FALSE
);
1323 HFONT hFont
= CreateFontIndirect(&metrics
.lfStatusFont
);
1324 HFONT hFontOld
= (HFONT
)SelectObject(memDC
, (HGDIOBJ
)hFont
);
1325 // find out how big the rectangle for the text has to be
1326 DrawText(memDC
, infostring
.get(), -1, &m_inforect
, DT_EDITCONTROL
| DT_EXPANDTABS
| DT_LEFT
| DT_VCENTER
| DT_CALCRECT
);
1328 // the text should be drawn with a four pixel offset to the window borders
1329 m_inforect
.top
= rect
.bottom
- (m_inforect
.bottom
-m_inforect
.top
) - border
;
1330 m_inforect
.bottom
= rect
.bottom
-4;
1332 // first draw an edge rectangle
1334 edgerect
.left
= m_inforect
.left
- border
;
1335 edgerect
.top
= m_inforect
.top
- border
;
1336 edgerect
.right
= m_inforect
.right
+ border
;
1337 edgerect
.bottom
= m_inforect
.bottom
+ border
;
1338 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &edgerect
, nullptr, 0, nullptr);
1339 DrawEdge(memDC
, &edgerect
, EDGE_BUMP
, BF_RECT
| BF_SOFT
);
1341 SetTextColor(memDC
, GetSysColor(COLOR_WINDOWTEXT
));
1342 DrawText(memDC
, infostring
.get(), -1, &m_inforect
, DT_EDITCONTROL
| DT_EXPANDTABS
| DT_LEFT
| DT_VCENTER
);
1343 SelectObject(memDC
, (HGDIOBJ
)hFontOld
);
1344 DeleteObject(hFont
);
1349 SetBkColor(memDC
, ::GetSysColor(COLOR_WINDOW
));
1350 SetTextColor(memDC
, ::GetSysColor(COLOR_WINDOWTEXT
));
1351 ::ExtTextOut(memDC
, 0, 0, ETO_OPAQUE
, &rect
, nullptr, 0, nullptr);
1353 ResString str
= ResString(hResource
, IDS_INVALIDIMAGEINFO
);
1356 NONCLIENTMETRICS metrics
= {0};
1357 metrics
.cbSize
= sizeof(NONCLIENTMETRICS
);
1358 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, 0, &metrics
, FALSE
);
1359 HFONT hFont
= CreateFontIndirect(&metrics
.lfStatusFont
);
1360 HFONT hFontOld
= (HFONT
)SelectObject(memDC
, (HGDIOBJ
)hFont
);
1362 if (GetTextExtentPoint32(memDC
, str
, (int)wcslen(str
), &stringsize
))
1364 int nStringLength
= stringsize
.cx
;
1367 max(rect
.left
+ ((rect
.right
-rect
.left
)-nStringLength
)/2, 1),
1368 rect
.top
+ ((rect
.bottom
-rect
.top
) - stringsize
.cy
)/2,
1375 SelectObject(memDC
, (HGDIOBJ
)hFontOld
);
1376 DeleteObject(hFont
);
1378 DrawViewTitle(memDC
, &fullrect
);
1380 EndPaint(hwnd
, &ps
);
1383 bool CPicWindow::CreateButtons()
1385 // Ensure that the common control DLL is loaded.
1386 INITCOMMONCONTROLSEX icex
;
1387 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
1388 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_WIN95_CLASSES
;
1389 InitCommonControlsEx(&icex
);
1391 hwndLeftBtn
= CreateWindowEx(0,
1394 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1397 (HMENU
)LEFTBUTTON_ID
,
1400 if (hwndLeftBtn
== INVALID_HANDLE_VALUE
)
1402 int iconWidth
= GetSystemMetrics(SM_CXSMICON
);
1403 int iconHeight
= GetSystemMetrics(SM_CYSMICON
);
1404 hLeft
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_BACKWARD
), iconWidth
, iconHeight
);
1405 SendMessage(hwndLeftBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hLeft
);
1406 hwndRightBtn
= CreateWindowEx(0,
1409 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1412 (HMENU
)RIGHTBUTTON_ID
,
1415 if (hwndRightBtn
== INVALID_HANDLE_VALUE
)
1417 hRight
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FORWARD
), iconWidth
, iconHeight
);
1418 SendMessage(hwndRightBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hRight
);
1419 hwndPlayBtn
= CreateWindowEx(0,
1422 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
,
1425 (HMENU
)PLAYBUTTON_ID
,
1428 if (hwndPlayBtn
== INVALID_HANDLE_VALUE
)
1430 hPlay
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_START
), iconWidth
, iconHeight
);
1431 hStop
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_STOP
), iconWidth
, iconHeight
);
1432 SendMessage(hwndPlayBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hPlay
);
1433 hwndAlphaToggleBtn
= CreateWindowEx(0,
1436 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
| BS_ICON
| BS_FLAT
| BS_NOTIFY
| BS_PUSHLIKE
,
1439 (HMENU
)ALPHATOGGLEBUTTON_ID
,
1442 if (hwndAlphaToggleBtn
== INVALID_HANDLE_VALUE
)
1444 hAlphaToggle
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ALPHATOGGLE
), iconWidth
, iconHeight
);
1445 SendMessage(hwndAlphaToggleBtn
, BM_SETIMAGE
, (WPARAM
)IMAGE_ICON
, (LPARAM
)hAlphaToggle
);
1448 ti
.cbSize
= sizeof(TOOLINFO
);
1449 ti
.uFlags
= TTF_IDISHWND
|TTF_SUBCLASS
;
1451 ti
.hinst
= hResource
;
1452 ti
.uId
= (UINT_PTR
)hwndAlphaToggleBtn
;
1453 ti
.lpszText
= LPSTR_TEXTCALLBACK
;
1454 // ToolTip control will cover the whole window
1459 SendMessage(hwndTT
, TTM_ADDTOOL
, 0, (LPARAM
) (LPTOOLINFO
) &ti
);
1460 ResString
sSelect(hResource
, IDS_SELECT
);
1461 hwndSelectBtn
= CreateWindowEx(0,
1464 WS_CHILD
| WS_VISIBLE
| BS_PUSHBUTTON
,
1467 (HMENU
)SELECTBUTTON_ID
,
1470 if (hwndPlayBtn
== INVALID_HANDLE_VALUE
)
1476 void CPicWindow::PositionChildren()
1478 const auto header_height
= CDPIAware::Instance().ScaleY(HEADER_HEIGHT
);
1479 const auto selBorder
= CDPIAware::Instance().ScaleX(100);
1481 ::GetClientRect(*this, &rect
);
1482 if (HasMultipleImages())
1484 int iconWidth
= GetSystemMetrics(SM_CXSMICON
);
1485 int iconHeight
= GetSystemMetrics(SM_CYSMICON
);
1486 SetWindowPos(hwndLeftBtn
, HWND_TOP
, rect
.left
+ iconWidth
/ 4, rect
.top
+ header_height
+ (header_height
-iconHeight
)/2, iconWidth
, iconHeight
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
1487 SetWindowPos(hwndRightBtn
, HWND_TOP
, rect
.left
+ iconWidth
+ iconWidth
/ 2, rect
.top
+ header_height
+ (header_height
- iconHeight
) / 2, iconWidth
, iconHeight
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
1489 SetWindowPos(hwndPlayBtn
, HWND_TOP
, rect
.left
+ iconWidth
* 2 + iconWidth
/ 2, rect
.top
+ header_height
+ (header_height
- iconHeight
) / 2, iconWidth
, iconHeight
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
1491 ShowWindow(hwndPlayBtn
, SW_HIDE
);
1495 ShowWindow(hwndLeftBtn
, SW_HIDE
);
1496 ShowWindow(hwndRightBtn
, SW_HIDE
);
1497 ShowWindow(hwndPlayBtn
, SW_HIDE
);
1500 SetWindowPos(hwndSelectBtn
, HWND_TOP
, rect
.right
- selBorder
, rect
.bottom
- header_height
, selBorder
, header_height
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
1502 ShowWindow(hwndSelectBtn
, SW_HIDE
);
1506 bool CPicWindow::HasMultipleImages()
1508 return (((nDimensions
> 1) || (nFrames
> 1)) && (!pSecondPic
));
1511 void CPicWindow::CreateTrackbar(HWND hwndParent
)
1513 HWND hwndTrack
= CreateWindowEx(
1514 0, // no extended styles
1515 TRACKBAR_CLASS
, // class name
1516 L
"Trackbar Control", // title (caption)
1517 WS_CHILD
| WS_VISIBLE
| TBS_VERT
| TBS_TOOLTIPS
| TBS_AUTOTICKS
, // style
1520 hwndParent
, // parent window
1521 (HMENU
)TRACKBAR_ID
, // control identifier
1523 nullptr // no WM_CREATE parameter
1526 SendMessage(hwndTrack
, TBM_SETRANGE
,
1527 (WPARAM
) TRUE
, // redraw flag
1528 (LPARAM
) MAKELONG(0, 16)); // min. & max. positions
1529 SendMessage(hwndTrack
, TBM_SETTIPSIDE
,
1533 m_AlphaSlider
.ConvertTrackbarToNice(hwndTrack
);
1536 void CPicWindow::BuildInfoString(TCHAR
* buf
, int size
, bool bTooltip
)
1538 // Unfortunately, we need two different strings for the tooltip
1539 // and the info box. Because the tooltips use a different tab size
1540 // than ExtTextOut(), and to keep the output aligned we therefore
1541 // need two different strings.
1542 // Note: some translations could end up with two identical strings, but
1543 // in English we need two - even if we wouldn't need two in English, some
1544 // translation might then need two again.
1545 if (pSecondPic
&& pTheOtherPic
)
1547 swprintf_s(buf
, size
,
1548 (TCHAR
const *)ResString(hResource
, bTooltip
? IDS_DUALIMAGEINFOTT
: IDS_DUALIMAGEINFO
),
1549 picture
.GetFileSizeAsText().c_str(), picture
.GetFileSizeAsText(false).c_str(),
1550 picture
.m_Width
, picture
.m_Height
,
1551 picture
.GetHorizontalResolution(), picture
.GetVerticalResolution(),
1552 picture
.m_ColorDepth
,
1554 pSecondPic
->GetFileSizeAsText().c_str(), pSecondPic
->GetFileSizeAsText(false).c_str(),
1555 pSecondPic
->m_Width
, pSecondPic
->m_Height
,
1556 pSecondPic
->GetHorizontalResolution(), pSecondPic
->GetVerticalResolution(),
1557 pSecondPic
->m_ColorDepth
,
1558 (UINT
)pTheOtherPic
->GetZoom());
1562 swprintf_s(buf
, size
,
1563 (TCHAR
const *)ResString(hResource
, bTooltip
? IDS_IMAGEINFOTT
: IDS_IMAGEINFO
),
1564 picture
.GetFileSizeAsText().c_str(), picture
.GetFileSizeAsText(false).c_str(),
1565 picture
.m_Width
, picture
.m_Height
,
1566 picture
.GetHorizontalResolution(), picture
.GetVerticalResolution(),
1567 picture
.m_ColorDepth
,
1572 void CPicWindow::SetZoomToWidth( long width
)
1574 m_linkedWidth
= width
;
1575 if (picture
.m_Width
)
1577 int zoom
= width
*100/picture
.m_Width
;
1578 SetZoom(zoom
, false, true);
1582 void CPicWindow::SetZoomToHeight( long height
)
1584 m_linkedHeight
= height
;
1585 if (picture
.m_Height
)
1587 int zoom
= height
*100/picture
.m_Height
;
1588 SetZoom(zoom
, false, true);