1
// TortoiseGitIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2015-2019 - TortoiseGit
4 // Copyright (C) 2006-2015, 2018, 2020-2021 - TortoiseSVN
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 "TortoiseIDiff.h"
24 #include "MainWindow.h"
26 #include "TaskbarUUID.h"
27 #include "PathUtils.h"
29 #include "LoadIconEx.h"
32 #include "DarkModeHelper.h"
34 #pragma comment(lib, "comctl32.lib")
36 std::wstring
CMainWindow::leftpicpath
;
37 std::wstring
CMainWindow::leftpictitle
;
39 std::wstring
CMainWindow::rightpicpath
;
40 std::wstring
CMainWindow::rightpictitle
;
42 const UINT TaskBarButtonCreated
= RegisterWindowMessage(L
"TaskbarButtonCreated");
44 bool CMainWindow::RegisterAndCreateWindow()
48 // Fill in the window class structure with default parameters
49 wcx
.cbSize
= sizeof(WNDCLASSEX
);
50 wcx
.style
= CS_HREDRAW
| CS_VREDRAW
;
51 wcx
.lpfnWndProc
= CWindow::stWinMsgHandler
;
54 wcx
.hInstance
= hResource
;
55 wcx
.hCursor
= LoadCursor(nullptr, IDC_SIZEWE
);
56 ResString
clsname(hResource
, IDS_APP_TITLE
);
57 wcx
.lpszClassName
= clsname
;
58 wcx
.hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
), GetSystemMetrics(SM_CXICON
), GetSystemMetrics(SM_CYICON
));
59 wcx
.hbrBackground
= reinterpret_cast<HBRUSH
>(COLOR_3DFACE
+ 1);
60 if (selectionPaths
.empty())
61 wcx
.lpszMenuName
= MAKEINTRESOURCE(IDC_TORTOISEIDIFF
);
63 wcx
.lpszMenuName
= MAKEINTRESOURCE(IDC_TORTOISEIDIFF2
);
64 wcx
.hIconSm
= LoadIconEx(wcx
.hInstance
, MAKEINTRESOURCE(IDI_TORTOISEIDIFF
));
65 if (RegisterWindow(&wcx
))
67 if (Create(WS_OVERLAPPEDWINDOW
| WS_CLIPCHILDREN
| WS_VISIBLE
, nullptr))
76 void CMainWindow::PositionChildren(RECT
* clientrect
/* = nullptr */)
81 const auto splitter_border
= CDPIAware::Instance().ScaleX(SPLITTER_BORDER
);
82 SendMessage(hwndTB
, TB_AUTOSIZE
, 0, 0);
83 GetWindowRect(hwndTB
, &tbRect
);
84 LONG tbHeight
= tbRect
.bottom
-tbRect
.top
-1;
85 HDWP hdwp
= BeginDeferWindowPos(3);
86 if (bOverlap
&& selectionPaths
.empty())
88 SetWindowPos(picWindow1
, nullptr, clientrect
->left
, clientrect
->top
+ tbHeight
, clientrect
->right
- clientrect
->left
, clientrect
->bottom
- clientrect
->top
- tbHeight
, SWP_SHOWWINDOW
);
94 if (selectionPaths
.size() != 3)
98 child
.left
= clientrect
->left
;
99 child
.top
= clientrect
->top
+tbHeight
;
100 child
.right
= clientrect
->right
;
101 child
.bottom
= nSplitterPos
- (splitter_border
/ 2);
102 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow1
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
103 child
.top
= nSplitterPos
+ (splitter_border
/ 2);
104 child
.bottom
= clientrect
->bottom
;
105 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow2
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
109 // three image windows
111 child
.left
= clientrect
->left
;
112 child
.top
= clientrect
->top
+tbHeight
;
113 child
.right
= clientrect
->right
;
114 child
.bottom
= nSplitterPos
- (splitter_border
/ 2);
115 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow1
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
116 child
.top
= nSplitterPos
+ (splitter_border
/ 2);
117 child
.bottom
= nSplitterPos2
- (splitter_border
/ 2);
118 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow2
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
119 child
.top
= nSplitterPos2
+ (splitter_border
/ 2);
120 child
.bottom
= clientrect
->bottom
;
121 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow3
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
126 if (selectionPaths
.size() != 3)
130 child
.left
= clientrect
->left
;
131 child
.top
= clientrect
->top
+tbHeight
;
132 child
.right
= nSplitterPos
- (splitter_border
/ 2);
133 child
.bottom
= clientrect
->bottom
;
134 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow1
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
135 child
.left
= nSplitterPos
+ (splitter_border
/ 2);
136 child
.right
= clientrect
->right
;
137 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow2
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
141 // three image windows
143 child
.left
= clientrect
->left
;
144 child
.top
= clientrect
->top
+tbHeight
;
145 child
.right
= nSplitterPos
- (splitter_border
/ 2);
146 child
.bottom
= clientrect
->bottom
;
147 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow1
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
148 child
.left
= nSplitterPos
+ (splitter_border
/ 2);
149 child
.right
= nSplitterPos2
- (splitter_border
/ 2);
150 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow2
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
151 child
.left
= nSplitterPos2
+ (splitter_border
/ 2);
152 child
.right
= clientrect
->right
;
153 if (hdwp
) hdwp
= DeferWindowPos(hdwp
, picWindow3
, nullptr, child
.left
, child
.top
, child
.right
- child
.left
, child
.bottom
- child
.top
, SWP_FRAMECHANGED
| SWP_SHOWWINDOW
);
157 if (hdwp
) EndDeferWindowPos(hdwp
);
158 picWindow1
.SetTransparentColor(transparentColor
);
159 picWindow2
.SetTransparentColor(transparentColor
);
160 picWindow3
.SetTransparentColor(transparentColor
);
161 InvalidateRect(*this, nullptr, FALSE
);
164 LRESULT CALLBACK
CMainWindow::WinMsgHandler(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
166 if (uMsg
== TaskBarButtonCreated
)
168 SetUUIDOverlayIcon(hwnd
);
170 auto optRet
= CTheme::HandleMenuBar(hwnd
, uMsg
, wParam
, lParam
);
171 if (optRet
.has_value())
172 return optRet
.value();
178 picWindow1
.RegisterAndCreateWindow(hwnd
);
179 picWindow2
.RegisterAndCreateWindow(hwnd
);
180 if (selectionPaths
.empty())
182 picWindow1
.SetPic(leftpicpath
, leftpictitle
, true);
183 picWindow2
.SetPic(rightpicpath
, rightpictitle
, false);
185 picWindow1
.SetOtherPicWindow(&picWindow2
);
186 picWindow2
.SetOtherPicWindow(&picWindow1
);
190 picWindow3
.RegisterAndCreateWindow(hwnd
);
192 picWindow1
.SetPic(selectionPaths
[FileTypeMine
], selectionTitles
[FileTypeMine
], false);
193 picWindow2
.SetPic(selectionPaths
[FileTypeBase
], selectionTitles
[FileTypeBase
], false);
194 picWindow3
.SetPic(selectionPaths
[FileTypeTheirs
], selectionTitles
[FileTypeTheirs
], false);
197 picWindow1
.SetSelectionMode(!selectionPaths
.empty());
198 picWindow2
.SetSelectionMode(!selectionPaths
.empty());
199 picWindow3
.SetSelectionMode(!selectionPaths
.empty());
202 // center the splitter
204 GetClientRect(hwnd
, &rect
);
205 if (selectionPaths
.size() != 3)
207 nSplitterPos
= (rect
.right
-rect
.left
)/2;
212 nSplitterPos
= (rect
.right
-rect
.left
)/3;
213 nSplitterPos2
= (rect
.right
-rect
.left
)*2/3;
216 PositionChildren(&rect
);
217 picWindow1
.FitImageInWindow();
218 picWindow2
.FitImageInWindow();
219 picWindow3
.FitImageInWindow();
221 m_themeCallbackId
= CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme()); });
222 SetTheme(CTheme::Instance().IsDarkTheme());
227 return DoCommand(LOWORD(wParam
), lParam
);
236 ::GetClientRect(*this, &rect
);
237 hdc
= BeginPaint(hwnd
, &ps
);
238 SetBkColor(hdc
, GetSysColor(COLOR_3DFACE
));
239 ::ExtTextOut(hdc
, 0, 0, ETO_OPAQUE
, &rect
, nullptr, 0, nullptr);
243 case WM_GETMINMAXINFO
:
245 auto mmi
= reinterpret_cast<MINMAXINFO
*>(lParam
);
246 mmi
->ptMinTrackSize
.x
= WINDOW_MINWIDTH
;
247 mmi
->ptMinTrackSize
.y
= WINDOW_MINHEIGHT
;
254 GetClientRect(hwnd
, &rect
);
258 GetWindowRect(hwndTB
, &tbRect
);
259 LONG tbHeight
= tbRect
.bottom
-tbRect
.top
-1;
260 if (selectionPaths
.size() != 3)
262 nSplitterPos
= (rect
.bottom
-rect
.top
)/2+tbHeight
;
267 nSplitterPos
= (rect
.bottom
-rect
.top
)/3+tbHeight
;
268 nSplitterPos2
= (rect
.bottom
-rect
.top
)*2/3+tbHeight
;
273 if (selectionPaths
.size() != 3)
275 nSplitterPos
= (rect
.right
-rect
.left
)/2;
280 nSplitterPos
= (rect
.right
-rect
.left
)/3;
281 nSplitterPos2
= (rect
.right
-rect
.left
)*2/3;
284 PositionChildren(&rect
);
289 if (reinterpret_cast<HWND
>(wParam
) == *this)
293 GetClientRect(*this, &rect
);
295 ScreenToClient(*this, &pt
);
296 if (PtInRect(&rect
, pt
))
300 HCURSOR hCur
= LoadCursor(nullptr, IDC_SIZENS
);
305 HCURSOR hCur
= LoadCursor(nullptr, IDC_SIZEWE
);
311 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
315 Splitter_OnLButtonDown(hwnd
, uMsg
, wParam
, lParam
);
318 Splitter_OnLButtonUp(hwnd
, uMsg
, wParam
, lParam
);
320 case WM_CAPTURECHANGED
:
321 Splitter_CaptureChanged();
324 Splitter_OnMouseMove(hwnd
, uMsg
, wParam
, lParam
);
328 // find out if the mouse cursor is over one of the views, and if
329 // it is, pass the mouse wheel message to that view
331 DWORD ptW
= GetMessagePos();
332 pt
.x
= GET_X_LPARAM(ptW
);
333 pt
.y
= GET_Y_LPARAM(ptW
);
335 GetWindowRect(picWindow1
, &rect
);
336 if (PtInRect(&rect
, pt
))
338 picWindow1
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
342 GetWindowRect(picWindow2
, &rect
);
343 if (PtInRect(&rect
, pt
))
345 picWindow2
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
349 GetWindowRect(picWindow3
, &rect
);
350 if (PtInRect(&rect
, pt
))
352 picWindow3
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
), GET_WHEEL_DELTA_WPARAM(wParam
));
360 // find out if the mouse cursor is over one of the views, and if
361 // it is, pass the mouse wheel message to that view
363 DWORD ptW
= GetMessagePos();
364 pt
.x
= GET_X_LPARAM(ptW
);
365 pt
.y
= GET_Y_LPARAM(ptW
);
367 GetWindowRect(picWindow1
, &rect
);
368 if (PtInRect(&rect
, pt
))
370 picWindow1
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
374 GetWindowRect(picWindow2
, &rect
);
375 if (PtInRect(&rect
, pt
))
377 picWindow2
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
381 GetWindowRect(picWindow3
, &rect
);
382 if (PtInRect(&rect
, pt
))
384 picWindow3
.OnMouseWheel(GET_KEYSTATE_WPARAM(wParam
)|MK_SHIFT
, GET_WHEEL_DELTA_WPARAM(wParam
));
392 auto pNMHDR
= reinterpret_cast<LPNMHDR
>(lParam
);
393 if (pNMHDR
->code
== TTN_GETDISPINFO
)
395 auto lpttt
= reinterpret_cast<LPTOOLTIPTEXT
>(lParam
);
396 lpttt
->hinst
= hResource
;
398 // Specify the resource identifier of the descriptive
399 // text for the given button.
400 wchar_t stringbuf
[MAX_PATH
] = { 0 };
402 mii
.cbSize
= sizeof(MENUITEMINFO
);
403 mii
.fMask
= MIIM_TYPE
;
404 mii
.dwTypeData
= stringbuf
;
405 mii
.cch
= _countof(stringbuf
);
406 GetMenuItemInfo(GetMenu(*this), static_cast<UINT
>(lpttt
->hdr
.idFrom
), FALSE
, &mii
);
407 wcscpy_s(lpttt
->lpszText
, 80, stringbuf
);
412 bWindowClosed
= TRUE
;
416 CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId
);
417 m_themeCallbackId
= 0;
418 ImageList_Destroy(hToolbarImgList
);
419 ::DestroyWindow(m_hwnd
);
421 case WM_SYSCOLORCHANGE
:
422 CTheme::Instance().OnSysColorChanged();
423 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme(), true);
426 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
432 LRESULT
CMainWindow::DoCommand(int id
, LPARAM lParam
)
440 picWindow1
.SetPic(leftpicpath
, L
"", true);
441 picWindow2
.SetPic(rightpicpath
, L
"", false);
444 picWindow1
.SetSecondPic(picWindow2
.GetPic(), rightpictitle
, rightpicpath
);
448 picWindow1
.SetSecondPic();
451 GetClientRect(*this, &rect
);
452 PositionChildren(&rect
);
453 picWindow1
.FitImageInWindow();
454 picWindow2
.FitImageInWindow();
458 case ID_VIEW_IMAGEINFO
:
460 bShowInfo
= !bShowInfo
;
461 HMENU hMenu
= GetMenu(*this);
462 UINT uCheck
= MF_BYCOMMAND
;
463 uCheck
|= bShowInfo
? MF_CHECKED
: MF_UNCHECKED
;
464 CheckMenuItem(hMenu
, ID_VIEW_IMAGEINFO
, uCheck
);
466 picWindow1
.ShowInfo(bShowInfo
);
467 picWindow2
.ShowInfo(bShowInfo
);
468 picWindow3
.ShowInfo(bShowInfo
);
470 // change the state of the toolbar button
472 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
473 tbi
.dwMask
= TBIF_STATE
;
474 tbi
.fsState
= bShowInfo
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
475 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_IMAGEINFO
, reinterpret_cast<LPARAM
>(&tbi
));
478 case ID_VIEW_OVERLAPIMAGES
:
480 bOverlap
= !bOverlap
;
481 HMENU hMenu
= GetMenu(*this);
482 UINT uCheck
= MF_BYCOMMAND
;
483 uCheck
|= bOverlap
? MF_CHECKED
: MF_UNCHECKED
;
484 CheckMenuItem(hMenu
, ID_VIEW_OVERLAPIMAGES
, uCheck
);
485 uCheck
|= ((m_BlendType
== CPicWindow::BLEND_ALPHA
) && bOverlap
) ? MF_CHECKED
: MF_UNCHECKED
;
486 CheckMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uCheck
);
487 UINT uEnabled
= MF_BYCOMMAND
;
488 uEnabled
|= bOverlap
? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
;
489 EnableMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uEnabled
);
491 // change the state of the toolbar button
493 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
494 tbi
.dwMask
= TBIF_STATE
;
495 tbi
.fsState
= bOverlap
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
496 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_OVERLAPIMAGES
, reinterpret_cast<LPARAM
>(&tbi
));
498 tbi
.fsState
= ((m_BlendType
== CPicWindow::BLEND_ALPHA
) && bOverlap
) ? TBSTATE_CHECKED
: 0;
500 tbi
.fsState
|= TBSTATE_ENABLED
;
503 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_BLENDALPHA
, reinterpret_cast<LPARAM
>(&tbi
));
508 tbi
.fsState
= bVertical
? TBSTATE_ENABLED
| TBSTATE_CHECKED
: TBSTATE_ENABLED
;
509 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_ARRANGEVERTICAL
, reinterpret_cast<LPARAM
>(&tbi
));
513 bLinkedPositions
= true;
514 picWindow1
.LinkPositions(bLinkedPositions
);
515 picWindow2
.LinkPositions(bLinkedPositions
);
516 tbi
.fsState
= TBSTATE_CHECKED
;
519 tbi
.fsState
= bLinkedPositions
? TBSTATE_ENABLED
| TBSTATE_CHECKED
: TBSTATE_ENABLED
;
520 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_LINKIMAGESTOGETHER
, reinterpret_cast<LPARAM
>(&tbi
));
522 ShowWindow(picWindow2
, bOverlap
? SW_HIDE
: SW_SHOW
);
526 picWindow1
.StopTimer();
527 picWindow2
.StopTimer();
528 picWindow1
.SetSecondPic(picWindow2
.GetPic(), rightpictitle
, rightpicpath
,
529 picWindow2
.GetHPos(), picWindow2
.GetVPos());
530 picWindow1
.SetBlendAlpha(m_BlendType
, 0.5f
);
534 picWindow1
.SetSecondPic();
536 picWindow1
.SetOverlapMode(bOverlap
);
537 picWindow2
.SetOverlapMode(bOverlap
);
541 GetClientRect(*this, &rect
);
542 PositionChildren(&rect
);
547 case ID_VIEW_BLENDALPHA
:
549 if (m_BlendType
== CPicWindow::BLEND_ALPHA
)
550 m_BlendType
= CPicWindow::BLEND_XOR
;
552 m_BlendType
= CPicWindow::BLEND_ALPHA
;
554 HMENU hMenu
= GetMenu(*this);
555 UINT uCheck
= MF_BYCOMMAND
;
556 uCheck
|= ((m_BlendType
== CPicWindow::BLEND_ALPHA
) && bOverlap
) ? MF_CHECKED
: MF_UNCHECKED
;
557 CheckMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uCheck
);
558 UINT uEnabled
= MF_BYCOMMAND
;
559 uEnabled
|= bOverlap
? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
;
560 EnableMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uEnabled
);
562 // change the state of the toolbar button
564 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
565 tbi
.dwMask
= TBIF_STATE
;
566 tbi
.fsState
= ((m_BlendType
== CPicWindow::BLEND_ALPHA
) && bOverlap
) ? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
567 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_BLENDALPHA
, reinterpret_cast<LPARAM
>(&tbi
));
568 picWindow1
.SetBlendAlpha(m_BlendType
, picWindow1
.GetBlendAlpha());
572 case ID_VIEW_TRANSPARENTCOLOR
:
574 static COLORREF customColors
[16] = {0};
575 CHOOSECOLOR ccDlg
= { 0 };
576 ccDlg
.lStructSize
= sizeof(ccDlg
);
577 ccDlg
.hwndOwner
= m_hwnd
;
578 ccDlg
.rgbResult
= transparentColor
;
579 ccDlg
.lpCustColors
= customColors
;
580 ccDlg
.Flags
= CC_RGBINIT
| CC_FULLOPEN
;
581 if(ChooseColor(&ccDlg
))
583 transparentColor
= ccDlg
.rgbResult
;
584 picWindow1
.SetTransparentColor(transparentColor
);
585 picWindow2
.SetTransparentColor(transparentColor
);
586 picWindow3
.SetTransparentColor(transparentColor
);
587 // The color picker takes the focus and we don't get it back.
588 ::SetFocus(picWindow1
);
592 case ID_VIEW_FITIMAGEWIDTHS
:
594 bFitWidths
= !bFitWidths
;
595 picWindow1
.FitWidths(bFitWidths
);
596 picWindow2
.FitWidths(bFitWidths
);
597 picWindow3
.FitWidths(bFitWidths
);
599 HMENU hMenu
= GetMenu(*this);
600 UINT uCheck
= MF_BYCOMMAND
;
601 uCheck
|= bFitWidths
? MF_CHECKED
: MF_UNCHECKED
;
602 CheckMenuItem(hMenu
, ID_VIEW_FITIMAGEWIDTHS
, uCheck
);
604 // change the state of the toolbar button
606 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
607 tbi
.dwMask
= TBIF_STATE
;
608 tbi
.fsState
= bFitWidths
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
609 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_FITIMAGEWIDTHS
, reinterpret_cast<LPARAM
>(&tbi
));
612 case ID_VIEW_FITIMAGEHEIGHTS
:
614 bFitHeights
= !bFitHeights
;
615 picWindow1
.FitHeights(bFitHeights
);
616 picWindow2
.FitHeights(bFitHeights
);
617 picWindow3
.FitHeights(bFitHeights
);
619 HMENU hMenu
= GetMenu(*this);
620 UINT uCheck
= MF_BYCOMMAND
;
621 uCheck
|= bFitHeights
? MF_CHECKED
: MF_UNCHECKED
;
622 CheckMenuItem(hMenu
, ID_VIEW_FITIMAGEHEIGHTS
, uCheck
);
624 // change the state of the toolbar button
626 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
627 tbi
.dwMask
= TBIF_STATE
;
628 tbi
.fsState
= bFitHeights
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
629 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_FITIMAGEHEIGHTS
, reinterpret_cast<LPARAM
>(&tbi
));
632 case ID_VIEW_LINKIMAGESTOGETHER
:
634 bLinkedPositions
= !bLinkedPositions
;
635 picWindow1
.LinkPositions(bLinkedPositions
);
636 picWindow2
.LinkPositions(bLinkedPositions
);
637 picWindow3
.LinkPositions(bLinkedPositions
);
639 HMENU hMenu
= GetMenu(*this);
640 UINT uCheck
= MF_BYCOMMAND
;
641 uCheck
|= bLinkedPositions
? MF_CHECKED
: MF_UNCHECKED
;
642 CheckMenuItem(hMenu
, ID_VIEW_LINKIMAGESTOGETHER
, uCheck
);
644 // change the state of the toolbar button
646 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
647 tbi
.dwMask
= TBIF_STATE
;
648 tbi
.fsState
= bLinkedPositions
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
649 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_LINKIMAGESTOGETHER
, reinterpret_cast<LPARAM
>(&tbi
));
653 picWindow1
.SetBlendAlpha(m_BlendType
, 0.0f
);
655 case ID_VIEW_ALPHA255
:
656 picWindow1
.SetBlendAlpha(m_BlendType
, 1.0f
);
658 case ID_VIEW_ALPHA127
:
659 picWindow1
.SetBlendAlpha(m_BlendType
, 0.5f
);
661 case ID_VIEW_ALPHATOGGLE
:
662 picWindow1
.ToggleAlpha();
664 case ID_VIEW_FITIMAGESINWINDOW
:
666 picWindow1
.FitImageInWindow();
667 picWindow2
.FitImageInWindow();
668 picWindow3
.FitImageInWindow();
671 case ID_VIEW_ORININALSIZE
:
673 picWindow1
.SetZoom(100, false);
674 picWindow2
.SetZoom(100, false);
675 picWindow3
.SetZoom(100, false);
676 picWindow1
.CenterImage();
677 picWindow2
.CenterImage();
678 picWindow3
.CenterImage();
683 picWindow1
.Zoom(true, false);
684 if ((!(bFitWidths
|| bFitHeights
))&&(!bOverlap
))
686 picWindow2
.Zoom(true, false);
687 picWindow3
.Zoom(true, false);
691 case ID_VIEW_ZOOMOUT
:
693 picWindow1
.Zoom(false, false);
694 if ((!(bFitWidths
|| bFitHeights
))&&(!bOverlap
))
696 picWindow2
.Zoom(false, false);
697 picWindow3
.Zoom(false, false);
701 case ID_VIEW_ARRANGEVERTICAL
:
703 bVertical
= !bVertical
;
705 GetClientRect(*this, &rect
);
709 GetWindowRect(hwndTB
, &tbRect
);
710 LONG tbHeight
= tbRect
.bottom
-tbRect
.top
-1;
711 if (selectionPaths
.size() != 3)
713 nSplitterPos
= (rect
.bottom
-rect
.top
)/2+tbHeight
;
718 nSplitterPos
= (rect
.bottom
-rect
.top
)/3+tbHeight
;
719 nSplitterPos2
= (rect
.bottom
-rect
.top
)*2/3+tbHeight
;
724 if (selectionPaths
.size() != 3)
726 nSplitterPos
= (rect
.right
-rect
.left
)/2;
731 nSplitterPos
= (rect
.right
-rect
.left
)/3;
732 nSplitterPos2
= (rect
.right
-rect
.left
)*2/3;
735 HMENU hMenu
= GetMenu(*this);
736 UINT uCheck
= MF_BYCOMMAND
;
737 uCheck
|= bVertical
? MF_CHECKED
: MF_UNCHECKED
;
738 CheckMenuItem(hMenu
, ID_VIEW_ARRANGEVERTICAL
, uCheck
);
739 // change the state of the toolbar button
741 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
742 tbi
.dwMask
= TBIF_STATE
;
743 tbi
.fsState
= bVertical
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
744 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_ARRANGEVERTICAL
, reinterpret_cast<LPARAM
>(&tbi
));
746 PositionChildren(&rect
);
751 CAboutDlg
dlg(*this);
752 dlg
.DoModal(hInst
, IDD_ABOUT
, *this);
755 case SELECTBUTTON_ID
:
757 auto hSource
= reinterpret_cast<HWND
>(lParam
);
758 FileType resolveWith
;
759 if (picWindow1
== hSource
)
760 resolveWith
= FileTypeMine
;
761 else if (picWindow2
== hSource
)
762 resolveWith
= FileTypeBase
;
763 else if (picWindow3
== hSource
)
764 resolveWith
= FileTypeTheirs
;
768 if (selectionResult
.empty())
770 PostQuitMessage(resolveWith
);
774 CopyFile(selectionPaths
[resolveWith
].c_str(), selectionResult
.c_str(), FALSE
);
776 CAutoBuf projectRoot
;
777 if (git_repository_discover(projectRoot
, CUnicodeUtils::GetUTF8(selectionResult
.c_str()), FALSE
, nullptr) < 0 && strstr(projectRoot
->ptr
, "/.git/"))
779 PostQuitMessage(resolveWith
);
783 CAutoRepository
repository(projectRoot
->ptr
);
786 PostQuitMessage(resolveWith
);
790 CStringA subpath
= CUnicodeUtils::GetUTF8(selectionResult
.c_str()).Mid(static_cast<int>(strlen(git_repository_workdir(repository
))));
793 if (git_repository_index(index
.GetPointer(), repository
) || (git_index_get_bypath(index
, subpath
, 1) == nullptr && git_index_get_bypath(index
, subpath
, 2) == nullptr))
795 PostQuitMessage(resolveWith
);
800 sTemp
.Format(ResString(hResource
, IDS_MARKASRESOLVED
), static_cast<LPCWSTR
>(CPathUtils::GetFileNameFromPath(selectionResult
.c_str())));
801 if (MessageBox(m_hwnd
, sTemp
, L
"TortoiseGitMerge", MB_YESNO
| MB_ICONQUESTION
) != IDYES
)
805 cmd
.Format(L
"\"%sTortoiseGitProc.exe\" /command:resolve /path:\"%s\" /closeonend:1 /noquestion /skipcheck /silent", static_cast<LPCWSTR
>(CPathUtils::GetAppDirectory()), selectionResult
.c_str());
807 cmd
.AppendFormat(L
" /resolvemsghwnd:%I64d /resolvemsgwparam:%I64d /resolvemsglparam:%I64d", reinterpret_cast<__int64
>(resolveMsgWnd
), static_cast<__int64
>(resolveMsgWParam
), static_cast<__int64
>(resolveMsgLParam
));
809 STARTUPINFO startup
= { 0 };
810 PROCESS_INFORMATION process
= { 0 };
811 startup
.cb
= sizeof(startup
);
813 if (!CreateProcess(nullptr, cmd
.GetBuffer(), nullptr, nullptr, FALSE
, CREATE_UNICODE_ENVIRONMENT
, nullptr, nullptr, &startup
, &process
))
816 PostQuitMessage(resolveWith
);
821 AllowSetForegroundWindow(process
.dwProcessId
);
823 CloseHandle(process
.hThread
);
824 CloseHandle(process
.hProcess
);
826 PostQuitMessage(resolveWith
);
829 case ID_VIEW_DARKMODE
:
830 CTheme::Instance().SetDarkTheme(!CTheme::Instance().IsDarkTheme());
833 ::PostQuitMessage(0);
843 void CMainWindow::DrawXorBar(HDC hdc
, int x1
, int y1
, int width
, int height
)
845 static WORD _dotPatternBmp
[8] =
847 0x0055, 0x00aa, 0x0055, 0x00aa,
848 0x0055, 0x00aa, 0x0055, 0x00aa
852 HBRUSH hbr
, hbrushOld
;
854 hbm
= CreateBitmap(8, 8, 1, 1, _dotPatternBmp
);
855 hbr
= CreatePatternBrush(hbm
);
857 SetBrushOrgEx(hdc
, x1
, y1
, 0);
858 hbrushOld
= static_cast<HBRUSH
>(SelectObject(hdc
, hbr
));
860 PatBlt(hdc
, x1
, y1
, width
, height
, PATINVERT
);
862 SelectObject(hdc
, hbrushOld
);
868 LRESULT
CMainWindow::Splitter_OnLButtonDown(HWND hwnd
, UINT
/*iMsg*/, WPARAM
/*wParam*/, LPARAM lParam
)
875 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
876 pt
.y
= static_cast<short>(HIWORD(lParam
));
878 GetClientRect(hwnd
, &clientrect
);
879 GetWindowRect(hwnd
, &rect
);
881 ClientToScreen(hwnd
, &zero
);
882 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
884 ClientToScreen(hwnd
, &pt
);
885 // find out which drag bar is used
887 if (!selectionPaths
.empty())
890 GetWindowRect(picWindow2
, &pic2Rect
);
893 if (pic2Rect
.bottom
<= pt
.y
)
898 if (pic2Rect
.right
<= pt
.x
)
903 //convert the mouse coordinates relative to the top-left of
908 //same for the window coordinates - make them relative to 0,0
909 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
913 if (pt
.x
> rect
.right
-4)
917 if (pt
.y
> rect
.bottom
-4)
918 pt
.y
= rect
.bottom
-4;
924 hdc
= GetWindowDC(hwnd
);
926 DrawXorBar(hdc
, clientrect
.left
, pt
.y
+2, clientrect
.right
-clientrect
.left
-2, 4);
928 DrawXorBar(hdc
, pt
.x
+2, clientrect
.top
, 4, clientrect
.bottom
-clientrect
.top
-2);
929 ReleaseDC(hwnd
, hdc
);
937 void CMainWindow::Splitter_CaptureChanged()
942 void CMainWindow::SetTheme(bool bDark
)
944 transparentColor
= ::GetSysColor(COLOR_WINDOW
);
945 picWindow1
.SetTransparentColor(transparentColor
);
946 picWindow2
.SetTransparentColor(transparentColor
);
947 picWindow3
.SetTransparentColor(transparentColor
);
950 DarkModeHelper::Instance().AllowDarkModeForApp(TRUE
);
952 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, TRUE
);
953 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetStockObject(BLACK_BRUSH
)));
954 if (FAILED(SetWindowTheme(*this, L
"DarkMode_Explorer", nullptr)))
955 SetWindowTheme(*this, L
"Explorer", nullptr);
956 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, TRUE
);
957 if (FAILED(SetWindowTheme(hwndTB
, L
"DarkMode_Explorer", nullptr)))
958 SetWindowTheme(hwndTB
, L
"Explorer", nullptr);
959 BOOL darkFlag
= TRUE
;
960 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
961 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
962 DarkModeHelper::Instance().FlushMenuThemes();
963 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
967 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE
);
968 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, FALSE
);
969 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, FALSE
);
970 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetSysColorBrush(COLOR_3DFACE
)));
971 SetWindowTheme(*this, L
"Explorer", nullptr);
972 SetWindowTheme(hwndTB
, L
"Explorer", nullptr);
973 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, FALSE
);
974 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, FALSE
);
975 BOOL darkFlag
= FALSE
;
976 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
977 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
978 DarkModeHelper::Instance().FlushMenuThemes();
979 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
980 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE
);
982 DarkModeHelper::Instance().RefreshTitleBarThemeColor(*this, bDark
);
984 HMENU hMenu
= GetMenu(*this);
985 UINT uCheck
= MF_BYCOMMAND
;
986 uCheck
|= CTheme::Instance().IsDarkTheme() ? MF_CHECKED
: MF_UNCHECKED
;
987 CheckMenuItem(hMenu
, ID_VIEW_DARKMODE
, uCheck
);
988 UINT uEnabled
= MF_BYCOMMAND
;
989 uEnabled
|= CTheme::Instance().IsDarkModeAllowed() ? MF_ENABLED
: MF_DISABLED
;
990 EnableMenuItem(hMenu
, ID_VIEW_DARKMODE
, uEnabled
);
992 ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME
| RDW_INVALIDATE
| RDW_ERASE
| RDW_INTERNALPAINT
| RDW_ALLCHILDREN
| RDW_UPDATENOW
);
995 LRESULT
CMainWindow::Splitter_OnLButtonUp(HWND hwnd
, UINT
/*iMsg*/, WPARAM
/*wParam*/, LPARAM lParam
)
1002 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
1003 pt
.y
= static_cast<short>(HIWORD(lParam
));
1005 if (bDragMode
== FALSE
)
1008 const auto bordersm
= CDPIAware::Instance().ScaleX(2);
1009 const auto borderl
= CDPIAware::Instance().ScaleY(4);
1011 GetClientRect(hwnd
, &clientrect
);
1012 GetWindowRect(hwnd
, &rect
);
1014 ClientToScreen(hwnd
, &zero
);
1015 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
1017 ClientToScreen(hwnd
, &pt
);
1021 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1025 if (pt
.x
> rect
.right
- borderl
)
1026 pt
.x
= rect
.right
- borderl
;
1029 if (pt
.y
> rect
.bottom
- borderl
)
1030 pt
.y
= rect
.bottom
- borderl
;
1032 hdc
= GetWindowDC(hwnd
);
1034 DrawXorBar(hdc
, clientrect
.left
, oldy
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1036 DrawXorBar(hdc
, oldx
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1037 ReleaseDC(hwnd
, hdc
);
1044 //convert the splitter position back to screen coords.
1045 GetWindowRect(hwnd
, &rect
);
1049 //now convert into CLIENT coordinates
1050 ScreenToClient(hwnd
, &pt
);
1051 GetClientRect(hwnd
, &rect
);
1052 #define MINWINSIZE 10
1055 if (selectionPaths
.size() != 3)
1057 nSplitterPos
= pt
.y
;
1063 if (pt
.y
< (nSplitterPos
+MINWINSIZE
))
1064 pt
.y
= nSplitterPos
+MINWINSIZE
;
1065 nSplitterPos2
= pt
.y
;
1069 if (pt
.y
> (nSplitterPos2
-MINWINSIZE
))
1070 pt
.y
= nSplitterPos2
-MINWINSIZE
;
1071 nSplitterPos
= pt
.y
;
1077 if (selectionPaths
.size() != 3)
1079 nSplitterPos
= pt
.x
;
1085 if (pt
.x
< (nSplitterPos
+MINWINSIZE
))
1086 pt
.x
= nSplitterPos
+MINWINSIZE
;
1087 nSplitterPos2
= pt
.x
;
1091 if (pt
.x
> (nSplitterPos2
-MINWINSIZE
))
1092 pt
.x
= nSplitterPos2
-MINWINSIZE
;
1093 nSplitterPos
= pt
.x
;
1100 //position the child controls
1101 PositionChildren(&rect
);
1105 LRESULT
CMainWindow::Splitter_OnMouseMove(HWND hwnd
, UINT
/*iMsg*/, WPARAM wParam
, LPARAM lParam
)
1112 if (bDragMode
== FALSE
)
1115 const auto bordersm
= CDPIAware::Instance().ScaleX(2);
1116 const auto borderl
= CDPIAware::Instance().ScaleY(4);
1118 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
1119 pt
.y
= static_cast<short>(HIWORD(lParam
));
1121 GetClientRect(hwnd
, &clientrect
);
1122 GetWindowRect(hwnd
, &rect
);
1124 ClientToScreen(hwnd
, &zero
);
1125 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
1127 //convert the mouse coordinates relative to the top-left of
1129 ClientToScreen(hwnd
, &pt
);
1133 //same for the window coordinates - make them relative to 0,0
1134 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1138 if (pt
.x
> rect
.right
- borderl
)
1139 pt
.x
= rect
.right
- borderl
;
1142 if (pt
.y
> rect
.bottom
- borderl
)
1143 pt
.y
= rect
.bottom
- borderl
;
1145 if ((wParam
& MK_LBUTTON
) && ((bVertical
&& (pt
.y
!= oldy
)) || (!bVertical
&& (pt
.x
!= oldx
))))
1147 HDC hdc
= GetWindowDC(hwnd
);
1151 DrawXorBar(hdc
, clientrect
.left
, oldy
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1152 DrawXorBar(hdc
, clientrect
.left
, pt
.y
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1156 DrawXorBar(hdc
, oldx
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1157 DrawXorBar(hdc
, pt
.x
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1160 ReleaseDC(hwnd
, hdc
);
1169 bool CMainWindow::OpenDialog()
1171 return (DialogBox(hResource
, MAKEINTRESOURCE(IDD_OPEN
), *this, reinterpret_cast<DLGPROC
>(OpenDlgProc
)) == IDOK
);
1174 BOOL CALLBACK
CMainWindow::OpenDlgProc(HWND hwndDlg
, UINT message
, WPARAM wParam
, LPARAM
/*lParam*/)
1180 CTheme::Instance().SetThemeForDialog(hwndDlg
, CTheme::Instance().IsDarkTheme());
1181 // center on the parent window
1182 HWND hParentWnd
= ::GetParent(hwndDlg
);
1183 RECT parentrect
, childrect
, centeredrect
;
1184 GetWindowRect(hParentWnd
, &parentrect
);
1185 GetWindowRect(hwndDlg
, &childrect
);
1186 centeredrect
.left
= parentrect
.left
+ ((parentrect
.right
-parentrect
.left
-childrect
.right
+childrect
.left
)/2);
1187 centeredrect
.right
= centeredrect
.left
+ (childrect
.right
-childrect
.left
);
1188 centeredrect
.top
= parentrect
.top
+ ((parentrect
.bottom
-parentrect
.top
-childrect
.bottom
+childrect
.top
)/2);
1189 centeredrect
.bottom
= centeredrect
.top
+ (childrect
.bottom
-childrect
.top
);
1190 SetWindowPos(hwndDlg
, nullptr, centeredrect
.left
, centeredrect
.top
, centeredrect
.right
- centeredrect
.left
, centeredrect
.bottom
- centeredrect
.top
, SWP_SHOWWINDOW
);
1192 if (!leftpicpath
.empty())
1193 SetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, leftpicpath
.c_str());
1198 switch (LOWORD(wParam
))
1200 case IDC_LEFTBROWSE
:
1202 wchar_t path
[MAX_PATH
] = { 0 };
1203 if (AskForFile(hwndDlg
, path
))
1205 SetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, path
);
1209 case IDC_RIGHTBROWSE
:
1211 wchar_t path
[MAX_PATH
] = { 0 };
1212 if (AskForFile(hwndDlg
, path
))
1214 SetDlgItemText(hwndDlg
, IDC_RIGHTIMAGE
, path
);
1220 wchar_t path
[MAX_PATH
] = { 0 };
1221 if (!GetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, path
, _countof(path
)))
1224 if (!GetDlgItemText(hwndDlg
, IDC_RIGHTIMAGE
, path
, _countof(path
)))
1226 rightpicpath
= path
;
1230 CTheme::Instance().SetThemeForDialog(hwndDlg
, false);
1231 EndDialog(hwndDlg
, wParam
);
1238 bool CMainWindow::AskForFile(HWND owner
, wchar_t* path
)
1240 OPENFILENAME ofn
= {0}; // common dialog box structure
1241 // Initialize OPENFILENAME
1242 ofn
.lStructSize
= sizeof(OPENFILENAME
);
1243 ofn
.hwndOwner
= owner
;
1244 ofn
.lpstrFile
= path
;
1245 ofn
.nMaxFile
= MAX_PATH
;
1246 ResString
sTitle(::hResource
, IDS_OPENIMAGEFILE
);
1247 ofn
.lpstrTitle
= sTitle
;
1248 ofn
.Flags
= OFN_DONTADDTORECENT
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
;
1249 ofn
.hInstance
= ::hResource
;
1250 wchar_t filters
[] = L
"Images\0*.wmf;*.jpg;*jpeg;*.bmp;*.gif;*.png;*.ico;*.dib;*.emf;*.webp\0All (*.*)\0*.*\0\0";
1251 ofn
.lpstrFilter
= filters
;
1252 ofn
.nFilterIndex
= 1;
1253 // Display the Open dialog box.
1254 if (GetOpenFileName(&ofn
)==FALSE
)
1261 bool CMainWindow::CreateToolbar()
1263 // Ensure that the common control DLL is loaded.
1264 INITCOMMONCONTROLSEX icex
;
1265 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
1266 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_WIN95_CLASSES
;
1267 InitCommonControlsEx(&icex
);
1269 hwndTB
= CreateWindowEx(TBSTYLE_EX_DOUBLEBUFFER
,
1271 static_cast<LPCWSTR
>(nullptr),
1272 WS_CHILD
| WS_BORDER
| WS_VISIBLE
| TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
,
1275 reinterpret_cast<HMENU
>(IDC_TORTOISEIDIFF
),
1278 if (hwndTB
== INVALID_HANDLE_VALUE
)
1281 SendMessage(hwndTB
, TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0);
1284 // create an imagelist containing the icons for the toolbar
1285 auto imgSizeX
= CDPIAware::Instance().ScaleX(24);
1286 auto imgSizeY
= CDPIAware::Instance().ScaleY(24);
1287 hToolbarImgList
= ImageList_Create(imgSizeX
, imgSizeY
, ILC_COLOR32
| ILC_MASK
| ILC_HIGHQUALITYSCALE
, 12, 4);
1288 if (!hToolbarImgList
)
1291 HICON hIcon
= nullptr;
1292 if (selectionPaths
.empty())
1294 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_OVERLAP
), imgSizeX
, imgSizeY
);
1295 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1296 tbb
[index
].idCommand
= ID_VIEW_OVERLAPIMAGES
;
1297 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1298 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1299 tbb
[index
].dwData
= 0;
1300 tbb
[index
++].iString
= 0;
1302 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_BLEND
), imgSizeX
, imgSizeY
);
1303 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1304 tbb
[index
].idCommand
= ID_VIEW_BLENDALPHA
;
1305 tbb
[index
].fsState
= 0;
1306 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1307 tbb
[index
].dwData
= 0;
1308 tbb
[index
++].iString
= 0;
1310 tbb
[index
].iBitmap
= 0;
1311 tbb
[index
].idCommand
= 0;
1312 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1313 tbb
[index
].fsStyle
= BTNS_SEP
;
1314 tbb
[index
].dwData
= 0;
1315 tbb
[index
++].iString
= 0;
1317 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_LINK
), imgSizeX
, imgSizeY
);
1318 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1319 tbb
[index
].idCommand
= ID_VIEW_LINKIMAGESTOGETHER
;
1320 tbb
[index
].fsState
= TBSTATE_ENABLED
| TBSTATE_CHECKED
;
1321 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1322 tbb
[index
].dwData
= 0;
1323 tbb
[index
++].iString
= 0;
1325 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITWIDTHS
), imgSizeX
, imgSizeY
);
1326 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1327 tbb
[index
].idCommand
= ID_VIEW_FITIMAGEWIDTHS
;
1328 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1329 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1330 tbb
[index
].dwData
= 0;
1331 tbb
[index
++].iString
= 0;
1333 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITHEIGHTS
), imgSizeX
, imgSizeY
);
1334 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1335 tbb
[index
].idCommand
= ID_VIEW_FITIMAGEHEIGHTS
;
1336 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1337 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1338 tbb
[index
].dwData
= 0;
1339 tbb
[index
++].iString
= 0;
1341 tbb
[index
].iBitmap
= 0;
1342 tbb
[index
].idCommand
= 0;
1343 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1344 tbb
[index
].fsStyle
= BTNS_SEP
;
1345 tbb
[index
].dwData
= 0;
1346 tbb
[index
++].iString
= 0;
1349 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITINWINDOW
), imgSizeX
, imgSizeY
);
1350 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1351 tbb
[index
].idCommand
= ID_VIEW_FITIMAGESINWINDOW
;
1352 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1353 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1354 tbb
[index
].dwData
= 0;
1355 tbb
[index
++].iString
= 0;
1357 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ORIGSIZE
), imgSizeX
, imgSizeY
);
1358 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1359 tbb
[index
].idCommand
= ID_VIEW_ORININALSIZE
;
1360 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1361 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1362 tbb
[index
].dwData
= 0;
1363 tbb
[index
++].iString
= 0;
1365 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ZOOMIN
), imgSizeX
, imgSizeY
);
1366 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1367 tbb
[index
].idCommand
= ID_VIEW_ZOOMIN
;
1368 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1369 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1370 tbb
[index
].dwData
= 0;
1371 tbb
[index
++].iString
= 0;
1373 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ZOOMOUT
), imgSizeX
, imgSizeY
);
1374 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1375 tbb
[index
].idCommand
= ID_VIEW_ZOOMOUT
;
1376 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1377 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1378 tbb
[index
].dwData
= 0;
1379 tbb
[index
++].iString
= 0;
1381 tbb
[index
].iBitmap
= 0;
1382 tbb
[index
].idCommand
= 0;
1383 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1384 tbb
[index
].fsStyle
= BTNS_SEP
;
1385 tbb
[index
].dwData
= 0;
1386 tbb
[index
++].iString
= 0;
1388 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_IMGINFO
), imgSizeX
, imgSizeY
);
1389 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1390 tbb
[index
].idCommand
= ID_VIEW_IMAGEINFO
;
1391 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1392 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1393 tbb
[index
].dwData
= 0;
1394 tbb
[index
++].iString
= 0;
1396 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_VERTICAL
), imgSizeX
, imgSizeY
);
1397 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1398 tbb
[index
].idCommand
= ID_VIEW_ARRANGEVERTICAL
;
1399 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1400 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1401 tbb
[index
].dwData
= 0;
1402 tbb
[index
++].iString
= 0;
1404 SendMessage(hwndTB
, TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(hToolbarImgList
));
1405 SendMessage(hwndTB
, TB_ADDBUTTONS
, index
, reinterpret_cast<LPARAM
>(&tbb
));
1406 SendMessage(hwndTB
, TB_AUTOSIZE
, 0, 0);
1407 ShowWindow(hwndTB
, SW_SHOW
);
1411 void CMainWindow::SetSelectionImage( FileType ft
, const std::wstring
& path
, const std::wstring
& title
)
1413 selectionPaths
[ft
] = path
;
1414 selectionTitles
[ft
] = title
;