Fix piping to TortoiseGitUDiff by checking for ERROR_BROKEN_PIPE and treating that...
[TortoiseGit.git] / src / TortoiseMerge / MainFrm.cpp
blobed5e229053a7a9e88cd44ed8b0bcd024ae96532c
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2008-2024 - TortoiseGit
4 // Copyright (C) 2004-2018, 2020 - TortoiseSVN
5 // Copyright (C) 2012-2014 - Sven Strickroth <email@cs-ware.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "TortoiseMerge.h"
23 #include "resource.h"
24 #include "OpenDlg.h"
25 #include "SysProgressDlg.h"
26 #include "Settings.h"
27 #include "MessageBox.h"
28 #include "AppUtils.h"
29 #include "PathUtils.h"
30 #include "MainFrm.h"
31 #include "LeftView.h"
32 #include "RightView.h"
33 #include "BottomView.h"
34 #include "DiffColors.h"
35 #include "SelectFileFilter.h"
36 #include "FormatMessageWrapper.h"
37 #include "TaskbarUUID.h"
38 #include "RegexFiltersDlg.h"
39 #include "DPIAware.h"
40 #include "Theme.h"
41 #include "StringUtils.h"
42 #include "Windows10Colors.h"
43 #include "DarkModeHelper.h"
44 #include "ThemeMFCVisualManager.h"
46 #ifdef _DEBUG
47 #define new DEBUG_NEW
48 #endif
50 // CMainFrame
51 #define IDT_RELOADCHECKTIMER 123
53 IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx)
55 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
56 ON_WM_CREATE()
57 ON_WM_DESTROY()
58 // Global help commands
59 ON_COMMAND(ID_HELP_FINDER, CFrameWndEx::OnHelpFinder)
60 ON_COMMAND(ID_HELP, CFrameWndEx::OnHelp)
61 ON_COMMAND(ID_CONTEXT_HELP, CFrameWndEx::OnContextHelp)
62 ON_COMMAND(ID_DEFAULT_HELP, CFrameWndEx::OnHelpFinder)
63 ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
64 ON_COMMAND(ID_VIEW_WHITESPACES, OnViewWhitespaces)
65 ON_WM_SIZE()
66 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
67 ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
68 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
69 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, OnUpdateFileSaveAs)
70 ON_COMMAND(ID_VIEW_ONEWAYDIFF, OnViewOnewaydiff)
71 ON_UPDATE_COMMAND_UI(ID_VIEW_ONEWAYDIFF, OnUpdateViewOnewaydiff)
72 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACES, OnUpdateViewWhitespaces)
73 ON_COMMAND(ID_VIEW_OPTIONS, OnViewOptions)
74 ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
75 ON_WM_CLOSE()
76 ON_WM_ACTIVATE()
77 ON_COMMAND(ID_FILE_RELOAD, OnFileReload)
78 ON_COMMAND(ID_VIEW_LINEDOWN, OnViewLinedown)
79 ON_COMMAND(ID_VIEW_LINEUP, OnViewLineup)
80 ON_COMMAND(ID_VIEW_MOVEDBLOCKS, OnViewMovedBlocks)
81 ON_UPDATE_COMMAND_UI(ID_VIEW_MOVEDBLOCKS, OnUpdateViewMovedBlocks)
82 ON_UPDATE_COMMAND_UI(ID_EDIT_MARKASRESOLVED, OnUpdateMergeMarkasresolved)
83 ON_COMMAND(ID_EDIT_MARKASRESOLVED, OnMergeMarkasresolved)
84 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTCONFLICT, OnUpdateMergeNextconflict)
85 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVIOUSCONFLICT, OnUpdateMergePreviousconflict)
86 ON_WM_MOVE()
87 ON_WM_MOVING()
88 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
89 ON_COMMAND(ID_VIEW_SWITCHLEFT, OnViewSwitchleft)
90 ON_UPDATE_COMMAND_UI(ID_VIEW_SWITCHLEFT, OnUpdateViewSwitchleft)
91 ON_COMMAND(ID_VIEW_LINELEFT, &CMainFrame::OnViewLineleft)
92 ON_COMMAND(ID_VIEW_LINERIGHT, &CMainFrame::OnViewLineright)
93 ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWFILELIST, &CMainFrame::OnUpdateViewShowfilelist)
94 ON_COMMAND(ID_VIEW_SHOWFILELIST, &CMainFrame::OnViewShowfilelist)
95 ON_COMMAND(ID_EDIT_USETHEIRBLOCK, &CMainFrame::OnEditUseTheirs)
96 ON_COMMAND(ID_EDIT_USEMYBLOCK, &CMainFrame::OnEditUseMine)
97 ON_COMMAND(ID_EDIT_USETHEIRTHENMYBLOCK, &CMainFrame::OnEditUseTheirsThenMine)
98 ON_COMMAND(ID_EDIT_USEMINETHENTHEIRBLOCK, &CMainFrame::OnEditUseMineThenTheirs)
99 ON_COMMAND(ID_EDIT_UNDO, &CMainFrame::OnEditUndo)
100 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CMainFrame::OnUpdateEditUndo)
101 ON_COMMAND(ID_EDIT_REDO, &CMainFrame::OnEditRedo)
102 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CMainFrame::OnUpdateEditRedo)
103 ON_COMMAND(ID_EDIT_ENABLE, &CMainFrame::OnEditEnable)
104 ON_UPDATE_COMMAND_UI(ID_EDIT_ENABLE, &CMainFrame::OnUpdateEditEnable)
105 ON_UPDATE_COMMAND_UI(ID_EDIT_USEMINETHENTHEIRBLOCK, &CMainFrame::OnUpdateEditUseminethentheirblock)
106 ON_UPDATE_COMMAND_UI(ID_EDIT_USEMYBLOCK, &CMainFrame::OnUpdateEditUsemyblock)
107 ON_UPDATE_COMMAND_UI(ID_EDIT_USETHEIRBLOCK, &CMainFrame::OnUpdateEditUsetheirblock)
108 ON_UPDATE_COMMAND_UI(ID_EDIT_USETHEIRTHENMYBLOCK, &CMainFrame::OnUpdateEditUsetheirthenmyblock)
109 ON_COMMAND(ID_VIEW_INLINEDIFFWORD, &CMainFrame::OnViewInlinediffword)
110 ON_UPDATE_COMMAND_UI(ID_VIEW_INLINEDIFFWORD, &CMainFrame::OnUpdateViewInlinediffword)
111 ON_COMMAND(ID_VIEW_INLINEDIFF, &CMainFrame::OnViewInlinediff)
112 ON_UPDATE_COMMAND_UI(ID_VIEW_INLINEDIFF, &CMainFrame::OnUpdateViewInlinediff)
113 ON_UPDATE_COMMAND_UI(ID_EDIT_CREATEUNIFIEDDIFFFILE, &CMainFrame::OnUpdateEditCreateunifieddifffile)
114 ON_COMMAND(ID_EDIT_CREATEUNIFIEDDIFFFILE, &CMainFrame::OnEditCreateunifieddifffile)
115 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFBAR, &CMainFrame::OnUpdateViewLinediffbar)
116 ON_COMMAND(ID_VIEW_LINEDIFFBAR, &CMainFrame::OnViewLinediffbar)
117 ON_UPDATE_COMMAND_UI(ID_VIEW_BARS, &CMainFrame::OnUpdateViewBars)
118 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATORBAR, &CMainFrame::OnUpdateViewLocatorbar)
119 ON_COMMAND(ID_VIEW_LOCATORBAR, &CMainFrame::OnViewLocatorbar)
120 ON_COMMAND(ID_EDIT_USELEFTBLOCK, &CMainFrame::OnEditUseleftblock)
121 ON_UPDATE_COMMAND_UI(ID_USEBLOCKS, &CMainFrame::OnUpdateUseBlock)
122 ON_UPDATE_COMMAND_UI(ID_EDIT_USELEFTBLOCK, &CMainFrame::OnUpdateEditUseleftblock)
123 ON_COMMAND(ID_EDIT_USELEFTFILE, &CMainFrame::OnEditUseleftfile)
124 ON_UPDATE_COMMAND_UI(ID_EDIT_USELEFTFILE, &CMainFrame::OnUpdateEditUseleftfile)
125 ON_COMMAND(ID_EDIT_USEBLOCKFROMLEFTBEFORERIGHT, &CMainFrame::OnEditUseblockfromleftbeforeright)
126 ON_UPDATE_COMMAND_UI(ID_EDIT_USEBLOCKFROMLEFTBEFORERIGHT, &CMainFrame::OnUpdateEditUseblockfromleftbeforeright)
127 ON_COMMAND(ID_EDIT_USEBLOCKFROMRIGHTBEFORELEFT, &CMainFrame::OnEditUseblockfromrightbeforeleft)
128 ON_UPDATE_COMMAND_UI(ID_EDIT_USEBLOCKFROMRIGHTBEFORELEFT, &CMainFrame::OnUpdateEditUseblockfromrightbeforeleft)
129 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTDIFFERENCE, &CMainFrame::OnUpdateNavigateNextdifference)
130 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVIOUSDIFFERENCE, &CMainFrame::OnUpdateNavigatePreviousdifference)
131 ON_COMMAND(ID_VIEW_COLLAPSED, &CMainFrame::OnViewCollapsed)
132 ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSED, &CMainFrame::OnUpdateViewCollapsed)
133 ON_COMMAND(ID_VIEW_COMPAREWHITESPACES, &CMainFrame::OnViewComparewhitespaces)
134 ON_UPDATE_COMMAND_UI(ID_VIEW_COMPAREWHITESPACES, &CMainFrame::OnUpdateViewComparewhitespaces)
135 ON_COMMAND(ID_VIEW_IGNOREWHITESPACECHANGES, &CMainFrame::OnViewIgnorewhitespacechanges)
136 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNOREWHITESPACECHANGES, &CMainFrame::OnUpdateViewIgnorewhitespacechanges)
137 ON_COMMAND(ID_VIEW_IGNOREALLWHITESPACECHANGES, &CMainFrame::OnViewIgnoreallwhitespacechanges)
138 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNOREALLWHITESPACECHANGES, &CMainFrame::OnUpdateViewIgnoreallwhitespacechanges)
139 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTINLINEDIFF, &CMainFrame::OnUpdateNavigateNextinlinediff)
140 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVINLINEDIFF, &CMainFrame::OnUpdateNavigatePrevinlinediff)
141 ON_COMMAND(ID_VIEW_WRAPLONGLINES, &CMainFrame::OnViewWraplonglines)
142 ON_UPDATE_COMMAND_UI(ID_VIEW_WRAPLONGLINES, &CMainFrame::OnUpdateViewWraplonglines)
143 ON_REGISTERED_MESSAGE( TaskBarButtonCreated, CMainFrame::OnTaskbarButtonCreated )
144 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, &CMainFrame::OnUpdateEditPaste)
145 ON_COMMAND(ID_INDICATOR_LEFTVIEW, &CMainFrame::OnIndicatorLeftview)
146 ON_COMMAND(ID_INDICATOR_RIGHTVIEW, &CMainFrame::OnIndicatorRightview)
147 ON_COMMAND(ID_INDICATOR_BOTTOMVIEW, &CMainFrame::OnIndicatorBottomview)
148 ON_WM_TIMER()
149 ON_COMMAND(ID_VIEW_IGNORECOMMENTS, &CMainFrame::OnViewIgnorecomments)
150 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNORECOMMENTS, &CMainFrame::OnUpdateViewIgnorecomments)
151 ON_COMMAND(ID_VIEW_IGNOREEOL, &CMainFrame::OnViewIgnoreEOL)
152 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNOREEOL, &CMainFrame::OnUpdateViewIgnoreEOL)
153 ON_COMMAND_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnRegexfilter)
154 ON_UPDATE_COMMAND_UI_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnUpdateViewRegexFilter)
155 ON_COMMAND(ID_REGEX_NO_FILTER, &CMainFrame::OnRegexNoFilter)
156 ON_UPDATE_COMMAND_UI(ID_REGEX_NO_FILTER, &CMainFrame::OnUpdateRegexNoFilter)
157 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
158 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
159 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
160 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
161 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
162 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
163 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
164 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
165 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
166 ON_UPDATE_COMMAND_UI(ID_EDIT_THREEWAY_ACTIONS, &CMainFrame::OnUpdateThreeWayActions)
167 ON_UPDATE_COMMAND_UI(ID_INDICATOR_COLUMN, &CMainFrame::OnUpdateColumnStatusBar)
168 ON_UPDATE_COMMAND_UI(ID_INDICATOR_MARKEDWORDS, &CMainFrame::OnUpdateMarkedWords)
169 ON_UPDATE_COMMAND_UI(ID_EDIT_FINDNEXTSTART, &CMainFrame::OnUpdateEnableIfSelection)
170 ON_UPDATE_COMMAND_UI(ID_EDIT_FINDPREVSTART, &CMainFrame::OnUpdateEnableIfSelection)
171 ON_COMMAND_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnEncodingLeft)
172 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnEncodingRight)
173 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnEncodingBottom)
174 ON_COMMAND_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnEOLLeft)
175 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnEOLRight)
176 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnEOLBottom)
177 ON_COMMAND_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnTabModeLeft)
178 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnTabModeRight)
179 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnTabModeBottom)
180 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingLeft)
181 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingRight)
182 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnUpdateEncodingBottom)
183 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnUpdateEOLLeft)
184 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnUpdateEOLRight)
185 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnUpdateEOLBottom)
186 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnUpdateTabModeLeft)
187 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnUpdateTabModeRight)
188 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnUpdateTabModeBottom)
189 ON_WM_SETTINGCHANGE()
190 ON_WM_SYSCOLORCHANGE()
191 ON_MESSAGE(WM_DPICHANGED, OnDPIChanged)
192 END_MESSAGE_MAP()
194 static UINT indicators[] =
196 ID_SEPARATOR, // status line indicator
197 ID_INDICATOR_LEFTVIEW,
198 ID_INDICATOR_RIGHTVIEW,
199 ID_INDICATOR_BOTTOMVIEW,
200 ID_INDICATOR_CAPS,
201 ID_INDICATOR_NUM,
202 ID_INDICATOR_SCRL
206 // CMainFrame construction/destruction
208 CMainFrame::CMainFrame()
209 : m_bInitSplitter(FALSE)
210 , m_regWrapLines(L"Software\\TortoiseGitMerge\\WrapLines", 0)
211 , m_regViewModedBlocks(L"Software\\TortoiseGitMerge\\ViewMovedBlocks", TRUE)
212 , m_regOneWay(L"Software\\TortoiseGitMerge\\OnePane")
213 , m_regCollapsed(L"Software\\TortoiseGitMerge\\Collapsed", 0)
214 , m_regInlineDiff(L"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE)
215 , m_regUseRibbons(L"Software\\TortoiseGitMerge\\UseRibbons", TRUE)
216 , m_regIgnoreComments(L"Software\\TortoiseGitMerge\\IgnoreComments", FALSE)
218 m_bOneWay = (0 != (static_cast<DWORD>(m_regOneWay)));
219 m_bCollapsed = !!static_cast<DWORD>(m_regCollapsed);
220 m_bViewMovedBlocks = !!static_cast<DWORD>(m_regViewModedBlocks);
221 m_bWrapLines = !!static_cast<DWORD>(m_regWrapLines);
222 m_bInlineDiff = !!m_regInlineDiff;
223 m_bUseRibbons = !!m_regUseRibbons;
226 CMainFrame::~CMainFrame()
230 LRESULT CMainFrame::OnTaskbarButtonCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
232 SetUUIDOverlayIcon(m_hWnd);
233 return 0;
236 int CMainFrame::InitRibbon()
238 if (!m_bUseRibbons)
239 return 0;
240 if (CTheme::Instance().IsDarkTheme())
241 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
242 else
243 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
245 if (HRESULT hr = m_pRibbonFramework.CoCreateInstance(__uuidof(UIRibbonFramework)); FAILED(hr))
247 TRACE(L"Failed to create ribbon framework (%08x)\n", hr);
248 return -1; // fail to create
251 m_pRibbonApp.reset(new CNativeRibbonApp(this, m_pRibbonFramework));
252 m_pRibbonApp->SetSettingsFileName(CPathUtils::GetAppDataDirectory() + L"TortoiseGitMerge-RibbonSettings");
254 if (HRESULT hr = m_pRibbonFramework->Initialize(m_hWnd, m_pRibbonApp.get()); FAILED(hr))
256 TRACE(L"Failed to initialize ribbon framework (%08x)\n", hr);
257 return -1; // fail to create
260 if (HRESULT hr = m_pRibbonFramework->LoadUI(AfxGetResourceHandle(), L"TORTOISEGITMERGERIBBON_RIBBON"); FAILED(hr))
262 TRACE(L"Failed to load ribbon UI (%08x)\n", hr);
263 return -1; // fail to create
266 m_themeCallbackId = CTheme::Instance().RegisterThemeChangeCallback([this]()
268 if (CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark())
269 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
270 else
271 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
272 SetTheme(CTheme::Instance().IsDarkTheme());});
273 SetTheme(CTheme::Instance().IsDarkTheme());
275 BuildRegexSubitems();
276 if (!m_wndRibbonStatusBar.Create(this))
278 TRACE0("Failed to create ribbon status bar\n");
279 return -1; // fail to create
282 // column info
283 CString sColumn;
284 sColumn.Format(IDS_INDICATOR_COLUMN, 0);
285 auto columnPane = new CMFCRibbonStatusBarPane(ID_INDICATOR_COLUMN, sColumn, FALSE);
286 m_wndRibbonStatusBar.AddElement(columnPane, L"");
287 sColumn.Format(IDS_INDICATOR_COLUMN, 999999);
288 columnPane->SetAlmostLargeText(sColumn);
289 // marked word counter
290 auto columnPaneMW = new CMFCRibbonStatusBarPane(ID_INDICATOR_MARKEDWORDS, L"", FALSE);
291 m_wndRibbonStatusBar.AddElement(columnPaneMW, L"");
292 columnPaneMW->SetAlmostLargeText(L"Marked words: l: XXXX | r: XXXX | b: XXXX");
294 CString sTooltip(MAKEINTRESOURCE(IDS_ENCODING_COMBO_TOOLTIP));
295 auto apBtnGroupLeft = std::make_unique<CMFCRibbonButtonsGroup>();
296 apBtnGroupLeft->SetID(ID_INDICATOR_LEFTVIEW);
297 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_LEFTVIEW)), TRUE));
298 CMFCRibbonButton* pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOENCODING, L"");
299 pButton->SetToolTipText(sTooltip);
300 FillEncodingButton(pButton, ID_INDICATOR_LEFTENCODINGSTART);
301 apBtnGroupLeft->AddButton(pButton);
302 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOEOL, L"");
303 FillEOLButton(pButton, ID_INDICATOR_LEFTEOLSTART);
304 apBtnGroupLeft->AddButton(pButton);
305 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, L"");
306 FillTabModeButton(pButton, ID_INDICATOR_LEFTTABMODESTART);
307 apBtnGroupLeft->AddButton(pButton);
308 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_LEFTVIEW, L"", TRUE));
309 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupLeft.release(), L"");
311 auto apBtnGroupRight = std::make_unique<CMFCRibbonButtonsGroup>();
312 apBtnGroupRight->SetID(ID_INDICATOR_RIGHTVIEW);
313 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_RIGHTVIEW)), TRUE));
314 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, L"");
315 pButton->SetToolTipText(sTooltip);
316 FillEncodingButton(pButton, ID_INDICATOR_RIGHTENCODINGSTART);
317 apBtnGroupRight->AddButton(pButton);
318 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOEOL, L"");
319 FillEOLButton(pButton, ID_INDICATOR_RIGHTEOLSTART);
320 apBtnGroupRight->AddButton(pButton);
321 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, L"");
322 FillTabModeButton(pButton, ID_INDICATOR_RIGHTTABMODESTART);
323 apBtnGroupRight->AddButton(pButton);
324 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_RIGHTVIEW, L"", TRUE));
325 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupRight.release(), L"");
327 return 0;
330 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
332 if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
333 return -1;
335 if (!m_bUseRibbons)
337 if (!m_wndMenuBar.Create(this))
339 TRACE0("Failed to create menubar\n");
340 return -1; // fail to create
342 m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
344 // prevent the menu bar from taking the focus on activation
345 CMFCPopupMenu::SetForceMenuFocus(FALSE);
346 if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
348 TRACE0("Failed to create toolbar\n");
349 return -1; // fail to create
351 m_wndToolBar.SetWindowText(L"Main");
352 if (!m_wndStatusBar.Create(this) ||
353 !m_wndStatusBar.SetIndicators(indicators,
354 _countof(indicators)))
356 TRACE0("Failed to create status bar\n");
357 return -1; // fail to create
359 m_wndStatusBar.EnablePaneDoubleClick();
361 m_themeCallbackId = CTheme::Instance().RegisterThemeChangeCallback([this]()
363 if (CTheme::Instance().IsDarkTheme() || CTheme::Instance().IsHighContrastModeDark())
364 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
365 else
366 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
367 SetTheme(CTheme::Instance().IsDarkTheme());
369 SetTheme(CTheme::Instance().IsDarkTheme());
372 if (!m_wndLocatorBar.Create(this, IDD_DIFFLOCATOR,
373 CBRS_ALIGN_LEFT | CBRS_SIZE_FIXED, ID_VIEW_LOCATORBAR))
375 TRACE0("Failed to create dialogbar\n");
376 return -1; // fail to create
378 if (!m_wndLineDiffBar.Create(this, IDD_LINEDIFF,
379 CBRS_ALIGN_BOTTOM | CBRS_SIZE_FIXED, ID_VIEW_LINEDIFFBAR))
381 TRACE0("Failed to create dialogbar\n");
382 return -1; // fail to create
384 m_wndLocatorBar.m_pMainFrm = this;
385 m_wndLineDiffBar.m_pMainFrm = this;
387 EnableDocking(CBRS_ALIGN_ANY);
388 if (!m_bUseRibbons)
390 m_wndMenuBar.EnableDocking(CBRS_ALIGN_TOP);
391 m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP);
392 DockPane(&m_wndMenuBar);
393 DockPane(&m_wndToolBar);
395 DockPane(&m_wndLocatorBar);
396 DockPane(&m_wndLineDiffBar);
397 ShowPane(&m_wndLocatorBar, true, false, true);
398 ShowPane(&m_wndLineDiffBar, true, false, true);
400 m_wndLocatorBar.EnableGripper(FALSE);
401 m_wndLineDiffBar.EnableGripper(FALSE);
403 return 0;
406 void CMainFrame::OnDestroy()
408 CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId);
409 if (m_pRibbonFramework)
410 m_pRibbonFramework->Destroy();
412 CFrameWndEx::OnDestroy();
415 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
417 cs.style &= ~WS_VISIBLE;
418 // extra styles needed to avoid refresh problems with the ribbon
419 cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
420 if (!CFrameWndEx::PreCreateWindow(cs))
421 return FALSE;
422 if (CTheme::Instance().IsDarkTheme())
424 WNDCLASSEX wndClass{};
425 wndClass.cbSize = sizeof(WNDCLASSEX);
426 GetClassInfoEx(AfxGetInstanceHandle(), cs.lpszClass, &wndClass);
427 UnregisterClass(cs.lpszClass, AfxGetInstanceHandle());
428 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOWTEXT + 1);
429 RegisterClassEx(&wndClass);
431 return TRUE;
434 // CMainFrame diagnostics
436 #ifdef _DEBUG
437 void CMainFrame::AssertValid() const
439 CFrameWndEx::AssertValid();
442 void CMainFrame::Dump(CDumpContext& dc) const
444 CFrameWndEx::Dump(dc);
447 #endif //_DEBUG
450 // CMainFrame message handlers
453 BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
455 CRect cr;
456 GetClientRect( &cr);
459 // split into three panes:
460 // -------------
461 // | | |
462 // | | |
463 // |------------
464 // | |
465 // | |
466 // |------------
468 // create a splitter with 2 rows, 1 column
469 if (!m_wndSplitter.CreateStatic(this, 2, 1))
471 TRACE0("Failed to CreateStaticSplitter\n");
472 return FALSE;
475 // add the second splitter pane - which is a nested splitter with 2 columns
476 if (!m_wndSplitter2.CreateStatic(
477 &m_wndSplitter, // our parent window is the first splitter
478 1, 2, // the new splitter is 1 row, 2 columns
479 WS_CHILD | WS_VISIBLE | WS_BORDER, // style, WS_BORDER is needed
480 m_wndSplitter.IdFromRowCol(0, 0)
481 // new splitter is in the first row, 1st column of first splitter
484 TRACE0("Failed to create nested splitter\n");
485 return FALSE;
487 // add the first splitter pane - the default view in row 0
488 if (!m_wndSplitter.CreateView(1, 0,
489 RUNTIME_CLASS(CBottomView), CSize(cr.Width(), cr.Height()), pContext))
491 TRACE0("Failed to create first pane\n");
492 return FALSE;
494 m_pwndBottomView = static_cast<CBottomView*>(m_wndSplitter.GetPane(1, 0));
495 m_pwndBottomView->m_pwndLocator = &m_wndLocatorBar;
496 m_pwndBottomView->m_pwndLineDiffBar = &m_wndLineDiffBar;
497 if (m_bUseRibbons)
498 m_pwndBottomView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
499 else
500 m_pwndBottomView->m_pwndStatusBar = &m_wndStatusBar;
501 m_pwndBottomView->m_pMainFrame = this;
503 // now create the two views inside the nested splitter
505 if (!m_wndSplitter2.CreateView(0, 0,
506 RUNTIME_CLASS(CLeftView), CSize(cr.Width()/2, cr.Height()/2), pContext))
508 TRACE0("Failed to create second pane\n");
509 return FALSE;
511 m_pwndLeftView = static_cast<CLeftView*>(m_wndSplitter2.GetPane(0, 0));
512 m_pwndLeftView->m_pwndLocator = &m_wndLocatorBar;
513 m_pwndLeftView->m_pwndLineDiffBar = &m_wndLineDiffBar;
514 if (m_bUseRibbons)
515 m_pwndLeftView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
516 else
517 m_pwndLeftView->m_pwndStatusBar = &m_wndStatusBar;
518 m_pwndLeftView->m_pMainFrame = this;
520 if (!m_wndSplitter2.CreateView(0, 1,
521 RUNTIME_CLASS(CRightView), CSize(cr.Width()/2, cr.Height()/2), pContext))
523 TRACE0("Failed to create third pane\n");
524 return FALSE;
526 m_pwndRightView = static_cast<CRightView*>(m_wndSplitter2.GetPane(0, 1));
527 m_pwndRightView->m_pwndLocator = &m_wndLocatorBar;
528 m_pwndRightView->m_pwndLineDiffBar = &m_wndLineDiffBar;
529 if (m_bUseRibbons)
530 m_pwndRightView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
531 else
532 m_pwndRightView->m_pwndStatusBar = &m_wndStatusBar;
533 m_pwndRightView->m_pMainFrame = this;
534 m_bInitSplitter = TRUE;
536 m_dlgFilePatches.Create(IDD_FILEPATCHES, this);
537 UpdateLayout();
538 return TRUE;
541 // Callback function
542 BOOL CMainFrame::PatchFile(CString sFilePath, bool /*bContentMods*/, bool bPropMods, CString sVersion, BOOL bAutoPatch)
544 //"dry run" was successful, so save the patched file somewhere...
545 CString sTempFile = CTempFiles::Instance().GetTempFilePathString();
546 CString sRejectedFile, sBasePath;
547 if (m_Patch.GetPatchResult(sFilePath, sTempFile, sRejectedFile, sBasePath) < 0)
549 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
550 return FALSE;
552 sFilePath = m_Patch.GetTargetPath() + L'\\' + sFilePath;
553 sFilePath.Replace('/', '\\');
554 if (sBasePath.IsEmpty())
555 sBasePath = sFilePath;
556 if (m_bReversedPatch)
558 m_Data.m_baseFile.SetFileName(sTempFile);
559 CString temp;
560 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
561 m_Data.m_baseFile.SetDescriptiveName(temp);
562 m_Data.m_yourFile.SetFileName(sFilePath);
563 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchOriginal));
564 m_Data.m_yourFile.SetDescriptiveName(temp);
565 m_Data.m_theirFile.SetOutOfUse();
566 m_Data.m_mergedFile.SetOutOfUse();
568 else
570 if ((!PathFileExists(sBasePath))||(PathIsDirectory(sBasePath)))
572 m_Data.m_baseFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
573 m_Data.m_baseFile.CreateEmptyFile();
575 else
577 m_Data.m_baseFile.SetFileName(sBasePath);
579 CString sDescription;
580 sDescription.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sBasePath)), static_cast<LPCWSTR>(m_Data.m_sPatchOriginal));
581 m_Data.m_baseFile.SetDescriptiveName(sDescription);
582 if (sBasePath == sFilePath)
584 m_Data.m_yourFile.SetFileName(sTempFile);
585 CString temp;
586 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sBasePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
587 m_Data.m_yourFile.SetDescriptiveName(temp);
588 m_Data.m_theirFile.SetOutOfUse();
590 else
592 if (!PathFileExists(sFilePath) || PathIsDirectory(sFilePath))
594 m_Data.m_yourFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
595 m_Data.m_yourFile.CreateEmptyFile();
596 CString temp;
597 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(CString(MAKEINTRESOURCE(IDS_NOTFOUNDVIEWTITLEINDICATOR))));
598 m_Data.m_yourFile.SetDescriptiveName(temp);
600 else
601 m_Data.m_yourFile.SetFileName(sFilePath);
602 m_Data.m_theirFile.SetFileName(sTempFile);
603 CString temp;
604 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
605 m_Data.m_theirFile.SetDescriptiveName(temp);
607 m_Data.m_mergedFile.SetFileName(sFilePath);
608 m_Data.m_bPatchRequired = bPropMods;
610 TRACE(L"comparing %s\nwith the patched result %s\n", static_cast<LPCWSTR>(sFilePath), static_cast<LPCWSTR>(sTempFile));
612 LoadViews();
613 if (!sRejectedFile.IsEmpty())
615 #if 0 // TGIT TODO
616 // start TortoiseUDiff with the rejected hunks
617 CString sTitle;
618 sTitle.Format(IDS_TITLE_REJECTEDHUNKS, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)));
619 CAppUtils::StartUnifiedDiffViewer(sRejectedFile, sTitle);
620 #endif
622 if (bAutoPatch)
624 if (sBasePath != sFilePath && HasConflictsWontKeep())
625 return FALSE;
627 PatchSave();
629 return TRUE;
632 // Callback function
633 BOOL CMainFrame::DiffFiles(CString sURL1, CString sRev1, CString sURL2, CString sRev2)
635 CString tempfile1 = CTempFiles::Instance().GetTempFilePathString();
636 CString tempfile2 = CTempFiles::Instance().GetTempFilePathString();
638 ASSERT(tempfile1.Compare(tempfile2));
640 CString sTemp;
641 CSysProgressDlg progDlg;
642 sTemp.Format(IDS_GETVERSIONOFFILE, static_cast<LPCWSTR>(sRev1));
643 progDlg.SetLine(1, sTemp, true);
644 progDlg.SetLine(2, sURL1, true);
645 sTemp.LoadString(IDS_GETVERSIONOFFILETITLE);
646 progDlg.SetTitle(sTemp);
647 progDlg.SetShowProgressBar(true);
648 progDlg.SetTime(FALSE);
649 progDlg.SetProgress(1,100);
650 progDlg.ShowModeless(this);
651 if (!CAppUtils::GetVersionedFile(sURL1, sRev1, tempfile1, &progDlg, m_hWnd))
653 progDlg.Stop();
654 CString sErrMsg;
655 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, static_cast<LPCWSTR>(sRev1), static_cast<LPCWSTR>(sURL1));
656 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
657 return FALSE;
659 sTemp.Format(IDS_GETVERSIONOFFILE, static_cast<LPCWSTR>(sRev2));
660 progDlg.SetLine(1, sTemp, true);
661 progDlg.SetLine(2, sURL2, true);
662 progDlg.SetProgress(50, 100);
663 if (!CAppUtils::GetVersionedFile(sURL2, sRev2, tempfile2, &progDlg, m_hWnd))
665 progDlg.Stop();
666 CString sErrMsg;
667 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, static_cast<LPCWSTR>(sRev2), static_cast<LPCWSTR>(sURL2));
668 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
669 return FALSE;
671 progDlg.SetProgress(100,100);
672 progDlg.Stop();
673 CString temp;
674 temp.Format(L"%s Revision %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sURL1)), static_cast<LPCWSTR>(sRev1));
675 m_Data.m_baseFile.SetFileName(tempfile1);
676 m_Data.m_baseFile.SetDescriptiveName(temp);
677 temp.Format(L"%s Revision %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sURL2)), static_cast<LPCWSTR>(sRev2));
678 m_Data.m_yourFile.SetFileName(tempfile2);
679 m_Data.m_yourFile.SetDescriptiveName(temp);
681 LoadViews();
683 return TRUE;
686 void CMainFrame::OnFileOpen()
688 if (CheckForSave(ECheckForSaveReason::Open)==IDCANCEL)
689 return;
690 return OnFileOpen(false);
693 void CMainFrame::OnFileOpen(bool fillyours)
695 COpenDlg dlg;
696 if (fillyours)
697 dlg.m_sBaseFile = m_Data.m_yourFile.GetFilename();
698 if (dlg.DoModal() != IDOK)
700 return;
702 m_bMarkedAsResolvedWasDone = false;
703 m_dlgFilePatches.ShowWindow(SW_HIDE);
704 m_dlgFilePatches.Init(nullptr, nullptr, CString(), nullptr);
705 TRACE(L"got the files:\n %s\n %s\n %s\n %s\n %s\n", static_cast<LPCWSTR>(dlg.m_sBaseFile), static_cast<LPCWSTR>(dlg.m_sTheirFile), static_cast<LPCWSTR>(dlg.m_sYourFile),
706 static_cast<LPCWSTR>(dlg.m_sUnifiedDiffFile), static_cast<LPCWSTR>(dlg.m_sPatchDirectory));
707 m_Data.m_baseFile.SetFileName(dlg.m_sBaseFile);
708 m_Data.m_theirFile.SetFileName(dlg.m_sTheirFile);
709 m_Data.m_yourFile.SetFileName(dlg.m_sYourFile);
710 m_Data.m_sDiffFile = dlg.m_sUnifiedDiffFile;
711 m_Data.m_sPatchPath = dlg.m_sPatchDirectory;
712 m_Data.m_mergedFile.SetOutOfUse();
713 CCrashReport::Instance().AddFile2(dlg.m_sBaseFile, nullptr, L"Basefile", CR_AF_MAKE_FILE_COPY);
714 CCrashReport::Instance().AddFile2(dlg.m_sTheirFile, nullptr, L"Theirfile", CR_AF_MAKE_FILE_COPY);
715 CCrashReport::Instance().AddFile2(dlg.m_sYourFile, nullptr, L"Yourfile", CR_AF_MAKE_FILE_COPY);
716 CCrashReport::Instance().AddFile2(dlg.m_sUnifiedDiffFile, nullptr, L"Difffile", CR_AF_MAKE_FILE_COPY);
718 if (!m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && m_Data.IsYourFileInUse())
720 // a diff between two files means "Yours" against "Base", not "Theirs" against "Yours"
721 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
723 if (m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && !m_Data.IsYourFileInUse())
725 // a diff between two files means "Yours" against "Base", not "Theirs" against "Base"
726 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
728 m_bSaveRequired = false;
730 LoadViews();
733 void CMainFrame::ClearViewNamesAndPaths()
735 m_pwndLeftView->m_sWindowName.Empty();
736 m_pwndLeftView->m_sFullFilePath.Empty();
737 m_pwndLeftView->m_sReflectedName.Empty();
738 m_pwndRightView->m_sWindowName.Empty();
739 m_pwndRightView->m_sFullFilePath.Empty();
740 m_pwndRightView->m_sReflectedName.Empty();
741 m_pwndBottomView->m_sWindowName.Empty();
742 m_pwndBottomView->m_sFullFilePath.Empty();
743 m_pwndBottomView->m_sReflectedName.Empty();
746 bool CMainFrame::LoadViews(int line)
748 LoadIgnoreCommentData();
749 m_Data.SetBlame(m_bBlame);
750 m_Data.SetMovedBlocks(m_bViewMovedBlocks);
751 m_bHasConflicts = false;
752 CBaseView* pwndActiveView = m_pwndLeftView;
753 int nOldLine = m_pwndRightView ? m_pwndRightView->m_nTopLine : -1;
754 int nOldLineNumber =
755 m_pwndRightView && m_pwndRightView->m_pViewData ?
756 m_pwndRightView->m_pViewData->GetLineNumber(m_pwndRightView->m_nTopLine) : -1;
757 POINT ptOldCaretPos = {-1, -1};
758 if (m_pwndRightView && m_pwndRightView->IsTarget())
759 ptOldCaretPos = m_pwndRightView->GetCaretPosition();
760 if (m_pwndBottomView && m_pwndBottomView->IsTarget())
761 ptOldCaretPos = m_pwndBottomView->GetCaretPosition();
762 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
763 sExt.TrimLeft(L".");
764 auto sC = m_IgnoreCommentsMap.find(sExt);
765 if (sC == m_IgnoreCommentsMap.end())
767 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
768 sC = m_IgnoreCommentsMap.find(sExt);
769 if (sC == m_IgnoreCommentsMap.end())
771 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
772 sC = m_IgnoreCommentsMap.find(sExt);
775 if (sC != m_IgnoreCommentsMap.end())
776 m_Data.SetCommentTokens(std::get<0>(sC->second), std::get<1>(sC->second), std::get<2>(sC->second));
777 else
778 m_Data.SetCommentTokens(L"", L"", L"");
779 if (!m_Data.Load())
781 m_pwndLeftView->BuildAllScreen2ViewVector();
782 m_pwndLeftView->DocumentUpdated();
783 m_pwndRightView->DocumentUpdated();
784 m_pwndBottomView->DocumentUpdated();
785 m_wndLocatorBar.DocumentUpdated();
786 m_wndLineDiffBar.DocumentUpdated();
787 ::MessageBox(m_hWnd, m_Data.GetError(), L"TortoiseGitMerge", MB_ICONERROR);
788 m_Data.m_mergedFile.SetOutOfUse();
789 m_bSaveRequired = false;
790 return false;
792 SetWindowTitle();
793 m_pwndLeftView->BuildAllScreen2ViewVector();
794 m_pwndLeftView->DocumentUpdated();
795 m_pwndRightView->DocumentUpdated();
796 m_pwndBottomView->DocumentUpdated();
797 m_wndLocatorBar.DocumentUpdated();
798 m_wndLineDiffBar.DocumentUpdated();
800 m_pwndLeftView->SetWritable(false);
801 m_pwndLeftView->SetWritableIsChangable(false);
802 m_pwndLeftView->SetTarget(false);
803 m_pwndRightView->SetWritable(false);
804 m_pwndRightView->SetWritableIsChangable(false);
805 m_pwndRightView->SetTarget(false);
806 m_pwndBottomView->SetWritable(false);
807 m_pwndBottomView->SetWritableIsChangable(false);
808 m_pwndBottomView->SetTarget(false);
810 if (!m_Data.IsBaseFileInUse())
812 CSysProgressDlg progDlg;
813 if (m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
815 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
817 else if ((!m_Data.m_sDiffFile.IsEmpty())&&(!m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg)))
819 progDlg.Stop();
820 ClearViewNamesAndPaths();
821 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
822 m_bSaveRequired = false;
823 return false;
825 progDlg.Stop();
826 if (m_Patch.GetNumberOfFiles() > 0)
828 CString betterpatchpath = m_Patch.CheckPatchPath(m_Data.m_sPatchPath);
829 if (betterpatchpath.CompareNoCase(m_Data.m_sPatchPath)!=0)
831 CString msg;
832 msg.FormatMessage(IDS_WARNBETTERPATCHPATHFOUND, static_cast<LPCWSTR>(m_Data.m_sPatchPath), static_cast<LPCWSTR>(betterpatchpath));
833 CTaskDialog taskdlg(msg,
834 CString(MAKEINTRESOURCE(IDS_WARNBETTERPATCHPATHFOUND_TASK2)),
835 L"TortoiseGitMerge",
837 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
838 CString task3;
839 WCHAR t3[MAX_PATH] = { 0 };
840 CString cp = betterpatchpath.Left(MAX_PATH - 1);
841 PathCompactPathEx(t3, cp, 50, 0);
842 task3.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK3, t3);
843 taskdlg.AddCommandControl(1, task3);
844 CString task4;
845 WCHAR t4[MAX_PATH] = { 0 };
846 cp = m_Data.m_sPatchPath.Left(MAX_PATH - 1);
847 PathCompactPathEx(t4, cp, 50, 0);
848 task4.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK4, t4);
849 taskdlg.AddCommandControl(2, task4);
850 taskdlg.SetDefaultCommandControl(1);
851 taskdlg.SetMainIcon(TD_INFORMATION_ICON);
852 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
853 if (taskdlg.DoModal(m_hWnd) == 1)
855 m_Data.m_sPatchPath = betterpatchpath;
856 m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg);
859 m_dlgFilePatches.Init(&m_Patch, this, m_Data.m_sPatchPath, this);
860 m_dlgFilePatches.ShowWindow(SW_SHOW);
861 ClearViewNamesAndPaths();
862 if (!m_wndSplitter.IsRowHidden(1))
863 m_wndSplitter.HideRow(1);
864 m_pwndLeftView->SetHidden(FALSE);
865 m_pwndRightView->SetHidden(FALSE);
866 m_pwndBottomView->SetHidden(TRUE);
869 if (m_Data.IsBaseFileInUse() && !m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
871 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
873 if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && !m_Data.IsTheirFileInUse())
875 //diff between YOUR and BASE
876 if (m_bOneWay)
878 pwndActiveView = m_pwndLeftView;
879 if (!m_wndSplitter2.IsColumnHidden(1))
880 m_wndSplitter2.HideColumn(1);
882 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseBoth;
883 m_pwndLeftView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
884 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
885 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName() + L" - " + m_Data.m_yourFile.GetWindowName();
886 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename() + L" - " + m_Data.m_yourFile.GetFilename();
887 m_pwndLeftView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
888 m_pwndLeftView->m_pWorkingFile = &m_Data.m_yourFile;
889 m_pwndLeftView->SetTarget();
890 m_pwndLeftView->SetWritableIsChangable(true);
892 m_pwndRightView->m_pViewData = nullptr;
893 m_pwndRightView->m_pWorkingFile = nullptr;
894 m_pwndBottomView->m_pViewData = nullptr;
895 m_pwndBottomView->m_pWorkingFile = nullptr;
897 if (!m_wndSplitter.IsRowHidden(1))
898 m_wndSplitter.HideRow(1);
899 m_pwndLeftView->SetHidden(FALSE);
900 m_pwndRightView->SetHidden(TRUE);
901 m_pwndBottomView->SetHidden(TRUE);
902 ::SetWindowPos(m_pwndLeftView->m_hWnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
904 else
906 pwndActiveView = m_pwndRightView;
907 if (m_wndSplitter2.IsColumnHidden(1))
908 m_wndSplitter2.ShowColumn();
910 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseLeft;
911 m_pwndLeftView->SetTextType(m_Data.m_arBaseFile.GetUnicodeType());
912 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arBaseFile.GetLineEndings());
913 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName();
914 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename();
915 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_baseFile.GetConvertedFileName();
916 m_pwndLeftView->m_sReflectedName = m_Data.m_baseFile.GetReflectedName();
917 m_pwndLeftView->m_pWorkingFile = &m_Data.m_baseFile;
918 m_pwndLeftView->SetWritableIsChangable(true);
920 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseRight;
921 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
922 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
923 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName();
924 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
925 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
926 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
927 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
928 m_pwndRightView->SetWritable();
929 m_pwndRightView->SetTarget();
931 m_pwndBottomView->m_pViewData = nullptr;
932 m_pwndBottomView->m_pWorkingFile = nullptr;
934 if (!m_wndSplitter.IsRowHidden(1))
935 m_wndSplitter.HideRow(1);
936 m_pwndLeftView->SetHidden(FALSE);
937 m_pwndRightView->SetHidden(FALSE);
938 m_pwndBottomView->SetHidden(TRUE);
940 bool hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods;
941 pwndActiveView->CheckModifications(hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods);
942 if (!hasMods && !hasConflicts)
944 // files appear identical, show a dialog informing the user that there are or might
945 // be other differences
946 bool hasEncodingDiff = m_Data.m_arBaseFile.GetUnicodeType() != m_Data.m_arYourFile.GetUnicodeType();
947 bool hasEOLDiff = m_Data.m_arBaseFile.GetLineEndings() != m_Data.m_arYourFile.GetLineEndings();
948 if (hasWhitespaceMods || hasEncodingDiff || hasEOLDiff)
950 // text is identical, but the files do not match
951 CString sWarning(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_MAIN));
952 CString sWhitespace(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_WHITESPACE));
953 CString sEncoding(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_ENCODING));
954 CString sEOL(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_EOL));
955 if (hasWhitespaceMods)
957 sWarning += L"\r\n";
958 sWarning += sWhitespace;
960 if (hasEncodingDiff)
962 sWarning += L"\r\n";
963 sWarning += sEncoding;
964 sWarning += L" (";
965 sWarning += m_Data.m_arBaseFile.GetEncodingName(m_Data.m_arBaseFile.GetUnicodeType());
966 sWarning += L", ";
967 sWarning += m_Data.m_arYourFile.GetEncodingName(m_Data.m_arYourFile.GetUnicodeType());
968 sWarning += L")";
970 if (hasEOLDiff)
972 sWarning += L"\r\n";
973 sWarning += sEOL;
975 AfxMessageBox(sWarning, MB_ICONINFORMATION);
979 else if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
981 //diff between THEIR, YOUR and BASE
982 m_pwndBottomView->SetWritable();
983 m_pwndBottomView->SetTarget();
984 pwndActiveView = m_pwndBottomView;
986 m_pwndLeftView->m_pViewData = &m_Data.m_TheirBaseBoth;
987 m_pwndLeftView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
988 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
989 m_pwndLeftView->m_sWindowName = m_Data.m_theirFile.GetWindowName(IDS_VIEWTITLE_THEIRS);
990 m_pwndLeftView->m_sFullFilePath = m_Data.m_theirFile.GetFilename();
991 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_theirFile.GetConvertedFileName();
992 m_pwndLeftView->m_sReflectedName = m_Data.m_theirFile.GetReflectedName();
993 m_pwndLeftView->m_pWorkingFile = &m_Data.m_theirFile;
995 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseBoth;
996 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
997 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
998 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName(IDS_VIEWTITLE_MINE);
999 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
1000 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
1001 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
1002 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
1004 m_pwndBottomView->m_pViewData = &m_Data.m_Diff3;
1005 m_pwndBottomView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
1006 m_pwndBottomView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
1007 m_pwndBottomView->m_sWindowName.LoadString(IDS_VIEWTITLE_MERGED);
1008 m_pwndBottomView->m_sWindowName += L" - " + m_Data.m_mergedFile.GetWindowName();
1009 m_pwndBottomView->m_sFullFilePath = m_Data.m_mergedFile.GetFilename();
1010 m_pwndBottomView->m_sConvertedFilePath = m_Data.m_mergedFile.GetConvertedFileName();
1011 m_pwndBottomView->m_sReflectedName = m_Data.m_mergedFile.GetReflectedName();
1012 m_pwndBottomView->m_pWorkingFile = &m_Data.m_mergedFile;
1014 if (m_wndSplitter2.IsColumnHidden(1))
1015 m_wndSplitter2.ShowColumn();
1016 if (m_wndSplitter.IsRowHidden(1))
1017 m_wndSplitter.ShowRow();
1018 m_pwndLeftView->SetHidden(FALSE);
1019 m_pwndRightView->SetHidden(FALSE);
1020 m_pwndBottomView->SetHidden(FALSE);
1021 // in three pane view, hide the line diff bar
1022 m_wndLineDiffBar.ShowPane(false, false, true);
1023 m_wndLineDiffBar.DocumentUpdated();
1025 if (!m_Data.m_mergedFile.InUse())
1027 m_Data.m_mergedFile.SetFileName(m_Data.m_yourFile.GetFilename());
1029 m_pwndLeftView->BuildAllScreen2ViewVector();
1030 m_pwndLeftView->DocumentUpdated();
1031 m_pwndRightView->DocumentUpdated();
1032 m_pwndBottomView->DocumentUpdated();
1033 m_wndLocatorBar.DocumentUpdated();
1034 m_wndLineDiffBar.DocumentUpdated();
1035 UpdateLayout();
1036 SetActiveView(pwndActiveView);
1038 if ((line >= -1) && m_pwndRightView->m_pViewData)
1040 int n = line == -1 ? min( nOldLineNumber, nOldLine ) : line;
1041 if (n >= 0)
1043 n = m_pwndRightView->m_pViewData->FindLineNumber(n);
1044 if (m_bCollapsed && line >= 0)
1046 // adjust the goto-line position if we're collapsed
1047 int step = m_pwndRightView->m_nTopLine > n ? -1 : 1;
1048 int skip = 0;
1049 for (int i = m_pwndRightView->m_nTopLine; i != n; i += step)
1051 if (m_pwndRightView->m_pViewData->GetHideState(i) == HideState::Hidden)
1052 ++skip;
1054 if (m_pwndRightView->m_pViewData->GetHideState(n) == HideState::Hidden)
1055 OnViewTextFoldUnfold();
1056 else
1057 n = n + (skip * step * -1);
1060 if (n < 0)
1061 n = nOldLine;
1063 m_pwndRightView->ScrollAllToLine(n);
1064 POINT p;
1065 p.x = 0;
1066 p.y = n;
1067 if ((ptOldCaretPos.x >= 0) || (ptOldCaretPos.y >= 0))
1068 p = ptOldCaretPos;
1069 m_pwndLeftView->SetCaretPosition(p);
1070 m_pwndRightView->SetCaretPosition(p);
1071 m_pwndBottomView->SetCaretPosition(p);
1072 m_pwndBottomView->ScrollToChar(0);
1073 m_pwndLeftView->ScrollToChar(0);
1074 m_pwndRightView->ScrollToChar(0);
1076 else
1078 CRegDWORD regFirstDiff = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstDiffOnLoad", TRUE);
1079 CRegDWORD regFirstConflict = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstConflictOnLoad", TRUE);
1080 bool bGoFirstDiff = (0 != static_cast<DWORD>(regFirstDiff));
1081 bool bGoFirstConflict = (0 != static_cast<DWORD>(regFirstConflict));
1082 if (bGoFirstConflict && (CheckResolved()>=0))
1084 pwndActiveView->GoToFirstConflict();
1085 // Ignore the first few Mouse Move messages, so that the line diff stays on
1086 // the first diff line until the user actually moves the mouse
1087 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1089 else if (bGoFirstDiff)
1091 pwndActiveView->GoToFirstDifference();
1092 // Ignore the first few Mouse Move messages, so that the line diff stays on
1093 // the first diff line until the user actually moves the mouse
1094 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1096 else
1098 // Avoid incorrect rendering of active pane.
1099 m_pwndBottomView->ScrollToChar(0);
1100 m_pwndLeftView->ScrollToChar(0);
1101 m_pwndRightView->ScrollToChar(0);
1104 CheckResolved();
1105 if (m_bHasConflicts && !m_bSaveRequiredOnConflicts)
1106 m_bSaveRequired = false;
1107 CUndo::GetInstance().Clear();
1108 return true;
1111 void CMainFrame::UpdateLayout()
1113 if (m_bInitSplitter)
1115 m_wndSplitter.CenterSplitter();
1116 m_wndSplitter2.CenterSplitter();
1120 void CMainFrame::RecalcLayout(BOOL bNotify)
1122 GetClientRect(&m_dockManager.m_rectInPlace);
1123 if (m_pRibbonApp)
1124 m_dockManager.m_rectInPlace.top += m_pRibbonApp->GetRibbonHeight();
1125 CFrameWndEx::RecalcLayout(bNotify);
1128 void CMainFrame::OnSize(UINT nType, int cx, int cy)
1130 CFrameWndEx::OnSize(nType, cx, cy);
1131 if (m_bInitSplitter && nType != SIZE_MINIMIZED)
1133 if (m_wndSplitter.GetSafeHwnd())
1135 if (m_wndSplitter.HasOldRowSize() && (m_wndSplitter.GetOldRowCount() == 2))
1137 int oldTotal = m_wndSplitter.GetOldRowSize(0) + m_wndSplitter.GetOldRowSize(1);
1138 if (oldTotal)
1140 int cxCur0, cxCur1, cxMin0, cxMin1;
1141 m_wndSplitter.GetRowInfo(0, cxCur0, cxMin0);
1142 m_wndSplitter.GetRowInfo(1, cxCur1, cxMin1);
1143 cxCur0 = m_wndSplitter.GetOldRowSize(0) * (cxCur0 + cxCur1) / oldTotal;
1144 cxCur1 = m_wndSplitter.GetOldRowSize(1) * (cxCur0 + cxCur1) / oldTotal;
1145 m_wndSplitter.SetRowInfo(0, cxCur0, 0);
1146 m_wndSplitter.SetRowInfo(1, cxCur1, 0);
1147 m_wndSplitter.RecalcLayout();
1151 if (m_wndSplitter2.HasOldColSize() && (m_wndSplitter2.GetOldColCount() == 2))
1153 int oldTotal = m_wndSplitter2.GetOldColSize(0) + m_wndSplitter2.GetOldColSize(1);
1154 if (oldTotal)
1156 int cyCur0, cyCur1, cyMin0, cyMin1;
1157 m_wndSplitter2.GetColumnInfo(0, cyCur0, cyMin0);
1158 m_wndSplitter2.GetColumnInfo(1, cyCur1, cyMin1);
1159 cyCur0 = m_wndSplitter2.GetOldColSize(0) * (cyCur0 + cyCur1) / oldTotal;
1160 cyCur1 = m_wndSplitter2.GetOldColSize(1) * (cyCur0 + cyCur1) / oldTotal;
1161 m_wndSplitter2.SetColumnInfo(0, cyCur0, 0);
1162 m_wndSplitter2.SetColumnInfo(1, cyCur1, 0);
1163 m_wndSplitter2.RecalcLayout();
1168 if ((nType == SIZE_RESTORED)&&m_bCheckReload)
1170 m_bCheckReload = false;
1171 CheckForReload();
1175 void CMainFrame::OnViewWhitespaces()
1177 CRegDWORD regViewWhitespaces = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
1178 BOOL bViewWhitespaces = regViewWhitespaces;
1179 if (m_pwndLeftView)
1180 bViewWhitespaces = m_pwndLeftView->m_bViewWhitespace;
1182 bViewWhitespaces = !bViewWhitespaces;
1183 regViewWhitespaces = bViewWhitespaces;
1184 if (m_pwndLeftView)
1186 m_pwndLeftView->m_bViewWhitespace = bViewWhitespaces;
1187 m_pwndLeftView->Invalidate();
1189 if (m_pwndRightView)
1191 m_pwndRightView->m_bViewWhitespace = bViewWhitespaces;
1192 m_pwndRightView->Invalidate();
1194 if (m_pwndBottomView)
1196 m_pwndBottomView->m_bViewWhitespace = bViewWhitespaces;
1197 m_pwndBottomView->Invalidate();
1201 void CMainFrame::OnUpdateViewWhitespaces(CCmdUI *pCmdUI)
1203 if (m_pwndLeftView)
1204 pCmdUI->SetCheck(m_pwndLeftView->m_bViewWhitespace);
1207 void CMainFrame::OnViewCollapsed()
1209 m_regCollapsed = !static_cast<DWORD>(m_regCollapsed);
1210 m_bCollapsed = !!static_cast<DWORD>(m_regCollapsed);
1212 OnViewTextFoldUnfold();
1213 m_wndLocatorBar.Invalidate();
1216 void CMainFrame::OnUpdateViewCollapsed(CCmdUI *pCmdUI)
1218 pCmdUI->SetCheck(m_bCollapsed);
1221 void CMainFrame::OnViewWraplonglines()
1223 m_bWrapLines = !static_cast<DWORD>(m_regWrapLines);
1224 m_regWrapLines = m_bWrapLines;
1226 if (m_pwndLeftView) m_pwndLeftView ->WrapChanged();
1227 if (m_pwndRightView) m_pwndRightView ->WrapChanged();
1228 if (m_pwndBottomView) m_pwndBottomView->WrapChanged();
1229 OnViewTextFoldUnfold();
1230 m_wndLocatorBar.DocumentUpdated();
1233 void CMainFrame::OnViewTextFoldUnfold()
1235 OnViewTextFoldUnfold(m_pwndLeftView);
1236 OnViewTextFoldUnfold(m_pwndRightView);
1237 OnViewTextFoldUnfold(m_pwndBottomView);
1240 void CMainFrame::OnViewTextFoldUnfold(CBaseView* view)
1242 if (view == 0)
1243 return;
1244 view->BuildAllScreen2ViewVector();
1245 view->UpdateCaret();
1246 view->Invalidate();
1247 view->EnsureCaretVisible();
1250 void CMainFrame::OnUpdateViewWraplonglines(CCmdUI *pCmdUI)
1252 pCmdUI->SetCheck(m_bWrapLines);
1255 void CMainFrame::OnViewOnewaydiff()
1257 if (CheckForSave(ECheckForSaveReason::Reload)==IDCANCEL)
1258 return;
1259 m_bOneWay = !m_bOneWay;
1260 m_regOneWay = m_bOneWay;
1261 ShowDiffBar(!m_bOneWay);
1262 LoadViews(-1);
1265 void CMainFrame::DiffLeftToBase()
1267 DiffTwo(m_Data.m_baseFile, m_Data.m_theirFile);
1270 void CMainFrame::DiffRightToBase()
1272 DiffTwo(m_Data.m_baseFile, m_Data.m_yourFile);
1275 void CMainFrame::DiffTwo(const CWorkingFile& file1, const CWorkingFile& file2)
1277 wchar_t ownpath[MAX_PATH] = { 0 };
1278 GetModuleFileName(nullptr, ownpath, MAX_PATH);
1279 CString sCmd;
1280 sCmd.Format(L"\"%s\"", ownpath);
1281 sCmd += L" /base:\"";
1282 sCmd += file1.GetFilename();
1283 sCmd += L"\" /mine:\"";
1284 sCmd += file2.GetFilename();
1285 sCmd += L'"';
1286 if (!file1.GetWindowName().IsEmpty())
1287 sCmd += L" /basename:\"" + file1.GetWindowName() + L"\"";
1288 if (!file2.GetWindowName().IsEmpty())
1289 sCmd += L" /yourname:\"" + file2.GetWindowName() + L"\"";
1291 CAppUtils::LaunchApplication(sCmd, CAppUtils::LaunchApplicationFlags());
1294 void CMainFrame::ShowDiffBar(bool bShow)
1296 if (bShow)
1298 // restore the line diff bar
1299 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
1300 m_wndLineDiffBar.DocumentUpdated();
1301 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
1302 m_wndLocatorBar.DocumentUpdated();
1304 else
1306 // in one way view, hide the line diff bar
1307 m_wndLineDiffBar.ShowPane(false, false, true);
1308 m_wndLineDiffBar.DocumentUpdated();
1312 // Implementation helper only,
1313 // use CTheme::Instance::SetDarkTheme to actually set the theme.
1314 #ifndef UI_PKEY_DarkModeRibbon
1315 DEFINE_UIPROPERTYKEY(UI_PKEY_DarkModeRibbon, VT_BOOL, 2004);
1316 DEFINE_UIPROPERTYKEY(UI_PKEY_ApplicationButtonColor, VT_UI4, 2003);
1317 #endif
1318 void CMainFrame::SetTheme(bool bDark)
1320 if (m_bUseRibbons)
1321 SetAccentColor();
1323 if (bDark)
1325 CComPtr<IPropertyStore> spPropertyStore;
1326 if (m_bUseRibbons && SUCCEEDED(m_pRibbonFramework->QueryInterface(&spPropertyStore)))
1328 DarkModeHelper::Instance().AllowDarkModeForApp(TRUE);
1329 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), TRUE);
1330 PROPVARIANT propvarDarkMode;
1331 InitPropVariantFromBoolean(1, &propvarDarkMode);
1332 spPropertyStore->SetValue(UI_PKEY_DarkModeRibbon, propvarDarkMode);
1333 spPropertyStore->Commit();
1335 SetClassLongPtr(GetSafeHwnd(), GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(GetStockObject(BLACK_BRUSH)));
1336 BOOL darkFlag = TRUE;
1337 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data = { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS, &darkFlag, sizeof(darkFlag) };
1338 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data);
1339 DarkModeHelper::Instance().FlushMenuThemes();
1340 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
1342 else
1344 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE);
1345 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), FALSE);
1346 CComPtr<IPropertyStore> spPropertyStore;
1347 if (m_bUseRibbons && SUCCEEDED(m_pRibbonFramework->QueryInterface(&spPropertyStore)))
1349 PROPVARIANT propvarDarkMode;
1350 InitPropVariantFromBoolean(false, &propvarDarkMode);
1351 spPropertyStore->SetValue(UI_PKEY_DarkModeRibbon, propvarDarkMode);
1352 spPropertyStore->Commit();
1354 SetClassLongPtr(GetSafeHwnd(), GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(GetSysColorBrush(COLOR_3DFACE)));
1355 BOOL darkFlag = FALSE;
1356 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data = { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS, &darkFlag, sizeof(darkFlag) };
1357 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data);
1358 DarkModeHelper::Instance().FlushMenuThemes();
1359 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
1361 ::RedrawWindow(GetSafeHwnd(), nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT | RDW_ALLCHILDREN | RDW_UPDATENOW);
1364 void CMainFrame::SetAccentColor()
1366 if (!m_bUseRibbons)
1367 return;
1369 // set the accent color for the main button
1370 if (m_pRibbonFramework)
1372 Win10Colors::AccentColor accentColor;
1373 if (SUCCEEDED(Win10Colors::GetAccentColor(accentColor)))
1375 BYTE h, s, b;
1376 CTheme::RGBToHSB(accentColor.accent, h, s, b);
1377 UI_HSBCOLOR aColor = UI_HSB(h, s, b);
1379 CComPtr<IPropertyStore> spPropertyStore;
1380 HRESULT hr = m_pRibbonFramework->QueryInterface(&spPropertyStore);
1381 if (SUCCEEDED(hr))
1383 PROPVARIANT propvarAccentColor;
1384 InitPropVariantFromUInt32(aColor, &propvarAccentColor);
1385 spPropertyStore->SetValue(UI_PKEY_ApplicationButtonColor, propvarAccentColor);
1386 spPropertyStore->Commit();
1392 int CMainFrame::CheckResolved()
1394 //only in three way diffs can be conflicts!
1395 m_bHasConflicts = true;
1396 if (IsViewGood(m_pwndBottomView))
1398 CViewData* viewdata = m_pwndBottomView->m_pViewData;
1399 if (viewdata)
1401 for (int i=0; i<viewdata->GetCount(); i++)
1403 const DiffState state = viewdata->GetState(i);
1404 if ((DiffState::Conflicted == state)||(DiffState::Conflicted_Ignored == state))
1405 return i;
1409 m_bHasConflicts = false;
1410 return -1;
1413 int CMainFrame::SaveFile(const CString& sFilePath)
1415 CBaseView* pView = nullptr;
1416 CViewData* pViewData = nullptr;
1417 CFileTextLines * pOriginFile = &m_Data.m_arBaseFile;
1418 if (IsViewGood(m_pwndBottomView))
1420 pView = m_pwndBottomView;
1421 pViewData = m_pwndBottomView->m_pViewData;
1423 else if (IsViewGood(m_pwndRightView))
1425 pView = m_pwndRightView;
1426 pViewData = m_pwndRightView->m_pViewData;
1427 if (m_Data.IsYourFileInUse())
1428 pOriginFile = &m_Data.m_arYourFile;
1429 else if (m_Data.IsTheirFileInUse())
1430 pOriginFile = &m_Data.m_arTheirFile;
1432 else
1434 // nothing to save!
1435 return 1;
1437 Invalidate();
1438 if ((pViewData)&&(pOriginFile))
1440 CFileTextLines file;
1441 pOriginFile->CopySettings(&file);
1442 CFileTextLines::SaveParams saveParams;
1443 saveParams.m_LineEndings = pView->GetLineEndings();
1444 saveParams.m_UnicodeType = pView->GetTextType();
1445 file.SetSaveParams(saveParams);
1446 for (int i=0; i<pViewData->GetCount(); i++)
1448 //only copy non-removed lines
1449 DiffState state = pViewData->GetState(i);
1450 switch (state)
1452 case DiffState::Conflicted:
1453 case DiffState::Conflicted_Ignored:
1455 int first = i;
1456 int last = i;
1459 last++;
1460 } while((last<pViewData->GetCount()) && ((pViewData->GetState(last)==DiffState::Conflicted)||(pViewData->GetState(last)==DiffState::Conflicted_Ignored)));
1461 // TortoiseGitMerge changes here
1462 file.Add(L"<<<<<<< .mine", m_pwndRightView->GetLineEndings());
1463 for (int j=first; j<last; j++)
1465 EOL lineending = m_pwndRightView->m_pViewData->GetLineEnding(j);
1466 if (lineending == EOL::NoEnding)
1467 lineending = m_pwndRightView->GetLineEndings();
1468 file.Add(m_pwndRightView->m_pViewData->GetLine(j), lineending);
1470 file.Add(L"=======", m_pwndRightView->GetLineEndings());
1471 for (int j=first; j<last; j++)
1473 EOL lineending = m_pwndLeftView->m_pViewData->GetLineEnding(j);
1474 if (lineending == EOL::NoEnding)
1475 lineending = m_pwndLeftView->GetLineEndings();
1476 file.Add(m_pwndLeftView->m_pViewData->GetLine(j), lineending);
1478 file.Add(L">>>>>>> .theirs", m_pwndRightView->GetLineEndings());
1479 i = last-1;
1481 break;
1482 case DiffState::Empty:
1483 case DiffState::ConflictEmpty:
1484 case DiffState::IdenticalRemoved:
1485 case DiffState::Removed:
1486 case DiffState::TheirsRemoved:
1487 case DiffState::YoursRemoved:
1488 case DiffState::ConflictResolvedEmpty:
1489 // do not save removed lines
1490 break;
1491 default:
1492 file.Add(pViewData->GetLine(i), pViewData->GetLineEnding(i));
1493 break;
1496 if (!file.Save(sFilePath, false, false))
1498 CMessageBox::Show(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
1499 return -1;
1501 if (sFilePath == m_Data.m_baseFile.GetFilename())
1503 m_Data.m_baseFile.StoreFileAttributes();
1505 if (sFilePath == m_Data.m_theirFile.GetFilename())
1507 m_Data.m_theirFile.StoreFileAttributes();
1509 if (sFilePath == m_Data.m_yourFile.GetFilename())
1511 m_Data.m_yourFile.StoreFileAttributes();
1513 /*if (sFilePath == m_Data.m_mergedFile.GetFilename())
1515 m_Data.m_mergedFile.StoreFileAttributes();
1516 }//*/
1517 m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
1518 if (IsViewGood(m_pwndBottomView))
1519 m_pwndBottomView->SetModified(FALSE);
1520 else if (IsViewGood(m_pwndRightView))
1521 m_pwndRightView->SetModified(FALSE);
1522 CUndo::GetInstance().MarkAsOriginalState(
1523 false,
1524 IsViewGood(m_pwndRightView) && (pViewData == m_pwndRightView->m_pViewData),
1525 IsViewGood(m_pwndBottomView) && (pViewData == m_pwndBottomView->m_pViewData));
1526 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL::NoEnding)
1527 return 0;
1528 return file.GetCount();
1530 return 1;
1533 void CMainFrame::OnFileSave()
1535 // when multiple files are set as writable we have to ask what file to save
1536 int nEditableViewCount =
1537 (CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsWritable() ? 1 : 0)
1538 + (CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsWritable() ? 1 : 0)
1539 + (CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsWritable() ? 1 : 0);
1540 bool bLeftIsModified = CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsModified();
1541 bool bRightIsModified = CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsModified();
1542 bool bBottomIsModified = CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsModified();
1543 int nModifiedViewCount =
1544 (bLeftIsModified ? 1 : 0)
1545 + (bRightIsModified ? 1 : 0)
1546 + (bBottomIsModified ? 1 : 0);
1547 if (nEditableViewCount>1)
1549 if (nModifiedViewCount == 1)
1551 if (bLeftIsModified)
1552 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1553 else
1554 FileSave();
1556 else if (nModifiedViewCount>0)
1558 // both views
1559 CTaskDialog taskdlg(CString(MAKEINTRESOURCE(IDS_SAVE_MORE)),
1560 CString(MAKEINTRESOURCE(IDS_SAVE)),
1561 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1563 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1564 CString sTaskTemp;
1565 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
1566 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, static_cast<LPCWSTR>(m_pwndLeftView->m_pWorkingFile->GetFilename()));
1567 else
1568 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
1569 taskdlg.AddCommandControl(201, sTaskTemp, bLeftIsModified);// left
1570 if (bLeftIsModified)
1571 taskdlg.SetDefaultCommandControl(201);
1572 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
1573 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, static_cast<LPCWSTR>(m_pwndRightView->m_pWorkingFile->GetFilename()));
1574 else
1575 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
1576 taskdlg.AddCommandControl(202, sTaskTemp, bRightIsModified); // right
1577 if (bRightIsModified)
1578 taskdlg.SetDefaultCommandControl(202);
1579 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2)), nModifiedViewCount>1); // both
1580 if (nModifiedViewCount > 1)
1581 taskdlg.SetDefaultCommandControl(203);
1582 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1583 taskdlg.SetMainIcon(TD_WARNING_ICON);
1584 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
1585 switch (ret)
1587 case 201: // left
1588 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1589 break;
1590 case 203: // both
1591 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1592 [[fallthrough]];
1593 case 202: // right
1594 m_pwndRightView->SaveFile();
1595 break;
1599 else
1601 // only target view was modified
1602 FileSave();
1606 void CMainFrame::PatchSave()
1608 bool bDoesNotExist = !PathFileExists(m_Data.m_mergedFile.GetFilename());
1609 if (m_Data.m_bPatchRequired)
1611 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1613 if (!PathIsDirectory(m_Data.m_mergedFile.GetFilename()))
1615 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1616 if (saveret==0)
1618 // file was saved with 0 lines, remove it.
1619 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1620 // just in case
1621 DeleteFile(m_Data.m_mergedFile.GetFilename());
1623 m_Data.m_mergedFile.StoreFileAttributes();
1624 if (m_Data.m_mergedFile.GetFilename() == m_Data.m_yourFile.GetFilename())
1625 m_Data.m_yourFile.StoreFileAttributes();
1626 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1628 // call TortoiseProc to add the new file to version control
1629 CString cmd = L"/command:add /noui /path:\"";
1630 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1631 CAppUtils::RunTortoiseGitProc(cmd);
1636 bool CMainFrame::FileSave(bool bCheckResolved /*=true*/)
1638 if (HasMarkedBlocks())
1640 CString sTitle(MAKEINTRESOURCE(IDS_WARNMARKEDBLOCKS));
1641 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_MARKEDBLOCKS));
1642 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
1643 CTaskDialog taskdlg(sTitle,
1644 sSubTitle,
1645 sAppName,
1647 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1648 taskdlg.AddCommandControl(10, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEINCLUDE)));
1649 taskdlg.AddCommandControl(11, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEEXCLUDE)));
1650 taskdlg.AddCommandControl(12, CString(MAKEINTRESOURCE(IDS_MARKEDBLCOKSSAVEIGNORE)));
1651 taskdlg.AddCommandControl(IDCANCEL, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_CANCEL_OPEN)));
1652 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1653 taskdlg.SetDefaultCommandControl(10);
1654 taskdlg.SetMainIcon(TD_WARNING_ICON);
1655 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
1656 if (ret == 10)
1657 m_pwndRightView->LeaveOnlyMarkedBlocks();
1658 else if (ret == 11)
1659 m_pwndRightView->UseViewFileOfMarked();
1660 else if (ret == 12)
1661 m_pwndRightView->UseViewFileExceptEdited();
1662 else
1663 return false;
1666 if (!m_Data.m_mergedFile.InUse())
1667 return FileSaveAs(bCheckResolved);
1668 // check if the file has the readonly attribute set
1669 bool bDoesNotExist = false;
1670 DWORD fAttribs = GetFileAttributes(m_Data.m_mergedFile.GetFilename());
1671 if ((fAttribs != INVALID_FILE_ATTRIBUTES)&&(fAttribs & FILE_ATTRIBUTE_READONLY))
1672 return FileSaveAs(bCheckResolved);
1673 if (fAttribs == INVALID_FILE_ATTRIBUTES)
1675 bDoesNotExist = (GetLastError() == ERROR_FILE_NOT_FOUND);
1677 if (bCheckResolved && HasConflictsWontKeep())
1678 return false;
1680 if ((static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGitMerge\\Backup"))) != 0)
1682 MoveFileEx(m_Data.m_mergedFile.GetFilename(), m_Data.m_mergedFile.GetFilename() + L".bak", MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
1684 if (m_Data.m_bPatchRequired)
1686 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1688 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1689 if (saveret==0)
1691 // file was saved with 0 lines!
1692 // ask the user if the file should be deleted
1693 CString msg;
1694 msg.Format(IDS_DELETEWHENEMPTY, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename())));
1695 CTaskDialog taskdlg(msg,
1696 CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK2)),
1697 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1699 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1700 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK3)));
1701 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK4)));
1702 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1703 taskdlg.SetDefaultCommandControl(1);
1704 taskdlg.SetMainIcon(TD_WARNING_ICON);
1705 if (taskdlg.DoModal(m_hWnd) == 1)
1707 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1708 DeleteFile(m_Data.m_mergedFile.GetFilename());
1711 else if (saveret < 0)
1713 // error while saving the file
1714 return false;
1717 // if we're in conflict resolve mode (three pane view), check if there are no more conflicts
1718 // and if there aren't, ask to mark the file as resolved
1719 if (IsViewGood(m_pwndBottomView) && !m_bHasConflicts && bCheckResolved)
1721 CString projectRoot;
1722 if (GitAdminDir::HasAdminDir(m_Data.m_mergedFile.GetFilename(), false, &projectRoot))
1724 CString subpath = m_Data.m_mergedFile.GetFilename();
1725 subpath.Replace(L'\\', L'/');
1726 if (subpath.GetLength() >= projectRoot.GetLength())
1728 if (subpath[projectRoot.GetLength()] == L'/')
1729 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength() - 1);
1730 else
1731 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength());
1734 CAutoRepository repository(projectRoot);
1735 bool hasConflictInIndex = false;
1738 if (!repository)
1739 break;
1741 CAutoIndex index;
1742 if (git_repository_index(index.GetPointer(), repository))
1743 break;
1745 CStringA path = CUnicodeUtils::GetUTF8(subpath);
1746 hasConflictInIndex = git_index_get_bypath(index, path, 1) || git_index_get_bypath(index, path, 2);
1747 } while(0);
1749 if (hasConflictInIndex)
1751 CString msg;
1752 msg.Format(IDS_MARKASRESOLVED, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename())));
1753 CTaskDialog taskdlg(msg,
1754 CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK2)),
1755 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1757 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1758 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK3)));
1759 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK4)));
1760 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1761 taskdlg.SetDefaultCommandControl(1);
1762 taskdlg.SetMainIcon(TD_WARNING_ICON);
1763 if (taskdlg.DoModal(m_hWnd) == 1)
1764 MarkAsResolved();
1769 m_bSaveRequired = false;
1770 m_Data.m_mergedFile.StoreFileAttributes();
1772 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1774 // call TortoiseProc to add the new file to version control
1775 CString cmd = L"/command:add /noui /path:\"";
1776 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1777 if(!CAppUtils::RunTortoiseGitProc(cmd))
1778 return false;
1780 return true;
1783 void CMainFrame::OnFileSaveAs()
1785 // ask what file to save as
1786 bool bHaveConflict = (CheckResolved() >= 0);
1787 CTaskDialog taskdlg(
1788 CString(MAKEINTRESOURCE(bHaveConflict ? IDS_ASKFORSAVEAS_MORECONFLICT : IDS_ASKFORSAVEAS_MORE)),
1789 CString(MAKEINTRESOURCE(IDS_ASKFORSAVEAS)),
1790 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1792 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1793 // default can be last view (target) as was in 1.7 or actual (where is cursor) as is in most text editor
1794 if (IsViewGood(m_pwndLeftView))
1796 taskdlg.AddCommandControl(201, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS))); // left
1797 taskdlg.SetDefaultCommandControl(201);
1799 if (IsViewGood(m_pwndRightView))
1801 taskdlg.AddCommandControl(202, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS))); // right
1802 taskdlg.SetDefaultCommandControl(202);
1804 if (IsViewGood(m_pwndBottomView))
1806 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEBOTTOMAS))); // bottom
1807 taskdlg.SetDefaultCommandControl(203);
1809 if (bHaveConflict)
1811 taskdlg.AddCommandControl(204, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_NEEDRESOLVE))); // resolve
1812 taskdlg.SetDefaultCommandControl(204);
1814 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1815 taskdlg.SetMainIcon(bHaveConflict ? TD_WARNING_ICON : TD_INFORMATION_ICON);
1816 int nCommand = static_cast<int>(taskdlg.DoModal(m_hWnd));
1817 CString sFileName;
1818 switch (nCommand)
1820 case 201: // left
1821 if (TryGetFileName(sFileName))
1823 // in 2, 3 view display we want to keep removed lines
1824 m_pwndLeftView->SaveFileTo(sFileName, IsViewGood(m_pwndRightView) ? SAVE_REMOVEDLINES : 0);
1826 break;
1827 case 202: // right
1828 if (TryGetFileName(sFileName))
1830 m_pwndRightView->SaveFileTo(sFileName);
1832 break;
1833 case 203: // bottom
1834 FileSaveAs();
1835 break;
1836 case 204: // continue resolving
1837 if (IsViewGood(m_pwndBottomView))
1839 m_pwndBottomView->GoToLine(CheckResolved());
1841 break;
1845 bool CMainFrame::FileSaveAs(bool bCheckResolved /*=true*/)
1847 if (bCheckResolved && HasConflictsWontKeep())
1848 return false;
1850 CString fileName;
1851 if(!TryGetFileName(fileName))
1852 return false;
1854 SaveFile(fileName);
1855 return true;
1858 void CMainFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
1860 BOOL bEnable = FALSE;
1861 if (m_Data.m_mergedFile.InUse())
1863 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1864 bEnable = TRUE;
1865 else if ( (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData)) &&
1866 (m_pwndRightView->IsModified() || (m_Data.m_yourFile.GetWindowName().Right(static_cast<int>(wcslen(L": patched"))).Compare(L": patched") == 0)))
1867 bEnable = TRUE;
1868 else if (IsViewGood(m_pwndLeftView)
1869 && (m_pwndLeftView->m_pViewData)
1870 && (m_pwndLeftView->IsModified()))
1871 bEnable = TRUE;
1873 pCmdUI->Enable(bEnable);
1876 void CMainFrame::OnUpdateFileSaveAs(CCmdUI *pCmdUI)
1878 // any file is open we can save it as
1879 BOOL bEnable = FALSE;
1880 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1881 bEnable = TRUE;
1882 else if (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData))
1883 bEnable = TRUE;
1884 else if (IsViewGood(m_pwndLeftView)&&(m_pwndLeftView->m_pViewData))
1885 bEnable = TRUE;
1886 pCmdUI->Enable(bEnable);
1889 void CMainFrame::OnUpdateViewOnewaydiff(CCmdUI *pCmdUI)
1891 pCmdUI->SetCheck(!m_bOneWay);
1892 BOOL bEnable = TRUE;
1893 if (IsViewGood(m_pwndBottomView))
1895 bEnable = FALSE;
1897 pCmdUI->Enable(bEnable);
1900 void CMainFrame::OnViewOptions()
1902 CString sTemp;
1903 sTemp.LoadString(IDS_SETTINGSTITLE);
1904 using SetThreadDpiAwarenessContextProc = DPI_AWARENESS_CONTEXT(WINAPI*)(DPI_AWARENESS_CONTEXT);
1905 SetThreadDpiAwarenessContextProc SetThreadDpiAwarenessContext = reinterpret_cast<SetThreadDpiAwarenessContextProc>(GetProcAddress(GetModuleHandle(L"user32"), "SetThreadDpiAwarenessContext"));
1906 DPI_AWARENESS_CONTEXT oldDpiAwareness = DPI_AWARENESS_CONTEXT_UNAWARE;
1907 // dialog does not work well with different dpi settings, disable awareness
1908 if (SetThreadDpiAwarenessContext)
1909 oldDpiAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
1910 CSettings dlg(sTemp);
1911 dlg.DoModal();
1912 if (SetThreadDpiAwarenessContext)
1913 SetThreadDpiAwarenessContext(oldDpiAwareness);
1914 CTheme::Instance().SetDarkTheme(dlg.IsDarkMode());
1915 if (dlg.IsReloadNeeded())
1917 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
1918 return;
1919 CDiffColors::GetInstance().LoadRegistry();
1920 LoadViews();
1921 return;
1923 CDiffColors::GetInstance().LoadRegistry();
1924 if (m_pwndBottomView)
1925 m_pwndBottomView->DocumentUpdated();
1926 if (m_pwndLeftView)
1927 m_pwndLeftView->DocumentUpdated();
1928 if (m_pwndRightView)
1929 m_pwndRightView->DocumentUpdated();
1932 void CMainFrame::OnClose()
1934 if (!IsWindowEnabled())
1935 return; // just in case someone sends a WM_CLOSE to the main window while another window (dialog,...) is open
1936 if (CheckForSave(ECheckForSaveReason::Close)!=IDCANCEL)
1938 WINDOWPLACEMENT wp;
1940 // before it is destroyed, save the position of the window
1941 wp.length = sizeof wp;
1943 if (GetWindowPlacement(&wp))
1946 if (IsIconic())
1947 // never restore to Iconic state
1948 wp.showCmd = SW_SHOW ;
1950 if ((wp.flags & WPF_RESTORETOMAXIMIZED) != 0)
1951 // if maximized and maybe iconic restore maximized state
1952 wp.showCmd = SW_SHOWMAXIMIZED ;
1954 // and write it
1955 WriteWindowPlacement(&wp);
1957 __super::OnClose();
1961 void CMainFrame::OnActivate(UINT nValue, CWnd* /*pwnd*/, BOOL /*bActivated?*/)
1963 if (nValue != 0) // activated
1965 if (IsIconic())
1967 m_bCheckReload = TRUE;
1969 else
1971 // use a timer to give other messages time to get processed
1972 // first, like e.g. the WM_CLOSE message in case the user
1973 // clicked the close button and that brought the window
1974 // to the front - in that case checking for reload wouldn't
1975 // do any good.
1976 SetTimer(IDT_RELOADCHECKTIMER, 300, nullptr);
1981 void CMainFrame::OnViewLinedown()
1983 OnViewLineUpDown(1);
1986 void CMainFrame::OnViewLineup()
1988 OnViewLineUpDown(-1);
1991 void CMainFrame::OnViewLineUpDown(int direction)
1993 if (m_pwndLeftView)
1994 m_pwndLeftView->ScrollToLine(m_pwndLeftView->m_nTopLine+direction);
1995 if (m_pwndRightView)
1996 m_pwndRightView->ScrollToLine(m_pwndRightView->m_nTopLine+direction);
1997 if (m_pwndBottomView)
1998 m_pwndBottomView->ScrollToLine(m_pwndBottomView->m_nTopLine+direction);
1999 m_wndLocatorBar.Invalidate();
2000 m_nMoveMovesToIgnore = MOVESTOIGNORE;
2003 void CMainFrame::OnViewLineleft()
2005 OnViewLineLeftRight(-1);
2008 void CMainFrame::OnViewLineright()
2010 OnViewLineLeftRight(1);
2013 void CMainFrame::OnViewLineLeftRight(int direction)
2015 if (m_pwndLeftView)
2016 m_pwndLeftView->ScrollSide(direction);
2017 if (m_pwndRightView)
2018 m_pwndRightView->ScrollSide(direction);
2019 if (m_pwndBottomView)
2020 m_pwndBottomView->ScrollSide(direction);
2023 void CMainFrame::OnEditUseTheirs()
2025 if (m_pwndBottomView)
2026 m_pwndBottomView->UseTheirTextBlock();
2028 void CMainFrame::OnUpdateEditUsetheirblock(CCmdUI *pCmdUI)
2030 pCmdUI->Enable(m_pwndBottomView && m_pwndBottomView->HasSelection());
2033 void CMainFrame::OnEditUseMine()
2035 if (m_pwndBottomView)
2036 m_pwndBottomView->UseMyTextBlock();
2038 void CMainFrame::OnUpdateEditUsemyblock(CCmdUI *pCmdUI)
2040 OnUpdateEditUsetheirblock(pCmdUI);
2043 void CMainFrame::OnEditUseTheirsThenMine()
2045 if (m_pwndBottomView)
2046 m_pwndBottomView->UseTheirAndYourBlock();
2049 void CMainFrame::OnUpdateEditUsetheirthenmyblock(CCmdUI *pCmdUI)
2051 OnUpdateEditUsetheirblock(pCmdUI);
2054 void CMainFrame::OnEditUseMineThenTheirs()
2056 if (m_pwndBottomView)
2057 m_pwndBottomView->UseYourAndTheirBlock();
2060 void CMainFrame::OnUpdateEditUseminethentheirblock(CCmdUI *pCmdUI)
2062 OnUpdateEditUsetheirblock(pCmdUI);
2065 void CMainFrame::OnEditUseleftblock()
2067 if (m_pwndBottomView->IsWindowVisible())
2068 m_pwndBottomView->UseRightBlock();
2069 else
2070 m_pwndRightView->UseLeftBlock();
2073 void CMainFrame::OnUpdateEditUseleftblock(CCmdUI *pCmdUI)
2075 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget() && m_pwndRightView->HasSelection());
2078 void CMainFrame::OnUpdateUseBlock(CCmdUI *pCmdUI)
2080 pCmdUI->Enable(TRUE);
2083 void CMainFrame::OnEditUseleftfile()
2085 if (m_pwndBottomView->IsWindowVisible())
2086 m_pwndBottomView->UseRightFile();
2087 else
2088 m_pwndRightView->UseLeftFile();
2091 void CMainFrame::OnUpdateEditUseleftfile(CCmdUI *pCmdUI)
2093 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget());
2096 void CMainFrame::OnEditUseblockfromleftbeforeright()
2098 if (m_pwndRightView)
2099 m_pwndRightView->UseBothLeftFirst();
2102 void CMainFrame::OnUpdateEditUseblockfromleftbeforeright(CCmdUI *pCmdUI)
2104 OnUpdateEditUseleftblock(pCmdUI);
2107 void CMainFrame::OnEditUseblockfromrightbeforeleft()
2109 if (m_pwndRightView)
2110 m_pwndRightView->UseBothRightFirst();
2113 void CMainFrame::OnUpdateEditUseblockfromrightbeforeleft(CCmdUI *pCmdUI)
2115 OnUpdateEditUseleftblock(pCmdUI);
2118 void CMainFrame::OnFileReload()
2120 if (CheckForSave(ECheckForSaveReason::Reload)==IDCANCEL)
2121 return;
2122 CDiffColors::GetInstance().LoadRegistry();
2123 LoadViews(-1);
2126 void CMainFrame::ActivateFrame(int nCmdShow)
2128 // nCmdShow is the normal show mode this frame should be in
2129 // translate default nCmdShow (-1)
2130 if (nCmdShow == -1)
2132 if (!IsWindowVisible())
2133 nCmdShow = SW_SHOWNORMAL;
2134 else if (IsIconic())
2135 nCmdShow = SW_RESTORE;
2138 // bring to top before showing
2139 BringToTop(nCmdShow);
2141 if (nCmdShow != -1)
2143 // show the window as specified
2144 WINDOWPLACEMENT wp;
2146 if ( !ReadWindowPlacement(&wp) )
2148 ShowWindow(nCmdShow);
2150 else
2152 if ( nCmdShow != SW_SHOWNORMAL )
2153 wp.showCmd = nCmdShow;
2155 SetWindowPlacement(&wp);
2158 // and finally, bring to top after showing
2159 BringToTop(nCmdShow);
2161 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme());
2164 BOOL CMainFrame::ReadWindowPlacement(WINDOWPLACEMENT * pwp)
2166 CRegString placement = CRegString(CString(L"Software\\TortoiseGitMerge\\WindowPos_") + GetMonitorSetupHash().c_str());
2167 CString sPlacement = placement;
2168 if (sPlacement.IsEmpty())
2169 return FALSE;
2170 int nRead = swscanf_s(sPlacement, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2171 &pwp->flags, &pwp->showCmd,
2172 &pwp->ptMinPosition.x, &pwp->ptMinPosition.y,
2173 &pwp->ptMaxPosition.x, &pwp->ptMaxPosition.y,
2174 &pwp->rcNormalPosition.left, &pwp->rcNormalPosition.top,
2175 &pwp->rcNormalPosition.right, &pwp->rcNormalPosition.bottom);
2176 if ( nRead != 10 )
2177 return FALSE;
2178 pwp->length = sizeof(WINDOWPLACEMENT);
2180 CDPIAware::Instance().ScaleWindowPlacement(GetSafeHwnd(), pwp);
2182 return TRUE;
2185 void CMainFrame::WriteWindowPlacement(WINDOWPLACEMENT * pwp)
2187 CRegString placement(CString(L"Software\\TortoiseGitMerge\\WindowPos_") + GetMonitorSetupHash().c_str());
2188 wchar_t szBuffer[_countof("-32767") * 8 + sizeof("65535") * 2];
2190 CDPIAware::Instance().UnscaleWindowPlacement(GetSafeHwnd(), pwp);
2192 swprintf_s(szBuffer, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2193 pwp->flags, pwp->showCmd,
2194 pwp->ptMinPosition.x, pwp->ptMinPosition.y,
2195 pwp->ptMaxPosition.x, pwp->ptMaxPosition.y,
2196 pwp->rcNormalPosition.left, pwp->rcNormalPosition.top,
2197 pwp->rcNormalPosition.right, pwp->rcNormalPosition.bottom);
2198 placement = szBuffer;
2201 void CMainFrame::OnUpdateMergeMarkasresolved(CCmdUI *pCmdUI)
2203 if (!pCmdUI)
2204 return;
2205 BOOL bEnable = FALSE;
2206 if ((!m_bReadOnly)&&(m_Data.m_mergedFile.InUse()))
2208 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2210 bEnable = !m_bMarkedAsResolvedWasDone;
2213 pCmdUI->Enable(bEnable);
2216 void CMainFrame::OnMergeMarkasresolved()
2218 if(HasConflictsWontKeep())
2219 return;
2221 // now check if the file has already been saved and if not, save it.
2222 if (m_Data.m_mergedFile.InUse())
2224 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2226 if (!FileSave(false))
2227 return;
2228 m_bSaveRequired = false;
2231 MarkAsResolved();
2234 BOOL CMainFrame::MarkAsResolved()
2236 if (m_bReadOnly)
2237 return FALSE;
2238 if (!IsViewGood(m_pwndBottomView))
2239 return FALSE;
2240 if (m_bMarkedAsResolvedWasDone)
2241 return FALSE;
2243 CString cmd = L"/command:resolve /path:\"";
2244 cmd += m_Data.m_mergedFile.GetFilename();
2245 cmd += L"\" /closeonend:1 /noquestion /skipcheck /silent";
2246 if (resolveMsgWnd)
2248 CString s;
2249 s.Format(L" /resolvemsghwnd:%I64d /resolvemsgwparam:%I64d /resolvemsglparam:%I64d", reinterpret_cast<__int64>(resolveMsgWnd), static_cast<__int64>(resolveMsgWParam), static_cast<__int64>(resolveMsgLParam));
2250 cmd += s;
2252 if(!CAppUtils::RunTortoiseGitProc(cmd))
2253 return FALSE;
2254 m_bSaveRequired = false;
2255 m_bMarkedAsResolvedWasDone = true;
2256 return TRUE;
2259 void CMainFrame::OnUpdateMergeNextconflict(CCmdUI *pCmdUI)
2261 BOOL bShow = FALSE;
2262 if (HasNextConflict(m_pwndBottomView))
2263 bShow = TRUE;
2264 else if (HasNextConflict(m_pwndRightView))
2265 bShow = TRUE;
2266 else if (HasNextConflict(m_pwndLeftView))
2267 bShow = TRUE;
2268 pCmdUI->Enable(bShow);
2271 bool CMainFrame::HasNextConflict(CBaseView* view)
2273 if (view == 0)
2274 return false;
2275 if (!view->IsTarget())
2276 return false;
2277 return view->HasNextConflict();
2280 void CMainFrame::OnUpdateMergePreviousconflict(CCmdUI *pCmdUI)
2282 BOOL bShow = FALSE;
2283 if (HasPrevConflict(m_pwndBottomView))
2284 bShow = TRUE;
2285 else if (HasPrevConflict(m_pwndRightView))
2286 bShow = TRUE;
2287 else if (HasPrevConflict(m_pwndLeftView))
2288 bShow = TRUE;
2289 pCmdUI->Enable(bShow);
2292 bool CMainFrame::HasPrevConflict(CBaseView* view)
2294 if (view == 0)
2295 return false;
2296 if (!view->IsTarget())
2297 return false;
2298 return view->HasPrevConflict();
2301 void CMainFrame::OnUpdateNavigateNextdifference(CCmdUI *pCmdUI)
2303 CBaseView* baseView = GetActiveBaseView();
2304 BOOL bShow = FALSE;
2305 if (baseView != 0)
2306 bShow = baseView->HasNextDiff();
2307 pCmdUI->Enable(bShow);
2310 void CMainFrame::OnUpdateNavigatePreviousdifference(CCmdUI *pCmdUI)
2312 CBaseView* baseView = GetActiveBaseView();
2313 BOOL bShow = FALSE;
2314 if (baseView != 0)
2315 bShow = baseView->HasPrevDiff();
2316 pCmdUI->Enable(bShow);
2319 void CMainFrame::OnUpdateNavigateNextinlinediff(CCmdUI *pCmdUI)
2321 BOOL bShow = FALSE;
2322 if (HasNextInlineDiff(m_pwndBottomView))
2323 bShow = TRUE;
2324 else if (HasNextInlineDiff(m_pwndRightView))
2325 bShow = TRUE;
2326 else if (HasNextInlineDiff(m_pwndLeftView))
2327 bShow = TRUE;
2328 pCmdUI->Enable(bShow);
2331 bool CMainFrame::HasNextInlineDiff(CBaseView* view)
2333 if (view == 0)
2334 return false;
2335 if (!view->IsTarget())
2336 return false;
2337 return view->HasNextInlineDiff();
2340 void CMainFrame::OnUpdateNavigatePrevinlinediff(CCmdUI *pCmdUI)
2342 BOOL bShow = FALSE;
2343 if (HasPrevInlineDiff(m_pwndBottomView))
2344 bShow = TRUE;
2345 else if (HasPrevInlineDiff(m_pwndRightView))
2346 bShow = TRUE;
2347 else if (HasPrevInlineDiff(m_pwndLeftView))
2348 bShow = TRUE;
2349 pCmdUI->Enable(bShow);
2352 bool CMainFrame::HasPrevInlineDiff(CBaseView* view)
2354 if (view == 0)
2355 return false;
2356 if (!view->IsTarget())
2357 return false;
2358 return view->HasPrevInlineDiff();
2361 void CMainFrame::OnMoving(UINT fwSide, LPRECT pRect)
2363 m_dlgFilePatches.ParentOnMoving(m_hWnd, pRect);
2364 __super::OnMoving(fwSide, pRect);
2367 void CMainFrame::OnUpdateEditCopy(CCmdUI *pCmdUI)
2369 BOOL bShow = FALSE;
2370 if ((m_pwndBottomView)&&(m_pwndBottomView->HasSelection()))
2371 bShow = TRUE;
2372 else if ((m_pwndRightView)&&(m_pwndRightView->HasSelection()))
2373 bShow = TRUE;
2374 else if ((m_pwndLeftView)&&(m_pwndLeftView->HasSelection()))
2375 bShow = TRUE;
2376 pCmdUI->Enable(bShow);
2379 void CMainFrame::OnUpdateEditPaste(CCmdUI *pCmdUI)
2381 BOOL bWritable = FALSE;
2382 if ((m_pwndBottomView)&&(m_pwndBottomView->IsWritable()))
2383 bWritable = TRUE;
2384 else if ((m_pwndRightView)&&(m_pwndRightView->IsWritable()))
2385 bWritable = TRUE;
2386 else if ((m_pwndLeftView)&&(m_pwndLeftView->IsWritable()))
2387 bWritable = TRUE;
2388 pCmdUI->Enable(bWritable && (::IsClipboardFormatAvailable(CF_TEXT) || ::IsClipboardFormatAvailable(CF_UNICODETEXT)));
2391 void CMainFrame::OnViewSwitchleft()
2393 if (CheckForSave(ECheckForSaveReason::Switch)!=IDCANCEL)
2395 CWorkingFile file = m_Data.m_baseFile;
2396 m_Data.m_baseFile = m_Data.m_yourFile;
2397 m_Data.m_yourFile = file;
2398 if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_yourFile.GetFilename())==0)
2400 m_Data.m_mergedFile = m_Data.m_baseFile;
2402 else if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_baseFile.GetFilename())==0)
2404 m_Data.m_mergedFile = m_Data.m_yourFile;
2406 LoadViews();
2410 void CMainFrame::OnUpdateViewSwitchleft(CCmdUI *pCmdUI)
2412 BOOL bEnable = !IsViewGood(m_pwndBottomView);
2413 pCmdUI->Enable(bEnable);
2416 void CMainFrame::OnUpdateViewShowfilelist(CCmdUI *pCmdUI)
2418 BOOL bEnable = m_dlgFilePatches.HasFiles();
2419 pCmdUI->Enable(bEnable);
2420 pCmdUI->SetCheck(m_dlgFilePatches.IsWindowVisible());
2423 void CMainFrame::OnViewShowfilelist()
2425 m_dlgFilePatches.ShowWindow(m_dlgFilePatches.IsWindowVisible() ? SW_HIDE : SW_SHOW);
2428 void CMainFrame::OnEditUndo()
2430 if (CUndo::GetInstance().CanUndo())
2432 CUndo::GetInstance().Undo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2436 void CMainFrame::OnUpdateEditUndo(CCmdUI *pCmdUI)
2438 pCmdUI->Enable(CUndo::GetInstance().CanUndo());
2441 void CMainFrame::OnEditRedo()
2443 if (CUndo::GetInstance().CanRedo())
2445 CUndo::GetInstance().Redo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2449 void CMainFrame::OnUpdateEditRedo(CCmdUI *pCmdUI)
2451 pCmdUI->Enable(CUndo::GetInstance().CanRedo());
2454 void CMainFrame::OnEditEnable()
2456 CBaseView * pView = GetActiveBaseView();
2457 if (pView && pView->IsReadonlyChangable())
2459 bool isReadOnly = pView->IsReadonly();
2460 pView->SetReadonly(!isReadOnly);
2464 void CMainFrame::OnUpdateEditEnable(CCmdUI *pCmdUI)
2466 CBaseView * pView = GetActiveBaseView();
2467 if (pView)
2469 pCmdUI->Enable(pView->IsReadonlyChangable() || !pView->IsReadonly());
2470 pCmdUI->SetCheck(!pView->IsReadonly());
2472 else
2474 pCmdUI->Enable(FALSE);
2478 void CMainFrame::OnIndicatorLeftview()
2480 if (m_bUseRibbons)
2481 return;
2482 if (IsViewGood(m_pwndLeftView))
2484 m_pwndLeftView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_LEFTVIEW);
2488 void CMainFrame::OnIndicatorRightview()
2490 if (m_bUseRibbons)
2491 return;
2492 if (IsViewGood(m_pwndRightView))
2494 m_pwndRightView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_RIGHTVIEW);
2498 void CMainFrame::OnIndicatorBottomview()
2500 if (m_bUseRibbons)
2501 return;
2502 if (IsViewGood(m_pwndBottomView))
2504 m_pwndBottomView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_BOTTOMVIEW);
2508 int CMainFrame::CheckForReload()
2510 static bool bLock = false; //we don't want to check when activated after MessageBox we just created ... this is simple, but we don't need multithread lock
2511 if (bLock)
2513 return IDNO;
2515 bLock = true;
2516 bool bSourceChanged =
2517 m_Data.m_baseFile.HasSourceFileChanged()
2518 || m_Data.m_yourFile.HasSourceFileChanged()
2519 || m_Data.m_theirFile.HasSourceFileChanged()
2520 /*|| m_Data.m_mergedFile.HasSourceFileChanged()*/;
2521 if (!bSourceChanged)
2523 bLock = false;
2524 return IDNO;
2527 CString msg = HasUnsavedEdits() ? CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDELOOSECHANGES)) : CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE));
2528 CTaskDialog taskdlg(msg,
2529 CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK2)),
2530 L"TortoiseGitMerge",
2532 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2533 CString sTask3;
2534 if (HasUnsavedEdits())
2535 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK3);
2536 else
2537 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK4);
2538 taskdlg.AddCommandControl(IDYES, sTask3);
2539 taskdlg.AddCommandControl(IDNO, CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK5)));
2540 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2541 taskdlg.SetDefaultCommandControl(IDYES);
2542 taskdlg.SetMainIcon(TD_WARNING_ICON);
2543 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2544 if (ret == IDYES)
2546 CDiffColors::GetInstance().LoadRegistry();
2547 LoadViews(-1);
2549 else
2551 if (IsViewGood(m_pwndBottomView)) // three pane view
2553 /*if (m_Data.m_sourceFile.HasSourceFileChanged())
2554 m_pwndBottomView->SetModified();
2555 if (m_Data.m_mergedFile.HasSourceFileChanged())
2556 m_pwndBottomView->SetModified();//*/
2557 if (m_Data.m_yourFile.HasSourceFileChanged())
2558 m_pwndRightView->SetModified();
2559 if (m_Data.m_theirFile.HasSourceFileChanged())
2560 m_pwndLeftView->SetModified();
2562 else if (IsViewGood(m_pwndRightView)) // two pane view
2564 if (m_Data.m_baseFile.HasSourceFileChanged())
2565 m_pwndLeftView->SetModified();
2566 if (m_Data.m_yourFile.HasSourceFileChanged())
2567 m_pwndRightView->SetModified();
2569 else
2571 if (m_Data.m_yourFile.HasSourceFileChanged())
2572 m_pwndLeftView->SetModified();
2575 // no reload just store updated file time
2576 m_Data.m_baseFile.StoreFileAttributes();
2577 m_Data.m_theirFile.StoreFileAttributes();
2578 m_Data.m_yourFile.StoreFileAttributes();
2579 //m_Data.m_mergedFile.StoreFileAttributes();
2581 bLock = false;
2582 return ret;
2585 int CMainFrame::CheckForSave(ECheckForSaveReason eReason)
2587 int idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2588 int idNoSave = IDS_ASKFORSAVE_TASK7;
2589 int idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2590 switch (eReason)
2592 case ECheckForSaveReason::Close:
2593 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2594 idNoSave = IDS_ASKFORSAVE_TASK4;
2595 idCancelAction = IDS_ASKFORSAVE_TASK5;
2596 break;
2597 case ECheckForSaveReason::Switch:
2598 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2599 //idNoSave = IDS_ASKFORSAVE_TASK7;
2600 idCancelAction = IDS_ASKFORSAVE_TASK8;
2601 break;
2602 case ECheckForSaveReason::Reload:
2603 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2604 //idNoSave = IDS_ASKFORSAVE_TASK7;
2605 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2606 break;
2607 case ECheckForSaveReason::Options:
2608 idTitle = IDS_WARNMODIFIEDLOOSECHANGESOPTIONS;
2609 //idNoSave = IDS_ASKFORSAVE_TASK7;
2610 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPTIONS;
2611 break;
2612 case ECheckForSaveReason::Open:
2613 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2614 idNoSave = IDS_ASKFORSAVE_NOSAVE_OPEN;
2615 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2616 break;
2619 CString sTitle(MAKEINTRESOURCE(idTitle));
2620 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_TASK2));
2621 CString sNoSave(MAKEINTRESOURCE(idNoSave));
2622 CString sCancelAction(MAKEINTRESOURCE(idCancelAction));
2623 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
2625 // TODO simplify logic, reduce code duplication
2626 if (CBaseView::IsViewGood(m_pwndBottomView))
2628 // three-way diff - by design only bottom can be changed
2629 // use 1.7 way to do that
2631 else if (CBaseView::IsViewGood(m_pwndRightView))
2633 // two-way diff -
2634 // in 1.7 version only right was saved, now left and/or right can be save, so we need to indicate what we are asking to save
2635 if (HasUnsavedEdits(m_pwndLeftView))
2637 // both views
2638 CTaskDialog taskdlg(sTitle,
2639 sSubTitle,
2640 sAppName,
2642 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2643 CString sTaskTemp;
2644 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
2645 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, static_cast<LPCWSTR>(m_pwndLeftView->m_pWorkingFile->GetFilename()));
2646 else
2647 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
2648 taskdlg.AddCommandControl(201, sTaskTemp); // left
2649 taskdlg.SetDefaultCommandControl(201);
2650 if (HasUnsavedEdits(m_pwndRightView))
2652 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
2653 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, static_cast<LPCWSTR>(m_pwndRightView->m_pWorkingFile->GetFilename()));
2654 else
2655 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
2656 taskdlg.AddCommandControl(202, sTaskTemp); // right
2657 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2))); // both
2658 taskdlg.SetDefaultCommandControl(203);
2660 taskdlg.AddCommandControl(IDNO, sNoSave); // none
2661 taskdlg.AddCommandControl(IDCANCEL, sCancelAction); // cancel
2662 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2663 taskdlg.SetMainIcon(TD_WARNING_ICON);
2664 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2665 switch (ret)
2667 case 201: // left
2668 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2669 break;
2670 case 203: // both
2671 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2672 [[fallthrough]];
2673 case 202: // right
2674 m_pwndRightView->SaveFile();
2675 break;
2677 if (ret != IDCANCEL && (eReason == ECheckForSaveReason::Close || eReason == ECheckForSaveReason::Open))
2678 DeleteBaseTheirsMineOnClose();
2679 return ret;
2681 else
2683 // only secondary (left) view
2685 // only right view - 1.7 implementation is used
2687 else if (CBaseView::IsViewGood(m_pwndLeftView))
2689 // only one view - only one to save
2690 // 1.7 FileSave don't support this mode
2691 if (HasUnsavedEdits(m_pwndLeftView))
2693 CTaskDialog taskdlg(sTitle,
2694 sSubTitle,
2695 sAppName,
2697 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2698 CString sTask3;
2699 if (m_Data.m_mergedFile.InUse())
2700 sTask3.Format(IDS_ASKFORSAVE_TASK3, static_cast<LPCWSTR>(m_Data.m_mergedFile.GetFilename()));
2701 else
2702 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2703 taskdlg.AddCommandControl(IDYES, sTask3);
2704 taskdlg.AddCommandControl(IDNO, sNoSave);
2705 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2706 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2707 taskdlg.SetDefaultCommandControl(IDYES);
2708 taskdlg.SetMainIcon(TD_WARNING_ICON);
2709 if (static_cast<UINT>(taskdlg.DoModal(m_hWnd)) == IDYES)
2711 if (m_pwndLeftView->SaveFile()<0)
2712 return IDCANCEL;
2715 if (eReason == ECheckForSaveReason::Close || eReason == ECheckForSaveReason::Open)
2716 DeleteBaseTheirsMineOnClose();
2717 return IDNO;
2719 else
2721 if (eReason == ECheckForSaveReason::Close || eReason == ECheckForSaveReason::Open)
2722 DeleteBaseTheirsMineOnClose();
2723 return IDNO; // nothing to save
2726 // 1.7 implementation
2727 UINT ret = IDNO;
2728 if (HasUnsavedEdits())
2730 CTaskDialog taskdlg(sTitle,
2731 sSubTitle,
2732 sAppName,
2734 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2735 CString sTask3;
2736 if (m_Data.m_mergedFile.InUse())
2737 sTask3.Format(IDS_ASKFORSAVE_TASK3, static_cast<LPCWSTR>(m_Data.m_mergedFile.GetFilename()));
2738 else
2739 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2740 taskdlg.AddCommandControl(IDYES, sTask3);
2741 taskdlg.AddCommandControl(IDNO, sNoSave);
2742 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2743 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2744 taskdlg.SetDefaultCommandControl(IDYES);
2745 taskdlg.SetMainIcon(TD_WARNING_ICON);
2746 ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2748 if (ret == IDYES)
2750 if (!FileSave())
2751 ret = IDCANCEL;
2755 if (ret != IDCANCEL && (eReason == ECheckForSaveReason::Close || eReason == ECheckForSaveReason::Open))
2756 DeleteBaseTheirsMineOnClose();
2758 return ret;
2761 void CMainFrame::DeleteBaseTheirsMineOnClose()
2763 if (!m_bDeleteBaseTheirsMineOnClose)
2764 return;
2766 m_bDeleteBaseTheirsMineOnClose = false;
2768 DeleteFile(m_Data.m_baseFile.GetFilename());
2769 DeleteFile(m_Data.m_theirFile.GetFilename());
2770 DeleteFile(m_Data.m_yourFile.GetFilename());
2773 bool CMainFrame::HasUnsavedEdits() const
2775 return HasUnsavedEdits(m_pwndBottomView) || HasUnsavedEdits(m_pwndRightView) || m_bSaveRequired;
2778 bool CMainFrame::HasUnsavedEdits(const CBaseView* view)
2780 if (!CBaseView::IsViewGood(view))
2781 return false;
2782 return view->IsModified();
2785 bool CMainFrame::HasMarkedBlocks() const
2787 return CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->HasMarkedBlocks();
2790 bool CMainFrame::IsViewGood(const CBaseView* view)
2792 return CBaseView::IsViewGood(view);
2795 void CMainFrame::OnViewInlinediffword()
2797 m_bInlineWordDiff = !m_bInlineWordDiff;
2798 if (m_pwndLeftView)
2800 m_pwndLeftView->SetInlineWordDiff(m_bInlineWordDiff);
2801 m_pwndLeftView->BuildAllScreen2ViewVector();
2802 m_pwndLeftView->DocumentUpdated();
2804 if (m_pwndRightView)
2806 m_pwndRightView->SetInlineWordDiff(m_bInlineWordDiff);
2807 m_pwndRightView->BuildAllScreen2ViewVector();
2808 m_pwndRightView->DocumentUpdated();
2810 if (m_pwndBottomView)
2812 m_pwndBottomView->SetInlineWordDiff(m_bInlineWordDiff);
2813 m_pwndBottomView->BuildAllScreen2ViewVector();
2814 m_pwndBottomView->DocumentUpdated();
2816 m_wndLineDiffBar.DocumentUpdated();
2819 void CMainFrame::OnUpdateViewInlinediffword(CCmdUI *pCmdUI)
2821 pCmdUI->Enable(m_bInlineDiff && IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2822 pCmdUI->SetCheck(m_bInlineWordDiff);
2825 void CMainFrame::OnViewInlinediff()
2827 m_bInlineDiff = !m_bInlineDiff;
2828 if (m_pwndLeftView)
2830 m_pwndLeftView->SetInlineDiff(m_bInlineDiff);
2831 m_pwndLeftView->BuildAllScreen2ViewVector();
2832 m_pwndLeftView->DocumentUpdated();
2834 if (m_pwndRightView)
2836 m_pwndRightView->SetInlineDiff(m_bInlineDiff);
2837 m_pwndRightView->BuildAllScreen2ViewVector();
2838 m_pwndRightView->DocumentUpdated();
2840 if (m_pwndBottomView)
2842 m_pwndBottomView->SetInlineDiff(m_bInlineDiff);
2843 m_pwndBottomView->BuildAllScreen2ViewVector();
2844 m_pwndBottomView->DocumentUpdated();
2846 m_wndLineDiffBar.DocumentUpdated();
2849 void CMainFrame::OnUpdateViewInlinediff(CCmdUI *pCmdUI)
2851 pCmdUI->Enable(IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2852 pCmdUI->SetCheck(m_bInlineDiff);
2855 void CMainFrame::OnUpdateEditCreateunifieddifffile(CCmdUI *pCmdUI)
2857 // "create unified diff file" is only available if two files
2858 // are diffed, not three.
2859 bool bEnabled = true;
2860 if (!IsViewGood(m_pwndLeftView))
2861 bEnabled = false;
2862 else if (!IsViewGood(m_pwndRightView))
2863 bEnabled = false;
2864 else if (IsViewGood(m_pwndBottomView)) //no negation here
2865 bEnabled = false;
2866 pCmdUI->Enable(bEnabled);
2869 void CMainFrame::OnEditCreateunifieddifffile()
2871 CString origFile, modifiedFile;
2872 CString origReflected, modifiedReflected;
2873 // the original file is the one on the left
2874 if (m_pwndLeftView)
2876 origFile = m_pwndLeftView->m_sFullFilePath;
2877 origReflected = m_pwndLeftView->m_sReflectedName;
2879 if (m_pwndRightView)
2881 modifiedFile = m_pwndRightView->m_sFullFilePath;
2882 modifiedReflected = m_pwndRightView->m_sReflectedName;
2884 if (origFile.IsEmpty() || modifiedFile.IsEmpty())
2885 return;
2887 CString outputFile;
2888 if(!TryGetFileName(outputFile))
2889 return;
2891 CRegStdDWORD regContextLines(L"Software\\TortoiseGitMerge\\ContextLines", static_cast<DWORD>(-1));
2892 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outputFile, regContextLines, true);
2893 // from here a hacky solution exchanges the paths included in the patch, see issue #2541
2894 if (origReflected.IsEmpty() || modifiedReflected.IsEmpty())
2895 return;
2896 CString projectDir1, projectDir2;
2897 if (!GitAdminDir::HasAdminDir(origReflected, &projectDir1) || !GitAdminDir::HasAdminDir(modifiedReflected, &projectDir2) || projectDir1 != projectDir2)
2898 return;
2899 CStringA origReflectedA = CUnicodeUtils::GetUTF8(origReflected.Mid(projectDir1.GetLength() + 1));
2900 CStringA modifiedReflectedA = CUnicodeUtils::GetUTF8(modifiedReflected.Mid(projectDir1.GetLength() + 1));
2901 origReflectedA.Replace('\\', '/');
2902 modifiedReflectedA.Replace('\\', '/');
2905 CStdioFile file(outputFile, CFile::typeBinary | CFile::modeReadWrite | CFile::shareExclusive); // w/o typeBinary \r gets dropped for some files
2906 if (file.GetLength() >= INT_MAX)
2908 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_FILE_TOOBIG, IDS_APPNAME, MB_ICONERROR);
2909 return;
2912 CStringA filecontent;
2913 int filelength = static_cast<int>(file.GetLength());
2914 int bytesread = static_cast<int>(file.Read(filecontent.GetBuffer(filelength), filelength));
2915 filecontent.ReleaseBuffer(bytesread);
2917 if (!CStringUtils::StartsWith(filecontent, "diff --git "))
2918 return;
2919 int lineend = filecontent.Find("\n");
2920 if (lineend <= static_cast<int>(strlen("diff --git ")))
2921 return;
2922 CStringA newStart = "diff --git \"a/" + origReflectedA + "\" \"b/" + modifiedReflectedA + "\"\n";
2923 if (!CStringUtils::StartsWith(static_cast<LPCSTR>(filecontent) + lineend, "\nindex "))
2924 return;
2925 int nextlineend = filecontent.Find("\n", lineend + 1);
2926 if (nextlineend <= lineend)
2927 return;
2928 newStart += filecontent.Mid(lineend + 1, nextlineend - lineend);
2929 lineend = filecontent.Find("\n--- ", nextlineend);
2930 nextlineend = filecontent.Find("\n@@ ", lineend);
2931 if (nextlineend <= lineend)
2932 return;
2933 newStart += "--- \"a/" + origReflectedA + "\"\n+++ \"b/" + modifiedReflectedA + "\"";
2934 filecontent = newStart + filecontent.Mid(nextlineend);
2935 file.SeekToBegin();
2936 file.Write(filecontent, static_cast<UINT>(filecontent.GetLength()));
2937 file.SetLength(file.GetPosition());
2938 file.Close();
2940 catch (CFileException* e)
2942 CString sErrorString;
2943 e->GetErrorMessage(CStrBuf(sErrorString, 4096), 4096);
2944 e->Delete();
2945 MessageBox(sErrorString, L"TortoiseGitMerge", MB_ICONERROR);
2949 void CMainFrame::OnUpdateViewLinediffbar(CCmdUI *pCmdUI)
2951 pCmdUI->SetCheck(m_bLineDiff);
2952 pCmdUI->Enable();
2955 void CMainFrame::OnViewLinediffbar()
2957 m_bLineDiff = !m_bLineDiff;
2958 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2959 m_wndLineDiffBar.DocumentUpdated();
2960 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2961 m_wndLocatorBar.DocumentUpdated();
2964 void CMainFrame::OnUpdateViewLocatorbar(CCmdUI *pCmdUI)
2966 pCmdUI->SetCheck(m_bLocatorBar);
2967 pCmdUI->Enable();
2970 void CMainFrame::OnUpdateViewBars(CCmdUI * pCmdUI)
2972 pCmdUI->Enable();
2975 void CMainFrame::OnViewLocatorbar()
2977 m_bLocatorBar = !m_bLocatorBar;
2978 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2979 m_wndLocatorBar.DocumentUpdated();
2980 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2981 m_wndLineDiffBar.DocumentUpdated();
2984 void CMainFrame::OnViewComparewhitespaces()
2986 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
2987 return;
2988 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2989 regIgnoreWS = static_cast<int>(IgnoreWS::None);
2990 LoadViews(-1);
2993 void CMainFrame::OnUpdateViewComparewhitespaces(CCmdUI *pCmdUI)
2995 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2996 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
2997 pCmdUI->SetCheck(ignoreWs == IgnoreWS::None);
3000 void CMainFrame::OnViewIgnorewhitespacechanges()
3002 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
3003 return;
3004 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3005 regIgnoreWS = static_cast<int>(IgnoreWS::WhiteSpaces);
3006 LoadViews(-1);
3009 void CMainFrame::OnUpdateViewIgnorewhitespacechanges(CCmdUI *pCmdUI)
3011 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3012 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
3013 pCmdUI->SetCheck(ignoreWs == IgnoreWS::WhiteSpaces);
3016 void CMainFrame::OnViewIgnoreallwhitespacechanges()
3018 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
3019 return;
3020 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3021 regIgnoreWS = static_cast<int>(IgnoreWS::AllWhiteSpaces);
3022 LoadViews(-1);
3025 void CMainFrame::OnUpdateViewIgnoreallwhitespacechanges(CCmdUI *pCmdUI)
3027 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3028 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
3029 pCmdUI->SetCheck(ignoreWs == IgnoreWS::AllWhiteSpaces);
3032 void CMainFrame::OnViewMovedBlocks()
3034 m_bViewMovedBlocks = !static_cast<DWORD>(m_regViewModedBlocks);
3035 m_regViewModedBlocks = m_bViewMovedBlocks;
3036 LoadViews(-1);
3039 void CMainFrame::OnUpdateViewMovedBlocks(CCmdUI *pCmdUI)
3041 pCmdUI->SetCheck(m_bViewMovedBlocks);
3042 BOOL bEnable = TRUE;
3043 if (IsViewGood(m_pwndBottomView))
3045 bEnable = FALSE;
3047 pCmdUI->Enable(bEnable);
3050 bool CMainFrame::HasConflictsWontKeep()
3052 const int nConflictLine = CheckResolved();
3053 if (nConflictLine < 0)
3054 return false;
3055 if (!m_pwndBottomView)
3056 return false;
3058 CString sTemp;
3059 sTemp.Format(IDS_ERR_MAINFRAME_FILEHASCONFLICTS, m_pwndBottomView->m_pViewData->GetLineNumber(nConflictLine)+1);
3060 CTaskDialog taskdlg(sTemp,
3061 CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK2)),
3062 L"TortoiseGitMerge",
3064 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
3065 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK3)));
3066 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK4)));
3067 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
3068 taskdlg.SetDefaultCommandControl(2);
3069 taskdlg.SetMainIcon(TD_ERROR_ICON);
3070 if (taskdlg.DoModal(m_hWnd) == 1)
3071 return false;
3073 m_pwndBottomView->GoToLine(nConflictLine);
3074 return true;
3077 bool CMainFrame::TryGetFileName(CString& result)
3079 return CCommonAppUtils::FileOpenSave(result, nullptr, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd);
3082 CBaseView* CMainFrame::GetActiveBaseView() const
3084 CView* activeView = GetActiveView();
3085 CBaseView* activeBase = dynamic_cast<CBaseView*>( activeView );
3086 return activeBase;
3089 void CMainFrame::SetWindowTitle()
3091 // try to find a suitable window title
3092 CString sYour = m_Data.m_yourFile.GetDescriptiveName();
3093 if (sYour.Find(L" - ") >= 0)
3094 sYour = sYour.Left(sYour.Find(L" - "));
3095 if (sYour.Find(L" : ") >= 0)
3096 sYour = sYour.Left(sYour.Find(L" : "));
3097 CString sTheir = m_Data.m_theirFile.GetDescriptiveName();
3098 if (sTheir.IsEmpty())
3099 sTheir = m_Data.m_baseFile.GetDescriptiveName();
3100 if (sTheir.Find(L" - ") >= 0)
3101 sTheir = sTheir.Left(sTheir.Find(L" - "));
3102 if (sTheir.Find(L" : ") >= 0)
3103 sTheir = sTheir.Left(sTheir.Find(L" : "));
3105 if (!sYour.IsEmpty() && !sTheir.IsEmpty())
3107 if (sYour.CompareNoCase(sTheir)==0)
3108 SetWindowText(sYour + L" - TortoiseGitMerge");
3109 else if ((sYour.GetLength() < 10) &&
3110 (sTheir.GetLength() < 10))
3111 SetWindowText(sYour + L" - " + sTheir + L" - TortoiseGitMerge");
3112 else
3114 // we have two very long descriptive texts here, which
3115 // means we have to find a way to use them as a window
3116 // title in a shorter way.
3117 // for simplicity, we just use the one from "yourfile"
3118 SetWindowText(sYour + L" - TortoiseGitMerge");
3121 else if (!sYour.IsEmpty())
3122 SetWindowText(sYour + L" - TortoiseGitMerge");
3123 else if (!sTheir.IsEmpty())
3124 SetWindowText(sTheir + L" - TortoiseGitMerge");
3125 else
3126 SetWindowText(L"TortoiseGitMerge");
3129 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
3131 switch (nIDEvent)
3133 case IDT_RELOADCHECKTIMER:
3134 KillTimer(nIDEvent);
3135 CheckForReload();
3136 break;
3139 __super::OnTimer(nIDEvent);
3142 void CMainFrame::LoadIgnoreCommentData()
3144 static bool bLoaded = false;
3145 if (bLoaded)
3146 return;
3147 CString sPath = CPathUtils::GetAppDataDirectory() + L"ignorecomments.txt";
3148 if (!PathFileExists(sPath))
3150 // ignore comments file does not exist (yet), so create a default one
3151 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_IGNORECOMMENTSTXT), L"config");
3152 if (hRes)
3154 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3155 if (hResourceLoaded)
3157 auto lpResLock = static_cast<char*>(LockResource(hResourceLoaded));
3158 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3159 if (lpResLock)
3161 HANDLE hFile = CreateFile(sPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3162 if (hFile != INVALID_HANDLE_VALUE)
3164 DWORD dwWritten = 0;
3165 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3166 CloseHandle(hFile);
3175 CStdioFile file;
3176 if (file.Open(sPath, CFile::modeRead))
3178 CString sLine;
3179 while (file.ReadString(sLine))
3181 int eqpos = sLine.Find('=');
3182 if (eqpos >= 0)
3184 CString sExts = sLine.Left(eqpos);
3185 CString sComments = sLine.Mid(eqpos+1);
3187 int pos = sComments.Find(',');
3188 CString sLineStart = sComments.Left(pos);
3189 pos = sComments.Find(',', pos);
3190 int pos2 = sComments.Find(',', pos+1);
3191 CString sBlockStart = sComments.Mid(pos+1, pos2-pos-1);
3192 CString sBlockEnd = sComments.Mid(pos2+1);
3194 auto commentTuple = std::make_tuple(sLineStart, sBlockStart, sBlockEnd);
3196 pos = 0;
3197 CString temp;
3198 for (;;)
3200 temp = sExts.Tokenize(L",", pos);
3201 if (temp.IsEmpty())
3203 break;
3205 ASSERT(m_IgnoreCommentsMap.find(temp) == m_IgnoreCommentsMap.end());
3206 m_IgnoreCommentsMap[temp] = commentTuple;
3212 catch (CFileException* e)
3214 e->Delete();
3216 bLoaded = true;
3219 void CMainFrame::OnViewIgnorecomments()
3221 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
3222 return;
3223 m_regIgnoreComments = !DWORD(m_regIgnoreComments);
3224 LoadViews(-1);
3227 void CMainFrame::OnUpdateViewIgnorecomments(CCmdUI *pCmdUI)
3229 // only enable if we have comments defined for this file extension
3230 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
3231 sExt.TrimLeft(L".");
3232 auto sC = m_IgnoreCommentsMap.find(sExt);
3233 if (sC == m_IgnoreCommentsMap.end())
3235 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
3236 sExt.TrimLeft(L".");
3237 sC = m_IgnoreCommentsMap.find(sExt);
3238 if (sC == m_IgnoreCommentsMap.end())
3240 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
3241 sExt.TrimLeft(L".");
3242 sC = m_IgnoreCommentsMap.find(sExt);
3245 pCmdUI->Enable(sC != m_IgnoreCommentsMap.end());
3247 pCmdUI->SetCheck(DWORD(m_regIgnoreComments) != 0);
3250 void CMainFrame::OnViewIgnoreEOL()
3252 if (CheckForSave(ECheckForSaveReason::Options) == IDCANCEL)
3253 return;
3254 CRegDWORD regIgnoreEOL(L"Software\\TortoiseGitMerge\\IgnoreEOL", TRUE);
3255 bool bIgnoreEOL = static_cast<DWORD>(regIgnoreEOL) != 0;
3256 bIgnoreEOL = !bIgnoreEOL;
3257 regIgnoreEOL = bIgnoreEOL;
3258 LoadViews();
3261 void CMainFrame::OnUpdateViewIgnoreEOL(CCmdUI* pCmdUI)
3263 CRegDWORD regIgnoreEOL(L"Software\\TortoiseGitMerge\\IgnoreEOL", TRUE);
3264 bool bIgnoreEOL = static_cast<DWORD>(regIgnoreEOL) != 0;
3265 pCmdUI->SetCheck(bIgnoreEOL);
3268 void CMainFrame::OnUpdateMarkedWords(CCmdUI* pCmdUI)
3270 CString sText;
3271 CString sTmp;
3272 if (IsViewGood(m_pwndLeftView) && m_pwndLeftView->GetMarkedWordCount())
3274 sTmp.Format(L"L: %d", m_pwndLeftView->GetMarkedWordCount());
3275 if (!sText.IsEmpty())
3276 sText += L" | ";
3277 sText += sTmp;
3279 if (IsViewGood(m_pwndRightView) && m_pwndRightView->GetMarkedWordCount())
3281 sTmp.Format(L"R: %d", m_pwndRightView->GetMarkedWordCount());
3282 if (!sText.IsEmpty())
3283 sText += L" | ";
3284 sText += sTmp;
3286 if (IsViewGood(m_pwndBottomView) && m_pwndBottomView->GetMarkedWordCount())
3288 sTmp.Format(L"B: %d", m_pwndBottomView->GetMarkedWordCount());
3289 if (!sText.IsEmpty())
3290 sText += L" | ";
3291 sText += sTmp;
3293 if (!sText.IsEmpty())
3295 CString sStatusBarText;
3296 sStatusBarText.Format(IDS_INDICATOR_MARKEDWORDCOUNT, LPCWSTR(sText));
3297 pCmdUI->SetText(sStatusBarText);
3298 pCmdUI->Enable(true);
3302 void CMainFrame::OnUpdateEnableIfSelection(CCmdUI* pCmdUI)
3304 bool bEnabled = false;
3305 auto pWndWithFocus = GetFocus();
3306 if (pWndWithFocus)
3308 if (pWndWithFocus == m_pwndBottomView)
3309 bEnabled = !m_pwndBottomView->GetSelectedText().IsEmpty();
3310 if (pWndWithFocus == m_pwndLeftView)
3311 bEnabled = !m_pwndLeftView->GetSelectedText().IsEmpty();
3312 if (pWndWithFocus == m_pwndRightView)
3313 bEnabled = !m_pwndRightView->GetSelectedText().IsEmpty();
3315 pCmdUI->Enable(bEnabled);
3318 void CMainFrame::OnRegexfilter(UINT cmd)
3320 if ((cmd == ID_REGEXFILTER)||(cmd == (ID_REGEXFILTER+1)))
3322 CRegexFiltersDlg dlg(this);
3323 dlg.SetIniFile(&m_regexIni);
3324 if (dlg.DoModal() == IDOK)
3326 FILE* pFile = nullptr;
3327 _wfopen_s(&pFile, CPathUtils::GetAppDataDirectory() + L"regexfilters.ini", L"wb");
3328 m_regexIni.SaveFile(pFile);
3329 fclose(pFile);
3331 BuildRegexSubitems();
3333 else
3335 if (cmd == static_cast<UINT>(m_regexIndex) && !m_bUseRibbons)
3337 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
3338 return;
3339 m_Data.SetRegexTokens(std::wregex(), L"");
3340 m_regexIndex = -1;
3341 LoadViews(-1);
3343 else if (cmd != static_cast<UINT>(m_regexIndex))
3345 CSimpleIni::TNamesDepend sections;
3346 m_regexIni.GetAllSections(sections);
3347 int index = ID_REGEXFILTER + 2;
3348 m_regexIndex = -1;
3349 for (const auto& section : sections)
3351 if (cmd == static_cast<UINT>(index))
3353 if (CheckForSave(ECheckForSaveReason::Options)==IDCANCEL)
3354 break;
3357 std::wregex rx(m_regexIni.GetValue(section.pItem, L"regex", L""));
3358 m_Data.SetRegexTokens(rx, m_regexIni.GetValue(section.pItem, L"replace", L""));
3360 catch (std::exception &ex)
3362 MessageBox(L"Regex is invalid!\r\n" + CString(ex.what()));
3364 m_regexIndex = index;
3367 LoadViews(-1);
3369 catch (const std::regex_error& ex)
3371 MessageBox(L"Regexp error caught:\r\n" + CString(ex.what()) + L"\r\nTrying to recover by unsetting it again.");
3372 m_Data.SetRegexTokens(std::wregex(), L"");
3373 m_regexIndex = -1;
3374 LoadViews(-1);
3376 break;
3378 ++index;
3384 void CMainFrame::OnUpdateViewRegexFilter( CCmdUI *pCmdUI )
3386 pCmdUI->Enable();
3387 pCmdUI->SetCheck(pCmdUI->m_nID == static_cast<UINT>(m_regexIndex));
3390 void CMainFrame::BuildRegexSubitems(CMFCPopupMenu* pMenuPopup)
3392 CString sIniPath = CPathUtils::GetAppDataDirectory() + L"regexfilters.ini";
3393 if (!PathFileExists(sIniPath))
3395 // ini file does not exist (yet), so create a default one
3396 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_REGEXFILTERINI), L"config");
3397 if (hRes)
3399 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3400 if (hResourceLoaded)
3402 auto lpResLock = static_cast<char*>(LockResource(hResourceLoaded));
3403 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3404 if (lpResLock)
3406 HANDLE hFile = CreateFile(sIniPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3407 if (hFile != INVALID_HANDLE_VALUE)
3409 DWORD dwWritten = 0;
3410 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3411 CloseHandle(hFile);
3418 m_regexIni.LoadFile(sIniPath);
3419 CSimpleIni::TNamesDepend sections;
3420 m_regexIni.GetAllSections(sections);
3422 if (m_bUseRibbons)
3424 std::list<CNativeRibbonDynamicItemInfo> items;
3425 int cmdIndex = 2;
3426 items.push_back(CNativeRibbonDynamicItemInfo(ID_REGEX_NO_FILTER, CString(MAKEINTRESOURCE(ID_REGEX_NO_FILTER)), IDB_REGEX_FILTER));
3427 for (const auto& section : sections)
3429 items.emplace_back(ID_REGEXFILTER + cmdIndex, section.pItem, IDB_REGEX_FILTER);
3430 cmdIndex++;
3433 m_pRibbonApp->SetItems(ID_REGEXFILTER, items);
3435 else if (pMenuPopup)
3437 int iIndex = -1;
3438 if (!CMFCToolBar::IsCustomizeMode() &&
3439 (iIndex = pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER)) >= 0)
3441 if (!sections.empty())
3442 pMenuPopup->InsertSeparator(iIndex + 1); // insert the separator at the end
3443 int cmdIndex = 2;
3444 for (const auto& section : sections)
3446 pMenuPopup->InsertItem(CMFCToolBarMenuButton(ID_REGEXFILTER + cmdIndex, nullptr, -1, section.pItem), iIndex + cmdIndex);
3447 cmdIndex++;
3453 void CMainFrame::FillEncodingButton( CMFCRibbonButton * pButton, int start )
3455 pButton->SetDefaultCommand(FALSE);
3456 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::ASCII), L"ASCII" ));
3457 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::BINARY), L"BINARY" ));
3458 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF16_LE), L"UTF-16LE" ));
3459 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF16_LEBOM), L"UTF-16LE BOM"));
3460 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF16_BE), L"UTF-16BE" ));
3461 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF16_BEBOM), L"UTF-16BE BOM"));
3462 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF32_LE), L"UTF-32LE" ));
3463 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF32_BE), L"UTF-32BE" ));
3464 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF8), L"UTF-8" ));
3465 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(CFileTextLines::UnicodeType::UTF8BOM), L"UTF-8 BOM" ));
3468 void CMainFrame::FillEOLButton( CMFCRibbonButton * pButton, int start )
3470 pButton->SetDefaultCommand(FALSE);
3471 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::LF) , L"LF" ));
3472 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::CRLF), L"CRLF"));
3473 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::LFCR), L"LRCR"));
3474 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::CR) , L"CR" ));
3475 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::VT) , L"VT" ));
3476 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::FF) , L"FF" ));
3477 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::NEL) , L"NEL" ));
3478 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::LS) , L"LS" ));
3479 pButton->AddSubItem(new CMFCRibbonButton(start + static_cast<int>(EOL::PS) , L"PS" ));
3482 void CMainFrame::FillTabModeButton(CMFCRibbonButton * pButton, int start)
3484 pButton->SetDefaultCommand(FALSE);
3485 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_NONE , L"Tab"));
3486 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_USESPACES , L"Space"));
3487 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3488 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_SMARTINDENT , L"Smart tab char"));
3489 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3490 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON1, L"1"));
3491 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON2, L"2"));
3492 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON4, L"4"));
3493 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON8, L"8"));
3494 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3495 pButton->AddSubItem(new CMFCRibbonButton(start + ENABLEEDITORCONFIG, L"EditorConfig"));
3498 bool CMainFrame::AdjustUnicodeTypeForLoad(CFileTextLines::UnicodeType& type)
3500 switch (type)
3502 case CFileTextLines::UnicodeType::AUTOTYPE:
3503 case CFileTextLines::UnicodeType::BINARY:
3504 return false;
3506 case CFileTextLines::UnicodeType::ASCII:
3507 case CFileTextLines::UnicodeType::UTF16_LE:
3508 case CFileTextLines::UnicodeType::UTF16_BE:
3509 case CFileTextLines::UnicodeType::UTF32_LE:
3510 case CFileTextLines::UnicodeType::UTF32_BE:
3511 case CFileTextLines::UnicodeType::UTF8:
3512 return true;
3514 case CFileTextLines::UnicodeType::UTF16_LEBOM:
3515 type = CFileTextLines::UnicodeType::UTF16_LE;
3516 return true;
3518 case CFileTextLines::UnicodeType::UTF16_BEBOM:
3519 type = CFileTextLines::UnicodeType::UTF16_BE;
3520 return true;
3522 case CFileTextLines::UnicodeType::UTF8BOM:
3523 type = CFileTextLines::UnicodeType::UTF8;
3524 return true;
3526 return false;
3529 void CMainFrame::OnEncodingLeft( UINT cmd )
3531 if (m_pwndLeftView)
3533 if (GetKeyState(VK_CONTROL) & 0x8000)
3535 // reload with selected encoding
3536 auto saveparams = m_Data.m_arBaseFile.GetSaveParams();
3537 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART);
3538 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3540 m_Data.m_arBaseFile.SetSaveParams(saveparams);
3541 m_Data.m_arBaseFile.KeepEncoding();
3542 LoadViews();
3545 else
3547 m_pwndLeftView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART));
3548 m_pwndLeftView->RefreshViews();
3553 void CMainFrame::OnEncodingRight( UINT cmd )
3555 if (m_pwndRightView)
3557 if (GetKeyState(VK_CONTROL) & 0x8000)
3559 // reload with selected encoding
3560 auto saveparams = m_Data.m_arYourFile.GetSaveParams();
3561 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART);
3562 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3564 m_Data.m_arYourFile.SetSaveParams(saveparams);
3565 m_Data.m_arYourFile.KeepEncoding();
3566 LoadViews();
3569 else
3571 m_pwndRightView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART));
3572 m_pwndRightView->RefreshViews();
3577 void CMainFrame::OnEncodingBottom( UINT cmd )
3579 if (m_pwndBottomView)
3581 if (GetKeyState(VK_CONTROL) & 0x8000)
3583 // reload with selected encoding
3584 auto saveparams = m_Data.m_arTheirFile.GetSaveParams();
3585 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART);
3586 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3588 m_Data.m_arTheirFile.SetSaveParams(saveparams);
3589 m_Data.m_arTheirFile.KeepEncoding();
3590 LoadViews();
3593 else
3595 m_pwndBottomView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART));
3596 m_pwndBottomView->RefreshViews();
3601 void CMainFrame::OnEOLLeft( UINT cmd )
3603 if (m_pwndLeftView)
3605 m_pwndLeftView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_LEFTEOLSTART));
3606 m_pwndLeftView->RefreshViews();
3610 void CMainFrame::OnEOLRight( UINT cmd )
3612 if (m_pwndRightView)
3614 m_pwndRightView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_RIGHTEOLSTART));
3615 m_pwndRightView->RefreshViews();
3619 void CMainFrame::OnEOLBottom( UINT cmd )
3621 if (m_pwndBottomView)
3623 m_pwndBottomView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_BOTTOMEOLSTART));
3624 m_pwndBottomView->RefreshViews();
3628 void CMainFrame::OnTabModeLeft( UINT cmd )
3630 OnTabMode(m_pwndLeftView, static_cast<int>(cmd) - ID_INDICATOR_LEFTTABMODESTART);
3633 void CMainFrame::OnTabModeRight( UINT cmd )
3635 OnTabMode(m_pwndRightView, static_cast<int>(cmd) - ID_INDICATOR_RIGHTTABMODESTART);
3638 void CMainFrame::OnTabModeBottom( UINT cmd )
3640 OnTabMode(m_pwndBottomView, static_cast<int>(cmd) - ID_INDICATOR_BOTTOMTABMODESTART);
3643 void CMainFrame::OnTabMode(CBaseView *view, int cmd)
3645 if (!view)
3646 return;
3647 int nTabMode = view->GetTabMode();
3648 if (cmd == TABMODE_NONE || cmd == TABMODE_USESPACES)
3649 view->SetTabMode((nTabMode & (~TABMODE_USESPACES)) | (cmd & TABMODE_USESPACES));
3650 else if (cmd == TABMODE_SMARTINDENT) // Toggle
3651 view->SetTabMode((nTabMode & (~TABMODE_SMARTINDENT)) | ((nTabMode & TABMODE_SMARTINDENT) ? 0 : TABMODE_SMARTINDENT));
3652 else if (cmd == TABSIZEBUTTON1)
3653 view->SetTabSize(1);
3654 else if (cmd == TABSIZEBUTTON2)
3655 view->SetTabSize(2);
3656 else if (cmd == TABSIZEBUTTON4)
3657 view->SetTabSize(4);
3658 else if (cmd == TABSIZEBUTTON8)
3659 view->SetTabSize(8);
3660 else if (cmd == ENABLEEDITORCONFIG)
3661 view->SetEditorConfigEnabled(!view->GetEditorConfigEnabled());
3662 view->RefreshViews();
3665 void CMainFrame::OnUpdateEncodingLeft( CCmdUI *pCmdUI )
3667 if (m_pwndLeftView)
3669 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_LEFTENCODINGSTART) == m_pwndLeftView->GetTextType());
3670 pCmdUI->Enable(m_pwndLeftView->IsWritable() || (GetKeyState(VK_CONTROL)&0x8000));
3672 else
3673 pCmdUI->Enable(FALSE);
3676 void CMainFrame::OnUpdateEncodingRight( CCmdUI *pCmdUI )
3678 if (m_pwndRightView)
3680 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_RIGHTENCODINGSTART) == m_pwndRightView->GetTextType());
3681 pCmdUI->Enable(m_pwndRightView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3683 else
3684 pCmdUI->Enable(FALSE);
3687 void CMainFrame::OnUpdateEncodingBottom( CCmdUI *pCmdUI )
3689 if (m_pwndBottomView)
3691 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_BOTTOMENCODINGSTART) == m_pwndBottomView->GetTextType());
3692 pCmdUI->Enable(m_pwndBottomView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3694 else
3695 pCmdUI->Enable(FALSE);
3698 void CMainFrame::OnUpdateEOLLeft( CCmdUI *pCmdUI )
3700 if (m_pwndLeftView)
3702 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_LEFTEOLSTART) == m_pwndLeftView->GetLineEndings());
3703 pCmdUI->Enable(m_pwndLeftView->IsWritable());
3705 else
3706 pCmdUI->Enable(FALSE);
3709 void CMainFrame::OnUpdateEOLRight( CCmdUI *pCmdUI )
3711 if (m_pwndRightView)
3713 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_RIGHTEOLSTART) == m_pwndRightView->GetLineEndings());
3714 pCmdUI->Enable(m_pwndRightView->IsWritable());
3716 else
3717 pCmdUI->Enable(FALSE);
3720 void CMainFrame::OnUpdateEOLBottom( CCmdUI *pCmdUI )
3722 if (m_pwndBottomView)
3724 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_BOTTOMEOLSTART) == m_pwndBottomView->GetLineEndings());
3725 pCmdUI->Enable(m_pwndBottomView->IsWritable());
3727 else
3728 pCmdUI->Enable(FALSE);
3731 void CMainFrame::OnUpdateTabModeLeft(CCmdUI *pCmdUI)
3733 OnUpdateTabMode(m_pwndLeftView, pCmdUI, ID_INDICATOR_LEFTTABMODESTART);
3736 void CMainFrame::OnUpdateTabModeRight(CCmdUI *pCmdUI)
3738 OnUpdateTabMode(m_pwndRightView, pCmdUI, ID_INDICATOR_RIGHTTABMODESTART);
3741 void CMainFrame::OnUpdateTabModeBottom(CCmdUI *pCmdUI)
3743 OnUpdateTabMode(m_pwndBottomView, pCmdUI, ID_INDICATOR_BOTTOMTABMODESTART);
3746 void CMainFrame::OnUpdateTabMode(CBaseView *view, CCmdUI *pCmdUI, int startid)
3748 if (view)
3750 int cmd = static_cast<int>(pCmdUI->m_nID) - startid;
3751 if (cmd == TABMODE_NONE)
3752 pCmdUI->SetCheck((view->GetTabMode() & TABMODE_USESPACES) == TABMODE_NONE);
3753 else if (cmd == TABMODE_USESPACES)
3754 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_USESPACES);
3755 else if (cmd == TABMODE_SMARTINDENT)
3756 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_SMARTINDENT);
3757 else if (cmd == TABSIZEBUTTON1)
3758 pCmdUI->SetCheck(view->GetTabSize() == 1);
3759 else if (cmd == TABSIZEBUTTON2)
3760 pCmdUI->SetCheck(view->GetTabSize() == 2);
3761 else if (cmd == TABSIZEBUTTON4)
3762 pCmdUI->SetCheck(view->GetTabSize() == 4);
3763 else if (cmd == TABSIZEBUTTON8)
3764 pCmdUI->SetCheck(view->GetTabSize() == 8);
3765 else if (cmd == ENABLEEDITORCONFIG)
3766 pCmdUI->SetCheck(view->GetEditorConfigEnabled());
3767 pCmdUI->Enable(view->IsWritable());
3768 if (cmd == ENABLEEDITORCONFIG)
3769 pCmdUI->Enable(view->IsWritable() && view->GetEditorConfigLoaded());
3771 else
3772 pCmdUI->Enable(FALSE);
3775 BOOL CMainFrame::OnShowPopupMenu(CMFCPopupMenu* pMenuPopup)
3777 __super::OnShowPopupMenu(pMenuPopup);
3779 if (!pMenuPopup)
3780 return TRUE;
3782 if (!CMFCToolBar::IsCustomizeMode() &&
3783 pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER) >= 0)
3785 BuildRegexSubitems(pMenuPopup);
3788 return TRUE;
3791 LRESULT CMainFrame::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
3793 __super::OnIdleUpdateCmdUI(wParam, lParam);
3795 if (m_pRibbonApp)
3797 auto bDisableIfNoHandler = static_cast<BOOL>(wParam);
3798 m_pRibbonApp->UpdateCmdUI(bDisableIfNoHandler);
3800 return 0;
3803 LRESULT CMainFrame::OnDPIChanged(WPARAM, LPARAM lParam)
3805 CDPIAware::Instance().Invalidate();
3806 if (m_pwndLeftView)
3807 m_pwndLeftView->DPIChanged();
3808 if (m_pwndRightView)
3809 m_pwndRightView->DPIChanged();
3810 if (m_pwndBottomView)
3811 m_pwndBottomView->DPIChanged();
3812 const RECT* rect = reinterpret_cast<RECT*>(lParam);
3813 SetWindowPos(nullptr, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
3814 ::RedrawWindow(GetSafeHwnd(), nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT | RDW_ALLCHILDREN | RDW_UPDATENOW);
3815 return 1; // let MFC handle this message as well
3818 void CMainFrame::OnUpdateThreeWayActions(CCmdUI* pCmdUI)
3820 pCmdUI->Enable();
3823 void CMainFrame::OnUpdateColumnStatusBar(CCmdUI* pCmdUI)
3825 int column = 0;
3826 auto pWndWithFocus = GetFocus();
3827 if (pWndWithFocus)
3829 if (pWndWithFocus == m_pwndBottomView)
3830 column = m_pwndBottomView->GetCaretViewPosition().x;
3831 if (pWndWithFocus == m_pwndLeftView)
3832 column = m_pwndLeftView->GetCaretViewPosition().x;
3833 if (pWndWithFocus == m_pwndRightView)
3834 column = m_pwndRightView->GetCaretViewPosition().x;
3836 CString sColumn;
3837 sColumn.Format(IDS_INDICATOR_COLUMN, column + 1);
3838 pCmdUI->SetText(sColumn);
3839 pCmdUI->Enable(true);
3842 void CMainFrame::OnRegexNoFilter()
3844 if (CheckForSave(ECheckForSaveReason::Options) == IDCANCEL)
3845 return;
3846 m_Data.SetRegexTokens(std::wregex(), L"");
3847 m_regexIndex = -1;
3848 LoadViews(-1);
3851 void CMainFrame::OnUpdateRegexNoFilter(CCmdUI * pCmdUI)
3853 pCmdUI->SetCheck(m_regexIndex < 0);
3856 void CMainFrame::OnSettingChange(UINT uFlags, LPCWSTR lpszSection)
3858 __super::OnSettingChange(uFlags, lpszSection);
3860 SetAccentColor();
3863 void CMainFrame::OnSysColorChange()
3865 __super::OnSysColorChange();
3867 CTheme::Instance().OnSysColorChanged();
3868 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme(), true);
3869 SetAccentColor();