Fixed issue #3770: TortoiseGitMerge can't apply NonANSI (e.g., UTF-8) patch
[TortoiseGit.git] / src / TortoiseMerge / MainFrm.cpp
blob4626674cf03901d2b71cd9026181c1be4cb50a63
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2008-2021 - 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_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnRegexfilter)
152 ON_UPDATE_COMMAND_UI_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnUpdateViewRegexFilter)
153 ON_COMMAND(ID_REGEX_NO_FILTER, &CMainFrame::OnRegexNoFilter)
154 ON_UPDATE_COMMAND_UI(ID_REGEX_NO_FILTER, &CMainFrame::OnUpdateRegexNoFilter)
155 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
156 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
157 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
158 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
159 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
160 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
161 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
162 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
163 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
164 ON_UPDATE_COMMAND_UI(ID_EDIT_THREEWAY_ACTIONS, &CMainFrame::OnUpdateThreeWayActions)
165 ON_UPDATE_COMMAND_UI(ID_INDICATOR_COLUMN, &CMainFrame::OnUpdateColumnStatusBar)
166 ON_UPDATE_COMMAND_UI(ID_INDICATOR_MARKEDWORDS, &CMainFrame::OnUpdateMarkedWords)
167 ON_UPDATE_COMMAND_UI(ID_EDIT_FINDNEXTSTART, &CMainFrame::OnUpdateEnableIfSelection)
168 ON_UPDATE_COMMAND_UI(ID_EDIT_FINDPREVSTART, &CMainFrame::OnUpdateEnableIfSelection)
169 ON_COMMAND_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnEncodingLeft)
170 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnEncodingRight)
171 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnEncodingBottom)
172 ON_COMMAND_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnEOLLeft)
173 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnEOLRight)
174 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnEOLBottom)
175 ON_COMMAND_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnTabModeLeft)
176 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnTabModeRight)
177 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnTabModeBottom)
178 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingLeft)
179 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingRight)
180 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnUpdateEncodingBottom)
181 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnUpdateEOLLeft)
182 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnUpdateEOLRight)
183 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnUpdateEOLBottom)
184 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnUpdateTabModeLeft)
185 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnUpdateTabModeRight)
186 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnUpdateTabModeBottom)
187 ON_WM_SETTINGCHANGE()
188 ON_WM_SYSCOLORCHANGE()
189 END_MESSAGE_MAP()
191 static UINT indicators[] =
193 ID_SEPARATOR, // status line indicator
194 ID_INDICATOR_LEFTVIEW,
195 ID_INDICATOR_RIGHTVIEW,
196 ID_INDICATOR_BOTTOMVIEW,
197 ID_INDICATOR_CAPS,
198 ID_INDICATOR_NUM,
199 ID_INDICATOR_SCRL
203 // CMainFrame construction/destruction
205 CMainFrame::CMainFrame()
206 : m_bInitSplitter(FALSE)
207 , m_bReversedPatch(FALSE)
208 , m_bHasConflicts(false)
209 , m_bMarkedAsResolvedWasDone(false)
210 , m_bInlineWordDiff(true)
211 , m_bLineDiff(true)
212 , m_bLocatorBar(true)
213 , m_nMoveMovesToIgnore(0)
214 , m_pwndLeftView(nullptr)
215 , m_pwndRightView(nullptr)
216 , m_pwndBottomView(nullptr)
217 , m_bReadOnly(false)
218 , m_bBlame(false)
219 , m_bCheckReload(false)
220 , m_bSaveRequired(false)
221 , m_bSaveRequiredOnConflicts(false)
222 , m_bDeleteBaseTheirsMineOnClose(false)
223 , resolveMsgWnd(0)
224 , resolveMsgWParam(0)
225 , resolveMsgLParam(0)
226 , m_regWrapLines(L"Software\\TortoiseGitMerge\\WrapLines", 0)
227 , m_regViewModedBlocks(L"Software\\TortoiseGitMerge\\ViewMovedBlocks", TRUE)
228 , m_regOneWay(L"Software\\TortoiseGitMerge\\OnePane")
229 , m_regCollapsed(L"Software\\TortoiseGitMerge\\Collapsed", 0)
230 , m_regInlineDiff(L"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE)
231 , m_regUseRibbons(L"Software\\TortoiseGitMerge\\UseRibbons", TRUE)
232 , m_regIgnoreComments(L"Software\\TortoiseGitMerge\\IgnoreComments", FALSE)
233 , m_regexIndex(-1)
234 , m_themeCallbackId(0)
236 m_bOneWay = (0 != (static_cast<DWORD>(m_regOneWay)));
237 m_bCollapsed = !!static_cast<DWORD>(m_regCollapsed);
238 m_bViewMovedBlocks = !!static_cast<DWORD>(m_regViewModedBlocks);
239 m_bWrapLines = !!static_cast<DWORD>(m_regWrapLines);
240 m_bInlineDiff = !!m_regInlineDiff;
241 m_bUseRibbons = !!m_regUseRibbons;
244 CMainFrame::~CMainFrame()
248 LRESULT CMainFrame::OnTaskbarButtonCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
250 SetUUIDOverlayIcon(m_hWnd);
251 return 0;
254 int CMainFrame::InitRibbon()
256 if (!m_bUseRibbons)
257 return 0;
259 if (HRESULT hr = m_pRibbonFramework.CoCreateInstance(__uuidof(UIRibbonFramework)); FAILED(hr))
261 TRACE(L"Failed to create ribbon framework (%08x)\n", hr);
262 return -1; // fail to create
265 m_pRibbonApp.reset(new CNativeRibbonApp(this, m_pRibbonFramework));
266 m_pRibbonApp->SetSettingsFileName(CPathUtils::GetAppDataDirectory() + L"TortoiseGitMerge-RibbonSettings");
268 if (HRESULT hr = m_pRibbonFramework->Initialize(m_hWnd, m_pRibbonApp.get()); FAILED(hr))
270 TRACE(L"Failed to initialize ribbon framework (%08x)\n", hr);
271 return -1; // fail to create
274 if (HRESULT hr = m_pRibbonFramework->LoadUI(AfxGetResourceHandle(), L"TORTOISEGITMERGERIBBON_RIBBON"); FAILED(hr))
276 TRACE(L"Failed to load ribbon UI (%08x)\n", hr);
277 return -1; // fail to create
280 m_themeCallbackId = CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme()); });
281 SetTheme(CTheme::Instance().IsDarkTheme());
283 BuildRegexSubitems();
284 if (!m_wndRibbonStatusBar.Create(this))
286 TRACE0("Failed to create ribbon status bar\n");
287 return -1; // fail to create
290 // column info
291 CString sColumn;
292 sColumn.Format(IDS_INDICATOR_COLUMN, 0);
293 auto columnPane = new CMFCRibbonStatusBarPane(ID_INDICATOR_COLUMN, sColumn, FALSE);
294 m_wndRibbonStatusBar.AddElement(columnPane, L"");
295 sColumn.Format(IDS_INDICATOR_COLUMN, 999999);
296 columnPane->SetAlmostLargeText(sColumn);
297 // marked word counter
298 auto columnPaneMW = new CMFCRibbonStatusBarPane(ID_INDICATOR_MARKEDWORDS, L"", FALSE);
299 m_wndRibbonStatusBar.AddElement(columnPaneMW, L"");
300 columnPaneMW->SetAlmostLargeText(L"Marked words: l: XXXX | r: XXXX | b: XXXX");
302 CString sTooltip(MAKEINTRESOURCE(IDS_ENCODING_COMBO_TOOLTIP));
303 auto apBtnGroupLeft = std::make_unique<CMFCRibbonButtonsGroup>();
304 apBtnGroupLeft->SetID(ID_INDICATOR_LEFTVIEW);
305 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_LEFTVIEW)), TRUE));
306 CMFCRibbonButton* pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOENCODING, L"");
307 pButton->SetToolTipText(sTooltip);
308 FillEncodingButton(pButton, ID_INDICATOR_LEFTENCODINGSTART);
309 apBtnGroupLeft->AddButton(pButton);
310 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOEOL, L"");
311 FillEOLButton(pButton, ID_INDICATOR_LEFTEOLSTART);
312 apBtnGroupLeft->AddButton(pButton);
313 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, L"");
314 FillTabModeButton(pButton, ID_INDICATOR_LEFTTABMODESTART);
315 apBtnGroupLeft->AddButton(pButton);
316 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_LEFTVIEW, L"", TRUE));
317 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupLeft.release(), L"");
319 auto apBtnGroupRight = std::make_unique<CMFCRibbonButtonsGroup>();
320 apBtnGroupRight->SetID(ID_INDICATOR_RIGHTVIEW);
321 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_RIGHTVIEW)), TRUE));
322 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, L"");
323 pButton->SetToolTipText(sTooltip);
324 FillEncodingButton(pButton, ID_INDICATOR_RIGHTENCODINGSTART);
325 apBtnGroupRight->AddButton(pButton);
326 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOEOL, L"");
327 FillEOLButton(pButton, ID_INDICATOR_RIGHTEOLSTART);
328 apBtnGroupRight->AddButton(pButton);
329 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, L"");
330 FillTabModeButton(pButton, ID_INDICATOR_RIGHTTABMODESTART);
331 apBtnGroupRight->AddButton(pButton);
332 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_RIGHTVIEW, L"", TRUE));
333 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupRight.release(), L"");
335 return 0;
338 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
340 if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
341 return -1;
343 if (!m_bUseRibbons)
345 if (!m_wndMenuBar.Create(this))
347 TRACE0("Failed to create menubar\n");
348 return -1; // fail to create
350 m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
352 // prevent the menu bar from taking the focus on activation
353 CMFCPopupMenu::SetForceMenuFocus(FALSE);
354 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))
356 TRACE0("Failed to create toolbar\n");
357 return -1; // fail to create
359 m_wndToolBar.SetWindowText(L"Main");
360 if (!m_wndStatusBar.Create(this) ||
361 !m_wndStatusBar.SetIndicators(indicators,
362 _countof(indicators)))
364 TRACE0("Failed to create status bar\n");
365 return -1; // fail to create
367 m_wndStatusBar.EnablePaneDoubleClick();
369 m_themeCallbackId = CTheme::Instance().RegisterThemeChangeCallback([this]() { SetTheme(CTheme::Instance().IsDarkTheme());});
370 SetTheme(CTheme::Instance().IsDarkTheme());
373 if (!m_wndLocatorBar.Create(this, IDD_DIFFLOCATOR,
374 CBRS_ALIGN_LEFT | CBRS_SIZE_FIXED, ID_VIEW_LOCATORBAR))
376 TRACE0("Failed to create dialogbar\n");
377 return -1; // fail to create
379 if (!m_wndLineDiffBar.Create(this, IDD_LINEDIFF,
380 CBRS_ALIGN_BOTTOM | CBRS_SIZE_FIXED, ID_VIEW_LINEDIFFBAR))
382 TRACE0("Failed to create dialogbar\n");
383 return -1; // fail to create
385 m_wndLocatorBar.m_pMainFrm = this;
386 m_wndLineDiffBar.m_pMainFrm = this;
388 EnableDocking(CBRS_ALIGN_ANY);
389 if (!m_bUseRibbons)
391 m_wndMenuBar.EnableDocking(CBRS_ALIGN_TOP);
392 m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP);
393 DockPane(&m_wndMenuBar);
394 DockPane(&m_wndToolBar);
396 DockPane(&m_wndLocatorBar);
397 DockPane(&m_wndLineDiffBar);
398 ShowPane(&m_wndLocatorBar, true, false, true);
399 ShowPane(&m_wndLineDiffBar, true, false, true);
401 m_wndLocatorBar.EnableGripper(FALSE);
402 m_wndLineDiffBar.EnableGripper(FALSE);
404 return 0;
407 void CMainFrame::OnDestroy()
409 CTheme::Instance().RemoveRegisteredCallback(m_themeCallbackId);
410 if (m_pRibbonFramework)
411 m_pRibbonFramework->Destroy();
413 CFrameWndEx::OnDestroy();
416 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
418 if( !CFrameWndEx::PreCreateWindow(cs) )
419 return FALSE;
420 return TRUE;
423 // CMainFrame diagnostics
425 #ifdef _DEBUG
426 void CMainFrame::AssertValid() const
428 CFrameWndEx::AssertValid();
431 void CMainFrame::Dump(CDumpContext& dc) const
433 CFrameWndEx::Dump(dc);
436 #endif //_DEBUG
439 // CMainFrame message handlers
442 BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
444 CRect cr;
445 GetClientRect( &cr);
448 // split into three panes:
449 // -------------
450 // | | |
451 // | | |
452 // |------------
453 // | |
454 // | |
455 // |------------
457 // create a splitter with 2 rows, 1 column
458 if (!m_wndSplitter.CreateStatic(this, 2, 1))
460 TRACE0("Failed to CreateStaticSplitter\n");
461 return FALSE;
464 // add the second splitter pane - which is a nested splitter with 2 columns
465 if (!m_wndSplitter2.CreateStatic(
466 &m_wndSplitter, // our parent window is the first splitter
467 1, 2, // the new splitter is 1 row, 2 columns
468 WS_CHILD | WS_VISIBLE | WS_BORDER, // style, WS_BORDER is needed
469 m_wndSplitter.IdFromRowCol(0, 0)
470 // new splitter is in the first row, 1st column of first splitter
473 TRACE0("Failed to create nested splitter\n");
474 return FALSE;
476 // add the first splitter pane - the default view in row 0
477 if (!m_wndSplitter.CreateView(1, 0,
478 RUNTIME_CLASS(CBottomView), CSize(cr.Width(), cr.Height()), pContext))
480 TRACE0("Failed to create first pane\n");
481 return FALSE;
483 m_pwndBottomView = static_cast<CBottomView*>(m_wndSplitter.GetPane(1, 0));
484 m_pwndBottomView->m_pwndLocator = &m_wndLocatorBar;
485 m_pwndBottomView->m_pwndLineDiffBar = &m_wndLineDiffBar;
486 if (m_bUseRibbons)
487 m_pwndBottomView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
488 else
489 m_pwndBottomView->m_pwndStatusBar = &m_wndStatusBar;
490 m_pwndBottomView->m_pMainFrame = this;
492 // now create the two views inside the nested splitter
494 if (!m_wndSplitter2.CreateView(0, 0,
495 RUNTIME_CLASS(CLeftView), CSize(cr.Width()/2, cr.Height()/2), pContext))
497 TRACE0("Failed to create second pane\n");
498 return FALSE;
500 m_pwndLeftView = static_cast<CLeftView*>(m_wndSplitter2.GetPane(0, 0));
501 m_pwndLeftView->m_pwndLocator = &m_wndLocatorBar;
502 m_pwndLeftView->m_pwndLineDiffBar = &m_wndLineDiffBar;
503 if (m_bUseRibbons)
504 m_pwndLeftView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
505 else
506 m_pwndLeftView->m_pwndStatusBar = &m_wndStatusBar;
507 m_pwndLeftView->m_pMainFrame = this;
509 if (!m_wndSplitter2.CreateView(0, 1,
510 RUNTIME_CLASS(CRightView), CSize(cr.Width()/2, cr.Height()/2), pContext))
512 TRACE0("Failed to create third pane\n");
513 return FALSE;
515 m_pwndRightView = static_cast<CRightView*>(m_wndSplitter2.GetPane(0, 1));
516 m_pwndRightView->m_pwndLocator = &m_wndLocatorBar;
517 m_pwndRightView->m_pwndLineDiffBar = &m_wndLineDiffBar;
518 if (m_bUseRibbons)
519 m_pwndRightView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
520 else
521 m_pwndRightView->m_pwndStatusBar = &m_wndStatusBar;
522 m_pwndRightView->m_pMainFrame = this;
523 m_bInitSplitter = TRUE;
525 m_dlgFilePatches.Create(IDD_FILEPATCHES, this);
526 UpdateLayout();
527 return TRUE;
530 // Callback function
531 BOOL CMainFrame::PatchFile(CString sFilePath, bool /*bContentMods*/, bool bPropMods, CString sVersion, BOOL bAutoPatch)
533 //"dry run" was successful, so save the patched file somewhere...
534 CString sTempFile = CTempFiles::Instance().GetTempFilePathString();
535 CString sRejectedFile, sBasePath;
536 if (m_Patch.GetPatchResult(sFilePath, sTempFile, sRejectedFile, sBasePath) < 0)
538 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
539 return FALSE;
541 sFilePath = m_Patch.GetTargetPath() + L'\\' + sFilePath;
542 sFilePath.Replace('/', '\\');
543 if (sBasePath.IsEmpty())
544 sBasePath = sFilePath;
545 if (m_bReversedPatch)
547 m_Data.m_baseFile.SetFileName(sTempFile);
548 CString temp;
549 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
550 m_Data.m_baseFile.SetDescriptiveName(temp);
551 m_Data.m_yourFile.SetFileName(sFilePath);
552 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchOriginal));
553 m_Data.m_yourFile.SetDescriptiveName(temp);
554 m_Data.m_theirFile.SetOutOfUse();
555 m_Data.m_mergedFile.SetOutOfUse();
557 else
559 if ((!PathFileExists(sBasePath))||(PathIsDirectory(sBasePath)))
561 m_Data.m_baseFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
562 m_Data.m_baseFile.CreateEmptyFile();
564 else
566 m_Data.m_baseFile.SetFileName(sBasePath);
568 CString sDescription;
569 sDescription.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sBasePath)), static_cast<LPCWSTR>(m_Data.m_sPatchOriginal));
570 m_Data.m_baseFile.SetDescriptiveName(sDescription);
571 if (sBasePath == sFilePath)
573 m_Data.m_yourFile.SetFileName(sTempFile);
574 CString temp;
575 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sBasePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
576 m_Data.m_yourFile.SetDescriptiveName(temp);
577 m_Data.m_theirFile.SetOutOfUse();
579 else
581 if (!PathFileExists(sFilePath) || PathIsDirectory(sFilePath))
583 m_Data.m_yourFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
584 m_Data.m_yourFile.CreateEmptyFile();
585 CString temp;
586 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(CString(MAKEINTRESOURCE(IDS_NOTFOUNDVIEWTITLEINDICATOR))));
587 m_Data.m_yourFile.SetDescriptiveName(temp);
589 else
590 m_Data.m_yourFile.SetFileName(sFilePath);
591 m_Data.m_theirFile.SetFileName(sTempFile);
592 CString temp;
593 temp.Format(L"%s %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)), static_cast<LPCWSTR>(m_Data.m_sPatchPatched));
594 m_Data.m_theirFile.SetDescriptiveName(temp);
596 m_Data.m_mergedFile.SetFileName(sFilePath);
597 m_Data.m_bPatchRequired = bPropMods;
599 TRACE(L"comparing %s\nwith the patched result %s\n", static_cast<LPCWSTR>(sFilePath), static_cast<LPCWSTR>(sTempFile));
601 LoadViews();
602 if (!sRejectedFile.IsEmpty())
604 #if 0 // TGIT TODO
605 // start TortoiseUDiff with the rejected hunks
606 CString sTitle;
607 sTitle.Format(IDS_TITLE_REJECTEDHUNKS, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sFilePath)));
608 CAppUtils::StartUnifiedDiffViewer(sRejectedFile, sTitle);
609 #endif
611 if (bAutoPatch)
613 if (sBasePath != sFilePath && HasConflictsWontKeep())
614 return FALSE;
616 PatchSave();
618 return TRUE;
621 // Callback function
622 BOOL CMainFrame::DiffFiles(CString sURL1, CString sRev1, CString sURL2, CString sRev2)
624 CString tempfile1 = CTempFiles::Instance().GetTempFilePathString();
625 CString tempfile2 = CTempFiles::Instance().GetTempFilePathString();
627 ASSERT(tempfile1.Compare(tempfile2));
629 CString sTemp;
630 CSysProgressDlg progDlg;
631 sTemp.Format(IDS_GETVERSIONOFFILE, static_cast<LPCWSTR>(sRev1));
632 progDlg.SetLine(1, sTemp, true);
633 progDlg.SetLine(2, sURL1, true);
634 sTemp.LoadString(IDS_GETVERSIONOFFILETITLE);
635 progDlg.SetTitle(sTemp);
636 progDlg.SetShowProgressBar(true);
637 progDlg.SetTime(FALSE);
638 progDlg.SetProgress(1,100);
639 progDlg.ShowModeless(this);
640 if (!CAppUtils::GetVersionedFile(sURL1, sRev1, tempfile1, &progDlg, m_hWnd))
642 progDlg.Stop();
643 CString sErrMsg;
644 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, static_cast<LPCWSTR>(sRev1), static_cast<LPCWSTR>(sURL1));
645 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
646 return FALSE;
648 sTemp.Format(IDS_GETVERSIONOFFILE, static_cast<LPCWSTR>(sRev2));
649 progDlg.SetLine(1, sTemp, true);
650 progDlg.SetLine(2, sURL2, true);
651 progDlg.SetProgress(50, 100);
652 if (!CAppUtils::GetVersionedFile(sURL2, sRev2, tempfile2, &progDlg, m_hWnd))
654 progDlg.Stop();
655 CString sErrMsg;
656 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, static_cast<LPCWSTR>(sRev2), static_cast<LPCWSTR>(sURL2));
657 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
658 return FALSE;
660 progDlg.SetProgress(100,100);
661 progDlg.Stop();
662 CString temp;
663 temp.Format(L"%s Revision %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sURL1)), static_cast<LPCWSTR>(sRev1));
664 m_Data.m_baseFile.SetFileName(tempfile1);
665 m_Data.m_baseFile.SetDescriptiveName(temp);
666 temp.Format(L"%s Revision %s", static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(sURL2)), static_cast<LPCWSTR>(sRev2));
667 m_Data.m_yourFile.SetFileName(tempfile2);
668 m_Data.m_yourFile.SetDescriptiveName(temp);
670 LoadViews();
672 return TRUE;
675 void CMainFrame::OnFileOpen()
677 if (CheckForSave(CHFSR_OPEN)==IDCANCEL)
678 return;
679 return OnFileOpen(false);
682 void CMainFrame::OnFileOpen(bool fillyours)
684 COpenDlg dlg;
685 if (fillyours)
686 dlg.m_sBaseFile = m_Data.m_yourFile.GetFilename();
687 if (dlg.DoModal() != IDOK)
689 return;
691 m_bMarkedAsResolvedWasDone = false;
692 m_dlgFilePatches.ShowWindow(SW_HIDE);
693 m_dlgFilePatches.Init(nullptr, nullptr, CString(), nullptr);
694 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),
695 static_cast<LPCWSTR>(dlg.m_sUnifiedDiffFile), static_cast<LPCWSTR>(dlg.m_sPatchDirectory));
696 m_Data.m_baseFile.SetFileName(dlg.m_sBaseFile);
697 m_Data.m_theirFile.SetFileName(dlg.m_sTheirFile);
698 m_Data.m_yourFile.SetFileName(dlg.m_sYourFile);
699 m_Data.m_sDiffFile = dlg.m_sUnifiedDiffFile;
700 m_Data.m_sPatchPath = dlg.m_sPatchDirectory;
701 m_Data.m_mergedFile.SetOutOfUse();
702 CCrashReport::Instance().AddFile2(dlg.m_sBaseFile, nullptr, L"Basefile", CR_AF_MAKE_FILE_COPY);
703 CCrashReport::Instance().AddFile2(dlg.m_sTheirFile, nullptr, L"Theirfile", CR_AF_MAKE_FILE_COPY);
704 CCrashReport::Instance().AddFile2(dlg.m_sYourFile, nullptr, L"Yourfile", CR_AF_MAKE_FILE_COPY);
705 CCrashReport::Instance().AddFile2(dlg.m_sUnifiedDiffFile, nullptr, L"Difffile", CR_AF_MAKE_FILE_COPY);
707 if (!m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && m_Data.IsYourFileInUse())
709 // a diff between two files means "Yours" against "Base", not "Theirs" against "Yours"
710 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
712 if (m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && !m_Data.IsYourFileInUse())
714 // a diff between two files means "Yours" against "Base", not "Theirs" against "Base"
715 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
717 m_bSaveRequired = false;
719 LoadViews();
722 void CMainFrame::ClearViewNamesAndPaths()
724 m_pwndLeftView->m_sWindowName.Empty();
725 m_pwndLeftView->m_sFullFilePath.Empty();
726 m_pwndLeftView->m_sReflectedName.Empty();
727 m_pwndRightView->m_sWindowName.Empty();
728 m_pwndRightView->m_sFullFilePath.Empty();
729 m_pwndRightView->m_sReflectedName.Empty();
730 m_pwndBottomView->m_sWindowName.Empty();
731 m_pwndBottomView->m_sFullFilePath.Empty();
732 m_pwndBottomView->m_sReflectedName.Empty();
735 bool CMainFrame::LoadViews(int line)
737 LoadIgnoreCommentData();
738 m_Data.SetBlame(m_bBlame);
739 m_Data.SetMovedBlocks(m_bViewMovedBlocks);
740 m_bHasConflicts = false;
741 CBaseView* pwndActiveView = m_pwndLeftView;
742 int nOldLine = m_pwndRightView ? m_pwndRightView->m_nTopLine : -1;
743 int nOldLineNumber =
744 m_pwndRightView && m_pwndRightView->m_pViewData ?
745 m_pwndRightView->m_pViewData->GetLineNumber(m_pwndRightView->m_nTopLine) : -1;
746 POINT ptOldCaretPos = {-1, -1};
747 if (m_pwndRightView && m_pwndRightView->IsTarget())
748 ptOldCaretPos = m_pwndRightView->GetCaretPosition();
749 if (m_pwndBottomView && m_pwndBottomView->IsTarget())
750 ptOldCaretPos = m_pwndBottomView->GetCaretPosition();
751 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
752 sExt.TrimLeft(L".");
753 auto sC = m_IgnoreCommentsMap.find(sExt);
754 if (sC == m_IgnoreCommentsMap.end())
756 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
757 sC = m_IgnoreCommentsMap.find(sExt);
758 if (sC == m_IgnoreCommentsMap.end())
760 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
761 sC = m_IgnoreCommentsMap.find(sExt);
764 if (sC != m_IgnoreCommentsMap.end())
765 m_Data.SetCommentTokens(std::get<0>(sC->second), std::get<1>(sC->second), std::get<2>(sC->second));
766 else
767 m_Data.SetCommentTokens(L"", L"", L"");
768 if (!m_Data.Load())
770 m_pwndLeftView->BuildAllScreen2ViewVector();
771 m_pwndLeftView->DocumentUpdated();
772 m_pwndRightView->DocumentUpdated();
773 m_pwndBottomView->DocumentUpdated();
774 m_wndLocatorBar.DocumentUpdated();
775 m_wndLineDiffBar.DocumentUpdated();
776 ::MessageBox(m_hWnd, m_Data.GetError(), L"TortoiseGitMerge", MB_ICONERROR);
777 m_Data.m_mergedFile.SetOutOfUse();
778 m_bSaveRequired = false;
779 return false;
781 SetWindowTitle();
782 m_pwndLeftView->BuildAllScreen2ViewVector();
783 m_pwndLeftView->DocumentUpdated();
784 m_pwndRightView->DocumentUpdated();
785 m_pwndBottomView->DocumentUpdated();
786 m_wndLocatorBar.DocumentUpdated();
787 m_wndLineDiffBar.DocumentUpdated();
789 m_pwndLeftView->SetWritable(false);
790 m_pwndLeftView->SetWritableIsChangable(false);
791 m_pwndLeftView->SetTarget(false);
792 m_pwndRightView->SetWritable(false);
793 m_pwndRightView->SetWritableIsChangable(false);
794 m_pwndRightView->SetTarget(false);
795 m_pwndBottomView->SetWritable(false);
796 m_pwndBottomView->SetWritableIsChangable(false);
797 m_pwndBottomView->SetTarget(false);
799 if (!m_Data.IsBaseFileInUse())
801 CSysProgressDlg progDlg;
802 if (m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
804 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
806 else if ((!m_Data.m_sDiffFile.IsEmpty())&&(!m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg)))
808 progDlg.Stop();
809 ClearViewNamesAndPaths();
810 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
811 m_bSaveRequired = false;
812 return false;
814 progDlg.Stop();
815 if (m_Patch.GetNumberOfFiles() > 0)
817 CString betterpatchpath = m_Patch.CheckPatchPath(m_Data.m_sPatchPath);
818 if (betterpatchpath.CompareNoCase(m_Data.m_sPatchPath)!=0)
820 CString msg;
821 msg.FormatMessage(IDS_WARNBETTERPATCHPATHFOUND, static_cast<LPCWSTR>(m_Data.m_sPatchPath), static_cast<LPCWSTR>(betterpatchpath));
822 CTaskDialog taskdlg(msg,
823 CString(MAKEINTRESOURCE(IDS_WARNBETTERPATCHPATHFOUND_TASK2)),
824 L"TortoiseGitMerge",
826 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
827 CString task3;
828 WCHAR t3[MAX_PATH] = { 0 };
829 CString cp = betterpatchpath.Left(MAX_PATH - 1);
830 PathCompactPathEx(t3, cp, 50, 0);
831 task3.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK3, t3);
832 taskdlg.AddCommandControl(1, task3);
833 CString task4;
834 WCHAR t4[MAX_PATH] = { 0 };
835 cp = m_Data.m_sPatchPath.Left(MAX_PATH - 1);
836 PathCompactPathEx(t4, cp, 50, 0);
837 task4.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK4, t4);
838 taskdlg.AddCommandControl(2, task4);
839 taskdlg.SetDefaultCommandControl(1);
840 taskdlg.SetMainIcon(TD_INFORMATION_ICON);
841 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
842 if (taskdlg.DoModal(m_hWnd) == 1)
844 m_Data.m_sPatchPath = betterpatchpath;
845 m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg);
848 m_dlgFilePatches.Init(&m_Patch, this, m_Data.m_sPatchPath, this);
849 m_dlgFilePatches.ShowWindow(SW_SHOW);
850 ClearViewNamesAndPaths();
851 if (!m_wndSplitter.IsRowHidden(1))
852 m_wndSplitter.HideRow(1);
853 m_pwndLeftView->SetHidden(FALSE);
854 m_pwndRightView->SetHidden(FALSE);
855 m_pwndBottomView->SetHidden(TRUE);
858 if (m_Data.IsBaseFileInUse() && !m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
860 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
862 if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && !m_Data.IsTheirFileInUse())
864 //diff between YOUR and BASE
865 if (m_bOneWay)
867 pwndActiveView = m_pwndLeftView;
868 if (!m_wndSplitter2.IsColumnHidden(1))
869 m_wndSplitter2.HideColumn(1);
871 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseBoth;
872 m_pwndLeftView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
873 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
874 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName() + L" - " + m_Data.m_yourFile.GetWindowName();
875 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename() + L" - " + m_Data.m_yourFile.GetFilename();
876 m_pwndLeftView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
877 m_pwndLeftView->m_pWorkingFile = &m_Data.m_yourFile;
878 m_pwndLeftView->SetTarget();
879 m_pwndLeftView->SetWritableIsChangable(true);
881 m_pwndRightView->m_pViewData = nullptr;
882 m_pwndRightView->m_pWorkingFile = nullptr;
883 m_pwndBottomView->m_pViewData = nullptr;
884 m_pwndBottomView->m_pWorkingFile = nullptr;
886 if (!m_wndSplitter.IsRowHidden(1))
887 m_wndSplitter.HideRow(1);
888 m_pwndLeftView->SetHidden(FALSE);
889 m_pwndRightView->SetHidden(TRUE);
890 m_pwndBottomView->SetHidden(TRUE);
891 ::SetWindowPos(m_pwndLeftView->m_hWnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
893 else
895 pwndActiveView = m_pwndRightView;
896 if (m_wndSplitter2.IsColumnHidden(1))
897 m_wndSplitter2.ShowColumn();
899 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseLeft;
900 m_pwndLeftView->SetTextType(m_Data.m_arBaseFile.GetUnicodeType());
901 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arBaseFile.GetLineEndings());
902 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName();
903 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename();
904 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_baseFile.GetConvertedFileName();
905 m_pwndLeftView->m_sReflectedName = m_Data.m_baseFile.GetReflectedName();
906 m_pwndLeftView->m_pWorkingFile = &m_Data.m_baseFile;
907 m_pwndLeftView->SetWritableIsChangable(true);
909 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseRight;
910 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
911 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
912 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName();
913 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
914 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
915 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
916 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
917 m_pwndRightView->SetWritable();
918 m_pwndRightView->SetTarget();
920 m_pwndBottomView->m_pViewData = nullptr;
921 m_pwndBottomView->m_pWorkingFile = nullptr;
923 if (!m_wndSplitter.IsRowHidden(1))
924 m_wndSplitter.HideRow(1);
925 m_pwndLeftView->SetHidden(FALSE);
926 m_pwndRightView->SetHidden(FALSE);
927 m_pwndBottomView->SetHidden(TRUE);
929 bool hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods;
930 pwndActiveView->CheckModifications(hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods);
931 if (!hasMods && !hasConflicts)
933 // files appear identical, show a dialog informing the user that there are or might
934 // be other differences
935 bool hasEncodingDiff = m_Data.m_arBaseFile.GetUnicodeType() != m_Data.m_arYourFile.GetUnicodeType();
936 bool hasEOLDiff = m_Data.m_arBaseFile.GetLineEndings() != m_Data.m_arYourFile.GetLineEndings();
937 if (hasWhitespaceMods || hasEncodingDiff || hasEOLDiff)
939 // text is identical, but the files do not match
940 CString sWarning(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_MAIN));
941 CString sWhitespace(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_WHITESPACE));
942 CString sEncoding(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_ENCODING));
943 CString sEOL(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_EOL));
944 if (hasWhitespaceMods)
946 sWarning += L"\r\n";
947 sWarning += sWhitespace;
949 if (hasEncodingDiff)
951 sWarning += L"\r\n";
952 sWarning += sEncoding;
953 sWarning += L" (";
954 sWarning += m_Data.m_arBaseFile.GetEncodingName(m_Data.m_arBaseFile.GetUnicodeType());
955 sWarning += L", ";
956 sWarning += m_Data.m_arYourFile.GetEncodingName(m_Data.m_arYourFile.GetUnicodeType());
957 sWarning += L")";
959 if (hasEOLDiff)
961 sWarning += L"\r\n";
962 sWarning += sEOL;
964 AfxMessageBox(sWarning, MB_ICONINFORMATION);
968 else if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
970 //diff between THEIR, YOUR and BASE
971 m_pwndBottomView->SetWritable();
972 m_pwndBottomView->SetTarget();
973 pwndActiveView = m_pwndBottomView;
975 m_pwndLeftView->m_pViewData = &m_Data.m_TheirBaseBoth;
976 m_pwndLeftView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
977 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
978 m_pwndLeftView->m_sWindowName = m_Data.m_theirFile.GetWindowName(IDS_VIEWTITLE_THEIRS);
979 m_pwndLeftView->m_sFullFilePath = m_Data.m_theirFile.GetFilename();
980 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_theirFile.GetConvertedFileName();
981 m_pwndLeftView->m_sReflectedName = m_Data.m_theirFile.GetReflectedName();
982 m_pwndLeftView->m_pWorkingFile = &m_Data.m_theirFile;
984 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseBoth;
985 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
986 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
987 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName(IDS_VIEWTITLE_MINE);
988 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
989 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
990 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
991 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
993 m_pwndBottomView->m_pViewData = &m_Data.m_Diff3;
994 m_pwndBottomView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
995 m_pwndBottomView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
996 m_pwndBottomView->m_sWindowName.LoadString(IDS_VIEWTITLE_MERGED);
997 m_pwndBottomView->m_sWindowName += L" - " + m_Data.m_mergedFile.GetWindowName();
998 m_pwndBottomView->m_sFullFilePath = m_Data.m_mergedFile.GetFilename();
999 m_pwndBottomView->m_sConvertedFilePath = m_Data.m_mergedFile.GetConvertedFileName();
1000 m_pwndBottomView->m_sReflectedName = m_Data.m_mergedFile.GetReflectedName();
1001 m_pwndBottomView->m_pWorkingFile = &m_Data.m_mergedFile;
1003 if (m_wndSplitter2.IsColumnHidden(1))
1004 m_wndSplitter2.ShowColumn();
1005 if (m_wndSplitter.IsRowHidden(1))
1006 m_wndSplitter.ShowRow();
1007 m_pwndLeftView->SetHidden(FALSE);
1008 m_pwndRightView->SetHidden(FALSE);
1009 m_pwndBottomView->SetHidden(FALSE);
1010 // in three pane view, hide the line diff bar
1011 m_wndLineDiffBar.ShowPane(false, false, true);
1012 m_wndLineDiffBar.DocumentUpdated();
1014 if (!m_Data.m_mergedFile.InUse())
1016 m_Data.m_mergedFile.SetFileName(m_Data.m_yourFile.GetFilename());
1018 m_pwndLeftView->BuildAllScreen2ViewVector();
1019 m_pwndLeftView->DocumentUpdated();
1020 m_pwndRightView->DocumentUpdated();
1021 m_pwndBottomView->DocumentUpdated();
1022 m_wndLocatorBar.DocumentUpdated();
1023 m_wndLineDiffBar.DocumentUpdated();
1024 UpdateLayout();
1025 SetActiveView(pwndActiveView);
1027 if ((line >= -1) && m_pwndRightView->m_pViewData)
1029 int n = line == -1 ? min( nOldLineNumber, nOldLine ) : line;
1030 if (n >= 0)
1032 n = m_pwndRightView->m_pViewData->FindLineNumber(n);
1033 if (m_bCollapsed && line >= 0)
1035 // adjust the goto-line position if we're collapsed
1036 int step = m_pwndRightView->m_nTopLine > n ? -1 : 1;
1037 int skip = 0;
1038 for (int i = m_pwndRightView->m_nTopLine; i != n; i += step)
1040 if (m_pwndRightView->m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN)
1041 ++skip;
1043 if (m_pwndRightView->m_pViewData->GetHideState(n) == HIDESTATE_HIDDEN)
1044 OnViewTextFoldUnfold();
1045 else
1046 n = n + (skip * step * -1);
1049 if (n < 0)
1050 n = nOldLine;
1052 m_pwndRightView->ScrollAllToLine(n);
1053 POINT p;
1054 p.x = 0;
1055 p.y = n;
1056 if ((ptOldCaretPos.x >= 0) || (ptOldCaretPos.y >= 0))
1057 p = ptOldCaretPos;
1058 m_pwndLeftView->SetCaretPosition(p);
1059 m_pwndRightView->SetCaretPosition(p);
1060 m_pwndBottomView->SetCaretPosition(p);
1061 m_pwndBottomView->ScrollToChar(0);
1062 m_pwndLeftView->ScrollToChar(0);
1063 m_pwndRightView->ScrollToChar(0);
1065 else
1067 CRegDWORD regFirstDiff = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstDiffOnLoad", TRUE);
1068 CRegDWORD regFirstConflict = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstConflictOnLoad", TRUE);
1069 bool bGoFirstDiff = (0 != static_cast<DWORD>(regFirstDiff));
1070 bool bGoFirstConflict = (0 != static_cast<DWORD>(regFirstConflict));
1071 if (bGoFirstConflict && (CheckResolved()>=0))
1073 pwndActiveView->GoToFirstConflict();
1074 // Ignore the first few Mouse Move messages, so that the line diff stays on
1075 // the first diff line until the user actually moves the mouse
1076 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1078 else if (bGoFirstDiff)
1080 pwndActiveView->GoToFirstDifference();
1081 // Ignore the first few Mouse Move messages, so that the line diff stays on
1082 // the first diff line until the user actually moves the mouse
1083 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1085 else
1087 // Avoid incorrect rendering of active pane.
1088 m_pwndBottomView->ScrollToChar(0);
1089 m_pwndLeftView->ScrollToChar(0);
1090 m_pwndRightView->ScrollToChar(0);
1093 CheckResolved();
1094 if (m_bHasConflicts && !m_bSaveRequiredOnConflicts)
1095 m_bSaveRequired = false;
1096 CUndo::GetInstance().Clear();
1097 return true;
1100 void CMainFrame::UpdateLayout()
1102 if (m_bInitSplitter)
1104 m_wndSplitter.CenterSplitter();
1105 m_wndSplitter2.CenterSplitter();
1109 void CMainFrame::RecalcLayout(BOOL bNotify)
1111 GetClientRect(&m_dockManager.m_rectInPlace);
1112 if (m_pRibbonApp)
1113 m_dockManager.m_rectInPlace.top += m_pRibbonApp->GetRibbonHeight();
1114 CFrameWndEx::RecalcLayout(bNotify);
1117 void CMainFrame::OnSize(UINT nType, int cx, int cy)
1119 CFrameWndEx::OnSize(nType, cx, cy);
1120 if (m_bInitSplitter && nType != SIZE_MINIMIZED)
1122 if (m_wndSplitter.GetSafeHwnd())
1124 if (m_wndSplitter.HasOldRowSize() && (m_wndSplitter.GetOldRowCount() == 2))
1126 int oldTotal = m_wndSplitter.GetOldRowSize(0) + m_wndSplitter.GetOldRowSize(1);
1127 if (oldTotal)
1129 int cxCur0, cxCur1, cxMin0, cxMin1;
1130 m_wndSplitter.GetRowInfo(0, cxCur0, cxMin0);
1131 m_wndSplitter.GetRowInfo(1, cxCur1, cxMin1);
1132 cxCur0 = m_wndSplitter.GetOldRowSize(0) * (cxCur0 + cxCur1) / oldTotal;
1133 cxCur1 = m_wndSplitter.GetOldRowSize(1) * (cxCur0 + cxCur1) / oldTotal;
1134 m_wndSplitter.SetRowInfo(0, cxCur0, 0);
1135 m_wndSplitter.SetRowInfo(1, cxCur1, 0);
1136 m_wndSplitter.RecalcLayout();
1140 if (m_wndSplitter2.HasOldColSize() && (m_wndSplitter2.GetOldColCount() == 2))
1142 int oldTotal = m_wndSplitter2.GetOldColSize(0) + m_wndSplitter2.GetOldColSize(1);
1143 if (oldTotal)
1145 int cyCur0, cyCur1, cyMin0, cyMin1;
1146 m_wndSplitter2.GetColumnInfo(0, cyCur0, cyMin0);
1147 m_wndSplitter2.GetColumnInfo(1, cyCur1, cyMin1);
1148 cyCur0 = m_wndSplitter2.GetOldColSize(0) * (cyCur0 + cyCur1) / oldTotal;
1149 cyCur1 = m_wndSplitter2.GetOldColSize(1) * (cyCur0 + cyCur1) / oldTotal;
1150 m_wndSplitter2.SetColumnInfo(0, cyCur0, 0);
1151 m_wndSplitter2.SetColumnInfo(1, cyCur1, 0);
1152 m_wndSplitter2.RecalcLayout();
1157 if ((nType == SIZE_RESTORED)&&m_bCheckReload)
1159 m_bCheckReload = false;
1160 CheckForReload();
1164 void CMainFrame::OnViewWhitespaces()
1166 CRegDWORD regViewWhitespaces = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
1167 BOOL bViewWhitespaces = regViewWhitespaces;
1168 if (m_pwndLeftView)
1169 bViewWhitespaces = m_pwndLeftView->m_bViewWhitespace;
1171 bViewWhitespaces = !bViewWhitespaces;
1172 regViewWhitespaces = bViewWhitespaces;
1173 if (m_pwndLeftView)
1175 m_pwndLeftView->m_bViewWhitespace = bViewWhitespaces;
1176 m_pwndLeftView->Invalidate();
1178 if (m_pwndRightView)
1180 m_pwndRightView->m_bViewWhitespace = bViewWhitespaces;
1181 m_pwndRightView->Invalidate();
1183 if (m_pwndBottomView)
1185 m_pwndBottomView->m_bViewWhitespace = bViewWhitespaces;
1186 m_pwndBottomView->Invalidate();
1190 void CMainFrame::OnUpdateViewWhitespaces(CCmdUI *pCmdUI)
1192 if (m_pwndLeftView)
1193 pCmdUI->SetCheck(m_pwndLeftView->m_bViewWhitespace);
1196 void CMainFrame::OnViewCollapsed()
1198 m_regCollapsed = !static_cast<DWORD>(m_regCollapsed);
1199 m_bCollapsed = !!static_cast<DWORD>(m_regCollapsed);
1201 OnViewTextFoldUnfold();
1202 m_wndLocatorBar.Invalidate();
1205 void CMainFrame::OnUpdateViewCollapsed(CCmdUI *pCmdUI)
1207 pCmdUI->SetCheck(m_bCollapsed);
1210 void CMainFrame::OnViewWraplonglines()
1212 m_bWrapLines = !static_cast<DWORD>(m_regWrapLines);
1213 m_regWrapLines = m_bWrapLines;
1215 if (m_pwndLeftView) m_pwndLeftView ->WrapChanged();
1216 if (m_pwndRightView) m_pwndRightView ->WrapChanged();
1217 if (m_pwndBottomView) m_pwndBottomView->WrapChanged();
1218 OnViewTextFoldUnfold();
1219 m_wndLocatorBar.DocumentUpdated();
1222 void CMainFrame::OnViewTextFoldUnfold()
1224 OnViewTextFoldUnfold(m_pwndLeftView);
1225 OnViewTextFoldUnfold(m_pwndRightView);
1226 OnViewTextFoldUnfold(m_pwndBottomView);
1229 void CMainFrame::OnViewTextFoldUnfold(CBaseView* view)
1231 if (view == 0)
1232 return;
1233 view->BuildAllScreen2ViewVector();
1234 view->UpdateCaret();
1235 view->Invalidate();
1236 view->EnsureCaretVisible();
1239 void CMainFrame::OnUpdateViewWraplonglines(CCmdUI *pCmdUI)
1241 pCmdUI->SetCheck(m_bWrapLines);
1244 void CMainFrame::OnViewOnewaydiff()
1246 if (CheckForSave(CHFSR_RELOAD)==IDCANCEL)
1247 return;
1248 m_bOneWay = !m_bOneWay;
1249 m_regOneWay = m_bOneWay;
1250 ShowDiffBar(!m_bOneWay);
1251 LoadViews(-1);
1254 void CMainFrame::DiffLeftToBase()
1256 DiffTwo(m_Data.m_baseFile, m_Data.m_theirFile);
1259 void CMainFrame::DiffRightToBase()
1261 DiffTwo(m_Data.m_baseFile, m_Data.m_yourFile);
1264 void CMainFrame::DiffTwo(const CWorkingFile& file1, const CWorkingFile& file2)
1266 wchar_t ownpath[MAX_PATH] = { 0 };
1267 GetModuleFileName(nullptr, ownpath, MAX_PATH);
1268 CString sCmd;
1269 sCmd.Format(L"\"%s\"", ownpath);
1270 sCmd += L" /base:\"";
1271 sCmd += file1.GetFilename();
1272 sCmd += L"\" /mine:\"";
1273 sCmd += file2.GetFilename();
1274 sCmd += L'"';
1275 if (!file1.GetWindowName().IsEmpty())
1276 sCmd += L" /basename:\"" + file1.GetWindowName() + L"\"";
1277 if (!file2.GetWindowName().IsEmpty())
1278 sCmd += L" /yourname:\"" + file2.GetWindowName() + L"\"";
1280 CAppUtils::LaunchApplication(sCmd, CAppUtils::LaunchApplicationFlags());
1283 void CMainFrame::ShowDiffBar(bool bShow)
1285 if (bShow)
1287 // restore the line diff bar
1288 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
1289 m_wndLineDiffBar.DocumentUpdated();
1290 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
1291 m_wndLocatorBar.DocumentUpdated();
1293 else
1295 // in one way view, hide the line diff bar
1296 m_wndLineDiffBar.ShowPane(false, false, true);
1297 m_wndLineDiffBar.DocumentUpdated();
1301 // Implementation helper only,
1302 // use CTheme::Instance::SetDarkTheme to actually set the theme.
1303 #ifndef UI_PKEY_DarkModeRibbon
1304 DEFINE_UIPROPERTYKEY(UI_PKEY_DarkModeRibbon, VT_BOOL, 2004);
1305 DEFINE_UIPROPERTYKEY(UI_PKEY_ApplicationButtonColor, VT_UI4, 2003);
1306 #endif
1307 void CMainFrame::SetTheme(bool bDark)
1309 if (m_bUseRibbons)
1310 SetAccentColor();
1312 if (bDark)
1314 CComPtr<IPropertyStore> spPropertyStore;
1315 if (m_bUseRibbons && SUCCEEDED(m_pRibbonFramework->QueryInterface(&spPropertyStore)))
1317 DarkModeHelper::Instance().AllowDarkModeForApp(TRUE);
1318 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), TRUE);
1319 PROPVARIANT propvarDarkMode;
1320 InitPropVariantFromBoolean(1, &propvarDarkMode);
1321 spPropertyStore->SetValue(UI_PKEY_DarkModeRibbon, propvarDarkMode);
1322 spPropertyStore->Commit();
1324 SetClassLongPtr(GetSafeHwnd(), GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(GetStockObject(BLACK_BRUSH)));
1325 BOOL darkFlag = TRUE;
1326 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data = { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS, &darkFlag, sizeof(darkFlag) };
1327 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data);
1328 DarkModeHelper::Instance().FlushMenuThemes();
1329 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
1331 else
1333 DarkModeHelper::Instance().AllowDarkModeForApp(FALSE);
1334 DarkModeHelper::Instance().AllowDarkModeForWindow(GetSafeHwnd(), FALSE);
1335 CComPtr<IPropertyStore> spPropertyStore;
1336 if (m_bUseRibbons && SUCCEEDED(m_pRibbonFramework->QueryInterface(&spPropertyStore)))
1338 PROPVARIANT propvarDarkMode;
1339 InitPropVariantFromBoolean(false, &propvarDarkMode);
1340 spPropertyStore->SetValue(UI_PKEY_DarkModeRibbon, propvarDarkMode);
1341 spPropertyStore->Commit();
1343 SetClassLongPtr(GetSafeHwnd(), GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(GetSysColorBrush(COLOR_3DFACE)));
1344 BOOL darkFlag = FALSE;
1345 DarkModeHelper::WINDOWCOMPOSITIONATTRIBDATA data = { DarkModeHelper::WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS, &darkFlag, sizeof(darkFlag) };
1346 DarkModeHelper::Instance().SetWindowCompositionAttribute(*this, &data);
1347 DarkModeHelper::Instance().FlushMenuThemes();
1348 DarkModeHelper::Instance().RefreshImmersiveColorPolicyState();
1349 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
1351 if (bDark || CTheme::Instance().IsHighContrastModeDark())
1352 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CThemeMFCVisualManager));
1353 ::RedrawWindow(GetSafeHwnd(), nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_INTERNALPAINT | RDW_ALLCHILDREN | RDW_UPDATENOW);
1356 void CMainFrame::SetAccentColor()
1358 if (!m_bUseRibbons)
1359 return;
1361 // set the accent color for the main button
1362 if (m_pRibbonFramework)
1364 Win10Colors::AccentColor accentColor;
1365 if (SUCCEEDED(Win10Colors::GetAccentColor(accentColor)))
1367 BYTE h, s, b;
1368 CTheme::RGBToHSB(accentColor.accent, h, s, b);
1369 UI_HSBCOLOR aColor = UI_HSB(h, s, b);
1371 CComPtr<IPropertyStore> spPropertyStore;
1372 HRESULT hr = m_pRibbonFramework->QueryInterface(&spPropertyStore);
1373 if (SUCCEEDED(hr))
1375 PROPVARIANT propvarAccentColor;
1376 InitPropVariantFromUInt32(aColor, &propvarAccentColor);
1377 spPropertyStore->SetValue(UI_PKEY_ApplicationButtonColor, propvarAccentColor);
1378 spPropertyStore->Commit();
1384 int CMainFrame::CheckResolved()
1386 //only in three way diffs can be conflicts!
1387 m_bHasConflicts = true;
1388 if (IsViewGood(m_pwndBottomView))
1390 CViewData* viewdata = m_pwndBottomView->m_pViewData;
1391 if (viewdata)
1393 for (int i=0; i<viewdata->GetCount(); i++)
1395 const DiffStates state = viewdata->GetState(i);
1396 if ((DIFFSTATE_CONFLICTED == state)||(DIFFSTATE_CONFLICTED_IGNORED == state))
1397 return i;
1401 m_bHasConflicts = false;
1402 return -1;
1405 int CMainFrame::SaveFile(const CString& sFilePath)
1407 CBaseView* pView = nullptr;
1408 CViewData* pViewData = nullptr;
1409 CFileTextLines * pOriginFile = &m_Data.m_arBaseFile;
1410 if (IsViewGood(m_pwndBottomView))
1412 pView = m_pwndBottomView;
1413 pViewData = m_pwndBottomView->m_pViewData;
1415 else if (IsViewGood(m_pwndRightView))
1417 pView = m_pwndRightView;
1418 pViewData = m_pwndRightView->m_pViewData;
1419 if (m_Data.IsYourFileInUse())
1420 pOriginFile = &m_Data.m_arYourFile;
1421 else if (m_Data.IsTheirFileInUse())
1422 pOriginFile = &m_Data.m_arTheirFile;
1424 else
1426 // nothing to save!
1427 return 1;
1429 Invalidate();
1430 if ((pViewData)&&(pOriginFile))
1432 CFileTextLines file;
1433 pOriginFile->CopySettings(&file);
1434 CFileTextLines::SaveParams saveParams;
1435 saveParams.m_LineEndings = pView->GetLineEndings();
1436 saveParams.m_UnicodeType = pView->GetTextType();
1437 file.SetSaveParams(saveParams);
1438 for (int i=0; i<pViewData->GetCount(); i++)
1440 //only copy non-removed lines
1441 DiffStates state = pViewData->GetState(i);
1442 switch (state)
1444 case DIFFSTATE_CONFLICTED:
1445 case DIFFSTATE_CONFLICTED_IGNORED:
1447 int first = i;
1448 int last = i;
1451 last++;
1452 } while((last<pViewData->GetCount()) && ((pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
1453 // TortoiseGitMerge changes here
1454 file.Add(L"<<<<<<< .mine", m_pwndRightView->GetLineEndings());
1455 for (int j=first; j<last; j++)
1457 EOL lineending = m_pwndRightView->m_pViewData->GetLineEnding(j);
1458 if (lineending == EOL_NOENDING)
1459 lineending = m_pwndRightView->GetLineEndings();
1460 file.Add(m_pwndRightView->m_pViewData->GetLine(j), lineending);
1462 file.Add(L"=======", m_pwndRightView->GetLineEndings());
1463 for (int j=first; j<last; j++)
1465 EOL lineending = m_pwndLeftView->m_pViewData->GetLineEnding(j);
1466 if (lineending == EOL_NOENDING)
1467 lineending = m_pwndLeftView->GetLineEndings();
1468 file.Add(m_pwndLeftView->m_pViewData->GetLine(j), lineending);
1470 file.Add(L">>>>>>> .theirs", m_pwndRightView->GetLineEndings());
1471 i = last-1;
1473 break;
1474 case DIFFSTATE_EMPTY:
1475 case DIFFSTATE_CONFLICTEMPTY:
1476 case DIFFSTATE_IDENTICALREMOVED:
1477 case DIFFSTATE_REMOVED:
1478 case DIFFSTATE_THEIRSREMOVED:
1479 case DIFFSTATE_YOURSREMOVED:
1480 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
1481 // do not save removed lines
1482 break;
1483 default:
1484 file.Add(pViewData->GetLine(i), pViewData->GetLineEnding(i));
1485 break;
1488 if (!file.Save(sFilePath, false, false))
1490 CMessageBox::Show(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
1491 return -1;
1493 if (sFilePath == m_Data.m_baseFile.GetFilename())
1495 m_Data.m_baseFile.StoreFileAttributes();
1497 if (sFilePath == m_Data.m_theirFile.GetFilename())
1499 m_Data.m_theirFile.StoreFileAttributes();
1501 if (sFilePath == m_Data.m_yourFile.GetFilename())
1503 m_Data.m_yourFile.StoreFileAttributes();
1505 /*if (sFilePath == m_Data.m_mergedFile.GetFilename())
1507 m_Data.m_mergedFile.StoreFileAttributes();
1508 }//*/
1509 m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
1510 if (IsViewGood(m_pwndBottomView))
1511 m_pwndBottomView->SetModified(FALSE);
1512 else if (IsViewGood(m_pwndRightView))
1513 m_pwndRightView->SetModified(FALSE);
1514 CUndo::GetInstance().MarkAsOriginalState(
1515 false,
1516 IsViewGood(m_pwndRightView) && (pViewData == m_pwndRightView->m_pViewData),
1517 IsViewGood(m_pwndBottomView) && (pViewData == m_pwndBottomView->m_pViewData));
1518 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
1519 return 0;
1520 return file.GetCount();
1522 return 1;
1525 void CMainFrame::OnFileSave()
1527 // when multiple files are set as writable we have to ask what file to save
1528 int nEditableViewCount =
1529 (CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsWritable() ? 1 : 0)
1530 + (CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsWritable() ? 1 : 0)
1531 + (CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsWritable() ? 1 : 0);
1532 bool bLeftIsModified = CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsModified();
1533 bool bRightIsModified = CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsModified();
1534 bool bBottomIsModified = CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsModified();
1535 int nModifiedViewCount =
1536 (bLeftIsModified ? 1 : 0)
1537 + (bRightIsModified ? 1 : 0)
1538 + (bBottomIsModified ? 1 : 0);
1539 if (nEditableViewCount>1)
1541 if (nModifiedViewCount == 1)
1543 if (bLeftIsModified)
1544 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1545 else
1546 FileSave();
1548 else if (nModifiedViewCount>0)
1550 // both views
1551 CTaskDialog taskdlg(CString(MAKEINTRESOURCE(IDS_SAVE_MORE)),
1552 CString(MAKEINTRESOURCE(IDS_SAVE)),
1553 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1555 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1556 CString sTaskTemp;
1557 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
1558 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, static_cast<LPCWSTR>(m_pwndLeftView->m_pWorkingFile->GetFilename()));
1559 else
1560 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
1561 taskdlg.AddCommandControl(201, sTaskTemp, bLeftIsModified);// left
1562 if (bLeftIsModified)
1563 taskdlg.SetDefaultCommandControl(201);
1564 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
1565 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, static_cast<LPCWSTR>(m_pwndRightView->m_pWorkingFile->GetFilename()));
1566 else
1567 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
1568 taskdlg.AddCommandControl(202, sTaskTemp, bRightIsModified); // right
1569 if (bRightIsModified)
1570 taskdlg.SetDefaultCommandControl(202);
1571 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2)), nModifiedViewCount>1); // both
1572 if (nModifiedViewCount > 1)
1573 taskdlg.SetDefaultCommandControl(203);
1574 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1575 taskdlg.SetMainIcon(TD_WARNING_ICON);
1576 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
1577 switch (ret)
1579 case 201: // left
1580 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1581 break;
1582 case 203: // both
1583 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1584 case 202: // right
1585 m_pwndRightView->SaveFile();
1586 break;
1590 else
1592 // only target view was modified
1593 FileSave();
1597 void CMainFrame::PatchSave()
1599 bool bDoesNotExist = !PathFileExists(m_Data.m_mergedFile.GetFilename());
1600 if (m_Data.m_bPatchRequired)
1602 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1604 if (!PathIsDirectory(m_Data.m_mergedFile.GetFilename()))
1606 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1607 if (saveret==0)
1609 // file was saved with 0 lines, remove it.
1610 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1611 // just in case
1612 DeleteFile(m_Data.m_mergedFile.GetFilename());
1614 m_Data.m_mergedFile.StoreFileAttributes();
1615 if (m_Data.m_mergedFile.GetFilename() == m_Data.m_yourFile.GetFilename())
1616 m_Data.m_yourFile.StoreFileAttributes();
1617 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1619 // call TortoiseProc to add the new file to version control
1620 CString cmd = L"/command:add /noui /path:\"";
1621 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1622 CAppUtils::RunTortoiseGitProc(cmd);
1627 bool CMainFrame::FileSave(bool bCheckResolved /*=true*/)
1629 if (HasMarkedBlocks())
1631 CString sTitle(MAKEINTRESOURCE(IDS_WARNMARKEDBLOCKS));
1632 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_MARKEDBLOCKS));
1633 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
1634 CTaskDialog taskdlg(sTitle,
1635 sSubTitle,
1636 sAppName,
1638 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1639 taskdlg.AddCommandControl(10, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEINCLUDE)));
1640 taskdlg.AddCommandControl(11, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEEXCLUDE)));
1641 taskdlg.AddCommandControl(12, CString(MAKEINTRESOURCE(IDS_MARKEDBLCOKSSAVEIGNORE)));
1642 taskdlg.AddCommandControl(IDCANCEL, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_CANCEL_OPEN)));
1643 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1644 taskdlg.SetDefaultCommandControl(10);
1645 taskdlg.SetMainIcon(TD_WARNING_ICON);
1646 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
1647 if (ret == 10)
1648 m_pwndRightView->LeaveOnlyMarkedBlocks();
1649 else if (ret == 11)
1650 m_pwndRightView->UseViewFileOfMarked();
1651 else if (ret == 12)
1652 m_pwndRightView->UseViewFileExceptEdited();
1653 else
1654 return false;
1657 if (!m_Data.m_mergedFile.InUse())
1658 return FileSaveAs(bCheckResolved);
1659 // check if the file has the readonly attribute set
1660 bool bDoesNotExist = false;
1661 DWORD fAttribs = GetFileAttributes(m_Data.m_mergedFile.GetFilename());
1662 if ((fAttribs != INVALID_FILE_ATTRIBUTES)&&(fAttribs & FILE_ATTRIBUTE_READONLY))
1663 return FileSaveAs(bCheckResolved);
1664 if (fAttribs == INVALID_FILE_ATTRIBUTES)
1666 bDoesNotExist = (GetLastError() == ERROR_FILE_NOT_FOUND);
1668 if (bCheckResolved && HasConflictsWontKeep())
1669 return false;
1671 if ((static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGitMerge\\Backup"))) != 0)
1673 MoveFileEx(m_Data.m_mergedFile.GetFilename(), m_Data.m_mergedFile.GetFilename() + L".bak", MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
1675 if (m_Data.m_bPatchRequired)
1677 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1679 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1680 if (saveret==0)
1682 // file was saved with 0 lines!
1683 // ask the user if the file should be deleted
1684 CString msg;
1685 msg.Format(IDS_DELETEWHENEMPTY, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename())));
1686 CTaskDialog taskdlg(msg,
1687 CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK2)),
1688 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1690 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1691 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK3)));
1692 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK4)));
1693 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1694 taskdlg.SetDefaultCommandControl(1);
1695 taskdlg.SetMainIcon(TD_WARNING_ICON);
1696 if (taskdlg.DoModal(m_hWnd) == 1)
1698 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1699 DeleteFile(m_Data.m_mergedFile.GetFilename());
1702 else if (saveret < 0)
1704 // error while saving the file
1705 return false;
1708 // if we're in conflict resolve mode (three pane view), check if there are no more conflicts
1709 // and if there aren't, ask to mark the file as resolved
1710 if (IsViewGood(m_pwndBottomView) && !m_bHasConflicts && bCheckResolved)
1712 CString projectRoot;
1713 if (GitAdminDir::HasAdminDir(m_Data.m_mergedFile.GetFilename(), false, &projectRoot))
1715 CString subpath = m_Data.m_mergedFile.GetFilename();
1716 subpath.Replace(L'\\', L'/');
1717 if (subpath.GetLength() >= projectRoot.GetLength())
1719 if (subpath[projectRoot.GetLength()] == L'/')
1720 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength() - 1);
1721 else
1722 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength());
1725 CAutoRepository repository(projectRoot);
1726 bool hasConflictInIndex = false;
1729 if (!repository)
1730 break;
1732 CAutoIndex index;
1733 if (git_repository_index(index.GetPointer(), repository))
1734 break;
1736 CStringA path = CUnicodeUtils::GetUTF8(subpath);
1737 hasConflictInIndex = git_index_get_bypath(index, path, 1) || git_index_get_bypath(index, path, 2);
1738 } while(0);
1740 if (hasConflictInIndex)
1742 CString msg;
1743 msg.Format(IDS_MARKASRESOLVED, static_cast<LPCWSTR>(CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename())));
1744 CTaskDialog taskdlg(msg,
1745 CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK2)),
1746 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1748 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1749 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK3)));
1750 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK4)));
1751 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1752 taskdlg.SetDefaultCommandControl(1);
1753 taskdlg.SetMainIcon(TD_WARNING_ICON);
1754 if (taskdlg.DoModal(m_hWnd) == 1)
1755 MarkAsResolved();
1760 m_bSaveRequired = false;
1761 m_Data.m_mergedFile.StoreFileAttributes();
1763 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1765 // call TortoiseProc to add the new file to version control
1766 CString cmd = L"/command:add /noui /path:\"";
1767 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1768 if(!CAppUtils::RunTortoiseGitProc(cmd))
1769 return false;
1771 return true;
1774 void CMainFrame::OnFileSaveAs()
1776 // ask what file to save as
1777 bool bHaveConflict = (CheckResolved() >= 0);
1778 CTaskDialog taskdlg(
1779 CString(MAKEINTRESOURCE(bHaveConflict ? IDS_ASKFORSAVEAS_MORECONFLICT : IDS_ASKFORSAVEAS_MORE)),
1780 CString(MAKEINTRESOURCE(IDS_ASKFORSAVEAS)),
1781 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1783 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1784 // default can be last view (target) as was in 1.7 or actual (where is cursor) as is in most text editor
1785 if (IsViewGood(m_pwndLeftView))
1787 taskdlg.AddCommandControl(201, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS))); // left
1788 taskdlg.SetDefaultCommandControl(201);
1790 if (IsViewGood(m_pwndRightView))
1792 taskdlg.AddCommandControl(202, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS))); // right
1793 taskdlg.SetDefaultCommandControl(202);
1795 if (IsViewGood(m_pwndBottomView))
1797 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEBOTTOMAS))); // bottom
1798 taskdlg.SetDefaultCommandControl(203);
1800 if (bHaveConflict)
1802 taskdlg.AddCommandControl(204, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_NEEDRESOLVE))); // resolve
1803 taskdlg.SetDefaultCommandControl(204);
1805 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1806 taskdlg.SetMainIcon(bHaveConflict ? TD_WARNING_ICON : TD_INFORMATION_ICON);
1807 int nCommand = static_cast<int>(taskdlg.DoModal(m_hWnd));
1808 CString sFileName;
1809 switch (nCommand)
1811 case 201: // left
1812 if (TryGetFileName(sFileName))
1814 // in 2, 3 view display we want to keep removed lines
1815 m_pwndLeftView->SaveFileTo(sFileName, IsViewGood(m_pwndRightView) ? SAVE_REMOVEDLINES : 0);
1817 break;
1818 case 202: // right
1819 if (TryGetFileName(sFileName))
1821 m_pwndRightView->SaveFileTo(sFileName);
1823 break;
1824 case 203: // bottom
1825 FileSaveAs();
1826 break;
1827 case 204: // continue resolving
1828 if (IsViewGood(m_pwndBottomView))
1830 m_pwndBottomView->GoToLine(CheckResolved());
1832 break;
1836 bool CMainFrame::FileSaveAs(bool bCheckResolved /*=true*/)
1838 if (bCheckResolved && HasConflictsWontKeep())
1839 return false;
1841 CString fileName;
1842 if(!TryGetFileName(fileName))
1843 return false;
1845 SaveFile(fileName);
1846 return true;
1849 void CMainFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
1851 BOOL bEnable = FALSE;
1852 if (m_Data.m_mergedFile.InUse())
1854 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1855 bEnable = TRUE;
1856 else if ( (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData)) &&
1857 (m_pwndRightView->IsModified() || (m_Data.m_yourFile.GetWindowName().Right(static_cast<int>(wcslen(L": patched"))).Compare(L": patched") == 0)))
1858 bEnable = TRUE;
1859 else if (IsViewGood(m_pwndLeftView)
1860 && (m_pwndLeftView->m_pViewData)
1861 && (m_pwndLeftView->IsModified()))
1862 bEnable = TRUE;
1864 pCmdUI->Enable(bEnable);
1867 void CMainFrame::OnUpdateFileSaveAs(CCmdUI *pCmdUI)
1869 // any file is open we can save it as
1870 BOOL bEnable = FALSE;
1871 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1872 bEnable = TRUE;
1873 else if (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData))
1874 bEnable = TRUE;
1875 else if (IsViewGood(m_pwndLeftView)&&(m_pwndLeftView->m_pViewData))
1876 bEnable = TRUE;
1877 pCmdUI->Enable(bEnable);
1880 void CMainFrame::OnUpdateViewOnewaydiff(CCmdUI *pCmdUI)
1882 pCmdUI->SetCheck(!m_bOneWay);
1883 BOOL bEnable = TRUE;
1884 if (IsViewGood(m_pwndBottomView))
1886 bEnable = FALSE;
1888 pCmdUI->Enable(bEnable);
1891 void CMainFrame::OnViewOptions()
1893 CString sTemp;
1894 sTemp.LoadString(IDS_SETTINGSTITLE);
1895 CSettings dlg(sTemp);
1896 dlg.DoModal();
1897 CTheme::Instance().SetDarkTheme(dlg.IsDarkMode());
1898 if (dlg.IsReloadNeeded())
1900 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
1901 return;
1902 CDiffColors::GetInstance().LoadRegistry();
1903 LoadViews();
1904 return;
1906 CDiffColors::GetInstance().LoadRegistry();
1907 if (m_pwndBottomView)
1908 m_pwndBottomView->DocumentUpdated();
1909 if (m_pwndLeftView)
1910 m_pwndLeftView->DocumentUpdated();
1911 if (m_pwndRightView)
1912 m_pwndRightView->DocumentUpdated();
1915 void CMainFrame::OnClose()
1917 if (!IsWindowEnabled())
1918 return; // just in case someone sends a WM_CLOSE to the main window while another window (dialog,...) is open
1919 if (CheckForSave(CHFSR_CLOSE)!=IDCANCEL)
1921 WINDOWPLACEMENT wp;
1923 // before it is destroyed, save the position of the window
1924 wp.length = sizeof wp;
1926 if (GetWindowPlacement(&wp))
1929 if (IsIconic())
1930 // never restore to Iconic state
1931 wp.showCmd = SW_SHOW ;
1933 if ((wp.flags & WPF_RESTORETOMAXIMIZED) != 0)
1934 // if maximized and maybe iconic restore maximized state
1935 wp.showCmd = SW_SHOWMAXIMIZED ;
1937 // and write it
1938 WriteWindowPlacement(&wp);
1940 __super::OnClose();
1944 void CMainFrame::OnActivate(UINT nValue, CWnd* /*pwnd*/, BOOL /*bActivated?*/)
1946 if (nValue != 0) // activated
1948 if (IsIconic())
1950 m_bCheckReload = TRUE;
1952 else
1954 // use a timer to give other messages time to get processed
1955 // first, like e.g. the WM_CLOSE message in case the user
1956 // clicked the close button and that brought the window
1957 // to the front - in that case checking for reload wouldn't
1958 // do any good.
1959 SetTimer(IDT_RELOADCHECKTIMER, 300, nullptr);
1964 void CMainFrame::OnViewLinedown()
1966 OnViewLineUpDown(1);
1969 void CMainFrame::OnViewLineup()
1971 OnViewLineUpDown(-1);
1974 void CMainFrame::OnViewLineUpDown(int direction)
1976 if (m_pwndLeftView)
1977 m_pwndLeftView->ScrollToLine(m_pwndLeftView->m_nTopLine+direction);
1978 if (m_pwndRightView)
1979 m_pwndRightView->ScrollToLine(m_pwndRightView->m_nTopLine+direction);
1980 if (m_pwndBottomView)
1981 m_pwndBottomView->ScrollToLine(m_pwndBottomView->m_nTopLine+direction);
1982 m_wndLocatorBar.Invalidate();
1983 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1986 void CMainFrame::OnViewLineleft()
1988 OnViewLineLeftRight(-1);
1991 void CMainFrame::OnViewLineright()
1993 OnViewLineLeftRight(1);
1996 void CMainFrame::OnViewLineLeftRight(int direction)
1998 if (m_pwndLeftView)
1999 m_pwndLeftView->ScrollSide(direction);
2000 if (m_pwndRightView)
2001 m_pwndRightView->ScrollSide(direction);
2002 if (m_pwndBottomView)
2003 m_pwndBottomView->ScrollSide(direction);
2006 void CMainFrame::OnEditUseTheirs()
2008 if (m_pwndBottomView)
2009 m_pwndBottomView->UseTheirTextBlock();
2011 void CMainFrame::OnUpdateEditUsetheirblock(CCmdUI *pCmdUI)
2013 pCmdUI->Enable(m_pwndBottomView && m_pwndBottomView->HasSelection());
2016 void CMainFrame::OnEditUseMine()
2018 if (m_pwndBottomView)
2019 m_pwndBottomView->UseMyTextBlock();
2021 void CMainFrame::OnUpdateEditUsemyblock(CCmdUI *pCmdUI)
2023 OnUpdateEditUsetheirblock(pCmdUI);
2026 void CMainFrame::OnEditUseTheirsThenMine()
2028 if (m_pwndBottomView)
2029 m_pwndBottomView->UseTheirAndYourBlock();
2032 void CMainFrame::OnUpdateEditUsetheirthenmyblock(CCmdUI *pCmdUI)
2034 OnUpdateEditUsetheirblock(pCmdUI);
2037 void CMainFrame::OnEditUseMineThenTheirs()
2039 if (m_pwndBottomView)
2040 m_pwndBottomView->UseYourAndTheirBlock();
2043 void CMainFrame::OnUpdateEditUseminethentheirblock(CCmdUI *pCmdUI)
2045 OnUpdateEditUsetheirblock(pCmdUI);
2048 void CMainFrame::OnEditUseleftblock()
2050 if (m_pwndBottomView->IsWindowVisible())
2051 m_pwndBottomView->UseRightBlock();
2052 else
2053 m_pwndRightView->UseLeftBlock();
2056 void CMainFrame::OnUpdateEditUseleftblock(CCmdUI *pCmdUI)
2058 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget() && m_pwndRightView->HasSelection());
2061 void CMainFrame::OnUpdateUseBlock(CCmdUI *pCmdUI)
2063 pCmdUI->Enable(TRUE);
2066 void CMainFrame::OnEditUseleftfile()
2068 if (m_pwndBottomView->IsWindowVisible())
2069 m_pwndBottomView->UseRightFile();
2070 else
2071 m_pwndRightView->UseLeftFile();
2074 void CMainFrame::OnUpdateEditUseleftfile(CCmdUI *pCmdUI)
2076 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget());
2079 void CMainFrame::OnEditUseblockfromleftbeforeright()
2081 if (m_pwndRightView)
2082 m_pwndRightView->UseBothLeftFirst();
2085 void CMainFrame::OnUpdateEditUseblockfromleftbeforeright(CCmdUI *pCmdUI)
2087 OnUpdateEditUseleftblock(pCmdUI);
2090 void CMainFrame::OnEditUseblockfromrightbeforeleft()
2092 if (m_pwndRightView)
2093 m_pwndRightView->UseBothRightFirst();
2096 void CMainFrame::OnUpdateEditUseblockfromrightbeforeleft(CCmdUI *pCmdUI)
2098 OnUpdateEditUseleftblock(pCmdUI);
2101 void CMainFrame::OnFileReload()
2103 if (CheckForSave(CHFSR_RELOAD)==IDCANCEL)
2104 return;
2105 CDiffColors::GetInstance().LoadRegistry();
2106 LoadViews(-1);
2109 void CMainFrame::ActivateFrame(int nCmdShow)
2111 // nCmdShow is the normal show mode this frame should be in
2112 // translate default nCmdShow (-1)
2113 if (nCmdShow == -1)
2115 if (!IsWindowVisible())
2116 nCmdShow = SW_SHOWNORMAL;
2117 else if (IsIconic())
2118 nCmdShow = SW_RESTORE;
2121 // bring to top before showing
2122 BringToTop(nCmdShow);
2124 if (nCmdShow != -1)
2126 // show the window as specified
2127 WINDOWPLACEMENT wp;
2129 if ( !ReadWindowPlacement(&wp) )
2131 ShowWindow(nCmdShow);
2133 else
2135 if ( nCmdShow != SW_SHOWNORMAL )
2136 wp.showCmd = nCmdShow;
2138 SetWindowPlacement(&wp);
2141 // and finally, bring to top after showing
2142 BringToTop(nCmdShow);
2144 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme());
2147 BOOL CMainFrame::ReadWindowPlacement(WINDOWPLACEMENT * pwp)
2149 CRegString placement = CRegString(CString(L"Software\\TortoiseGitMerge\\WindowPos_") + GetMonitorSetupHash().c_str());
2150 CString sPlacement = placement;
2151 if (sPlacement.IsEmpty())
2152 return FALSE;
2153 int nRead = swscanf_s(sPlacement, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2154 &pwp->flags, &pwp->showCmd,
2155 &pwp->ptMinPosition.x, &pwp->ptMinPosition.y,
2156 &pwp->ptMaxPosition.x, &pwp->ptMaxPosition.y,
2157 &pwp->rcNormalPosition.left, &pwp->rcNormalPosition.top,
2158 &pwp->rcNormalPosition.right, &pwp->rcNormalPosition.bottom);
2159 if ( nRead != 10 )
2160 return FALSE;
2161 pwp->length = sizeof(WINDOWPLACEMENT);
2163 CDPIAware::Instance().ScaleWindowPlacement(pwp);
2165 return TRUE;
2168 void CMainFrame::WriteWindowPlacement(WINDOWPLACEMENT * pwp)
2170 CRegString placement(CString(L"Software\\TortoiseGitMerge\\WindowPos_") + GetMonitorSetupHash().c_str());
2171 wchar_t szBuffer[_countof("-32767") * 8 + sizeof("65535") * 2];
2173 CDPIAware::Instance().UnscaleWindowPlacement(pwp);
2175 swprintf_s(szBuffer, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2176 pwp->flags, pwp->showCmd,
2177 pwp->ptMinPosition.x, pwp->ptMinPosition.y,
2178 pwp->ptMaxPosition.x, pwp->ptMaxPosition.y,
2179 pwp->rcNormalPosition.left, pwp->rcNormalPosition.top,
2180 pwp->rcNormalPosition.right, pwp->rcNormalPosition.bottom);
2181 placement = szBuffer;
2184 void CMainFrame::OnUpdateMergeMarkasresolved(CCmdUI *pCmdUI)
2186 if (!pCmdUI)
2187 return;
2188 BOOL bEnable = FALSE;
2189 if ((!m_bReadOnly)&&(m_Data.m_mergedFile.InUse()))
2191 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2193 bEnable = !m_bMarkedAsResolvedWasDone;
2196 pCmdUI->Enable(bEnable);
2199 void CMainFrame::OnMergeMarkasresolved()
2201 if(HasConflictsWontKeep())
2202 return;
2204 // now check if the file has already been saved and if not, save it.
2205 if (m_Data.m_mergedFile.InUse())
2207 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2209 if (!FileSave(false))
2210 return;
2211 m_bSaveRequired = false;
2214 MarkAsResolved();
2217 BOOL CMainFrame::MarkAsResolved()
2219 if (m_bReadOnly)
2220 return FALSE;
2221 if (!IsViewGood(m_pwndBottomView))
2222 return FALSE;
2223 if (m_bMarkedAsResolvedWasDone)
2224 return FALSE;
2226 CString cmd = L"/command:resolve /path:\"";
2227 cmd += m_Data.m_mergedFile.GetFilename();
2228 cmd += L"\" /closeonend:1 /noquestion /skipcheck /silent";
2229 if (resolveMsgWnd)
2231 CString s;
2232 s.Format(L" /resolvemsghwnd:%I64d /resolvemsgwparam:%I64d /resolvemsglparam:%I64d", reinterpret_cast<__int64>(resolveMsgWnd), static_cast<__int64>(resolveMsgWParam), static_cast<__int64>(resolveMsgLParam));
2233 cmd += s;
2235 if(!CAppUtils::RunTortoiseGitProc(cmd))
2236 return FALSE;
2237 m_bSaveRequired = false;
2238 m_bMarkedAsResolvedWasDone = true;
2239 return TRUE;
2242 void CMainFrame::OnUpdateMergeNextconflict(CCmdUI *pCmdUI)
2244 BOOL bShow = FALSE;
2245 if (HasNextConflict(m_pwndBottomView))
2246 bShow = TRUE;
2247 else if (HasNextConflict(m_pwndRightView))
2248 bShow = TRUE;
2249 else if (HasNextConflict(m_pwndLeftView))
2250 bShow = TRUE;
2251 pCmdUI->Enable(bShow);
2254 bool CMainFrame::HasNextConflict(CBaseView* view)
2256 if (view == 0)
2257 return false;
2258 if (!view->IsTarget())
2259 return false;
2260 return view->HasNextConflict();
2263 void CMainFrame::OnUpdateMergePreviousconflict(CCmdUI *pCmdUI)
2265 BOOL bShow = FALSE;
2266 if (HasPrevConflict(m_pwndBottomView))
2267 bShow = TRUE;
2268 else if (HasPrevConflict(m_pwndRightView))
2269 bShow = TRUE;
2270 else if (HasPrevConflict(m_pwndLeftView))
2271 bShow = TRUE;
2272 pCmdUI->Enable(bShow);
2275 bool CMainFrame::HasPrevConflict(CBaseView* view)
2277 if (view == 0)
2278 return false;
2279 if (!view->IsTarget())
2280 return false;
2281 return view->HasPrevConflict();
2284 void CMainFrame::OnUpdateNavigateNextdifference(CCmdUI *pCmdUI)
2286 CBaseView* baseView = GetActiveBaseView();
2287 BOOL bShow = FALSE;
2288 if (baseView != 0)
2289 bShow = baseView->HasNextDiff();
2290 pCmdUI->Enable(bShow);
2293 void CMainFrame::OnUpdateNavigatePreviousdifference(CCmdUI *pCmdUI)
2295 CBaseView* baseView = GetActiveBaseView();
2296 BOOL bShow = FALSE;
2297 if (baseView != 0)
2298 bShow = baseView->HasPrevDiff();
2299 pCmdUI->Enable(bShow);
2302 void CMainFrame::OnUpdateNavigateNextinlinediff(CCmdUI *pCmdUI)
2304 BOOL bShow = FALSE;
2305 if (HasNextInlineDiff(m_pwndBottomView))
2306 bShow = TRUE;
2307 else if (HasNextInlineDiff(m_pwndRightView))
2308 bShow = TRUE;
2309 else if (HasNextInlineDiff(m_pwndLeftView))
2310 bShow = TRUE;
2311 pCmdUI->Enable(bShow);
2314 bool CMainFrame::HasNextInlineDiff(CBaseView* view)
2316 if (view == 0)
2317 return false;
2318 if (!view->IsTarget())
2319 return false;
2320 return view->HasNextInlineDiff();
2323 void CMainFrame::OnUpdateNavigatePrevinlinediff(CCmdUI *pCmdUI)
2325 BOOL bShow = FALSE;
2326 if (HasPrevInlineDiff(m_pwndBottomView))
2327 bShow = TRUE;
2328 else if (HasPrevInlineDiff(m_pwndRightView))
2329 bShow = TRUE;
2330 else if (HasPrevInlineDiff(m_pwndLeftView))
2331 bShow = TRUE;
2332 pCmdUI->Enable(bShow);
2335 bool CMainFrame::HasPrevInlineDiff(CBaseView* view)
2337 if (view == 0)
2338 return false;
2339 if (!view->IsTarget())
2340 return false;
2341 return view->HasPrevInlineDiff();
2344 void CMainFrame::OnMoving(UINT fwSide, LPRECT pRect)
2346 // if the pathfilelist dialog is attached to the mainframe,
2347 // move it along with the mainframe
2348 if (::IsWindow(m_dlgFilePatches.m_hWnd))
2350 RECT patchrect;
2351 m_dlgFilePatches.GetWindowRect(&patchrect);
2352 if (::IsWindow(m_hWnd))
2354 RECT thisrect;
2355 GetWindowRect(&thisrect);
2356 if (patchrect.right == thisrect.left)
2358 m_dlgFilePatches.SetWindowPos(nullptr, patchrect.left - (thisrect.left - pRect->left), patchrect.top - (thisrect.top - pRect->top),
2359 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
2363 __super::OnMoving(fwSide, pRect);
2366 void CMainFrame::OnUpdateEditCopy(CCmdUI *pCmdUI)
2368 BOOL bShow = FALSE;
2369 if ((m_pwndBottomView)&&(m_pwndBottomView->HasSelection()))
2370 bShow = TRUE;
2371 else if ((m_pwndRightView)&&(m_pwndRightView->HasSelection()))
2372 bShow = TRUE;
2373 else if ((m_pwndLeftView)&&(m_pwndLeftView->HasSelection()))
2374 bShow = TRUE;
2375 pCmdUI->Enable(bShow);
2378 void CMainFrame::OnUpdateEditPaste(CCmdUI *pCmdUI)
2380 BOOL bWritable = FALSE;
2381 if ((m_pwndBottomView)&&(m_pwndBottomView->IsWritable()))
2382 bWritable = TRUE;
2383 else if ((m_pwndRightView)&&(m_pwndRightView->IsWritable()))
2384 bWritable = TRUE;
2385 else if ((m_pwndLeftView)&&(m_pwndLeftView->IsWritable()))
2386 bWritable = TRUE;
2387 pCmdUI->Enable(bWritable && ::IsClipboardFormatAvailable(CF_TEXT));
2390 void CMainFrame::OnViewSwitchleft()
2392 if (CheckForSave(CHFSR_SWITCH)!=IDCANCEL)
2394 CWorkingFile file = m_Data.m_baseFile;
2395 m_Data.m_baseFile = m_Data.m_yourFile;
2396 m_Data.m_yourFile = file;
2397 if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_yourFile.GetFilename())==0)
2399 m_Data.m_mergedFile = m_Data.m_baseFile;
2401 else if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_baseFile.GetFilename())==0)
2403 m_Data.m_mergedFile = m_Data.m_yourFile;
2405 LoadViews();
2409 void CMainFrame::OnUpdateViewSwitchleft(CCmdUI *pCmdUI)
2411 BOOL bEnable = !IsViewGood(m_pwndBottomView);
2412 pCmdUI->Enable(bEnable);
2415 void CMainFrame::OnUpdateViewShowfilelist(CCmdUI *pCmdUI)
2417 BOOL bEnable = m_dlgFilePatches.HasFiles();
2418 pCmdUI->Enable(bEnable);
2419 pCmdUI->SetCheck(m_dlgFilePatches.IsWindowVisible());
2422 void CMainFrame::OnViewShowfilelist()
2424 m_dlgFilePatches.ShowWindow(m_dlgFilePatches.IsWindowVisible() ? SW_HIDE : SW_SHOW);
2427 void CMainFrame::OnEditUndo()
2429 if (CUndo::GetInstance().CanUndo())
2431 CUndo::GetInstance().Undo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2435 void CMainFrame::OnUpdateEditUndo(CCmdUI *pCmdUI)
2437 pCmdUI->Enable(CUndo::GetInstance().CanUndo());
2440 void CMainFrame::OnEditRedo()
2442 if (CUndo::GetInstance().CanRedo())
2444 CUndo::GetInstance().Redo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2448 void CMainFrame::OnUpdateEditRedo(CCmdUI *pCmdUI)
2450 pCmdUI->Enable(CUndo::GetInstance().CanRedo());
2453 void CMainFrame::OnEditEnable()
2455 CBaseView * pView = GetActiveBaseView();
2456 if (pView && pView->IsReadonlyChangable())
2458 bool isReadOnly = pView->IsReadonly();
2459 pView->SetReadonly(!isReadOnly);
2463 void CMainFrame::OnUpdateEditEnable(CCmdUI *pCmdUI)
2465 CBaseView * pView = GetActiveBaseView();
2466 if (pView)
2468 pCmdUI->Enable(pView->IsReadonlyChangable() || !pView->IsReadonly());
2469 pCmdUI->SetCheck(!pView->IsReadonly());
2471 else
2473 pCmdUI->Enable(FALSE);
2477 void CMainFrame::OnIndicatorLeftview()
2479 if (m_bUseRibbons)
2480 return;
2481 if (IsViewGood(m_pwndLeftView))
2483 m_pwndLeftView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_LEFTVIEW);
2487 void CMainFrame::OnIndicatorRightview()
2489 if (m_bUseRibbons)
2490 return;
2491 if (IsViewGood(m_pwndRightView))
2493 m_pwndRightView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_RIGHTVIEW);
2497 void CMainFrame::OnIndicatorBottomview()
2499 if (m_bUseRibbons)
2500 return;
2501 if (IsViewGood(m_pwndBottomView))
2503 m_pwndBottomView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_BOTTOMVIEW);
2507 int CMainFrame::CheckForReload()
2509 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
2510 if (bLock)
2512 return IDNO;
2514 bLock = true;
2515 bool bSourceChanged =
2516 m_Data.m_baseFile.HasSourceFileChanged()
2517 || m_Data.m_yourFile.HasSourceFileChanged()
2518 || m_Data.m_theirFile.HasSourceFileChanged()
2519 /*|| m_Data.m_mergedFile.HasSourceFileChanged()*/;
2520 if (!bSourceChanged)
2522 bLock = false;
2523 return IDNO;
2526 CString msg = HasUnsavedEdits() ? CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDELOOSECHANGES)) : CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE));
2527 CTaskDialog taskdlg(msg,
2528 CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK2)),
2529 L"TortoiseGitMerge",
2531 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2532 CString sTask3;
2533 if (HasUnsavedEdits())
2534 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK3);
2535 else
2536 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK4);
2537 taskdlg.AddCommandControl(IDYES, sTask3);
2538 taskdlg.AddCommandControl(IDNO, CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK5)));
2539 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2540 taskdlg.SetDefaultCommandControl(IDYES);
2541 taskdlg.SetMainIcon(TD_WARNING_ICON);
2542 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2543 if (ret == IDYES)
2545 CDiffColors::GetInstance().LoadRegistry();
2546 LoadViews(-1);
2548 else
2550 if (IsViewGood(m_pwndBottomView)) // three pane view
2552 /*if (m_Data.m_sourceFile.HasSourceFileChanged())
2553 m_pwndBottomView->SetModified();
2554 if (m_Data.m_mergedFile.HasSourceFileChanged())
2555 m_pwndBottomView->SetModified();//*/
2556 if (m_Data.m_yourFile.HasSourceFileChanged())
2557 m_pwndRightView->SetModified();
2558 if (m_Data.m_theirFile.HasSourceFileChanged())
2559 m_pwndLeftView->SetModified();
2561 else if (IsViewGood(m_pwndRightView)) // two pane view
2563 if (m_Data.m_baseFile.HasSourceFileChanged())
2564 m_pwndLeftView->SetModified();
2565 if (m_Data.m_yourFile.HasSourceFileChanged())
2566 m_pwndRightView->SetModified();
2568 else
2570 if (m_Data.m_yourFile.HasSourceFileChanged())
2571 m_pwndLeftView->SetModified();
2574 // no reload just store updated file time
2575 m_Data.m_baseFile.StoreFileAttributes();
2576 m_Data.m_theirFile.StoreFileAttributes();
2577 m_Data.m_yourFile.StoreFileAttributes();
2578 //m_Data.m_mergedFile.StoreFileAttributes();
2580 bLock = false;
2581 return ret;
2584 int CMainFrame::CheckForSave(ECheckForSaveReason eReason)
2586 int idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2587 int idNoSave = IDS_ASKFORSAVE_TASK7;
2588 int idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2589 switch (eReason)
2591 case CHFSR_CLOSE:
2592 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2593 idNoSave = IDS_ASKFORSAVE_TASK4;
2594 idCancelAction = IDS_ASKFORSAVE_TASK5;
2595 break;
2596 case CHFSR_SWITCH:
2597 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2598 //idNoSave = IDS_ASKFORSAVE_TASK7;
2599 idCancelAction = IDS_ASKFORSAVE_TASK8;
2600 break;
2601 case CHFSR_RELOAD:
2602 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2603 //idNoSave = IDS_ASKFORSAVE_TASK7;
2604 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2605 break;
2606 case CHFSR_OPTIONS:
2607 idTitle = IDS_WARNMODIFIEDLOOSECHANGESOPTIONS;
2608 //idNoSave = IDS_ASKFORSAVE_TASK7;
2609 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPTIONS;
2610 break;
2611 case CHFSR_OPEN:
2612 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2613 idNoSave = IDS_ASKFORSAVE_NOSAVE_OPEN;
2614 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2615 break;
2618 CString sTitle(MAKEINTRESOURCE(idTitle));
2619 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_TASK2));
2620 CString sNoSave(MAKEINTRESOURCE(idNoSave));
2621 CString sCancelAction(MAKEINTRESOURCE(idCancelAction));
2622 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
2624 // TODO simplify logic, reduce code duplication
2625 if (CBaseView::IsViewGood(m_pwndBottomView))
2627 // three-way diff - by design only bottom can be changed
2628 // use 1.7 way to do that
2630 else if (CBaseView::IsViewGood(m_pwndRightView))
2632 // two-way diff -
2633 // 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
2634 if (HasUnsavedEdits(m_pwndLeftView))
2636 // both views
2637 CTaskDialog taskdlg(sTitle,
2638 sSubTitle,
2639 sAppName,
2641 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2642 CString sTaskTemp;
2643 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
2644 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, static_cast<LPCWSTR>(m_pwndLeftView->m_pWorkingFile->GetFilename()));
2645 else
2646 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
2647 taskdlg.AddCommandControl(201, sTaskTemp); // left
2648 taskdlg.SetDefaultCommandControl(201);
2649 if (HasUnsavedEdits(m_pwndRightView))
2651 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
2652 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, static_cast<LPCWSTR>(m_pwndRightView->m_pWorkingFile->GetFilename()));
2653 else
2654 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
2655 taskdlg.AddCommandControl(202, sTaskTemp); // right
2656 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2))); // both
2657 taskdlg.SetDefaultCommandControl(203);
2659 taskdlg.AddCommandControl(IDNO, sNoSave); // none
2660 taskdlg.AddCommandControl(IDCANCEL, sCancelAction); // cancel
2661 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2662 taskdlg.SetMainIcon(TD_WARNING_ICON);
2663 UINT ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2664 switch (ret)
2666 case 201: // left
2667 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2668 break;
2669 case 203: // both
2670 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2671 case 202: // right
2672 m_pwndRightView->SaveFile();
2673 break;
2675 if (ret != IDCANCEL && (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN))
2676 DeleteBaseTheirsMineOnClose();
2677 return ret;
2679 else
2681 // only secondary (left) view
2683 // only right view - 1.7 implementation is used
2685 else if (CBaseView::IsViewGood(m_pwndLeftView))
2687 // only one view - only one to save
2688 // 1.7 FileSave don't support this mode
2689 if (HasUnsavedEdits(m_pwndLeftView))
2691 CTaskDialog taskdlg(sTitle,
2692 sSubTitle,
2693 sAppName,
2695 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2696 CString sTask3;
2697 if (m_Data.m_mergedFile.InUse())
2698 sTask3.Format(IDS_ASKFORSAVE_TASK3, static_cast<LPCWSTR>(m_Data.m_mergedFile.GetFilename()));
2699 else
2700 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2701 taskdlg.AddCommandControl(IDYES, sTask3);
2702 taskdlg.AddCommandControl(IDNO, sNoSave);
2703 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2704 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2705 taskdlg.SetDefaultCommandControl(IDYES);
2706 taskdlg.SetMainIcon(TD_WARNING_ICON);
2707 if (static_cast<UINT>(taskdlg.DoModal(m_hWnd)) == IDYES)
2709 if (m_pwndLeftView->SaveFile()<0)
2710 return IDCANCEL;
2713 if (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN)
2714 DeleteBaseTheirsMineOnClose();
2715 return IDNO;
2717 else
2719 if (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN)
2720 DeleteBaseTheirsMineOnClose();
2721 return IDNO; // nothing to save
2724 // 1.7 implementation
2725 UINT ret = IDNO;
2726 if (HasUnsavedEdits())
2728 CTaskDialog taskdlg(sTitle,
2729 sSubTitle,
2730 sAppName,
2732 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2733 CString sTask3;
2734 if (m_Data.m_mergedFile.InUse())
2735 sTask3.Format(IDS_ASKFORSAVE_TASK3, static_cast<LPCWSTR>(m_Data.m_mergedFile.GetFilename()));
2736 else
2737 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2738 taskdlg.AddCommandControl(IDYES, sTask3);
2739 taskdlg.AddCommandControl(IDNO, sNoSave);
2740 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2741 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2742 taskdlg.SetDefaultCommandControl(IDYES);
2743 taskdlg.SetMainIcon(TD_WARNING_ICON);
2744 ret = static_cast<UINT>(taskdlg.DoModal(m_hWnd));
2746 if (ret == IDYES)
2748 if (!FileSave())
2749 ret = IDCANCEL;
2753 if (ret != IDCANCEL && (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN))
2754 DeleteBaseTheirsMineOnClose();
2756 return ret;
2759 void CMainFrame::DeleteBaseTheirsMineOnClose()
2761 if (!m_bDeleteBaseTheirsMineOnClose)
2762 return;
2764 m_bDeleteBaseTheirsMineOnClose = false;
2766 DeleteFile(m_Data.m_baseFile.GetFilename());
2767 DeleteFile(m_Data.m_theirFile.GetFilename());
2768 DeleteFile(m_Data.m_yourFile.GetFilename());
2771 bool CMainFrame::HasUnsavedEdits() const
2773 return HasUnsavedEdits(m_pwndBottomView) || HasUnsavedEdits(m_pwndRightView) || m_bSaveRequired;
2776 bool CMainFrame::HasUnsavedEdits(const CBaseView* view)
2778 if (!CBaseView::IsViewGood(view))
2779 return false;
2780 return view->IsModified();
2783 bool CMainFrame::HasMarkedBlocks() const
2785 return CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->HasMarkedBlocks();
2788 bool CMainFrame::IsViewGood(const CBaseView* view)
2790 return CBaseView::IsViewGood(view);
2793 void CMainFrame::OnViewInlinediffword()
2795 m_bInlineWordDiff = !m_bInlineWordDiff;
2796 if (m_pwndLeftView)
2798 m_pwndLeftView->SetInlineWordDiff(m_bInlineWordDiff);
2799 m_pwndLeftView->BuildAllScreen2ViewVector();
2800 m_pwndLeftView->DocumentUpdated();
2802 if (m_pwndRightView)
2804 m_pwndRightView->SetInlineWordDiff(m_bInlineWordDiff);
2805 m_pwndRightView->BuildAllScreen2ViewVector();
2806 m_pwndRightView->DocumentUpdated();
2808 if (m_pwndBottomView)
2810 m_pwndBottomView->SetInlineWordDiff(m_bInlineWordDiff);
2811 m_pwndBottomView->BuildAllScreen2ViewVector();
2812 m_pwndBottomView->DocumentUpdated();
2814 m_wndLineDiffBar.DocumentUpdated();
2817 void CMainFrame::OnUpdateViewInlinediffword(CCmdUI *pCmdUI)
2819 pCmdUI->Enable(m_bInlineDiff && IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2820 pCmdUI->SetCheck(m_bInlineWordDiff);
2823 void CMainFrame::OnViewInlinediff()
2825 m_bInlineDiff = !m_bInlineDiff;
2826 if (m_pwndLeftView)
2828 m_pwndLeftView->SetInlineDiff(m_bInlineDiff);
2829 m_pwndLeftView->BuildAllScreen2ViewVector();
2830 m_pwndLeftView->DocumentUpdated();
2832 if (m_pwndRightView)
2834 m_pwndRightView->SetInlineDiff(m_bInlineDiff);
2835 m_pwndRightView->BuildAllScreen2ViewVector();
2836 m_pwndRightView->DocumentUpdated();
2838 if (m_pwndBottomView)
2840 m_pwndBottomView->SetInlineDiff(m_bInlineDiff);
2841 m_pwndBottomView->BuildAllScreen2ViewVector();
2842 m_pwndBottomView->DocumentUpdated();
2844 m_wndLineDiffBar.DocumentUpdated();
2847 void CMainFrame::OnUpdateViewInlinediff(CCmdUI *pCmdUI)
2849 pCmdUI->Enable(IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2850 pCmdUI->SetCheck(m_bInlineDiff);
2853 void CMainFrame::OnUpdateEditCreateunifieddifffile(CCmdUI *pCmdUI)
2855 // "create unified diff file" is only available if two files
2856 // are diffed, not three.
2857 bool bEnabled = true;
2858 if (!IsViewGood(m_pwndLeftView))
2859 bEnabled = false;
2860 else if (!IsViewGood(m_pwndRightView))
2861 bEnabled = false;
2862 else if (IsViewGood(m_pwndBottomView)) //no negation here
2863 bEnabled = false;
2864 pCmdUI->Enable(bEnabled);
2867 void CMainFrame::OnEditCreateunifieddifffile()
2869 CString origFile, modifiedFile;
2870 CString origReflected, modifiedReflected;
2871 // the original file is the one on the left
2872 if (m_pwndLeftView)
2874 origFile = m_pwndLeftView->m_sFullFilePath;
2875 origReflected = m_pwndLeftView->m_sReflectedName;
2877 if (m_pwndRightView)
2879 modifiedFile = m_pwndRightView->m_sFullFilePath;
2880 modifiedReflected = m_pwndRightView->m_sReflectedName;
2882 if (origFile.IsEmpty() || modifiedFile.IsEmpty())
2883 return;
2885 CString outputFile;
2886 if(!TryGetFileName(outputFile))
2887 return;
2889 CRegStdDWORD regContextLines(L"Software\\TortoiseGitMerge\\ContextLines", static_cast<DWORD>(-1));
2890 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outputFile, regContextLines, true);
2891 // from here a hacky solution exchanges the paths included in the patch, see issue #2541
2892 if (origReflected.IsEmpty() || modifiedReflected.IsEmpty())
2893 return;
2894 CString projectDir1, projectDir2;
2895 if (!GitAdminDir::HasAdminDir(origReflected, &projectDir1) || !GitAdminDir::HasAdminDir(modifiedReflected, &projectDir2) || projectDir1 != projectDir2)
2896 return;
2897 CStringA origReflectedA = CUnicodeUtils::GetUTF8(origReflected.Mid(projectDir1.GetLength() + 1));
2898 CStringA modifiedReflectedA = CUnicodeUtils::GetUTF8(modifiedReflected.Mid(projectDir1.GetLength() + 1));
2899 origReflectedA.Replace('\\', '/');
2900 modifiedReflectedA.Replace('\\', '/');
2903 CStdioFile file;
2904 // w/o typeBinary for some files \r gets dropped
2905 if (!file.Open(outputFile, CFile::typeBinary | CFile::modeReadWrite | CFile::shareExclusive))
2906 return;
2908 CStringA filecontent;
2909 UINT filelength = static_cast<UINT>(file.GetLength());
2910 int bytesread = static_cast<int>(file.Read(filecontent.GetBuffer(filelength), filelength));
2911 filecontent.ReleaseBuffer(bytesread);
2913 if (!CStringUtils::StartsWith(filecontent, "diff --git "))
2914 return;
2915 int lineend = filecontent.Find("\n");
2916 if (lineend <= static_cast<int>(strlen("diff --git ")))
2917 return;
2918 CStringA newStart = "diff --git \"a/" + origReflectedA + "\" \"b/" + modifiedReflectedA + "\"\n";
2919 if (!CStringUtils::StartsWith(filecontent.GetBuffer() + lineend, "\nindex "))
2920 return;
2921 int nextlineend = filecontent.Find("\n", lineend + 1);
2922 if (nextlineend <= lineend)
2923 return;
2924 newStart += filecontent.Mid(lineend + 1, nextlineend - lineend);
2925 lineend = filecontent.Find("\n--- ", nextlineend);
2926 nextlineend = filecontent.Find("\n@@ ", lineend);
2927 if (nextlineend <= lineend)
2928 return;
2929 newStart += "--- \"a/" + origReflectedA + "\"\n+++ \"b/" + modifiedReflectedA + "\"";
2930 filecontent = newStart + filecontent.Mid(nextlineend);
2931 file.SeekToBegin();
2932 file.Write(filecontent, static_cast<UINT>(filecontent.GetLength()));
2933 file.SetLength(file.GetPosition());
2934 file.Close();
2936 catch (CFileException*)
2941 void CMainFrame::OnUpdateViewLinediffbar(CCmdUI *pCmdUI)
2943 pCmdUI->SetCheck(m_bLineDiff);
2944 pCmdUI->Enable();
2947 void CMainFrame::OnViewLinediffbar()
2949 m_bLineDiff = !m_bLineDiff;
2950 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2951 m_wndLineDiffBar.DocumentUpdated();
2952 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2953 m_wndLocatorBar.DocumentUpdated();
2956 void CMainFrame::OnUpdateViewLocatorbar(CCmdUI *pCmdUI)
2958 pCmdUI->SetCheck(m_bLocatorBar);
2959 pCmdUI->Enable();
2962 void CMainFrame::OnUpdateViewBars(CCmdUI * pCmdUI)
2964 pCmdUI->Enable();
2967 void CMainFrame::OnViewLocatorbar()
2969 m_bLocatorBar = !m_bLocatorBar;
2970 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2971 m_wndLocatorBar.DocumentUpdated();
2972 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2973 m_wndLineDiffBar.DocumentUpdated();
2976 void CMainFrame::OnViewComparewhitespaces()
2978 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
2979 return;
2980 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2981 regIgnoreWS = static_cast<int>(IgnoreWS::None);
2982 LoadViews(-1);
2985 void CMainFrame::OnUpdateViewComparewhitespaces(CCmdUI *pCmdUI)
2987 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2988 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
2989 pCmdUI->SetCheck(ignoreWs == IgnoreWS::None);
2992 void CMainFrame::OnViewIgnorewhitespacechanges()
2994 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
2995 return;
2996 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2997 regIgnoreWS = static_cast<int>(IgnoreWS::WhiteSpaces);
2998 LoadViews(-1);
3001 void CMainFrame::OnUpdateViewIgnorewhitespacechanges(CCmdUI *pCmdUI)
3003 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3004 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
3005 pCmdUI->SetCheck(ignoreWs == IgnoreWS::WhiteSpaces);
3008 void CMainFrame::OnViewIgnoreallwhitespacechanges()
3010 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3011 return;
3012 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3013 regIgnoreWS = static_cast<int>(IgnoreWS::AllWhiteSpaces);
3014 LoadViews(-1);
3017 void CMainFrame::OnUpdateViewIgnoreallwhitespacechanges(CCmdUI *pCmdUI)
3019 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
3020 IgnoreWS ignoreWs = static_cast<IgnoreWS>(static_cast<DWORD>(regIgnoreWS));
3021 pCmdUI->SetCheck(ignoreWs == IgnoreWS::AllWhiteSpaces);
3024 void CMainFrame::OnViewMovedBlocks()
3026 m_bViewMovedBlocks = !static_cast<DWORD>(m_regViewModedBlocks);
3027 m_regViewModedBlocks = m_bViewMovedBlocks;
3028 LoadViews(-1);
3031 void CMainFrame::OnUpdateViewMovedBlocks(CCmdUI *pCmdUI)
3033 pCmdUI->SetCheck(m_bViewMovedBlocks);
3034 BOOL bEnable = TRUE;
3035 if (IsViewGood(m_pwndBottomView))
3037 bEnable = FALSE;
3039 pCmdUI->Enable(bEnable);
3042 bool CMainFrame::HasConflictsWontKeep()
3044 const int nConflictLine = CheckResolved();
3045 if (nConflictLine < 0)
3046 return false;
3047 if (!m_pwndBottomView)
3048 return false;
3050 CString sTemp;
3051 sTemp.Format(IDS_ERR_MAINFRAME_FILEHASCONFLICTS, m_pwndBottomView->m_pViewData->GetLineNumber(nConflictLine)+1);
3052 CTaskDialog taskdlg(sTemp,
3053 CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK2)),
3054 L"TortoiseGitMerge",
3056 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
3057 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK3)));
3058 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK4)));
3059 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
3060 taskdlg.SetDefaultCommandControl(2);
3061 taskdlg.SetMainIcon(TD_ERROR_ICON);
3062 if (taskdlg.DoModal(m_hWnd) == 1)
3063 return false;
3065 m_pwndBottomView->GoToLine(nConflictLine);
3066 return true;
3069 bool CMainFrame::TryGetFileName(CString& result)
3071 return CCommonAppUtils::FileOpenSave(result, nullptr, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd);
3074 CBaseView* CMainFrame::GetActiveBaseView() const
3076 CView* activeView = GetActiveView();
3077 CBaseView* activeBase = dynamic_cast<CBaseView*>( activeView );
3078 return activeBase;
3081 void CMainFrame::SetWindowTitle()
3083 // try to find a suitable window title
3084 CString sYour = m_Data.m_yourFile.GetDescriptiveName();
3085 if (sYour.Find(L" - ") >= 0)
3086 sYour = sYour.Left(sYour.Find(L" - "));
3087 if (sYour.Find(L" : ") >= 0)
3088 sYour = sYour.Left(sYour.Find(L" : "));
3089 CString sTheir = m_Data.m_theirFile.GetDescriptiveName();
3090 if (sTheir.IsEmpty())
3091 sTheir = m_Data.m_baseFile.GetDescriptiveName();
3092 if (sTheir.Find(L" - ") >= 0)
3093 sTheir = sTheir.Left(sTheir.Find(L" - "));
3094 if (sTheir.Find(L" : ") >= 0)
3095 sTheir = sTheir.Left(sTheir.Find(L" : "));
3097 if (!sYour.IsEmpty() && !sTheir.IsEmpty())
3099 if (sYour.CompareNoCase(sTheir)==0)
3100 SetWindowText(sYour + L" - TortoiseGitMerge");
3101 else if ((sYour.GetLength() < 10) &&
3102 (sTheir.GetLength() < 10))
3103 SetWindowText(sYour + L" - " + sTheir + L" - TortoiseGitMerge");
3104 else
3106 // we have two very long descriptive texts here, which
3107 // means we have to find a way to use them as a window
3108 // title in a shorter way.
3109 // for simplicity, we just use the one from "yourfile"
3110 SetWindowText(sYour + L" - TortoiseGitMerge");
3113 else if (!sYour.IsEmpty())
3114 SetWindowText(sYour + L" - TortoiseGitMerge");
3115 else if (!sTheir.IsEmpty())
3116 SetWindowText(sTheir + L" - TortoiseGitMerge");
3117 else
3118 SetWindowText(L"TortoiseGitMerge");
3121 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
3123 switch (nIDEvent)
3125 case IDT_RELOADCHECKTIMER:
3126 KillTimer(nIDEvent);
3127 CheckForReload();
3128 break;
3131 __super::OnTimer(nIDEvent);
3134 void CMainFrame::LoadIgnoreCommentData()
3136 static bool bLoaded = false;
3137 if (bLoaded)
3138 return;
3139 CString sPath = CPathUtils::GetAppDataDirectory() + L"ignorecomments.txt";
3140 if (!PathFileExists(sPath))
3142 // ignore comments file does not exist (yet), so create a default one
3143 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_IGNORECOMMENTSTXT), L"config");
3144 if (hRes)
3146 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3147 if (hResourceLoaded)
3149 auto lpResLock = static_cast<char*>(LockResource(hResourceLoaded));
3150 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3151 if (lpResLock)
3153 HANDLE hFile = CreateFile(sPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3154 if (hFile != INVALID_HANDLE_VALUE)
3156 DWORD dwWritten = 0;
3157 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3158 CloseHandle(hFile);
3167 CStdioFile file;
3168 if (file.Open(sPath, CFile::modeRead))
3170 CString sLine;
3171 while (file.ReadString(sLine))
3173 int eqpos = sLine.Find('=');
3174 if (eqpos >= 0)
3176 CString sExts = sLine.Left(eqpos);
3177 CString sComments = sLine.Mid(eqpos+1);
3179 int pos = sComments.Find(',');
3180 CString sLineStart = sComments.Left(pos);
3181 pos = sComments.Find(',', pos);
3182 int pos2 = sComments.Find(',', pos+1);
3183 CString sBlockStart = sComments.Mid(pos+1, pos2-pos-1);
3184 CString sBlockEnd = sComments.Mid(pos2+1);
3186 auto commentTuple = std::make_tuple(sLineStart, sBlockStart, sBlockEnd);
3188 pos = 0;
3189 CString temp;
3190 for (;;)
3192 temp = sExts.Tokenize(L",", pos);
3193 if (temp.IsEmpty())
3195 break;
3197 ASSERT(m_IgnoreCommentsMap.find(temp) == m_IgnoreCommentsMap.end());
3198 m_IgnoreCommentsMap[temp] = commentTuple;
3204 catch (CFileException* e)
3206 e->Delete();
3208 bLoaded = true;
3211 void CMainFrame::OnViewIgnorecomments()
3213 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3214 return;
3215 m_regIgnoreComments = !DWORD(m_regIgnoreComments);
3216 LoadViews(-1);
3219 void CMainFrame::OnUpdateViewIgnorecomments(CCmdUI *pCmdUI)
3221 // only enable if we have comments defined for this file extension
3222 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
3223 sExt.TrimLeft(L".");
3224 auto sC = m_IgnoreCommentsMap.find(sExt);
3225 if (sC == m_IgnoreCommentsMap.end())
3227 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
3228 sExt.TrimLeft(L".");
3229 sC = m_IgnoreCommentsMap.find(sExt);
3230 if (sC == m_IgnoreCommentsMap.end())
3232 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
3233 sExt.TrimLeft(L".");
3234 sC = m_IgnoreCommentsMap.find(sExt);
3237 pCmdUI->Enable(sC != m_IgnoreCommentsMap.end());
3239 pCmdUI->SetCheck(DWORD(m_regIgnoreComments) != 0);
3242 void CMainFrame::OnUpdateMarkedWords(CCmdUI* pCmdUI)
3244 CString sText;
3245 CString sTmp;
3246 if (IsViewGood(m_pwndLeftView) && m_pwndLeftView->GetMarkedWordCount())
3248 sTmp.Format(L"L: %d", m_pwndLeftView->GetMarkedWordCount());
3249 if (!sText.IsEmpty())
3250 sText += L" | ";
3251 sText += sTmp;
3253 if (IsViewGood(m_pwndRightView) && m_pwndRightView->GetMarkedWordCount())
3255 sTmp.Format(L"R: %d", m_pwndRightView->GetMarkedWordCount());
3256 if (!sText.IsEmpty())
3257 sText += L" | ";
3258 sText += sTmp;
3260 if (IsViewGood(m_pwndBottomView) && m_pwndBottomView->GetMarkedWordCount())
3262 sTmp.Format(L"B: %d", m_pwndBottomView->GetMarkedWordCount());
3263 if (!sText.IsEmpty())
3264 sText += L" | ";
3265 sText += sTmp;
3267 if (!sText.IsEmpty())
3269 CString sStatusBarText;
3270 sStatusBarText.Format(IDS_INDICATOR_MARKEDWORDCOUNT, LPCWSTR(sText));
3271 pCmdUI->SetText(sStatusBarText);
3272 pCmdUI->Enable(true);
3276 void CMainFrame::OnUpdateEnableIfSelection(CCmdUI* pCmdUI)
3278 bool bEnabled = false;
3279 auto pWndWithFocus = GetFocus();
3280 if (pWndWithFocus)
3282 if (pWndWithFocus == m_pwndBottomView)
3283 bEnabled = !m_pwndBottomView->GetSelectedText().IsEmpty();
3284 if (pWndWithFocus == m_pwndLeftView)
3285 bEnabled = !m_pwndLeftView->GetSelectedText().IsEmpty();
3286 if (pWndWithFocus == m_pwndRightView)
3287 bEnabled = !m_pwndRightView->GetSelectedText().IsEmpty();
3289 pCmdUI->Enable(bEnabled);
3292 void CMainFrame::OnRegexfilter(UINT cmd)
3294 if ((cmd == ID_REGEXFILTER)||(cmd == (ID_REGEXFILTER+1)))
3296 CRegexFiltersDlg dlg(this);
3297 dlg.SetIniFile(&m_regexIni);
3298 if (dlg.DoModal() == IDOK)
3300 FILE* pFile = nullptr;
3301 _wfopen_s(&pFile, CPathUtils::GetAppDataDirectory() + L"regexfilters.ini", L"wb");
3302 m_regexIni.SaveFile(pFile);
3303 fclose(pFile);
3305 BuildRegexSubitems();
3307 else
3309 if (cmd == static_cast<UINT>(m_regexIndex) && !m_bUseRibbons)
3311 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3312 return;
3313 m_Data.SetRegexTokens(std::wregex(), L"");
3314 m_regexIndex = -1;
3315 LoadViews(-1);
3317 else if (cmd != static_cast<UINT>(m_regexIndex))
3319 CSimpleIni::TNamesDepend sections;
3320 m_regexIni.GetAllSections(sections);
3321 int index = ID_REGEXFILTER + 2;
3322 m_regexIndex = -1;
3323 for (const auto& section : sections)
3325 if (cmd == static_cast<UINT>(index))
3327 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3328 break;
3331 std::wregex rx(m_regexIni.GetValue(section.pItem, L"regex", L""));
3332 m_Data.SetRegexTokens(rx, m_regexIni.GetValue(section.pItem, L"replace", L""));
3334 catch (std::exception &ex)
3336 MessageBox(L"Regex is invalid!\r\n" + CString(ex.what()));
3338 m_regexIndex = index;
3341 LoadViews(-1);
3343 catch (const std::regex_error& ex)
3345 MessageBox(L"Regexp error caught:\r\n" + CString(ex.what()) + L"\r\nTrying to recover by unsetting it again.");
3346 m_Data.SetRegexTokens(std::wregex(), L"");
3347 m_regexIndex = -1;
3348 LoadViews(-1);
3350 break;
3352 ++index;
3358 void CMainFrame::OnUpdateViewRegexFilter( CCmdUI *pCmdUI )
3360 pCmdUI->Enable();
3361 pCmdUI->SetCheck(pCmdUI->m_nID == static_cast<UINT>(m_regexIndex));
3364 void CMainFrame::BuildRegexSubitems(CMFCPopupMenu* pMenuPopup)
3366 CString sIniPath = CPathUtils::GetAppDataDirectory() + L"regexfilters.ini";
3367 if (!PathFileExists(sIniPath))
3369 // ini file does not exist (yet), so create a default one
3370 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_REGEXFILTERINI), L"config");
3371 if (hRes)
3373 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3374 if (hResourceLoaded)
3376 auto lpResLock = static_cast<char*>(LockResource(hResourceLoaded));
3377 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3378 if (lpResLock)
3380 HANDLE hFile = CreateFile(sIniPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3381 if (hFile != INVALID_HANDLE_VALUE)
3383 DWORD dwWritten = 0;
3384 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3385 CloseHandle(hFile);
3392 m_regexIni.LoadFile(sIniPath);
3393 CSimpleIni::TNamesDepend sections;
3394 m_regexIni.GetAllSections(sections);
3396 if (m_bUseRibbons)
3398 std::list<CNativeRibbonDynamicItemInfo> items;
3399 int cmdIndex = 2;
3400 items.push_back(CNativeRibbonDynamicItemInfo(ID_REGEX_NO_FILTER, CString(MAKEINTRESOURCE(ID_REGEX_NO_FILTER)), IDB_REGEX_FILTER));
3401 for (const auto& section : sections)
3403 items.emplace_back(ID_REGEXFILTER + cmdIndex, section.pItem, IDB_REGEX_FILTER);
3404 cmdIndex++;
3407 m_pRibbonApp->SetItems(ID_REGEXFILTER, items);
3409 else if (pMenuPopup)
3411 int iIndex = -1;
3412 if (!CMFCToolBar::IsCustomizeMode() &&
3413 (iIndex = pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER)) >= 0)
3415 if (!sections.empty())
3416 pMenuPopup->InsertSeparator(iIndex + 1); // insert the separator at the end
3417 int cmdIndex = 2;
3418 for (const auto& section : sections)
3420 pMenuPopup->InsertItem(CMFCToolBarMenuButton(ID_REGEXFILTER + cmdIndex, nullptr, -1, section.pItem), iIndex + cmdIndex);
3421 cmdIndex++;
3427 void CMainFrame::FillEncodingButton( CMFCRibbonButton * pButton, int start )
3429 pButton->SetDefaultCommand(FALSE);
3430 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::ASCII, L"ASCII" ));
3431 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::BINARY, L"BINARY" ));
3432 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_LE, L"UTF-16LE" ));
3433 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_LEBOM, L"UTF-16LE BOM"));
3434 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_BE, L"UTF-16BE" ));
3435 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_BEBOM, L"UTF-16BE BOM"));
3436 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF32_LE, L"UTF-32LE" ));
3437 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF32_BE, L"UTF-32BE" ));
3438 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF8, L"UTF-8" ));
3439 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF8BOM, L"UTF-8 BOM" ));
3442 void CMainFrame::FillEOLButton( CMFCRibbonButton * pButton, int start )
3444 pButton->SetDefaultCommand(FALSE);
3445 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LF , L"LF" ));
3446 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_CRLF, L"CRLF"));
3447 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LFCR, L"LRCR"));
3448 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_CR , L"CR" ));
3449 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_VT , L"VT" ));
3450 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_FF , L"FF" ));
3451 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_NEL , L"NEL" ));
3452 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LS , L"LS" ));
3453 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_PS , L"PS" ));
3456 void CMainFrame::FillTabModeButton(CMFCRibbonButton * pButton, int start)
3458 pButton->SetDefaultCommand(FALSE);
3459 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_NONE , L"Tab"));
3460 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_USESPACES , L"Space"));
3461 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3462 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_SMARTINDENT , L"Smart tab char"));
3463 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3464 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON1, L"1"));
3465 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON2, L"2"));
3466 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON4, L"4"));
3467 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON8, L"8"));
3468 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3469 pButton->AddSubItem(new CMFCRibbonButton(start + ENABLEEDITORCONFIG, L"EditorConfig"));
3472 bool CMainFrame::AdjustUnicodeTypeForLoad(CFileTextLines::UnicodeType& type)
3474 switch (type)
3476 case CFileTextLines::UnicodeType::AUTOTYPE:
3477 case CFileTextLines::UnicodeType::BINARY:
3478 return false;
3480 case CFileTextLines::UnicodeType::ASCII:
3481 case CFileTextLines::UnicodeType::UTF16_LE:
3482 case CFileTextLines::UnicodeType::UTF16_BE:
3483 case CFileTextLines::UnicodeType::UTF32_LE:
3484 case CFileTextLines::UnicodeType::UTF32_BE:
3485 case CFileTextLines::UnicodeType::UTF8:
3486 return true;
3488 case CFileTextLines::UnicodeType::UTF16_LEBOM:
3489 type = CFileTextLines::UnicodeType::UTF16_LE;
3490 return true;
3492 case CFileTextLines::UnicodeType::UTF16_BEBOM:
3493 type = CFileTextLines::UnicodeType::UTF16_BE;
3494 return true;
3496 case CFileTextLines::UnicodeType::UTF8BOM:
3497 type = CFileTextLines::UnicodeType::UTF8;
3498 return true;
3500 return false;
3503 void CMainFrame::OnEncodingLeft( UINT cmd )
3505 if (m_pwndLeftView)
3507 if (GetKeyState(VK_CONTROL) & 0x8000)
3509 // reload with selected encoding
3510 auto saveparams = m_Data.m_arBaseFile.GetSaveParams();
3511 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART);
3512 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3514 m_Data.m_arBaseFile.SetSaveParams(saveparams);
3515 m_Data.m_arBaseFile.KeepEncoding();
3516 LoadViews();
3519 else
3521 m_pwndLeftView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART));
3522 m_pwndLeftView->RefreshViews();
3527 void CMainFrame::OnEncodingRight( UINT cmd )
3529 if (m_pwndRightView)
3531 if (GetKeyState(VK_CONTROL) & 0x8000)
3533 // reload with selected encoding
3534 auto saveparams = m_Data.m_arYourFile.GetSaveParams();
3535 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART);
3536 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3538 m_Data.m_arYourFile.SetSaveParams(saveparams);
3539 m_Data.m_arYourFile.KeepEncoding();
3540 LoadViews();
3543 else
3545 m_pwndRightView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART));
3546 m_pwndRightView->RefreshViews();
3551 void CMainFrame::OnEncodingBottom( UINT cmd )
3553 if (m_pwndBottomView)
3555 if (GetKeyState(VK_CONTROL) & 0x8000)
3557 // reload with selected encoding
3558 auto saveparams = m_Data.m_arTheirFile.GetSaveParams();
3559 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART);
3560 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3562 m_Data.m_arTheirFile.SetSaveParams(saveparams);
3563 m_Data.m_arTheirFile.KeepEncoding();
3564 LoadViews();
3567 else
3569 m_pwndBottomView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART));
3570 m_pwndBottomView->RefreshViews();
3575 void CMainFrame::OnEOLLeft( UINT cmd )
3577 if (m_pwndLeftView)
3579 m_pwndLeftView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_LEFTEOLSTART));
3580 m_pwndLeftView->RefreshViews();
3584 void CMainFrame::OnEOLRight( UINT cmd )
3586 if (m_pwndRightView)
3588 m_pwndRightView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_RIGHTEOLSTART));
3589 m_pwndRightView->RefreshViews();
3593 void CMainFrame::OnEOLBottom( UINT cmd )
3595 if (m_pwndBottomView)
3597 m_pwndBottomView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_BOTTOMEOLSTART));
3598 m_pwndBottomView->RefreshViews();
3602 void CMainFrame::OnTabModeLeft( UINT cmd )
3604 OnTabMode(m_pwndLeftView, static_cast<int>(cmd) - ID_INDICATOR_LEFTTABMODESTART);
3607 void CMainFrame::OnTabModeRight( UINT cmd )
3609 OnTabMode(m_pwndRightView, static_cast<int>(cmd) - ID_INDICATOR_RIGHTTABMODESTART);
3612 void CMainFrame::OnTabModeBottom( UINT cmd )
3614 OnTabMode(m_pwndBottomView, static_cast<int>(cmd) - ID_INDICATOR_BOTTOMTABMODESTART);
3617 void CMainFrame::OnTabMode(CBaseView *view, int cmd)
3619 if (!view)
3620 return;
3621 int nTabMode = view->GetTabMode();
3622 if (cmd == TABMODE_NONE || cmd == TABMODE_USESPACES)
3623 view->SetTabMode((nTabMode & (~TABMODE_USESPACES)) | (cmd & TABMODE_USESPACES));
3624 else if (cmd == TABMODE_SMARTINDENT) // Toggle
3625 view->SetTabMode((nTabMode & (~TABMODE_SMARTINDENT)) | ((nTabMode & TABMODE_SMARTINDENT) ? 0 : TABMODE_SMARTINDENT));
3626 else if (cmd == TABSIZEBUTTON1)
3627 view->SetTabSize(1);
3628 else if (cmd == TABSIZEBUTTON2)
3629 view->SetTabSize(2);
3630 else if (cmd == TABSIZEBUTTON4)
3631 view->SetTabSize(4);
3632 else if (cmd == TABSIZEBUTTON8)
3633 view->SetTabSize(8);
3634 else if (cmd == ENABLEEDITORCONFIG)
3635 view->SetEditorConfigEnabled(!view->GetEditorConfigEnabled());
3636 view->RefreshViews();
3639 void CMainFrame::OnUpdateEncodingLeft( CCmdUI *pCmdUI )
3641 if (m_pwndLeftView)
3643 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_LEFTENCODINGSTART) == m_pwndLeftView->GetTextType());
3644 pCmdUI->Enable(m_pwndLeftView->IsWritable() || (GetKeyState(VK_CONTROL)&0x8000));
3646 else
3647 pCmdUI->Enable(FALSE);
3650 void CMainFrame::OnUpdateEncodingRight( CCmdUI *pCmdUI )
3652 if (m_pwndRightView)
3654 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_RIGHTENCODINGSTART) == m_pwndRightView->GetTextType());
3655 pCmdUI->Enable(m_pwndRightView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3657 else
3658 pCmdUI->Enable(FALSE);
3661 void CMainFrame::OnUpdateEncodingBottom( CCmdUI *pCmdUI )
3663 if (m_pwndBottomView)
3665 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_BOTTOMENCODINGSTART) == m_pwndBottomView->GetTextType());
3666 pCmdUI->Enable(m_pwndBottomView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3668 else
3669 pCmdUI->Enable(FALSE);
3672 void CMainFrame::OnUpdateEOLLeft( CCmdUI *pCmdUI )
3674 if (m_pwndLeftView)
3676 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_LEFTEOLSTART) == m_pwndLeftView->GetLineEndings());
3677 pCmdUI->Enable(m_pwndLeftView->IsWritable());
3679 else
3680 pCmdUI->Enable(FALSE);
3683 void CMainFrame::OnUpdateEOLRight( CCmdUI *pCmdUI )
3685 if (m_pwndRightView)
3687 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_RIGHTEOLSTART) == m_pwndRightView->GetLineEndings());
3688 pCmdUI->Enable(m_pwndRightView->IsWritable());
3690 else
3691 pCmdUI->Enable(FALSE);
3694 void CMainFrame::OnUpdateEOLBottom( CCmdUI *pCmdUI )
3696 if (m_pwndBottomView)
3698 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_BOTTOMEOLSTART) == m_pwndBottomView->GetLineEndings());
3699 pCmdUI->Enable(m_pwndBottomView->IsWritable());
3701 else
3702 pCmdUI->Enable(FALSE);
3705 void CMainFrame::OnUpdateTabModeLeft(CCmdUI *pCmdUI)
3707 OnUpdateTabMode(m_pwndLeftView, pCmdUI, ID_INDICATOR_LEFTTABMODESTART);
3710 void CMainFrame::OnUpdateTabModeRight(CCmdUI *pCmdUI)
3712 OnUpdateTabMode(m_pwndRightView, pCmdUI, ID_INDICATOR_RIGHTTABMODESTART);
3715 void CMainFrame::OnUpdateTabModeBottom(CCmdUI *pCmdUI)
3717 OnUpdateTabMode(m_pwndBottomView, pCmdUI, ID_INDICATOR_BOTTOMTABMODESTART);
3720 void CMainFrame::OnUpdateTabMode(CBaseView *view, CCmdUI *pCmdUI, int startid)
3722 if (view)
3724 int cmd = static_cast<int>(pCmdUI->m_nID) - startid;
3725 if (cmd == TABMODE_NONE)
3726 pCmdUI->SetCheck((view->GetTabMode() & TABMODE_USESPACES) == TABMODE_NONE);
3727 else if (cmd == TABMODE_USESPACES)
3728 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_USESPACES);
3729 else if (cmd == TABMODE_SMARTINDENT)
3730 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_SMARTINDENT);
3731 else if (cmd == TABSIZEBUTTON1)
3732 pCmdUI->SetCheck(view->GetTabSize() == 1);
3733 else if (cmd == TABSIZEBUTTON2)
3734 pCmdUI->SetCheck(view->GetTabSize() == 2);
3735 else if (cmd == TABSIZEBUTTON4)
3736 pCmdUI->SetCheck(view->GetTabSize() == 4);
3737 else if (cmd == TABSIZEBUTTON8)
3738 pCmdUI->SetCheck(view->GetTabSize() == 8);
3739 else if (cmd == ENABLEEDITORCONFIG)
3740 pCmdUI->SetCheck(view->GetEditorConfigEnabled());
3741 pCmdUI->Enable(view->IsWritable());
3742 if (cmd == ENABLEEDITORCONFIG)
3743 pCmdUI->Enable(view->IsWritable() && view->GetEditorConfigLoaded());
3745 else
3746 pCmdUI->Enable(FALSE);
3749 BOOL CMainFrame::OnShowPopupMenu(CMFCPopupMenu* pMenuPopup)
3751 __super::OnShowPopupMenu(pMenuPopup);
3753 if (!pMenuPopup)
3754 return TRUE;
3756 if (!CMFCToolBar::IsCustomizeMode() &&
3757 pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER) >= 0)
3759 BuildRegexSubitems(pMenuPopup);
3762 return TRUE;
3765 LRESULT CMainFrame::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
3767 __super::OnIdleUpdateCmdUI(wParam, lParam);
3769 if (m_pRibbonApp)
3771 auto bDisableIfNoHandler = static_cast<BOOL>(wParam);
3772 m_pRibbonApp->UpdateCmdUI(bDisableIfNoHandler);
3774 return 0;
3777 void CMainFrame::OnUpdateThreeWayActions(CCmdUI * pCmdUI)
3779 pCmdUI->Enable();
3782 void CMainFrame::OnUpdateColumnStatusBar(CCmdUI* pCmdUI)
3784 int column = 0;
3785 auto pWndWithFocus = GetFocus();
3786 if (pWndWithFocus)
3788 if (pWndWithFocus == m_pwndBottomView)
3789 column = m_pwndBottomView->GetCaretViewPosition().x;
3790 if (pWndWithFocus == m_pwndLeftView)
3791 column = m_pwndLeftView->GetCaretViewPosition().x;
3792 if (pWndWithFocus == m_pwndRightView)
3793 column = m_pwndRightView->GetCaretViewPosition().x;
3795 CString sColumn;
3796 sColumn.Format(IDS_INDICATOR_COLUMN, column + 1);
3797 pCmdUI->SetText(sColumn);
3798 pCmdUI->Enable(true);
3801 void CMainFrame::OnRegexNoFilter()
3803 if (CheckForSave(CHFSR_OPTIONS) == IDCANCEL)
3804 return;
3805 m_Data.SetRegexTokens(std::wregex(), L"");
3806 m_regexIndex = -1;
3807 LoadViews(-1);
3810 void CMainFrame::OnUpdateRegexNoFilter(CCmdUI * pCmdUI)
3812 pCmdUI->SetCheck(m_regexIndex < 0);
3815 void CMainFrame::OnSettingChange(UINT uFlags, LPCWSTR lpszSection)
3817 __super::OnSettingChange(uFlags, lpszSection);
3819 SetAccentColor();
3822 void CMainFrame::OnSysColorChange()
3824 __super::OnSysColorChange();
3826 CTheme::Instance().OnSysColorChanged();
3827 CTheme::Instance().SetDarkTheme(CTheme::Instance().IsDarkTheme(), true);
3828 SetAccentColor();