1
// TortoiseGitIDiff - an image diff viewer in TortoiseSVN
3 // Copyright (C) 2015-2019, 2023 - TortoiseGit
4 // Copyright (C) 2006-2015, 2018, 2020-2021, 2023 - 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(*this, 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
[FileType::Mine
], selectionTitles
[FileType::Mine
], false);
193 picWindow2
.SetPic(selectionPaths
[FileType::Base
], selectionTitles
[FileType::Base
], false);
194 picWindow3
.SetPic(selectionPaths
[FileType::Theirs
], selectionTitles
[FileType::Theirs
], 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 CDPIAware::Instance().Invalidate();
427 ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME
| RDW_INVALIDATE
| RDW_ERASE
| RDW_INTERNALPAINT
| RDW_ALLCHILDREN
| RDW_UPDATENOW
);
430 return DefWindowProc(hwnd
, uMsg
, wParam
, lParam
);
436 LRESULT
CMainWindow::DoCommand(int id
, LPARAM lParam
)
444 picWindow1
.SetPic(leftpicpath
, L
"", true);
445 picWindow2
.SetPic(rightpicpath
, L
"", false);
448 picWindow1
.SetSecondPic(picWindow2
.GetPic(), rightpictitle
, rightpicpath
);
452 picWindow1
.SetSecondPic();
455 GetClientRect(*this, &rect
);
456 PositionChildren(&rect
);
457 picWindow1
.FitImageInWindow();
458 picWindow2
.FitImageInWindow();
462 case ID_VIEW_IMAGEINFO
:
464 bShowInfo
= !bShowInfo
;
465 HMENU hMenu
= GetMenu(*this);
466 UINT uCheck
= MF_BYCOMMAND
;
467 uCheck
|= bShowInfo
? MF_CHECKED
: MF_UNCHECKED
;
468 CheckMenuItem(hMenu
, ID_VIEW_IMAGEINFO
, uCheck
);
470 picWindow1
.ShowInfo(bShowInfo
);
471 picWindow2
.ShowInfo(bShowInfo
);
472 picWindow3
.ShowInfo(bShowInfo
);
474 // change the state of the toolbar button
476 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
477 tbi
.dwMask
= TBIF_STATE
;
478 tbi
.fsState
= bShowInfo
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
479 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_IMAGEINFO
, reinterpret_cast<LPARAM
>(&tbi
));
482 case ID_VIEW_OVERLAPIMAGES
:
484 bOverlap
= !bOverlap
;
485 HMENU hMenu
= GetMenu(*this);
486 UINT uCheck
= MF_BYCOMMAND
;
487 uCheck
|= bOverlap
? MF_CHECKED
: MF_UNCHECKED
;
488 CheckMenuItem(hMenu
, ID_VIEW_OVERLAPIMAGES
, uCheck
);
489 uCheck
|= ((m_BlendType
== CPicWindow::BlendType::Alpha
) && bOverlap
) ? MF_CHECKED
: MF_UNCHECKED
;
490 CheckMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uCheck
);
491 UINT uEnabled
= MF_BYCOMMAND
;
492 uEnabled
|= bOverlap
? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
;
493 EnableMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uEnabled
);
495 // change the state of the toolbar button
497 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
498 tbi
.dwMask
= TBIF_STATE
;
499 tbi
.fsState
= bOverlap
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
500 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_OVERLAPIMAGES
, reinterpret_cast<LPARAM
>(&tbi
));
502 tbi
.fsState
= ((m_BlendType
== CPicWindow::BlendType::Alpha
) && bOverlap
) ? TBSTATE_CHECKED
: 0;
504 tbi
.fsState
|= TBSTATE_ENABLED
;
507 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_BLENDALPHA
, reinterpret_cast<LPARAM
>(&tbi
));
512 tbi
.fsState
= bVertical
? TBSTATE_ENABLED
| TBSTATE_CHECKED
: TBSTATE_ENABLED
;
513 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_ARRANGEVERTICAL
, reinterpret_cast<LPARAM
>(&tbi
));
517 bLinkedPositions
= true;
518 picWindow1
.LinkPositions(bLinkedPositions
);
519 picWindow2
.LinkPositions(bLinkedPositions
);
520 tbi
.fsState
= TBSTATE_CHECKED
;
523 tbi
.fsState
= bLinkedPositions
? TBSTATE_ENABLED
| TBSTATE_CHECKED
: TBSTATE_ENABLED
;
524 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_LINKIMAGESTOGETHER
, reinterpret_cast<LPARAM
>(&tbi
));
526 ShowWindow(picWindow2
, bOverlap
? SW_HIDE
: SW_SHOW
);
530 picWindow1
.StopTimer();
531 picWindow2
.StopTimer();
532 picWindow1
.SetSecondPic(picWindow2
.GetPic(), rightpictitle
, rightpicpath
,
533 picWindow2
.GetHPos(), picWindow2
.GetVPos());
534 picWindow1
.SetBlendAlpha(m_BlendType
, 0.5f
);
538 picWindow1
.SetSecondPic();
540 picWindow1
.SetOverlapMode(bOverlap
);
541 picWindow2
.SetOverlapMode(bOverlap
);
545 GetClientRect(*this, &rect
);
546 PositionChildren(&rect
);
551 case ID_VIEW_BLENDALPHA
:
553 if (m_BlendType
== CPicWindow::BlendType::Alpha
)
554 m_BlendType
= CPicWindow::BlendType::Xor
;
556 m_BlendType
= CPicWindow::BlendType::Alpha
;
558 HMENU hMenu
= GetMenu(*this);
559 UINT uCheck
= MF_BYCOMMAND
;
560 uCheck
|= ((m_BlendType
== CPicWindow::BlendType::Alpha
) && bOverlap
) ? MF_CHECKED
: MF_UNCHECKED
;
561 CheckMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uCheck
);
562 UINT uEnabled
= MF_BYCOMMAND
;
563 uEnabled
|= bOverlap
? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
;
564 EnableMenuItem(hMenu
, ID_VIEW_BLENDALPHA
, uEnabled
);
566 // change the state of the toolbar button
568 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
569 tbi
.dwMask
= TBIF_STATE
;
570 tbi
.fsState
= ((m_BlendType
== CPicWindow::BlendType::Alpha
) && bOverlap
) ? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
571 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_BLENDALPHA
, reinterpret_cast<LPARAM
>(&tbi
));
572 picWindow1
.SetBlendAlpha(m_BlendType
, picWindow1
.GetBlendAlpha());
576 case ID_VIEW_TRANSPARENTCOLOR
:
578 static COLORREF customColors
[16] = {0};
579 CHOOSECOLOR ccDlg
= { 0 };
580 ccDlg
.lStructSize
= sizeof(ccDlg
);
581 ccDlg
.hwndOwner
= m_hwnd
;
582 ccDlg
.rgbResult
= transparentColor
;
583 ccDlg
.lpCustColors
= customColors
;
584 ccDlg
.Flags
= CC_RGBINIT
| CC_FULLOPEN
;
585 if(ChooseColor(&ccDlg
))
587 transparentColor
= ccDlg
.rgbResult
;
588 picWindow1
.SetTransparentColor(transparentColor
);
589 picWindow2
.SetTransparentColor(transparentColor
);
590 picWindow3
.SetTransparentColor(transparentColor
);
591 // The color picker takes the focus and we don't get it back.
592 ::SetFocus(picWindow1
);
596 case ID_VIEW_FITIMAGEWIDTHS
:
598 bFitWidths
= !bFitWidths
;
599 picWindow1
.FitWidths(bFitWidths
);
600 picWindow2
.FitWidths(bFitWidths
);
601 picWindow3
.FitWidths(bFitWidths
);
603 HMENU hMenu
= GetMenu(*this);
604 UINT uCheck
= MF_BYCOMMAND
;
605 uCheck
|= bFitWidths
? MF_CHECKED
: MF_UNCHECKED
;
606 CheckMenuItem(hMenu
, ID_VIEW_FITIMAGEWIDTHS
, uCheck
);
608 // change the state of the toolbar button
610 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
611 tbi
.dwMask
= TBIF_STATE
;
612 tbi
.fsState
= bFitWidths
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
613 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_FITIMAGEWIDTHS
, reinterpret_cast<LPARAM
>(&tbi
));
616 case ID_VIEW_FITIMAGEHEIGHTS
:
618 bFitHeights
= !bFitHeights
;
619 picWindow1
.FitHeights(bFitHeights
);
620 picWindow2
.FitHeights(bFitHeights
);
621 picWindow3
.FitHeights(bFitHeights
);
623 HMENU hMenu
= GetMenu(*this);
624 UINT uCheck
= MF_BYCOMMAND
;
625 uCheck
|= bFitHeights
? MF_CHECKED
: MF_UNCHECKED
;
626 CheckMenuItem(hMenu
, ID_VIEW_FITIMAGEHEIGHTS
, uCheck
);
628 // change the state of the toolbar button
630 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
631 tbi
.dwMask
= TBIF_STATE
;
632 tbi
.fsState
= bFitHeights
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
633 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_FITIMAGEHEIGHTS
, reinterpret_cast<LPARAM
>(&tbi
));
636 case ID_VIEW_LINKIMAGESTOGETHER
:
638 bLinkedPositions
= !bLinkedPositions
;
639 picWindow1
.LinkPositions(bLinkedPositions
);
640 picWindow2
.LinkPositions(bLinkedPositions
);
641 picWindow3
.LinkPositions(bLinkedPositions
);
643 HMENU hMenu
= GetMenu(*this);
644 UINT uCheck
= MF_BYCOMMAND
;
645 uCheck
|= bLinkedPositions
? MF_CHECKED
: MF_UNCHECKED
;
646 CheckMenuItem(hMenu
, ID_VIEW_LINKIMAGESTOGETHER
, uCheck
);
648 // change the state of the toolbar button
650 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
651 tbi
.dwMask
= TBIF_STATE
;
652 tbi
.fsState
= bLinkedPositions
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
653 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_LINKIMAGESTOGETHER
, reinterpret_cast<LPARAM
>(&tbi
));
657 picWindow1
.SetBlendAlpha(m_BlendType
, 0.0f
);
659 case ID_VIEW_ALPHA255
:
660 picWindow1
.SetBlendAlpha(m_BlendType
, 1.0f
);
662 case ID_VIEW_ALPHA127
:
663 picWindow1
.SetBlendAlpha(m_BlendType
, 0.5f
);
665 case ID_VIEW_ALPHATOGGLE
:
666 picWindow1
.ToggleAlpha();
668 case ID_VIEW_FITIMAGESINWINDOW
:
670 picWindow1
.FitImageInWindow();
671 picWindow2
.FitImageInWindow();
672 picWindow3
.FitImageInWindow();
675 case ID_VIEW_ORININALSIZE
:
677 picWindow1
.SetZoom(100, false);
678 picWindow2
.SetZoom(100, false);
679 picWindow3
.SetZoom(100, false);
680 picWindow1
.CenterImage();
681 picWindow2
.CenterImage();
682 picWindow3
.CenterImage();
687 picWindow1
.Zoom(true, false);
688 if ((!(bFitWidths
|| bFitHeights
))&&(!bOverlap
))
690 picWindow2
.Zoom(true, false);
691 picWindow3
.Zoom(true, false);
695 case ID_VIEW_ZOOMOUT
:
697 picWindow1
.Zoom(false, false);
698 if ((!(bFitWidths
|| bFitHeights
))&&(!bOverlap
))
700 picWindow2
.Zoom(false, false);
701 picWindow3
.Zoom(false, false);
705 case ID_VIEW_ARRANGEVERTICAL
:
707 bVertical
= !bVertical
;
709 GetClientRect(*this, &rect
);
713 GetWindowRect(hwndTB
, &tbRect
);
714 LONG tbHeight
= tbRect
.bottom
-tbRect
.top
-1;
715 if (selectionPaths
.size() != 3)
717 nSplitterPos
= (rect
.bottom
-rect
.top
)/2+tbHeight
;
722 nSplitterPos
= (rect
.bottom
-rect
.top
)/3+tbHeight
;
723 nSplitterPos2
= (rect
.bottom
-rect
.top
)*2/3+tbHeight
;
728 if (selectionPaths
.size() != 3)
730 nSplitterPos
= (rect
.right
-rect
.left
)/2;
735 nSplitterPos
= (rect
.right
-rect
.left
)/3;
736 nSplitterPos2
= (rect
.right
-rect
.left
)*2/3;
739 HMENU hMenu
= GetMenu(*this);
740 UINT uCheck
= MF_BYCOMMAND
;
741 uCheck
|= bVertical
? MF_CHECKED
: MF_UNCHECKED
;
742 CheckMenuItem(hMenu
, ID_VIEW_ARRANGEVERTICAL
, uCheck
);
743 // change the state of the toolbar button
745 tbi
.cbSize
= sizeof(TBBUTTONINFO
);
746 tbi
.dwMask
= TBIF_STATE
;
747 tbi
.fsState
= bVertical
? TBSTATE_CHECKED
| TBSTATE_ENABLED
: TBSTATE_ENABLED
;
748 SendMessage(hwndTB
, TB_SETBUTTONINFO
, ID_VIEW_ARRANGEVERTICAL
, reinterpret_cast<LPARAM
>(&tbi
));
750 PositionChildren(&rect
);
755 CAboutDlg
dlg(*this);
756 dlg
.DoModal(hInst
, IDD_ABOUT
, *this);
759 case SELECTBUTTON_ID
:
761 auto hSource
= reinterpret_cast<HWND
>(lParam
);
762 FileType resolveWith
;
763 if (picWindow1
== hSource
)
764 resolveWith
= FileType::Mine
;
765 else if (picWindow2
== hSource
)
766 resolveWith
= FileType::Base
;
767 else if (picWindow3
== hSource
)
768 resolveWith
= FileType::Theirs
;
772 if (selectionResult
.empty())
774 PostQuitMessage(static_cast<int>(resolveWith
));
778 CopyFile(selectionPaths
[resolveWith
].c_str(), selectionResult
.c_str(), FALSE
);
780 CAutoBuf projectRoot
;
781 if (git_repository_discover(projectRoot
, CUnicodeUtils::GetUTF8(selectionResult
.c_str()), FALSE
, nullptr) < 0 && strstr(projectRoot
->ptr
, "/.git/"))
783 PostQuitMessage(static_cast<int>(resolveWith
));
787 CAutoRepository
repository(projectRoot
->ptr
);
790 PostQuitMessage(static_cast<int>(resolveWith
));
794 CStringA subpath
= CUnicodeUtils::GetUTF8(selectionResult
.c_str()).Mid(static_cast<int>(strlen(git_repository_workdir(repository
))));
797 if (git_repository_index(index
.GetPointer(), repository
) || (git_index_get_bypath(index
, subpath
, 1) == nullptr && git_index_get_bypath(index
, subpath
, 2) == nullptr))
799 PostQuitMessage(static_cast<int>(resolveWith
));
804 sTemp
.Format(ResString(hResource
, IDS_MARKASRESOLVED
), static_cast<LPCWSTR
>(CPathUtils::GetFileNameFromPath(selectionResult
.c_str())));
805 if (MessageBox(m_hwnd
, sTemp
, L
"TortoiseGitMerge", MB_YESNO
| MB_ICONQUESTION
) != IDYES
)
809 cmd
.Format(L
"\"%sTortoiseGitProc.exe\" /command:resolve /path:\"%s\" /closeonend:1 /noquestion /skipcheck /silent", static_cast<LPCWSTR
>(CPathUtils::GetAppDirectory()), selectionResult
.c_str());
811 cmd
.AppendFormat(L
" /resolvemsghwnd:%I64d /resolvemsgwparam:%I64d /resolvemsglparam:%I64d", reinterpret_cast<__int64
>(resolveMsgWnd
), static_cast<__int64
>(resolveMsgWParam
), static_cast<__int64
>(resolveMsgLParam
));
813 STARTUPINFO startup
= { 0 };
814 PROCESS_INFORMATION process
= { 0 };
815 startup
.cb
= sizeof(startup
);
817 if (!CreateProcess(nullptr, cmd
.GetBuffer(), nullptr, nullptr, FALSE
, CREATE_UNICODE_ENVIRONMENT
, nullptr, nullptr, &startup
, &process
))
820 PostQuitMessage(static_cast<int>(resolveWith
));
825 AllowSetForegroundWindow(process
.dwProcessId
);
827 CloseHandle(process
.hThread
);
828 CloseHandle(process
.hProcess
);
830 PostQuitMessage(static_cast<int>(resolveWith
));
833 case ID_VIEW_DARKMODE
:
834 CTheme::Instance().SetDarkTheme(!CTheme::Instance().IsDarkTheme());
837 ::PostQuitMessage(0);
847 void CMainWindow::DrawXorBar(HDC hdc
, int x1
, int y1
, int width
, int height
)
849 static WORD _dotPatternBmp
[8] =
851 0x0055, 0x00aa, 0x0055, 0x00aa,
852 0x0055, 0x00aa, 0x0055, 0x00aa
856 HBRUSH hbr
, hbrushOld
;
858 hbm
= CreateBitmap(8, 8, 1, 1, _dotPatternBmp
);
859 hbr
= CreatePatternBrush(hbm
);
861 SetBrushOrgEx(hdc
, x1
, y1
, 0);
862 hbrushOld
= static_cast<HBRUSH
>(SelectObject(hdc
, hbr
));
864 PatBlt(hdc
, x1
, y1
, width
, height
, PATINVERT
);
866 SelectObject(hdc
, hbrushOld
);
872 LRESULT
CMainWindow::Splitter_OnLButtonDown(HWND hwnd
, UINT
/*iMsg*/, WPARAM
/*wParam*/, LPARAM lParam
)
879 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
880 pt
.y
= static_cast<short>(HIWORD(lParam
));
882 GetClientRect(hwnd
, &clientrect
);
883 GetWindowRect(hwnd
, &rect
);
885 ClientToScreen(hwnd
, &zero
);
886 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
888 ClientToScreen(hwnd
, &pt
);
889 // find out which drag bar is used
891 if (!selectionPaths
.empty())
894 GetWindowRect(picWindow2
, &pic2Rect
);
897 if (pic2Rect
.bottom
<= pt
.y
)
902 if (pic2Rect
.right
<= pt
.x
)
907 //convert the mouse coordinates relative to the top-left of
912 //same for the window coordinates - make them relative to 0,0
913 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
917 if (pt
.x
> rect
.right
-4)
921 if (pt
.y
> rect
.bottom
-4)
922 pt
.y
= rect
.bottom
-4;
928 hdc
= GetWindowDC(hwnd
);
930 DrawXorBar(hdc
, clientrect
.left
, pt
.y
+2, clientrect
.right
-clientrect
.left
-2, 4);
932 DrawXorBar(hdc
, pt
.x
+2, clientrect
.top
, 4, clientrect
.bottom
-clientrect
.top
-2);
933 ReleaseDC(hwnd
, hdc
);
941 void CMainWindow::Splitter_CaptureChanged()
946 void CMainWindow::SetTheme(bool bDark
)
948 transparentColor
= ::GetSysColor(COLOR_WINDOW
);
949 picWindow1
.SetTransparentColor(transparentColor
);
950 picWindow2
.SetTransparentColor(transparentColor
);
951 picWindow3
.SetTransparentColor(transparentColor
);
954 DarkModeHelper::Instance().AllowDarkModeForApp(TRUE
);
956 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, TRUE
);
957 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetStockObject(BLACK_BRUSH
)));
958 if (FAILED(SetWindowTheme(*this, L
"DarkMode_Explorer", nullptr)))
959 SetWindowTheme(*this, L
"Explorer", nullptr);
960 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, TRUE
);
961 if (FAILED(SetWindowTheme(hwndTB
, L
"DarkMode_Explorer", nullptr)))
962 SetWindowTheme(hwndTB
, L
"Explorer", nullptr);
963 BOOL darkFlag
= TRUE
;
964 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
965 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
966 DarkModeHelper::Instance().FlushMenuThemes();
967 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
971 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE
);
972 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, FALSE
);
973 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, FALSE
);
974 SetClassLongPtr(*this, GCLP_HBRBACKGROUND
, reinterpret_cast<LONG_PTR
>(GetSysColorBrush(COLOR_3DFACE
)));
975 SetWindowTheme(*this, L
"Explorer", nullptr);
976 SetWindowTheme(hwndTB
, L
"Explorer", nullptr);
977 DarkModeHelper::Instance().AllowDarkModeForWindow(*this, FALSE
);
978 DarkModeHelper::Instance().AllowDarkModeForWindow(hwndTB
, FALSE
);
979 BOOL darkFlag
= FALSE
;
980 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data
= { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS
, &darkFlag
, sizeof(darkFlag
) };
981 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data
);
982 DarkModeHelper::Instance().FlushMenuThemes();
983 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
984 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE
);
986 DarkModeHelper::Instance().RefreshTitleBarThemeColor(*this, bDark
);
988 HMENU hMenu
= GetMenu(*this);
989 UINT uCheck
= MF_BYCOMMAND
;
990 uCheck
|= CTheme::Instance().IsDarkTheme() ? MF_CHECKED
: MF_UNCHECKED
;
991 CheckMenuItem(hMenu
, ID_VIEW_DARKMODE
, uCheck
);
992 UINT uEnabled
= MF_BYCOMMAND
;
993 uEnabled
|= CTheme::Instance().IsDarkModeAllowed() ? MF_ENABLED
: MF_DISABLED
;
994 EnableMenuItem(hMenu
, ID_VIEW_DARKMODE
, uEnabled
);
996 ::RedrawWindow(*this, nullptr, nullptr, RDW_FRAME
| RDW_INVALIDATE
| RDW_ERASE
| RDW_INTERNALPAINT
| RDW_ALLCHILDREN
| RDW_UPDATENOW
);
999 LRESULT
CMainWindow::Splitter_OnLButtonUp(HWND hwnd
, UINT
/*iMsg*/, WPARAM
/*wParam*/, LPARAM lParam
)
1006 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
1007 pt
.y
= static_cast<short>(HIWORD(lParam
));
1009 if (bDragMode
== FALSE
)
1012 const auto bordersm
= CDPIAware::Instance().ScaleX(*this, 2);
1013 const auto borderl
= CDPIAware::Instance().ScaleY(*this, 4);
1015 GetClientRect(hwnd
, &clientrect
);
1016 GetWindowRect(hwnd
, &rect
);
1018 ClientToScreen(hwnd
, &zero
);
1019 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
1021 ClientToScreen(hwnd
, &pt
);
1025 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1029 if (pt
.x
> rect
.right
- borderl
)
1030 pt
.x
= rect
.right
- borderl
;
1033 if (pt
.y
> rect
.bottom
- borderl
)
1034 pt
.y
= rect
.bottom
- borderl
;
1036 hdc
= GetWindowDC(hwnd
);
1038 DrawXorBar(hdc
, clientrect
.left
, oldy
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1040 DrawXorBar(hdc
, oldx
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1041 ReleaseDC(hwnd
, hdc
);
1048 //convert the splitter position back to screen coords.
1049 GetWindowRect(hwnd
, &rect
);
1053 //now convert into CLIENT coordinates
1054 ScreenToClient(hwnd
, &pt
);
1055 GetClientRect(hwnd
, &rect
);
1056 #define MINWINSIZE 10
1059 if (selectionPaths
.size() != 3)
1061 nSplitterPos
= pt
.y
;
1067 if (pt
.y
< (nSplitterPos
+MINWINSIZE
))
1068 pt
.y
= nSplitterPos
+MINWINSIZE
;
1069 nSplitterPos2
= pt
.y
;
1073 if (pt
.y
> (nSplitterPos2
-MINWINSIZE
))
1074 pt
.y
= nSplitterPos2
-MINWINSIZE
;
1075 nSplitterPos
= pt
.y
;
1081 if (selectionPaths
.size() != 3)
1083 nSplitterPos
= pt
.x
;
1089 if (pt
.x
< (nSplitterPos
+MINWINSIZE
))
1090 pt
.x
= nSplitterPos
+MINWINSIZE
;
1091 nSplitterPos2
= pt
.x
;
1095 if (pt
.x
> (nSplitterPos2
-MINWINSIZE
))
1096 pt
.x
= nSplitterPos2
-MINWINSIZE
;
1097 nSplitterPos
= pt
.x
;
1104 //position the child controls
1105 PositionChildren(&rect
);
1109 LRESULT
CMainWindow::Splitter_OnMouseMove(HWND hwnd
, UINT
/*iMsg*/, WPARAM wParam
, LPARAM lParam
)
1116 if (bDragMode
== FALSE
)
1119 const auto bordersm
= CDPIAware::Instance().ScaleX(*this, 2);
1120 const auto borderl
= CDPIAware::Instance().ScaleY(*this, 4);
1122 pt
.x
= static_cast<short>(LOWORD(lParam
)); // horizontal position of cursor
1123 pt
.y
= static_cast<short>(HIWORD(lParam
));
1125 GetClientRect(hwnd
, &clientrect
);
1126 GetWindowRect(hwnd
, &rect
);
1128 ClientToScreen(hwnd
, &zero
);
1129 OffsetRect(&clientrect
, zero
.x
-rect
.left
, zero
.y
-rect
.top
);
1131 //convert the mouse coordinates relative to the top-left of
1133 ClientToScreen(hwnd
, &pt
);
1137 //same for the window coordinates - make them relative to 0,0
1138 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1142 if (pt
.x
> rect
.right
- borderl
)
1143 pt
.x
= rect
.right
- borderl
;
1146 if (pt
.y
> rect
.bottom
- borderl
)
1147 pt
.y
= rect
.bottom
- borderl
;
1149 if ((wParam
& MK_LBUTTON
) && ((bVertical
&& (pt
.y
!= oldy
)) || (!bVertical
&& (pt
.x
!= oldx
))))
1151 HDC hdc
= GetWindowDC(hwnd
);
1155 DrawXorBar(hdc
, clientrect
.left
, oldy
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1156 DrawXorBar(hdc
, clientrect
.left
, pt
.y
+ bordersm
, clientrect
.right
- clientrect
.left
- bordersm
, borderl
);
1160 DrawXorBar(hdc
, oldx
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1161 DrawXorBar(hdc
, pt
.x
+ bordersm
, clientrect
.top
, borderl
, clientrect
.bottom
- clientrect
.top
- bordersm
);
1164 ReleaseDC(hwnd
, hdc
);
1173 bool CMainWindow::OpenDialog()
1175 return (DialogBox(hResource
, MAKEINTRESOURCE(IDD_OPEN
), *this, reinterpret_cast<DLGPROC
>(OpenDlgProc
)) == IDOK
);
1178 BOOL CALLBACK
CMainWindow::OpenDlgProc(HWND hwndDlg
, UINT message
, WPARAM wParam
, LPARAM
/*lParam*/)
1184 CTheme::Instance().SetThemeForDialog(hwndDlg
, CTheme::Instance().IsDarkTheme());
1185 // center on the parent window
1186 HWND hParentWnd
= ::GetParent(hwndDlg
);
1187 RECT parentrect
, childrect
, centeredrect
;
1188 GetWindowRect(hParentWnd
, &parentrect
);
1189 GetWindowRect(hwndDlg
, &childrect
);
1190 centeredrect
.left
= parentrect
.left
+ ((parentrect
.right
-parentrect
.left
-childrect
.right
+childrect
.left
)/2);
1191 centeredrect
.right
= centeredrect
.left
+ (childrect
.right
-childrect
.left
);
1192 centeredrect
.top
= parentrect
.top
+ ((parentrect
.bottom
-parentrect
.top
-childrect
.bottom
+childrect
.top
)/2);
1193 centeredrect
.bottom
= centeredrect
.top
+ (childrect
.bottom
-childrect
.top
);
1194 SetWindowPos(hwndDlg
, nullptr, centeredrect
.left
, centeredrect
.top
, centeredrect
.right
- centeredrect
.left
, centeredrect
.bottom
- centeredrect
.top
, SWP_SHOWWINDOW
);
1196 if (!leftpicpath
.empty())
1197 SetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, leftpicpath
.c_str());
1202 switch (LOWORD(wParam
))
1204 case IDC_LEFTBROWSE
:
1206 wchar_t path
[MAX_PATH
] = { 0 };
1207 if (AskForFile(hwndDlg
, path
))
1209 SetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, path
);
1213 case IDC_RIGHTBROWSE
:
1215 wchar_t path
[MAX_PATH
] = { 0 };
1216 if (AskForFile(hwndDlg
, path
))
1218 SetDlgItemText(hwndDlg
, IDC_RIGHTIMAGE
, path
);
1224 wchar_t path
[MAX_PATH
] = { 0 };
1225 if (!GetDlgItemText(hwndDlg
, IDC_LEFTIMAGE
, path
, _countof(path
)))
1228 if (!GetDlgItemText(hwndDlg
, IDC_RIGHTIMAGE
, path
, _countof(path
)))
1230 rightpicpath
= path
;
1234 CTheme::Instance().SetThemeForDialog(hwndDlg
, false);
1235 EndDialog(hwndDlg
, wParam
);
1242 bool CMainWindow::AskForFile(HWND owner
, wchar_t* path
)
1244 OPENFILENAME ofn
= {0}; // common dialog box structure
1245 // Initialize OPENFILENAME
1246 ofn
.lStructSize
= sizeof(OPENFILENAME
);
1247 ofn
.hwndOwner
= owner
;
1248 ofn
.lpstrFile
= path
;
1249 ofn
.nMaxFile
= MAX_PATH
;
1250 ResString
sTitle(::hResource
, IDS_OPENIMAGEFILE
);
1251 ofn
.lpstrTitle
= sTitle
;
1252 ofn
.Flags
= OFN_DONTADDTORECENT
| OFN_FILEMUSTEXIST
| OFN_EXPLORER
;
1253 ofn
.hInstance
= ::hResource
;
1254 constexpr wchar_t filters
[] = L
"Images\0*.wmf;*.jpg;*jpeg;*.bmp;*.gif;*.png;*.ico;*.dib;*.emf;*.webp;*.svg;*.svgz\0All (*.*)\0*.*\0\0";
1255 ofn
.lpstrFilter
= filters
;
1256 ofn
.nFilterIndex
= 1;
1257 // Display the Open dialog box.
1258 if (GetOpenFileName(&ofn
)==FALSE
)
1265 bool CMainWindow::CreateToolbar()
1267 // Ensure that the common control DLL is loaded.
1268 INITCOMMONCONTROLSEX icex
;
1269 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
1270 icex
.dwICC
= ICC_BAR_CLASSES
| ICC_WIN95_CLASSES
;
1271 InitCommonControlsEx(&icex
);
1273 hwndTB
= CreateWindowEx(TBSTYLE_EX_DOUBLEBUFFER
,
1275 static_cast<LPCWSTR
>(nullptr),
1276 WS_CHILD
| WS_BORDER
| WS_VISIBLE
| TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
,
1279 reinterpret_cast<HMENU
>(IDC_TORTOISEIDIFF
),
1282 if (hwndTB
== INVALID_HANDLE_VALUE
)
1285 SendMessage(hwndTB
, TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0);
1288 // create an imagelist containing the icons for the toolbar
1289 auto imgSizeX
= CDPIAware::Instance().ScaleX(*this, 24);
1290 auto imgSizeY
= CDPIAware::Instance().ScaleY(*this, 24);
1291 hToolbarImgList
= ImageList_Create(imgSizeX
, imgSizeY
, ILC_COLOR32
| ILC_MASK
| ILC_HIGHQUALITYSCALE
, 12, 4);
1292 if (!hToolbarImgList
)
1295 HICON hIcon
= nullptr;
1296 if (selectionPaths
.empty())
1298 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_OVERLAP
), imgSizeX
, imgSizeY
);
1299 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1300 tbb
[index
].idCommand
= ID_VIEW_OVERLAPIMAGES
;
1301 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1302 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1303 tbb
[index
].dwData
= 0;
1304 tbb
[index
++].iString
= 0;
1306 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_BLEND
), imgSizeX
, imgSizeY
);
1307 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1308 tbb
[index
].idCommand
= ID_VIEW_BLENDALPHA
;
1309 tbb
[index
].fsState
= 0;
1310 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1311 tbb
[index
].dwData
= 0;
1312 tbb
[index
++].iString
= 0;
1314 tbb
[index
].iBitmap
= 0;
1315 tbb
[index
].idCommand
= 0;
1316 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1317 tbb
[index
].fsStyle
= BTNS_SEP
;
1318 tbb
[index
].dwData
= 0;
1319 tbb
[index
++].iString
= 0;
1321 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_LINK
), imgSizeX
, imgSizeY
);
1322 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1323 tbb
[index
].idCommand
= ID_VIEW_LINKIMAGESTOGETHER
;
1324 tbb
[index
].fsState
= TBSTATE_ENABLED
| TBSTATE_CHECKED
;
1325 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1326 tbb
[index
].dwData
= 0;
1327 tbb
[index
++].iString
= 0;
1329 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITWIDTHS
), imgSizeX
, imgSizeY
);
1330 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1331 tbb
[index
].idCommand
= ID_VIEW_FITIMAGEWIDTHS
;
1332 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1333 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1334 tbb
[index
].dwData
= 0;
1335 tbb
[index
++].iString
= 0;
1337 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITHEIGHTS
), imgSizeX
, imgSizeY
);
1338 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1339 tbb
[index
].idCommand
= ID_VIEW_FITIMAGEHEIGHTS
;
1340 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1341 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1342 tbb
[index
].dwData
= 0;
1343 tbb
[index
++].iString
= 0;
1345 tbb
[index
].iBitmap
= 0;
1346 tbb
[index
].idCommand
= 0;
1347 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1348 tbb
[index
].fsStyle
= BTNS_SEP
;
1349 tbb
[index
].dwData
= 0;
1350 tbb
[index
++].iString
= 0;
1353 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_FITINWINDOW
), imgSizeX
, imgSizeY
);
1354 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1355 tbb
[index
].idCommand
= ID_VIEW_FITIMAGESINWINDOW
;
1356 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1357 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1358 tbb
[index
].dwData
= 0;
1359 tbb
[index
++].iString
= 0;
1361 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ORIGSIZE
), imgSizeX
, imgSizeY
);
1362 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1363 tbb
[index
].idCommand
= ID_VIEW_ORININALSIZE
;
1364 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1365 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1366 tbb
[index
].dwData
= 0;
1367 tbb
[index
++].iString
= 0;
1369 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ZOOMIN
), imgSizeX
, imgSizeY
);
1370 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1371 tbb
[index
].idCommand
= ID_VIEW_ZOOMIN
;
1372 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1373 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1374 tbb
[index
].dwData
= 0;
1375 tbb
[index
++].iString
= 0;
1377 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_ZOOMOUT
), imgSizeX
, imgSizeY
);
1378 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1379 tbb
[index
].idCommand
= ID_VIEW_ZOOMOUT
;
1380 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1381 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1382 tbb
[index
].dwData
= 0;
1383 tbb
[index
++].iString
= 0;
1385 tbb
[index
].iBitmap
= 0;
1386 tbb
[index
].idCommand
= 0;
1387 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1388 tbb
[index
].fsStyle
= BTNS_SEP
;
1389 tbb
[index
].dwData
= 0;
1390 tbb
[index
++].iString
= 0;
1392 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_IMGINFO
), imgSizeX
, imgSizeY
);
1393 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1394 tbb
[index
].idCommand
= ID_VIEW_IMAGEINFO
;
1395 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1396 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1397 tbb
[index
].dwData
= 0;
1398 tbb
[index
++].iString
= 0;
1400 hIcon
= LoadIconEx(hResource
, MAKEINTRESOURCE(IDI_VERTICAL
), imgSizeX
, imgSizeY
);
1401 tbb
[index
].iBitmap
= ImageList_AddIcon(hToolbarImgList
, hIcon
);
1402 tbb
[index
].idCommand
= ID_VIEW_ARRANGEVERTICAL
;
1403 tbb
[index
].fsState
= TBSTATE_ENABLED
;
1404 tbb
[index
].fsStyle
= BTNS_BUTTON
;
1405 tbb
[index
].dwData
= 0;
1406 tbb
[index
++].iString
= 0;
1408 SendMessage(hwndTB
, TB_SETIMAGELIST
, 0, reinterpret_cast<LPARAM
>(hToolbarImgList
));
1409 SendMessage(hwndTB
, TB_ADDBUTTONS
, index
, reinterpret_cast<LPARAM
>(&tbb
));
1410 SendMessage(hwndTB
, TB_AUTOSIZE
, 0, 0);
1411 ShowWindow(hwndTB
, SW_SHOW
);
1415 void CMainWindow::SetSelectionImage( FileType ft
, const std::wstring
& path
, const std::wstring
& title
)
1417 selectionPaths
[ft
] = path
;
1418 selectionTitles
[ft
] = title
;