Get rid of magic numbers
[TortoiseGit.git] / src / TortoiseMerge / MainFrm.cpp
blobb7779a2312ada8bf9e88301f55b4b2b533b8ee1a
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2008-2018 - TortoiseGit
4 // Copyright (C) 2004-2018 - 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 "OpenDlg.h"
24 #include "SysProgressDlg.h"
25 #include "Settings.h"
26 #include "MessageBox.h"
27 #include "AppUtils.h"
28 #include "PathUtils.h"
29 #include "MainFrm.h"
30 #include "LeftView.h"
31 #include "RightView.h"
32 #include "BottomView.h"
33 #include "DiffColors.h"
34 #include "SelectFileFilter.h"
35 #include "FormatMessageWrapper.h"
36 #include "TaskbarUUID.h"
37 #include "RegexFiltersDlg.h"
39 #ifdef _DEBUG
40 #define new DEBUG_NEW
41 #endif
43 // CMainFrame
44 #define IDT_RELOADCHECKTIMER 123
46 IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx)
48 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
49 ON_WM_CREATE()
50 ON_WM_DESTROY()
51 // Global help commands
52 ON_COMMAND(ID_HELP_FINDER, CFrameWndEx::OnHelpFinder)
53 ON_COMMAND(ID_HELP, CFrameWndEx::OnHelp)
54 ON_COMMAND(ID_CONTEXT_HELP, CFrameWndEx::OnContextHelp)
55 ON_COMMAND(ID_DEFAULT_HELP, CFrameWndEx::OnHelpFinder)
56 ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
57 ON_COMMAND(ID_VIEW_WHITESPACES, OnViewWhitespaces)
58 ON_WM_SIZE()
59 ON_COMMAND(ID_FILE_SAVE, OnFileSave)
60 ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
61 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
62 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, OnUpdateFileSaveAs)
63 ON_COMMAND(ID_VIEW_ONEWAYDIFF, OnViewOnewaydiff)
64 ON_UPDATE_COMMAND_UI(ID_VIEW_ONEWAYDIFF, OnUpdateViewOnewaydiff)
65 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACES, OnUpdateViewWhitespaces)
66 ON_COMMAND(ID_VIEW_OPTIONS, OnViewOptions)
67 ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
68 ON_WM_CLOSE()
69 ON_WM_ACTIVATE()
70 ON_COMMAND(ID_FILE_RELOAD, OnFileReload)
71 ON_COMMAND(ID_VIEW_LINEDOWN, OnViewLinedown)
72 ON_COMMAND(ID_VIEW_LINEUP, OnViewLineup)
73 ON_COMMAND(ID_VIEW_MOVEDBLOCKS, OnViewMovedBlocks)
74 ON_UPDATE_COMMAND_UI(ID_VIEW_MOVEDBLOCKS, OnUpdateViewMovedBlocks)
75 ON_UPDATE_COMMAND_UI(ID_EDIT_MARKASRESOLVED, OnUpdateMergeMarkasresolved)
76 ON_COMMAND(ID_EDIT_MARKASRESOLVED, OnMergeMarkasresolved)
77 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTCONFLICT, OnUpdateMergeNextconflict)
78 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVIOUSCONFLICT, OnUpdateMergePreviousconflict)
79 ON_WM_MOVE()
80 ON_WM_MOVING()
81 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
82 ON_COMMAND(ID_VIEW_SWITCHLEFT, OnViewSwitchleft)
83 ON_UPDATE_COMMAND_UI(ID_VIEW_SWITCHLEFT, OnUpdateViewSwitchleft)
84 ON_COMMAND(ID_VIEW_LINELEFT, &CMainFrame::OnViewLineleft)
85 ON_COMMAND(ID_VIEW_LINERIGHT, &CMainFrame::OnViewLineright)
86 ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWFILELIST, &CMainFrame::OnUpdateViewShowfilelist)
87 ON_COMMAND(ID_VIEW_SHOWFILELIST, &CMainFrame::OnViewShowfilelist)
88 ON_COMMAND(ID_EDIT_USETHEIRBLOCK, &CMainFrame::OnEditUseTheirs)
89 ON_COMMAND(ID_EDIT_USEMYBLOCK, &CMainFrame::OnEditUseMine)
90 ON_COMMAND(ID_EDIT_USETHEIRTHENMYBLOCK, &CMainFrame::OnEditUseTheirsThenMine)
91 ON_COMMAND(ID_EDIT_USEMINETHENTHEIRBLOCK, &CMainFrame::OnEditUseMineThenTheirs)
92 ON_COMMAND(ID_EDIT_UNDO, &CMainFrame::OnEditUndo)
93 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CMainFrame::OnUpdateEditUndo)
94 ON_COMMAND(ID_EDIT_REDO, &CMainFrame::OnEditRedo)
95 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CMainFrame::OnUpdateEditRedo)
96 ON_COMMAND(ID_EDIT_ENABLE, &CMainFrame::OnEditEnable)
97 ON_UPDATE_COMMAND_UI(ID_EDIT_ENABLE, &CMainFrame::OnUpdateEditEnable)
98 ON_UPDATE_COMMAND_UI(ID_EDIT_USEMINETHENTHEIRBLOCK, &CMainFrame::OnUpdateEditUseminethentheirblock)
99 ON_UPDATE_COMMAND_UI(ID_EDIT_USEMYBLOCK, &CMainFrame::OnUpdateEditUsemyblock)
100 ON_UPDATE_COMMAND_UI(ID_EDIT_USETHEIRBLOCK, &CMainFrame::OnUpdateEditUsetheirblock)
101 ON_UPDATE_COMMAND_UI(ID_EDIT_USETHEIRTHENMYBLOCK, &CMainFrame::OnUpdateEditUsetheirthenmyblock)
102 ON_COMMAND(ID_VIEW_INLINEDIFFWORD, &CMainFrame::OnViewInlinediffword)
103 ON_UPDATE_COMMAND_UI(ID_VIEW_INLINEDIFFWORD, &CMainFrame::OnUpdateViewInlinediffword)
104 ON_COMMAND(ID_VIEW_INLINEDIFF, &CMainFrame::OnViewInlinediff)
105 ON_UPDATE_COMMAND_UI(ID_VIEW_INLINEDIFF, &CMainFrame::OnUpdateViewInlinediff)
106 ON_UPDATE_COMMAND_UI(ID_EDIT_CREATEUNIFIEDDIFFFILE, &CMainFrame::OnUpdateEditCreateunifieddifffile)
107 ON_COMMAND(ID_EDIT_CREATEUNIFIEDDIFFFILE, &CMainFrame::OnEditCreateunifieddifffile)
108 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFBAR, &CMainFrame::OnUpdateViewLinediffbar)
109 ON_COMMAND(ID_VIEW_LINEDIFFBAR, &CMainFrame::OnViewLinediffbar)
110 ON_UPDATE_COMMAND_UI(ID_VIEW_BARS, &CMainFrame::OnUpdateViewBars)
111 ON_UPDATE_COMMAND_UI(ID_VIEW_LOCATORBAR, &CMainFrame::OnUpdateViewLocatorbar)
112 ON_COMMAND(ID_VIEW_LOCATORBAR, &CMainFrame::OnViewLocatorbar)
113 ON_COMMAND(ID_EDIT_USELEFTBLOCK, &CMainFrame::OnEditUseleftblock)
114 ON_UPDATE_COMMAND_UI(ID_USEBLOCKS, &CMainFrame::OnUpdateUseBlock)
115 ON_UPDATE_COMMAND_UI(ID_EDIT_USELEFTBLOCK, &CMainFrame::OnUpdateEditUseleftblock)
116 ON_COMMAND(ID_EDIT_USELEFTFILE, &CMainFrame::OnEditUseleftfile)
117 ON_UPDATE_COMMAND_UI(ID_EDIT_USELEFTFILE, &CMainFrame::OnUpdateEditUseleftfile)
118 ON_COMMAND(ID_EDIT_USEBLOCKFROMLEFTBEFORERIGHT, &CMainFrame::OnEditUseblockfromleftbeforeright)
119 ON_UPDATE_COMMAND_UI(ID_EDIT_USEBLOCKFROMLEFTBEFORERIGHT, &CMainFrame::OnUpdateEditUseblockfromleftbeforeright)
120 ON_COMMAND(ID_EDIT_USEBLOCKFROMRIGHTBEFORELEFT, &CMainFrame::OnEditUseblockfromrightbeforeleft)
121 ON_UPDATE_COMMAND_UI(ID_EDIT_USEBLOCKFROMRIGHTBEFORELEFT, &CMainFrame::OnUpdateEditUseblockfromrightbeforeleft)
122 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTDIFFERENCE, &CMainFrame::OnUpdateNavigateNextdifference)
123 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVIOUSDIFFERENCE, &CMainFrame::OnUpdateNavigatePreviousdifference)
124 ON_COMMAND(ID_VIEW_COLLAPSED, &CMainFrame::OnViewCollapsed)
125 ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSED, &CMainFrame::OnUpdateViewCollapsed)
126 ON_COMMAND(ID_VIEW_COMPAREWHITESPACES, &CMainFrame::OnViewComparewhitespaces)
127 ON_UPDATE_COMMAND_UI(ID_VIEW_COMPAREWHITESPACES, &CMainFrame::OnUpdateViewComparewhitespaces)
128 ON_COMMAND(ID_VIEW_IGNOREWHITESPACECHANGES, &CMainFrame::OnViewIgnorewhitespacechanges)
129 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNOREWHITESPACECHANGES, &CMainFrame::OnUpdateViewIgnorewhitespacechanges)
130 ON_COMMAND(ID_VIEW_IGNOREALLWHITESPACECHANGES, &CMainFrame::OnViewIgnoreallwhitespacechanges)
131 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNOREALLWHITESPACECHANGES, &CMainFrame::OnUpdateViewIgnoreallwhitespacechanges)
132 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_NEXTINLINEDIFF, &CMainFrame::OnUpdateNavigateNextinlinediff)
133 ON_UPDATE_COMMAND_UI(ID_NAVIGATE_PREVINLINEDIFF, &CMainFrame::OnUpdateNavigatePrevinlinediff)
134 ON_COMMAND(ID_VIEW_WRAPLONGLINES, &CMainFrame::OnViewWraplonglines)
135 ON_UPDATE_COMMAND_UI(ID_VIEW_WRAPLONGLINES, &CMainFrame::OnUpdateViewWraplonglines)
136 ON_REGISTERED_MESSAGE( TaskBarButtonCreated, CMainFrame::OnTaskbarButtonCreated )
137 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, &CMainFrame::OnUpdateEditPaste)
138 ON_COMMAND(ID_INDICATOR_LEFTVIEW, &CMainFrame::OnIndicatorLeftview)
139 ON_COMMAND(ID_INDICATOR_RIGHTVIEW, &CMainFrame::OnIndicatorRightview)
140 ON_COMMAND(ID_INDICATOR_BOTTOMVIEW, &CMainFrame::OnIndicatorBottomview)
141 ON_WM_TIMER()
142 ON_COMMAND(ID_VIEW_IGNORECOMMENTS, &CMainFrame::OnViewIgnorecomments)
143 ON_UPDATE_COMMAND_UI(ID_VIEW_IGNORECOMMENTS, &CMainFrame::OnUpdateViewIgnorecomments)
144 ON_COMMAND_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnRegexfilter)
145 ON_UPDATE_COMMAND_UI_RANGE(ID_REGEXFILTER, ID_REGEXFILTER+400, &CMainFrame::OnUpdateViewRegexFilter)
146 ON_COMMAND(ID_REGEX_NO_FILTER, &CMainFrame::OnRegexNoFilter)
147 ON_UPDATE_COMMAND_UI(ID_REGEX_NO_FILTER, &CMainFrame::OnUpdateRegexNoFilter)
148 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
149 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
150 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOENCODING, &CMainFrame::OnDummyEnabled)
151 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
152 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
153 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOEOL, &CMainFrame::OnDummyEnabled)
154 ON_COMMAND(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
155 ON_COMMAND(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
156 ON_COMMAND(ID_INDICATOR_BOTTOMVIEWCOMBOTABMODE, &CMainFrame::OnDummyEnabled)
157 ON_UPDATE_COMMAND_UI(ID_EDIT_THREEWAY_ACTIONS, &CMainFrame::OnUpdateThreeWayActions)
158 ON_COMMAND_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnEncodingLeft)
159 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnEncodingRight)
160 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnEncodingBottom)
161 ON_COMMAND_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnEOLLeft)
162 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnEOLRight)
163 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnEOLBottom)
164 ON_COMMAND_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnTabModeLeft)
165 ON_COMMAND_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnTabModeRight)
166 ON_COMMAND_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnTabModeBottom)
167 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTENCODINGSTART, ID_INDICATOR_LEFTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingLeft)
168 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTENCODINGSTART, ID_INDICATOR_RIGHTENCODINGSTART+19, &CMainFrame::OnUpdateEncodingRight)
169 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMENCODINGSTART, ID_INDICATOR_BOTTOMENCODINGSTART+19, &CMainFrame::OnUpdateEncodingBottom)
170 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTEOLSTART, ID_INDICATOR_LEFTEOLSTART+19, &CMainFrame::OnUpdateEOLLeft)
171 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTEOLSTART, ID_INDICATOR_RIGHTEOLSTART+19, &CMainFrame::OnUpdateEOLRight)
172 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMEOLSTART, ID_INDICATOR_BOTTOMEOLSTART+19, &CMainFrame::OnUpdateEOLBottom)
173 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_LEFTTABMODESTART, ID_INDICATOR_LEFTTABMODESTART+19, &CMainFrame::OnUpdateTabModeLeft)
174 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_RIGHTTABMODESTART, ID_INDICATOR_RIGHTTABMODESTART+19, &CMainFrame::OnUpdateTabModeRight)
175 ON_UPDATE_COMMAND_UI_RANGE(ID_INDICATOR_BOTTOMTABMODESTART, ID_INDICATOR_BOTTOMTABMODESTART+19, &CMainFrame::OnUpdateTabModeBottom)
176 END_MESSAGE_MAP()
178 static UINT indicators[] =
180 ID_SEPARATOR, // status line indicator
181 ID_INDICATOR_LEFTVIEW,
182 ID_INDICATOR_RIGHTVIEW,
183 ID_INDICATOR_BOTTOMVIEW,
184 ID_INDICATOR_CAPS,
185 ID_INDICATOR_NUM,
186 ID_INDICATOR_SCRL
190 // CMainFrame construction/destruction
192 CMainFrame::CMainFrame()
193 : m_bInitSplitter(FALSE)
194 , m_bReversedPatch(FALSE)
195 , m_bHasConflicts(false)
196 , m_bInlineWordDiff(true)
197 , m_bLineDiff(true)
198 , m_bLocatorBar(true)
199 , m_nMoveMovesToIgnore(0)
200 , m_pwndLeftView(nullptr)
201 , m_pwndRightView(nullptr)
202 , m_pwndBottomView(nullptr)
203 , m_bReadOnly(false)
204 , m_bBlame(false)
205 , m_bCheckReload(false)
206 , m_bSaveRequired(false)
207 , m_bSaveRequiredOnConflicts(false)
208 , m_bDeleteBaseTheirsMineOnClose(false)
209 , resolveMsgWnd(0)
210 , resolveMsgWParam(0)
211 , resolveMsgLParam(0)
212 , m_regWrapLines(L"Software\\TortoiseGitMerge\\WrapLines", 0)
213 , m_regViewModedBlocks(L"Software\\TortoiseGitMerge\\ViewMovedBlocks", TRUE)
214 , m_regOneWay(L"Software\\TortoiseGitMerge\\OnePane")
215 , m_regCollapsed(L"Software\\TortoiseGitMerge\\Collapsed", 0)
216 , m_regInlineDiff(L"Software\\TortoiseGitMerge\\DisplayBinDiff", TRUE)
217 , m_regUseRibbons(L"Software\\TortoiseGitMerge\\UseRibbons", TRUE)
218 , m_regIgnoreComments(L"Software\\TortoiseGitMerge\\IgnoreComments", FALSE)
219 , m_regexIndex(-1)
221 m_bOneWay = (0 != ((DWORD)m_regOneWay));
222 m_bCollapsed = !!(DWORD)m_regCollapsed;
223 m_bViewMovedBlocks = !!(DWORD)m_regViewModedBlocks;
224 m_bWrapLines = !!(DWORD)m_regWrapLines;
225 m_bInlineDiff = !!m_regInlineDiff;
226 m_bUseRibbons = !!m_regUseRibbons;
229 CMainFrame::~CMainFrame()
233 LRESULT CMainFrame::OnTaskbarButtonCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
235 SetUUIDOverlayIcon(m_hWnd);
236 return 0;
240 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
242 if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
243 return -1;
245 if (m_bUseRibbons)
247 HRESULT hr;
248 hr = m_pRibbonFramework.CoCreateInstance(__uuidof(UIRibbonFramework));
249 if (FAILED(hr))
251 TRACE(L"Failed to create ribbon framework (%08x)\n", hr);
252 return -1; // fail to create
255 m_pRibbonApp.reset(new CNativeRibbonApp(this, m_pRibbonFramework));
256 m_pRibbonApp->SetSettingsFileName(CPathUtils::GetAppDataDirectory() + L"TortoiseGitMerge-RibbonSettings");
258 hr = m_pRibbonFramework->Initialize(m_hWnd, m_pRibbonApp.get());
259 if (FAILED(hr))
261 TRACE(L"Failed to initialize ribbon framework (%08x)\n", hr);
262 return -1; // fail to create
265 hr = m_pRibbonFramework->LoadUI(AfxGetResourceHandle(), L"TORTOISEGITMERGERIBBON_RIBBON");
266 if (FAILED(hr))
268 TRACE(L"Failed to load ribbon UI (%08x)\n", hr);
269 return -1; // fail to create
271 BuildRegexSubitems();
272 if (!m_wndRibbonStatusBar.Create(this))
274 TRACE0("Failed to create ribbon status bar\n");
275 return -1; // fail to create
277 m_wndRibbonStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(AFX_IDS_IDLEMESSAGE)), TRUE), L"");
279 CString sTooltip(MAKEINTRESOURCE(IDS_ENCODING_COMBO_TOOLTIP));
280 auto apBtnGroupLeft = std::make_unique<CMFCRibbonButtonsGroup>();
281 apBtnGroupLeft->SetID(ID_INDICATOR_LEFTVIEW);
282 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_LEFTVIEW)), TRUE));
283 CMFCRibbonButton * pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOENCODING, L"");
284 pButton->SetToolTipText(sTooltip);
285 FillEncodingButton(pButton, ID_INDICATOR_LEFTENCODINGSTART);
286 apBtnGroupLeft->AddButton(pButton);
287 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOEOL, L"");
288 FillEOLButton(pButton, ID_INDICATOR_LEFTEOLSTART);
289 apBtnGroupLeft->AddButton(pButton);
290 pButton = new CMFCRibbonButton(ID_INDICATOR_LEFTVIEWCOMBOTABMODE, L"");
291 FillTabModeButton(pButton, ID_INDICATOR_LEFTTABMODESTART);
292 apBtnGroupLeft->AddButton(pButton);
293 apBtnGroupLeft->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_LEFTVIEW, L"", TRUE));
294 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupLeft.release(), L"");
296 auto apBtnGroupRight = std::make_unique<CMFCRibbonButtonsGroup>();
297 apBtnGroupRight->SetID(ID_INDICATOR_RIGHTVIEW);
298 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_SEPARATOR, CString(MAKEINTRESOURCE(IDS_STATUSBAR_RIGHTVIEW)), TRUE));
299 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOENCODING, L"");
300 pButton->SetToolTipText(sTooltip);
301 FillEncodingButton(pButton, ID_INDICATOR_RIGHTENCODINGSTART);
302 apBtnGroupRight->AddButton(pButton);
303 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOEOL, L"");
304 FillEOLButton(pButton, ID_INDICATOR_RIGHTEOLSTART);
305 apBtnGroupRight->AddButton(pButton);
306 pButton = new CMFCRibbonButton(ID_INDICATOR_RIGHTVIEWCOMBOTABMODE, L"");
307 FillTabModeButton(pButton, ID_INDICATOR_RIGHTTABMODESTART);
308 apBtnGroupRight->AddButton(pButton);
309 apBtnGroupRight->AddButton(new CMFCRibbonStatusBarPane(ID_INDICATOR_RIGHTVIEW, L"", TRUE));
310 m_wndRibbonStatusBar.AddExtendedElement(apBtnGroupRight.release(), L"");
312 else
314 if (!m_wndMenuBar.Create(this))
316 TRACE0("Failed to create menubar\n");
317 return -1; // fail to create
319 m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
321 // prevent the menu bar from taking the focus on activation
322 CMFCPopupMenu::SetForceMenuFocus(FALSE);
323 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))
325 TRACE0("Failed to create toolbar\n");
326 return -1; // fail to create
328 m_wndToolBar.SetWindowText(L"Main");
329 if (!m_wndStatusBar.Create(this) ||
330 !m_wndStatusBar.SetIndicators(indicators,
331 _countof(indicators)))
333 TRACE0("Failed to create status bar\n");
334 return -1; // fail to create
336 m_wndStatusBar.EnablePaneDoubleClick();
339 if (!m_wndLocatorBar.Create(this, IDD_DIFFLOCATOR,
340 CBRS_ALIGN_LEFT | CBRS_SIZE_FIXED, ID_VIEW_LOCATORBAR))
342 TRACE0("Failed to create dialogbar\n");
343 return -1; // fail to create
345 if (!m_wndLineDiffBar.Create(this, IDD_LINEDIFF,
346 CBRS_ALIGN_BOTTOM | CBRS_SIZE_FIXED, ID_VIEW_LINEDIFFBAR))
348 TRACE0("Failed to create dialogbar\n");
349 return -1; // fail to create
351 m_wndLocatorBar.m_pMainFrm = this;
352 m_wndLineDiffBar.m_pMainFrm = this;
354 EnableDocking(CBRS_ALIGN_ANY);
355 if (!m_bUseRibbons)
357 m_wndMenuBar.EnableDocking(CBRS_ALIGN_TOP);
358 m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP);
359 DockPane(&m_wndMenuBar);
360 DockPane(&m_wndToolBar);
362 DockPane(&m_wndLocatorBar);
363 DockPane(&m_wndLineDiffBar);
364 ShowPane(&m_wndLocatorBar, true, false, true);
365 ShowPane(&m_wndLineDiffBar, true, false, true);
367 m_wndLocatorBar.EnableGripper(FALSE);
368 m_wndLineDiffBar.EnableGripper(FALSE);
370 return 0;
373 void CMainFrame::OnDestroy()
375 if (m_pRibbonFramework)
376 m_pRibbonFramework->Destroy();
378 CFrameWndEx::OnDestroy();
381 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
383 if( !CFrameWndEx::PreCreateWindow(cs) )
384 return FALSE;
385 return TRUE;
388 // CMainFrame diagnostics
390 #ifdef _DEBUG
391 void CMainFrame::AssertValid() const
393 CFrameWndEx::AssertValid();
396 void CMainFrame::Dump(CDumpContext& dc) const
398 CFrameWndEx::Dump(dc);
401 #endif //_DEBUG
404 // CMainFrame message handlers
407 BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
409 CRect cr;
410 GetClientRect( &cr);
413 // split into three panes:
414 // -------------
415 // | | |
416 // | | |
417 // |------------
418 // | |
419 // | |
420 // |------------
422 // create a splitter with 2 rows, 1 column
423 if (!m_wndSplitter.CreateStatic(this, 2, 1))
425 TRACE0("Failed to CreateStaticSplitter\n");
426 return FALSE;
429 // add the second splitter pane - which is a nested splitter with 2 columns
430 if (!m_wndSplitter2.CreateStatic(
431 &m_wndSplitter, // our parent window is the first splitter
432 1, 2, // the new splitter is 1 row, 2 columns
433 WS_CHILD | WS_VISIBLE | WS_BORDER, // style, WS_BORDER is needed
434 m_wndSplitter.IdFromRowCol(0, 0)
435 // new splitter is in the first row, 1st column of first splitter
438 TRACE0("Failed to create nested splitter\n");
439 return FALSE;
441 // add the first splitter pane - the default view in row 0
442 if (!m_wndSplitter.CreateView(1, 0,
443 RUNTIME_CLASS(CBottomView), CSize(cr.Width(), cr.Height()), pContext))
445 TRACE0("Failed to create first pane\n");
446 return FALSE;
448 m_pwndBottomView = static_cast<CBottomView*>(m_wndSplitter.GetPane(1, 0));
449 m_pwndBottomView->m_pwndLocator = &m_wndLocatorBar;
450 m_pwndBottomView->m_pwndLineDiffBar = &m_wndLineDiffBar;
451 if (m_bUseRibbons)
452 m_pwndBottomView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
453 else
454 m_pwndBottomView->m_pwndStatusBar = &m_wndStatusBar;
455 m_pwndBottomView->m_pMainFrame = this;
457 // now create the two views inside the nested splitter
459 if (!m_wndSplitter2.CreateView(0, 0,
460 RUNTIME_CLASS(CLeftView), CSize(cr.Width()/2, cr.Height()/2), pContext))
462 TRACE0("Failed to create second pane\n");
463 return FALSE;
465 m_pwndLeftView = static_cast<CLeftView*>(m_wndSplitter2.GetPane(0, 0));
466 m_pwndLeftView->m_pwndLocator = &m_wndLocatorBar;
467 m_pwndLeftView->m_pwndLineDiffBar = &m_wndLineDiffBar;
468 if (m_bUseRibbons)
469 m_pwndLeftView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
470 else
471 m_pwndLeftView->m_pwndStatusBar = &m_wndStatusBar;
472 m_pwndLeftView->m_pMainFrame = this;
474 if (!m_wndSplitter2.CreateView(0, 1,
475 RUNTIME_CLASS(CRightView), CSize(cr.Width()/2, cr.Height()/2), pContext))
477 TRACE0("Failed to create third pane\n");
478 return FALSE;
480 m_pwndRightView = static_cast<CRightView*>(m_wndSplitter2.GetPane(0, 1));
481 m_pwndRightView->m_pwndLocator = &m_wndLocatorBar;
482 m_pwndRightView->m_pwndLineDiffBar = &m_wndLineDiffBar;
483 if (m_bUseRibbons)
484 m_pwndRightView->m_pwndRibbonStatusBar = &m_wndRibbonStatusBar;
485 else
486 m_pwndRightView->m_pwndStatusBar = &m_wndStatusBar;
487 m_pwndRightView->m_pMainFrame = this;
488 m_bInitSplitter = TRUE;
490 m_dlgFilePatches.Create(IDD_FILEPATCHES, this);
491 UpdateLayout();
492 return TRUE;
495 // Callback function
496 BOOL CMainFrame::PatchFile(CString sFilePath, bool /*bContentMods*/, bool bPropMods, CString sVersion, BOOL bAutoPatch)
498 //"dry run" was successful, so save the patched file somewhere...
499 CString sTempFile = CTempFiles::Instance().GetTempFilePathString();
500 CString sRejectedFile, sBasePath;
501 if (m_Patch.GetPatchResult(sFilePath, sTempFile, sRejectedFile, sBasePath) < 0)
503 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
504 return FALSE;
506 sFilePath = m_Patch.GetTargetPath() + L'\\' + sFilePath;
507 sFilePath.Replace('/', '\\');
508 if (sBasePath.IsEmpty())
509 sBasePath = sFilePath;
510 if (m_bReversedPatch)
512 m_Data.m_baseFile.SetFileName(sTempFile);
513 CString temp;
514 temp.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sFilePath), (LPCTSTR)m_Data.m_sPatchPatched);
515 m_Data.m_baseFile.SetDescriptiveName(temp);
516 m_Data.m_yourFile.SetFileName(sFilePath);
517 temp.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sFilePath), (LPCTSTR)m_Data.m_sPatchOriginal);
518 m_Data.m_yourFile.SetDescriptiveName(temp);
519 m_Data.m_theirFile.SetOutOfUse();
520 m_Data.m_mergedFile.SetOutOfUse();
522 else
524 if ((!PathFileExists(sBasePath))||(PathIsDirectory(sBasePath)))
526 m_Data.m_baseFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
527 m_Data.m_baseFile.CreateEmptyFile();
529 else
531 m_Data.m_baseFile.SetFileName(sBasePath);
533 CString sDescription;
534 sDescription.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sBasePath), (LPCTSTR)m_Data.m_sPatchOriginal);
535 m_Data.m_baseFile.SetDescriptiveName(sDescription);
536 if (sBasePath == sFilePath)
538 m_Data.m_yourFile.SetFileName(sTempFile);
539 CString temp;
540 temp.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sBasePath), (LPCTSTR)m_Data.m_sPatchPatched);
541 m_Data.m_yourFile.SetDescriptiveName(temp);
542 m_Data.m_theirFile.SetOutOfUse();
544 else
546 if (!PathFileExists(sFilePath) || PathIsDirectory(sFilePath))
548 m_Data.m_yourFile.SetFileName(CTempFiles::Instance().GetTempFilePathString());
549 m_Data.m_yourFile.CreateEmptyFile();
550 CString temp;
551 temp.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sFilePath), (LPCTSTR)CString(MAKEINTRESOURCE(IDS_NOTFOUNDVIEWTITLEINDICATOR)));
552 m_Data.m_yourFile.SetDescriptiveName(temp);
554 else
555 m_Data.m_yourFile.SetFileName(sFilePath);
556 m_Data.m_theirFile.SetFileName(sTempFile);
557 CString temp;
558 temp.Format(L"%s %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sFilePath), (LPCTSTR)m_Data.m_sPatchPatched);
559 m_Data.m_theirFile.SetDescriptiveName(temp);
561 m_Data.m_mergedFile.SetFileName(sFilePath);
562 m_Data.m_bPatchRequired = bPropMods;
564 TRACE(L"comparing %s\nwith the patched result %s\n", (LPCTSTR)sFilePath, (LPCTSTR)sTempFile);
566 LoadViews();
567 if (!sRejectedFile.IsEmpty())
569 #if 0 // TGIT TODO
570 // start TortoiseUDiff with the rejected hunks
571 CString sTitle;
572 sTitle.Format(IDS_TITLE_REJECTEDHUNKS, (LPCTSTR)CPathUtils::GetFileNameFromPath(sFilePath));
573 CAppUtils::StartUnifiedDiffViewer(sRejectedFile, sTitle);
574 #endif
576 if (bAutoPatch)
578 if (sBasePath != sFilePath && HasConflictsWontKeep())
579 return FALSE;
581 PatchSave();
583 return TRUE;
586 // Callback function
587 BOOL CMainFrame::DiffFiles(CString sURL1, CString sRev1, CString sURL2, CString sRev2)
589 CString tempfile1 = CTempFiles::Instance().GetTempFilePathString();
590 CString tempfile2 = CTempFiles::Instance().GetTempFilePathString();
592 ASSERT(tempfile1.Compare(tempfile2));
594 CString sTemp;
595 CSysProgressDlg progDlg;
596 sTemp.Format(IDS_GETVERSIONOFFILE, (LPCTSTR)sRev1);
597 progDlg.SetLine(1, sTemp, true);
598 progDlg.SetLine(2, sURL1, true);
599 sTemp.LoadString(IDS_GETVERSIONOFFILETITLE);
600 progDlg.SetTitle(sTemp);
601 progDlg.SetShowProgressBar(true);
602 progDlg.SetTime(FALSE);
603 progDlg.SetProgress(1,100);
604 progDlg.ShowModeless(this);
605 if (!CAppUtils::GetVersionedFile(sURL1, sRev1, tempfile1, &progDlg, m_hWnd))
607 progDlg.Stop();
608 CString sErrMsg;
609 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, (LPCTSTR)sRev1, (LPCTSTR)sURL1);
610 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
611 return FALSE;
613 sTemp.Format(IDS_GETVERSIONOFFILE, (LPCTSTR)sRev2);
614 progDlg.SetLine(1, sTemp, true);
615 progDlg.SetLine(2, sURL2, true);
616 progDlg.SetProgress(50, 100);
617 if (!CAppUtils::GetVersionedFile(sURL2, sRev2, tempfile2, &progDlg, m_hWnd))
619 progDlg.Stop();
620 CString sErrMsg;
621 sErrMsg.FormatMessage(IDS_ERR_MAINFRAME_FILEVERSIONNOTFOUND, (LPCTSTR)sRev2, (LPCTSTR)sURL2);
622 MessageBox(sErrMsg, nullptr, MB_ICONERROR);
623 return FALSE;
625 progDlg.SetProgress(100,100);
626 progDlg.Stop();
627 CString temp;
628 temp.Format(L"%s Revision %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sURL1), (LPCTSTR)sRev1);
629 m_Data.m_baseFile.SetFileName(tempfile1);
630 m_Data.m_baseFile.SetDescriptiveName(temp);
631 temp.Format(L"%s Revision %s", (LPCTSTR)CPathUtils::GetFileNameFromPath(sURL2), (LPCTSTR)sRev2);
632 m_Data.m_yourFile.SetFileName(tempfile2);
633 m_Data.m_yourFile.SetDescriptiveName(temp);
635 LoadViews();
637 return TRUE;
640 void CMainFrame::OnFileOpen()
642 if (CheckForSave(CHFSR_OPEN)==IDCANCEL)
643 return;
644 return OnFileOpen(false);
647 void CMainFrame::OnFileOpen(bool fillyours)
649 COpenDlg dlg;
650 if (fillyours)
651 dlg.m_sBaseFile = m_Data.m_yourFile.GetFilename();
652 if (dlg.DoModal() != IDOK)
654 return;
656 m_dlgFilePatches.ShowWindow(SW_HIDE);
657 m_dlgFilePatches.Init(nullptr, nullptr, CString(), nullptr);
658 TRACE(L"got the files:\n %s\n %s\n %s\n %s\n %s\n", (LPCTSTR)dlg.m_sBaseFile, (LPCTSTR)dlg.m_sTheirFile, (LPCTSTR)dlg.m_sYourFile,
659 (LPCTSTR)dlg.m_sUnifiedDiffFile, (LPCTSTR)dlg.m_sPatchDirectory);
660 m_Data.m_baseFile.SetFileName(dlg.m_sBaseFile);
661 m_Data.m_theirFile.SetFileName(dlg.m_sTheirFile);
662 m_Data.m_yourFile.SetFileName(dlg.m_sYourFile);
663 m_Data.m_sDiffFile = dlg.m_sUnifiedDiffFile;
664 m_Data.m_sPatchPath = dlg.m_sPatchDirectory;
665 m_Data.m_mergedFile.SetOutOfUse();
666 CCrashReport::Instance().AddFile2(dlg.m_sBaseFile, nullptr, L"Basefile", CR_AF_MAKE_FILE_COPY);
667 CCrashReport::Instance().AddFile2(dlg.m_sTheirFile, nullptr, L"Theirfile", CR_AF_MAKE_FILE_COPY);
668 CCrashReport::Instance().AddFile2(dlg.m_sYourFile, nullptr, L"Yourfile", CR_AF_MAKE_FILE_COPY);
669 CCrashReport::Instance().AddFile2(dlg.m_sUnifiedDiffFile, nullptr, L"Difffile", CR_AF_MAKE_FILE_COPY);
671 if (!m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && m_Data.IsYourFileInUse())
673 // a diff between two files means "Yours" against "Base", not "Theirs" against "Yours"
674 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
676 if (m_Data.IsBaseFileInUse() && m_Data.IsTheirFileInUse() && !m_Data.IsYourFileInUse())
678 // a diff between two files means "Yours" against "Base", not "Theirs" against "Base"
679 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
681 m_bSaveRequired = false;
683 LoadViews();
686 void CMainFrame::ClearViewNamesAndPaths()
688 m_pwndLeftView->m_sWindowName.Empty();
689 m_pwndLeftView->m_sFullFilePath.Empty();
690 m_pwndLeftView->m_sReflectedName.Empty();
691 m_pwndRightView->m_sWindowName.Empty();
692 m_pwndRightView->m_sFullFilePath.Empty();
693 m_pwndRightView->m_sReflectedName.Empty();
694 m_pwndBottomView->m_sWindowName.Empty();
695 m_pwndBottomView->m_sFullFilePath.Empty();
696 m_pwndBottomView->m_sReflectedName.Empty();
699 bool CMainFrame::LoadViews(int line)
701 LoadIgnoreCommentData();
702 m_Data.SetBlame(m_bBlame);
703 m_Data.SetMovedBlocks(m_bViewMovedBlocks);
704 m_bHasConflicts = false;
705 CBaseView* pwndActiveView = m_pwndLeftView;
706 int nOldLine = m_pwndRightView ? m_pwndRightView->m_nTopLine : -1;
707 int nOldLineNumber =
708 m_pwndRightView && m_pwndRightView->m_pViewData ?
709 m_pwndRightView->m_pViewData->GetLineNumber(m_pwndRightView->m_nTopLine) : -1;
710 POINT ptOldCaretPos = {-1, -1};
711 if (m_pwndRightView && m_pwndRightView->IsTarget())
712 ptOldCaretPos = m_pwndRightView->GetCaretPosition();
713 if (m_pwndBottomView && m_pwndBottomView->IsTarget())
714 ptOldCaretPos = m_pwndBottomView->GetCaretPosition();
715 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
716 sExt.TrimLeft(L".");
717 auto sC = m_IgnoreCommentsMap.find(sExt);
718 if (sC == m_IgnoreCommentsMap.end())
720 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
721 sC = m_IgnoreCommentsMap.find(sExt);
722 if (sC == m_IgnoreCommentsMap.end())
724 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
725 sC = m_IgnoreCommentsMap.find(sExt);
728 if (sC != m_IgnoreCommentsMap.end())
729 m_Data.SetCommentTokens(std::get<0>(sC->second), std::get<1>(sC->second), std::get<2>(sC->second));
730 else
731 m_Data.SetCommentTokens(L"", L"", L"");
732 if (!m_Data.Load())
734 m_pwndLeftView->BuildAllScreen2ViewVector();
735 m_pwndLeftView->DocumentUpdated();
736 m_pwndRightView->DocumentUpdated();
737 m_pwndBottomView->DocumentUpdated();
738 m_wndLocatorBar.DocumentUpdated();
739 m_wndLineDiffBar.DocumentUpdated();
740 ::MessageBox(m_hWnd, m_Data.GetError(), L"TortoiseGitMerge", MB_ICONERROR);
741 m_Data.m_mergedFile.SetOutOfUse();
742 m_bSaveRequired = false;
743 return false;
745 SetWindowTitle();
746 m_pwndLeftView->BuildAllScreen2ViewVector();
747 m_pwndLeftView->DocumentUpdated();
748 m_pwndRightView->DocumentUpdated();
749 m_pwndBottomView->DocumentUpdated();
750 m_wndLocatorBar.DocumentUpdated();
751 m_wndLineDiffBar.DocumentUpdated();
753 m_pwndLeftView->SetWritable(false);
754 m_pwndLeftView->SetWritableIsChangable(false);
755 m_pwndLeftView->SetTarget(false);
756 m_pwndRightView->SetWritable(false);
757 m_pwndRightView->SetWritableIsChangable(false);
758 m_pwndRightView->SetTarget(false);
759 m_pwndBottomView->SetWritable(false);
760 m_pwndBottomView->SetWritableIsChangable(false);
761 m_pwndBottomView->SetTarget(false);
763 if (!m_Data.IsBaseFileInUse())
765 CSysProgressDlg progDlg;
766 if (m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
768 m_Data.m_baseFile.TransferDetailsFrom(m_Data.m_theirFile);
770 else if ((!m_Data.m_sDiffFile.IsEmpty())&&(!m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg)))
772 progDlg.Stop();
773 ClearViewNamesAndPaths();
774 MessageBox(m_Patch.GetErrorMessage(), nullptr, MB_ICONERROR);
775 m_bSaveRequired = false;
776 return false;
778 progDlg.Stop();
779 if (m_Patch.GetNumberOfFiles() > 0)
781 CString betterpatchpath = m_Patch.CheckPatchPath(m_Data.m_sPatchPath);
782 if (betterpatchpath.CompareNoCase(m_Data.m_sPatchPath)!=0)
784 CString msg;
785 msg.FormatMessage(IDS_WARNBETTERPATCHPATHFOUND, (LPCTSTR)m_Data.m_sPatchPath, (LPCTSTR)betterpatchpath);
786 CTaskDialog taskdlg(msg,
787 CString(MAKEINTRESOURCE(IDS_WARNBETTERPATCHPATHFOUND_TASK2)),
788 L"TortoiseGitMerge",
790 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
791 CString task3;
792 WCHAR t3[MAX_PATH] = { 0 };
793 CString cp = betterpatchpath.Left(MAX_PATH - 1);
794 PathCompactPathEx(t3, cp, 50, 0);
795 task3.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK3, t3);
796 taskdlg.AddCommandControl(1, task3);
797 CString task4;
798 WCHAR t4[MAX_PATH] = { 0 };
799 cp = m_Data.m_sPatchPath.Left(MAX_PATH - 1);
800 PathCompactPathEx(t4, cp, 50, 0);
801 task4.Format(IDS_WARNBETTERPATCHPATHFOUND_TASK4, t4);
802 taskdlg.AddCommandControl(2, task4);
803 taskdlg.SetDefaultCommandControl(1);
804 taskdlg.SetMainIcon(TD_INFORMATION_ICON);
805 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
806 if (taskdlg.DoModal(m_hWnd) == 1)
808 m_Data.m_sPatchPath = betterpatchpath;
809 m_Patch.Init(m_Data.m_sDiffFile, m_Data.m_sPatchPath, &progDlg);
812 m_dlgFilePatches.Init(&m_Patch, this, m_Data.m_sPatchPath, this);
813 m_dlgFilePatches.ShowWindow(SW_SHOW);
814 ClearViewNamesAndPaths();
815 if (!m_wndSplitter.IsRowHidden(1))
816 m_wndSplitter.HideRow(1);
817 m_pwndLeftView->SetHidden(FALSE);
818 m_pwndRightView->SetHidden(FALSE);
819 m_pwndBottomView->SetHidden(TRUE);
822 if (m_Data.IsBaseFileInUse() && !m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
824 m_Data.m_yourFile.TransferDetailsFrom(m_Data.m_theirFile);
826 if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && !m_Data.IsTheirFileInUse())
828 //diff between YOUR and BASE
829 if (m_bOneWay)
831 pwndActiveView = m_pwndLeftView;
832 if (!m_wndSplitter2.IsColumnHidden(1))
833 m_wndSplitter2.HideColumn(1);
835 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseBoth;
836 m_pwndLeftView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
837 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
838 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName() + L" - " + m_Data.m_yourFile.GetWindowName();
839 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename() + L" - " + m_Data.m_yourFile.GetFilename();
840 m_pwndLeftView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
841 m_pwndLeftView->m_pWorkingFile = &m_Data.m_yourFile;
842 m_pwndLeftView->SetTarget();
843 m_pwndLeftView->SetWritableIsChangable(true);
845 m_pwndRightView->m_pViewData = nullptr;
846 m_pwndRightView->m_pWorkingFile = nullptr;
847 m_pwndBottomView->m_pViewData = nullptr;
848 m_pwndBottomView->m_pWorkingFile = nullptr;
850 if (!m_wndSplitter.IsRowHidden(1))
851 m_wndSplitter.HideRow(1);
852 m_pwndLeftView->SetHidden(FALSE);
853 m_pwndRightView->SetHidden(TRUE);
854 m_pwndBottomView->SetHidden(TRUE);
855 ::SetWindowPos(m_pwndLeftView->m_hWnd, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
857 else
859 pwndActiveView = m_pwndRightView;
860 if (m_wndSplitter2.IsColumnHidden(1))
861 m_wndSplitter2.ShowColumn();
863 m_pwndLeftView->m_pViewData = &m_Data.m_YourBaseLeft;
864 m_pwndLeftView->SetTextType(m_Data.m_arBaseFile.GetUnicodeType());
865 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arBaseFile.GetLineEndings());
866 m_pwndLeftView->m_sWindowName = m_Data.m_baseFile.GetWindowName();
867 m_pwndLeftView->m_sFullFilePath = m_Data.m_baseFile.GetFilename();
868 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_baseFile.GetConvertedFileName();
869 m_pwndLeftView->m_sReflectedName = m_Data.m_baseFile.GetReflectedName();
870 m_pwndLeftView->m_pWorkingFile = &m_Data.m_baseFile;
871 m_pwndLeftView->SetWritableIsChangable(true);
873 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseRight;
874 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
875 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
876 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName();
877 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
878 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
879 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
880 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
881 m_pwndRightView->SetWritable();
882 m_pwndRightView->SetTarget();
884 m_pwndBottomView->m_pViewData = nullptr;
885 m_pwndBottomView->m_pWorkingFile = nullptr;
887 if (!m_wndSplitter.IsRowHidden(1))
888 m_wndSplitter.HideRow(1);
889 m_pwndLeftView->SetHidden(FALSE);
890 m_pwndRightView->SetHidden(FALSE);
891 m_pwndBottomView->SetHidden(TRUE);
893 bool hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods;
894 pwndActiveView->CheckModifications(hasMods, hasConflicts, hasWhitespaceMods, hasFilteredMods);
895 if (!hasMods && !hasConflicts)
897 // files appear identical, show a dialog informing the user that there are or might
898 // be other differences
899 bool hasEncodingDiff = m_Data.m_arBaseFile.GetUnicodeType() != m_Data.m_arYourFile.GetUnicodeType();
900 bool hasEOLDiff = m_Data.m_arBaseFile.GetLineEndings() != m_Data.m_arYourFile.GetLineEndings();
901 if (hasWhitespaceMods || hasEncodingDiff || hasEOLDiff)
903 // text is identical, but the files do not match
904 CString sWarning(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_MAIN));
905 CString sWhitespace(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_WHITESPACE));
906 CString sEncoding(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_ENCODING));
907 CString sEOL(MAKEINTRESOURCE(IDS_TEXTIDENTICAL_EOL));
908 if (hasWhitespaceMods)
910 sWarning += L"\r\n";
911 sWarning += sWhitespace;
913 if (hasEncodingDiff)
915 sWarning += L"\r\n";
916 sWarning += sEncoding;
917 sWarning += L" (";
918 sWarning += m_Data.m_arBaseFile.GetEncodingName(m_Data.m_arBaseFile.GetUnicodeType());
919 sWarning += L", ";
920 sWarning += m_Data.m_arYourFile.GetEncodingName(m_Data.m_arYourFile.GetUnicodeType());
921 sWarning += L")";
923 if (hasEOLDiff)
925 sWarning += L"\r\n";
926 sWarning += sEOL;
928 AfxMessageBox(sWarning, MB_ICONINFORMATION);
932 else if (m_Data.IsBaseFileInUse() && m_Data.IsYourFileInUse() && m_Data.IsTheirFileInUse())
934 //diff between THEIR, YOUR and BASE
935 m_pwndBottomView->SetWritable();
936 m_pwndBottomView->SetTarget();
937 pwndActiveView = m_pwndBottomView;
939 m_pwndLeftView->m_pViewData = &m_Data.m_TheirBaseBoth;
940 m_pwndLeftView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
941 m_pwndLeftView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
942 m_pwndLeftView->m_sWindowName = m_Data.m_theirFile.GetWindowName(IDS_VIEWTITLE_THEIRS);
943 m_pwndLeftView->m_sFullFilePath = m_Data.m_theirFile.GetFilename();
944 m_pwndLeftView->m_sConvertedFilePath = m_Data.m_theirFile.GetConvertedFileName();
945 m_pwndLeftView->m_sReflectedName = m_Data.m_theirFile.GetReflectedName();
946 m_pwndLeftView->m_pWorkingFile = &m_Data.m_theirFile;
948 m_pwndRightView->m_pViewData = &m_Data.m_YourBaseBoth;
949 m_pwndRightView->SetTextType(m_Data.m_arYourFile.GetUnicodeType());
950 m_pwndRightView->SetLineEndingStyle(m_Data.m_arYourFile.GetLineEndings());
951 m_pwndRightView->m_sWindowName = m_Data.m_yourFile.GetWindowName(IDS_VIEWTITLE_MINE);
952 m_pwndRightView->m_sFullFilePath = m_Data.m_yourFile.GetFilename();
953 m_pwndRightView->m_sConvertedFilePath = m_Data.m_yourFile.GetConvertedFileName();
954 m_pwndRightView->m_sReflectedName = m_Data.m_yourFile.GetReflectedName();
955 m_pwndRightView->m_pWorkingFile = &m_Data.m_yourFile;
957 m_pwndBottomView->m_pViewData = &m_Data.m_Diff3;
958 m_pwndBottomView->SetTextType(m_Data.m_arTheirFile.GetUnicodeType());
959 m_pwndBottomView->SetLineEndingStyle(m_Data.m_arTheirFile.GetLineEndings());
960 m_pwndBottomView->m_sWindowName.LoadString(IDS_VIEWTITLE_MERGED);
961 m_pwndBottomView->m_sWindowName += L" - " + m_Data.m_mergedFile.GetWindowName();
962 m_pwndBottomView->m_sFullFilePath = m_Data.m_mergedFile.GetFilename();
963 m_pwndBottomView->m_sConvertedFilePath = m_Data.m_mergedFile.GetConvertedFileName();
964 m_pwndBottomView->m_sReflectedName = m_Data.m_mergedFile.GetReflectedName();
965 m_pwndBottomView->m_pWorkingFile = &m_Data.m_mergedFile;
967 if (m_wndSplitter2.IsColumnHidden(1))
968 m_wndSplitter2.ShowColumn();
969 if (m_wndSplitter.IsRowHidden(1))
970 m_wndSplitter.ShowRow();
971 m_pwndLeftView->SetHidden(FALSE);
972 m_pwndRightView->SetHidden(FALSE);
973 m_pwndBottomView->SetHidden(FALSE);
974 // in three pane view, hide the line diff bar
975 m_wndLineDiffBar.ShowPane(false, false, true);
976 m_wndLineDiffBar.DocumentUpdated();
978 if (!m_Data.m_mergedFile.InUse())
980 m_Data.m_mergedFile.SetFileName(m_Data.m_yourFile.GetFilename());
982 m_pwndLeftView->BuildAllScreen2ViewVector();
983 m_pwndLeftView->DocumentUpdated();
984 m_pwndRightView->DocumentUpdated();
985 m_pwndBottomView->DocumentUpdated();
986 m_wndLocatorBar.DocumentUpdated();
987 m_wndLineDiffBar.DocumentUpdated();
988 UpdateLayout();
989 SetActiveView(pwndActiveView);
991 if ((line >= -1) && m_pwndRightView->m_pViewData)
993 int n = line == -1 ? min( nOldLineNumber, nOldLine ) : line;
994 if (n >= 0)
996 n = m_pwndRightView->m_pViewData->FindLineNumber(n);
997 if (m_bCollapsed)
999 // adjust the goto-line position if we're collapsed
1000 int step = m_pwndRightView->m_nTopLine > n ? -1 : 1;
1001 int skip = 0;
1002 for (int i = m_pwndRightView->m_nTopLine; i != n; i += step)
1004 if (m_pwndRightView->m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN)
1005 ++skip;
1007 if (m_pwndRightView->m_pViewData->GetHideState(n) == HIDESTATE_HIDDEN)
1008 OnViewTextFoldUnfold();
1009 else
1010 n = n + (skip * step * -1);
1013 if (n < 0)
1014 n = nOldLine;
1016 m_pwndRightView->ScrollAllToLine(n);
1017 POINT p;
1018 p.x = 0;
1019 p.y = n;
1020 if ((ptOldCaretPos.x >= 0) || (ptOldCaretPos.y >= 0))
1021 p = ptOldCaretPos;
1022 m_pwndLeftView->SetCaretPosition(p);
1023 m_pwndRightView->SetCaretPosition(p);
1024 m_pwndBottomView->SetCaretPosition(p);
1025 m_pwndBottomView->ScrollToChar(0);
1026 m_pwndLeftView->ScrollToChar(0);
1027 m_pwndRightView->ScrollToChar(0);
1029 else
1031 CRegDWORD regFirstDiff = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstDiffOnLoad", TRUE);
1032 CRegDWORD regFirstConflict = CRegDWORD(L"Software\\TortoiseGitMerge\\FirstConflictOnLoad", TRUE);
1033 bool bGoFirstDiff = (0 != (DWORD)regFirstDiff);
1034 bool bGoFirstConflict = (0 != (DWORD)regFirstConflict);
1035 if (bGoFirstConflict && (CheckResolved()>=0))
1037 pwndActiveView->GoToFirstConflict();
1038 // Ignore the first few Mouse Move messages, so that the line diff stays on
1039 // the first diff line until the user actually moves the mouse
1040 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1042 else if (bGoFirstDiff)
1044 pwndActiveView->GoToFirstDifference();
1045 // Ignore the first few Mouse Move messages, so that the line diff stays on
1046 // the first diff line until the user actually moves the mouse
1047 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1049 else
1051 // Avoid incorrect rendering of active pane.
1052 m_pwndBottomView->ScrollToChar(0);
1053 m_pwndLeftView->ScrollToChar(0);
1054 m_pwndRightView->ScrollToChar(0);
1057 CheckResolved();
1058 if (m_bHasConflicts && !m_bSaveRequiredOnConflicts)
1059 m_bSaveRequired = false;
1060 CUndo::GetInstance().Clear();
1061 return true;
1064 void CMainFrame::UpdateLayout()
1066 if (m_bInitSplitter)
1068 m_wndSplitter.CenterSplitter();
1069 m_wndSplitter2.CenterSplitter();
1073 void CMainFrame::RecalcLayout(BOOL bNotify)
1075 GetClientRect(&m_dockManager.m_rectInPlace);
1076 if (m_pRibbonApp)
1077 m_dockManager.m_rectInPlace.top += m_pRibbonApp->GetRibbonHeight();
1078 CFrameWndEx::RecalcLayout(bNotify);
1081 void CMainFrame::OnSize(UINT nType, int cx, int cy)
1083 CFrameWndEx::OnSize(nType, cx, cy);
1084 if (m_bInitSplitter && nType != SIZE_MINIMIZED)
1086 if (m_wndSplitter.GetSafeHwnd())
1088 if (m_wndSplitter.HasOldRowSize() && (m_wndSplitter.GetOldRowCount() == 2))
1090 int oldTotal = m_wndSplitter.GetOldRowSize(0) + m_wndSplitter.GetOldRowSize(1);
1091 if (oldTotal)
1093 int cxCur0, cxCur1, cxMin0, cxMin1;
1094 m_wndSplitter.GetRowInfo(0, cxCur0, cxMin0);
1095 m_wndSplitter.GetRowInfo(1, cxCur1, cxMin1);
1096 cxCur0 = m_wndSplitter.GetOldRowSize(0) * (cxCur0 + cxCur1) / oldTotal;
1097 cxCur1 = m_wndSplitter.GetOldRowSize(1) * (cxCur0 + cxCur1) / oldTotal;
1098 m_wndSplitter.SetRowInfo(0, cxCur0, 0);
1099 m_wndSplitter.SetRowInfo(1, cxCur1, 0);
1100 m_wndSplitter.RecalcLayout();
1104 if (m_wndSplitter2.HasOldColSize() && (m_wndSplitter2.GetOldColCount() == 2))
1106 int oldTotal = m_wndSplitter2.GetOldColSize(0) + m_wndSplitter2.GetOldColSize(1);
1107 if (oldTotal)
1109 int cyCur0, cyCur1, cyMin0, cyMin1;
1110 m_wndSplitter2.GetColumnInfo(0, cyCur0, cyMin0);
1111 m_wndSplitter2.GetColumnInfo(1, cyCur1, cyMin1);
1112 cyCur0 = m_wndSplitter2.GetOldColSize(0) * (cyCur0 + cyCur1) / oldTotal;
1113 cyCur1 = m_wndSplitter2.GetOldColSize(1) * (cyCur0 + cyCur1) / oldTotal;
1114 m_wndSplitter2.SetColumnInfo(0, cyCur0, 0);
1115 m_wndSplitter2.SetColumnInfo(1, cyCur1, 0);
1116 m_wndSplitter2.RecalcLayout();
1121 if ((nType == SIZE_RESTORED)&&m_bCheckReload)
1123 m_bCheckReload = false;
1124 CheckForReload();
1128 void CMainFrame::OnViewWhitespaces()
1130 CRegDWORD regViewWhitespaces = CRegDWORD(L"Software\\TortoiseGitMerge\\ViewWhitespaces", 1);
1131 BOOL bViewWhitespaces = regViewWhitespaces;
1132 if (m_pwndLeftView)
1133 bViewWhitespaces = m_pwndLeftView->m_bViewWhitespace;
1135 bViewWhitespaces = !bViewWhitespaces;
1136 regViewWhitespaces = bViewWhitespaces;
1137 if (m_pwndLeftView)
1139 m_pwndLeftView->m_bViewWhitespace = bViewWhitespaces;
1140 m_pwndLeftView->Invalidate();
1142 if (m_pwndRightView)
1144 m_pwndRightView->m_bViewWhitespace = bViewWhitespaces;
1145 m_pwndRightView->Invalidate();
1147 if (m_pwndBottomView)
1149 m_pwndBottomView->m_bViewWhitespace = bViewWhitespaces;
1150 m_pwndBottomView->Invalidate();
1154 void CMainFrame::OnUpdateViewWhitespaces(CCmdUI *pCmdUI)
1156 if (m_pwndLeftView)
1157 pCmdUI->SetCheck(m_pwndLeftView->m_bViewWhitespace);
1160 void CMainFrame::OnViewCollapsed()
1162 m_regCollapsed = !(DWORD)m_regCollapsed;
1163 m_bCollapsed = !!(DWORD)m_regCollapsed;
1165 OnViewTextFoldUnfold();
1166 m_wndLocatorBar.Invalidate();
1169 void CMainFrame::OnUpdateViewCollapsed(CCmdUI *pCmdUI)
1171 pCmdUI->SetCheck(m_bCollapsed);
1174 void CMainFrame::OnViewWraplonglines()
1176 m_bWrapLines = !(DWORD)m_regWrapLines;
1177 m_regWrapLines = m_bWrapLines;
1179 if (m_pwndLeftView) m_pwndLeftView ->WrapChanged();
1180 if (m_pwndRightView) m_pwndRightView ->WrapChanged();
1181 if (m_pwndBottomView) m_pwndBottomView->WrapChanged();
1182 OnViewTextFoldUnfold();
1183 m_wndLocatorBar.DocumentUpdated();
1186 void CMainFrame::OnViewTextFoldUnfold()
1188 OnViewTextFoldUnfold(m_pwndLeftView);
1189 OnViewTextFoldUnfold(m_pwndRightView);
1190 OnViewTextFoldUnfold(m_pwndBottomView);
1193 void CMainFrame::OnViewTextFoldUnfold(CBaseView* view)
1195 if (view == 0)
1196 return;
1197 view->BuildAllScreen2ViewVector();
1198 view->UpdateCaret();
1199 view->Invalidate();
1200 view->EnsureCaretVisible();
1203 void CMainFrame::OnUpdateViewWraplonglines(CCmdUI *pCmdUI)
1205 pCmdUI->SetCheck(m_bWrapLines);
1208 void CMainFrame::OnViewOnewaydiff()
1210 if (CheckForSave(CHFSR_RELOAD)==IDCANCEL)
1211 return;
1212 m_bOneWay = !m_bOneWay;
1213 m_regOneWay = m_bOneWay;
1214 ShowDiffBar(!m_bOneWay);
1215 LoadViews(-1);
1218 void CMainFrame::DiffLeftToBase()
1220 DiffTwo(m_Data.m_baseFile, m_Data.m_theirFile);
1223 void CMainFrame::DiffRightToBase()
1225 DiffTwo(m_Data.m_baseFile, m_Data.m_yourFile);
1228 void CMainFrame::DiffTwo(const CWorkingFile& file1, const CWorkingFile& file2)
1230 wchar_t ownpath[MAX_PATH] = { 0 };
1231 GetModuleFileName(nullptr, ownpath, MAX_PATH);
1232 CString sCmd;
1233 sCmd.Format(L"\"%s\"", ownpath);
1234 sCmd += L" /base:\"";
1235 sCmd += file1.GetFilename();
1236 sCmd += L"\" /mine:\"";
1237 sCmd += file2.GetFilename();
1238 sCmd += L'"';
1239 if (!file1.GetWindowName().IsEmpty())
1240 sCmd += L" /basename:\"" + file1.GetWindowName() + L"\"";
1241 if (!file2.GetWindowName().IsEmpty())
1242 sCmd += L" /yourname:\"" + file2.GetWindowName() + L"\"";
1244 CAppUtils::LaunchApplication(sCmd, 0, false);
1247 void CMainFrame::ShowDiffBar(bool bShow)
1249 if (bShow)
1251 // restore the line diff bar
1252 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
1253 m_wndLineDiffBar.DocumentUpdated();
1254 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
1255 m_wndLocatorBar.DocumentUpdated();
1257 else
1259 // in one way view, hide the line diff bar
1260 m_wndLineDiffBar.ShowPane(false, false, true);
1261 m_wndLineDiffBar.DocumentUpdated();
1265 int CMainFrame::CheckResolved()
1267 //only in three way diffs can be conflicts!
1268 m_bHasConflicts = true;
1269 if (IsViewGood(m_pwndBottomView))
1271 CViewData* viewdata = m_pwndBottomView->m_pViewData;
1272 if (viewdata)
1274 for (int i=0; i<viewdata->GetCount(); i++)
1276 const DiffStates state = viewdata->GetState(i);
1277 if ((DIFFSTATE_CONFLICTED == state)||(DIFFSTATE_CONFLICTED_IGNORED == state))
1278 return i;
1282 m_bHasConflicts = false;
1283 return -1;
1286 int CMainFrame::SaveFile(const CString& sFilePath)
1288 CBaseView* pView = nullptr;
1289 CViewData* pViewData = nullptr;
1290 CFileTextLines * pOriginFile = &m_Data.m_arBaseFile;
1291 if (IsViewGood(m_pwndBottomView))
1293 pView = m_pwndBottomView;
1294 pViewData = m_pwndBottomView->m_pViewData;
1296 else if (IsViewGood(m_pwndRightView))
1298 pView = m_pwndRightView;
1299 pViewData = m_pwndRightView->m_pViewData;
1300 if (m_Data.IsYourFileInUse())
1301 pOriginFile = &m_Data.m_arYourFile;
1302 else if (m_Data.IsTheirFileInUse())
1303 pOriginFile = &m_Data.m_arTheirFile;
1305 else
1307 // nothing to save!
1308 return 1;
1310 Invalidate();
1311 if ((pViewData)&&(pOriginFile))
1313 CFileTextLines file;
1314 pOriginFile->CopySettings(&file);
1315 CFileTextLines::SaveParams saveParams;
1316 saveParams.m_LineEndings = pView->GetLineEndings();
1317 saveParams.m_UnicodeType = pView->GetTextType();
1318 file.SetSaveParams(saveParams);
1319 for (int i=0; i<pViewData->GetCount(); i++)
1321 //only copy non-removed lines
1322 DiffStates state = pViewData->GetState(i);
1323 switch (state)
1325 case DIFFSTATE_CONFLICTED:
1326 case DIFFSTATE_CONFLICTED_IGNORED:
1328 int first = i;
1329 int last = i;
1332 last++;
1333 } while((last<pViewData->GetCount()) && ((pViewData->GetState(last)==DIFFSTATE_CONFLICTED)||(pViewData->GetState(last)==DIFFSTATE_CONFLICTED_IGNORED)));
1334 // TortoiseGitMerge changes here
1335 file.Add(L"<<<<<<< .mine", m_pwndRightView->GetLineEndings());
1336 for (int j=first; j<last; j++)
1338 EOL lineending = m_pwndRightView->m_pViewData->GetLineEnding(j);
1339 if (lineending == EOL_NOENDING)
1340 lineending = m_pwndRightView->GetLineEndings();
1341 file.Add(m_pwndRightView->m_pViewData->GetLine(j), lineending);
1343 file.Add(L"=======", m_pwndRightView->GetLineEndings());
1344 for (int j=first; j<last; j++)
1346 EOL lineending = m_pwndLeftView->m_pViewData->GetLineEnding(j);
1347 if (lineending == EOL_NOENDING)
1348 lineending = m_pwndLeftView->GetLineEndings();
1349 file.Add(m_pwndLeftView->m_pViewData->GetLine(j), lineending);
1351 file.Add(L">>>>>>> .theirs", m_pwndRightView->GetLineEndings());
1352 i = last-1;
1354 break;
1355 case DIFFSTATE_EMPTY:
1356 case DIFFSTATE_CONFLICTEMPTY:
1357 case DIFFSTATE_IDENTICALREMOVED:
1358 case DIFFSTATE_REMOVED:
1359 case DIFFSTATE_THEIRSREMOVED:
1360 case DIFFSTATE_YOURSREMOVED:
1361 case DIFFSTATE_CONFLICTRESOLVEDEMPTY:
1362 // do not save removed lines
1363 break;
1364 default:
1365 file.Add(pViewData->GetLine(i), pViewData->GetLineEnding(i));
1366 break;
1369 if (!file.Save(sFilePath, false, false))
1371 CMessageBox::Show(m_hWnd, file.GetErrorString(), L"TortoiseGitMerge", MB_ICONERROR);
1372 return -1;
1374 if (sFilePath == m_Data.m_baseFile.GetFilename())
1376 m_Data.m_baseFile.StoreFileAttributes();
1378 if (sFilePath == m_Data.m_theirFile.GetFilename())
1380 m_Data.m_theirFile.StoreFileAttributes();
1382 if (sFilePath == m_Data.m_yourFile.GetFilename())
1384 m_Data.m_yourFile.StoreFileAttributes();
1386 /*if (sFilePath == m_Data.m_mergedFile.GetFilename())
1388 m_Data.m_mergedFile.StoreFileAttributes();
1389 }//*/
1390 m_dlgFilePatches.SetFileStatusAsPatched(sFilePath);
1391 if (IsViewGood(m_pwndBottomView))
1392 m_pwndBottomView->SetModified(FALSE);
1393 else if (IsViewGood(m_pwndRightView))
1394 m_pwndRightView->SetModified(FALSE);
1395 CUndo::GetInstance().MarkAsOriginalState(
1396 false,
1397 IsViewGood(m_pwndRightView) && (pViewData == m_pwndRightView->m_pViewData),
1398 IsViewGood(m_pwndBottomView) && (pViewData == m_pwndBottomView->m_pViewData));
1399 if (file.GetCount() == 1 && file.GetAt(0).IsEmpty() && file.GetLineEnding(0) == EOL_NOENDING)
1400 return 0;
1401 return file.GetCount();
1403 return 1;
1406 void CMainFrame::OnFileSave()
1408 // when multiple files are set as writable we have to ask what file to save
1409 int nEditableViewCount =
1410 (CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsWritable() ? 1 : 0)
1411 + (CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsWritable() ? 1 : 0)
1412 + (CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsWritable() ? 1 : 0);
1413 bool bLeftIsModified = CBaseView::IsViewGood(m_pwndLeftView) && m_pwndLeftView->IsModified();
1414 bool bRightIsModified = CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->IsModified();
1415 bool bBottomIsModified = CBaseView::IsViewGood(m_pwndBottomView) && m_pwndBottomView->IsModified();
1416 int nModifiedViewCount =
1417 (bLeftIsModified ? 1 : 0)
1418 + (bRightIsModified ? 1 : 0)
1419 + (bBottomIsModified ? 1 : 0);
1420 if (nEditableViewCount>1)
1422 if (nModifiedViewCount == 1)
1424 if (bLeftIsModified)
1425 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1426 else
1427 FileSave();
1429 else if (nModifiedViewCount>0)
1431 // both views
1432 CTaskDialog taskdlg(CString(MAKEINTRESOURCE(IDS_SAVE_MORE)),
1433 CString(MAKEINTRESOURCE(IDS_SAVE)),
1434 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1436 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1437 CString sTaskTemp;
1438 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
1439 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, (LPCTSTR)m_pwndLeftView->m_pWorkingFile->GetFilename());
1440 else
1441 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
1442 taskdlg.AddCommandControl(201, sTaskTemp, bLeftIsModified);// left
1443 if (bLeftIsModified)
1444 taskdlg.SetDefaultCommandControl(201);
1445 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
1446 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, (LPCTSTR)m_pwndRightView->m_pWorkingFile->GetFilename());
1447 else
1448 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
1449 taskdlg.AddCommandControl(202, sTaskTemp, bRightIsModified); // right
1450 if (bRightIsModified)
1451 taskdlg.SetDefaultCommandControl(202);
1452 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2)), nModifiedViewCount>1); // both
1453 if (nModifiedViewCount > 1)
1454 taskdlg.SetDefaultCommandControl(203);
1455 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1456 taskdlg.SetMainIcon(TD_WARNING_ICON);
1457 UINT ret = (UINT)taskdlg.DoModal(m_hWnd);
1458 switch (ret)
1460 case 201: // left
1461 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1462 break;
1463 case 203: // both
1464 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
1465 case 202: // right
1466 m_pwndRightView->SaveFile();
1467 break;
1471 else
1473 // only target view was modified
1474 FileSave();
1478 void CMainFrame::PatchSave()
1480 bool bDoesNotExist = !PathFileExists(m_Data.m_mergedFile.GetFilename());
1481 if (m_Data.m_bPatchRequired)
1483 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1485 if (!PathIsDirectory(m_Data.m_mergedFile.GetFilename()))
1487 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1488 if (saveret==0)
1490 // file was saved with 0 lines, remove it.
1491 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1492 // just in case
1493 DeleteFile(m_Data.m_mergedFile.GetFilename());
1495 m_Data.m_mergedFile.StoreFileAttributes();
1496 if (m_Data.m_mergedFile.GetFilename() == m_Data.m_yourFile.GetFilename())
1497 m_Data.m_yourFile.StoreFileAttributes();
1498 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1500 // call TortoiseProc to add the new file to version control
1501 CString cmd = L"/command:add /noui /path:\"";
1502 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1503 CAppUtils::RunTortoiseGitProc(cmd);
1508 bool CMainFrame::FileSave(bool bCheckResolved /*=true*/)
1510 if (HasMarkedBlocks())
1512 CString sTitle(MAKEINTRESOURCE(IDS_WARNMARKEDBLOCKS));
1513 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_MARKEDBLOCKS));
1514 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
1515 CTaskDialog taskdlg(sTitle,
1516 sSubTitle,
1517 sAppName,
1519 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1520 taskdlg.AddCommandControl(10, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEINCLUDE)));
1521 taskdlg.AddCommandControl(11, CString(MAKEINTRESOURCE(IDS_MARKEDBLOCKSSAVEEXCLUDE)));
1522 taskdlg.AddCommandControl(12, CString(MAKEINTRESOURCE(IDS_MARKEDBLCOKSSAVEIGNORE)));
1523 taskdlg.AddCommandControl(IDCANCEL, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_CANCEL_OPEN)));
1524 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1525 taskdlg.SetDefaultCommandControl(10);
1526 taskdlg.SetMainIcon(TD_WARNING_ICON);
1527 UINT ret = (UINT)taskdlg.DoModal(m_hWnd);
1528 if (ret == 10)
1529 m_pwndRightView->LeaveOnlyMarkedBlocks();
1530 else if (ret == 11)
1531 m_pwndRightView->UseViewFileOfMarked();
1532 else if (ret == 12)
1533 m_pwndRightView->UseViewFileExceptEdited();
1534 else
1535 return false;
1538 if (!m_Data.m_mergedFile.InUse())
1539 return FileSaveAs(bCheckResolved);
1540 // check if the file has the readonly attribute set
1541 bool bDoesNotExist = false;
1542 DWORD fAttribs = GetFileAttributes(m_Data.m_mergedFile.GetFilename());
1543 if ((fAttribs != INVALID_FILE_ATTRIBUTES)&&(fAttribs & FILE_ATTRIBUTE_READONLY))
1544 return FileSaveAs(bCheckResolved);
1545 if (fAttribs == INVALID_FILE_ATTRIBUTES)
1547 bDoesNotExist = (GetLastError() == ERROR_FILE_NOT_FOUND);
1549 if (bCheckResolved && HasConflictsWontKeep())
1550 return false;
1552 if (((DWORD)CRegDWORD(L"Software\\TortoiseGitMerge\\Backup")) != 0)
1554 MoveFileEx(m_Data.m_mergedFile.GetFilename(), m_Data.m_mergedFile.GetFilename() + L".bak", MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
1556 if (m_Data.m_bPatchRequired)
1558 m_Patch.PatchPath(m_Data.m_mergedFile.GetFilename());
1560 int saveret = SaveFile(m_Data.m_mergedFile.GetFilename());
1561 if (saveret==0)
1563 // file was saved with 0 lines!
1564 // ask the user if the file should be deleted
1565 CString msg;
1566 msg.Format(IDS_DELETEWHENEMPTY, (LPCTSTR)CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename()));
1567 CTaskDialog taskdlg(msg,
1568 CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK2)),
1569 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1571 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1572 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK3)));
1573 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_DELETEWHENEMPTY_TASK4)));
1574 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1575 taskdlg.SetDefaultCommandControl(1);
1576 taskdlg.SetMainIcon(TD_WARNING_ICON);
1577 if (taskdlg.DoModal(m_hWnd) == 1)
1579 m_Patch.RemoveFile(m_Data.m_mergedFile.GetFilename());
1580 DeleteFile(m_Data.m_mergedFile.GetFilename());
1583 else if (saveret < 0)
1585 // error while saving the file
1586 return false;
1589 // if we're in conflict resolve mode (three pane view), check if there are no more conflicts
1590 // and if there aren't, ask to mark the file as resolved
1591 if (IsViewGood(m_pwndBottomView) && !m_bHasConflicts && bCheckResolved)
1593 CString projectRoot;
1594 if (GitAdminDir::HasAdminDir(m_Data.m_mergedFile.GetFilename(), false, &projectRoot))
1596 CString subpath = m_Data.m_mergedFile.GetFilename();
1597 subpath.Replace(L'\\', L'/');
1598 if (subpath.GetLength() >= projectRoot.GetLength())
1600 if (subpath[projectRoot.GetLength()] == L'/')
1601 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength() - 1);
1602 else
1603 subpath = subpath.Right(subpath.GetLength() - projectRoot.GetLength());
1606 CAutoRepository repository(projectRoot);
1607 bool hasConflictInIndex = false;
1610 if (!repository)
1611 break;
1613 CAutoIndex index;
1614 if (git_repository_index(index.GetPointer(), repository))
1615 break;
1617 CStringA path = CUnicodeUtils::GetMulti(subpath, CP_UTF8);
1618 hasConflictInIndex = git_index_get_bypath(index, path, 1) || git_index_get_bypath(index, path, 2);
1619 } while(0);
1621 if (hasConflictInIndex)
1623 CString msg;
1624 msg.Format(IDS_MARKASRESOLVED, (LPCTSTR)CPathUtils::GetFileNameFromPath(m_Data.m_mergedFile.GetFilename()));
1625 CTaskDialog taskdlg(msg,
1626 CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK2)),
1627 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1629 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1630 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK3)));
1631 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_MARKASRESOLVED_TASK4)));
1632 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1633 taskdlg.SetDefaultCommandControl(1);
1634 taskdlg.SetMainIcon(TD_WARNING_ICON);
1635 if (taskdlg.DoModal(m_hWnd) == 1)
1636 MarkAsResolved();
1641 m_bSaveRequired = false;
1642 m_Data.m_mergedFile.StoreFileAttributes();
1644 if (bDoesNotExist && (DWORD(CRegDWORD(L"Software\\TortoiseGitMerge\\AutoAdd", TRUE))))
1646 // call TortoiseProc to add the new file to version control
1647 CString cmd = L"/command:add /noui /path:\"";
1648 cmd += m_Data.m_mergedFile.GetFilename() + L'"';
1649 if(!CAppUtils::RunTortoiseGitProc(cmd))
1650 return false;
1652 return true;
1655 void CMainFrame::OnFileSaveAs()
1657 // ask what file to save as
1658 bool bHaveConflict = (CheckResolved() >= 0);
1659 CTaskDialog taskdlg(
1660 CString(MAKEINTRESOURCE(bHaveConflict ? IDS_ASKFORSAVEAS_MORECONFLICT : IDS_ASKFORSAVEAS_MORE)),
1661 CString(MAKEINTRESOURCE(IDS_ASKFORSAVEAS)),
1662 CString(MAKEINTRESOURCE(IDS_APPNAME)),
1664 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
1665 // default can be last view (target) as was in 1.7 or actual (where is cursor) as is in most text editor
1666 if (IsViewGood(m_pwndLeftView))
1668 taskdlg.AddCommandControl(201, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS))); // left
1669 taskdlg.SetDefaultCommandControl(201);
1671 if (IsViewGood(m_pwndRightView))
1673 taskdlg.AddCommandControl(202, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS))); // right
1674 taskdlg.SetDefaultCommandControl(202);
1676 if (IsViewGood(m_pwndBottomView))
1678 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEBOTTOMAS))); // bottom
1679 taskdlg.SetDefaultCommandControl(203);
1681 if (bHaveConflict)
1683 taskdlg.AddCommandControl(204, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_NEEDRESOLVE))); // resolve
1684 taskdlg.SetDefaultCommandControl(204);
1686 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
1687 taskdlg.SetMainIcon(bHaveConflict ? TD_WARNING_ICON : TD_INFORMATION_ICON);
1688 int nCommand = (int)taskdlg.DoModal(m_hWnd);
1689 CString sFileName;
1690 switch (nCommand)
1692 case 201: // left
1693 if (TryGetFileName(sFileName))
1695 // in 2, 3 view display we want to keep removed lines
1696 m_pwndLeftView->SaveFileTo(sFileName, IsViewGood(m_pwndRightView) ? SAVE_REMOVEDLINES : 0);
1698 break;
1699 case 202: // right
1700 if (TryGetFileName(sFileName))
1702 m_pwndRightView->SaveFileTo(sFileName);
1704 break;
1705 case 203: // bottom
1706 FileSaveAs();
1707 break;
1708 case 204: // continue resolving
1709 if (IsViewGood(m_pwndBottomView))
1711 m_pwndBottomView->GoToLine(CheckResolved());
1713 break;
1717 bool CMainFrame::FileSaveAs(bool bCheckResolved /*=true*/)
1719 if (bCheckResolved && HasConflictsWontKeep())
1720 return false;
1722 CString fileName;
1723 if(!TryGetFileName(fileName))
1724 return false;
1726 SaveFile(fileName);
1727 return true;
1730 void CMainFrame::OnUpdateFileSave(CCmdUI *pCmdUI)
1732 BOOL bEnable = FALSE;
1733 if (m_Data.m_mergedFile.InUse())
1735 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1736 bEnable = TRUE;
1737 else if ( (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData)) &&
1738 (m_pwndRightView->IsModified() || (m_Data.m_yourFile.GetWindowName().Right((int)wcslen(L": patched")).Compare(L": patched") == 0)))
1739 bEnable = TRUE;
1740 else if (IsViewGood(m_pwndLeftView)
1741 && (m_pwndLeftView->m_pViewData)
1742 && (m_pwndLeftView->IsModified()))
1743 bEnable = TRUE;
1745 pCmdUI->Enable(bEnable);
1748 void CMainFrame::OnUpdateFileSaveAs(CCmdUI *pCmdUI)
1750 // any file is open we can save it as
1751 BOOL bEnable = FALSE;
1752 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
1753 bEnable = TRUE;
1754 else if (IsViewGood(m_pwndRightView)&&(m_pwndRightView->m_pViewData))
1755 bEnable = TRUE;
1756 else if (IsViewGood(m_pwndLeftView)&&(m_pwndLeftView->m_pViewData))
1757 bEnable = TRUE;
1758 pCmdUI->Enable(bEnable);
1761 void CMainFrame::OnUpdateViewOnewaydiff(CCmdUI *pCmdUI)
1763 pCmdUI->SetCheck(!m_bOneWay);
1764 BOOL bEnable = TRUE;
1765 if (IsViewGood(m_pwndBottomView))
1767 bEnable = FALSE;
1769 pCmdUI->Enable(bEnable);
1772 void CMainFrame::OnViewOptions()
1774 CString sTemp;
1775 sTemp.LoadString(IDS_SETTINGSTITLE);
1776 CSettings dlg(sTemp);
1777 dlg.DoModal();
1778 if (dlg.IsReloadNeeded())
1780 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
1781 return;
1782 CDiffColors::GetInstance().LoadRegistry();
1783 LoadViews();
1784 return;
1786 CDiffColors::GetInstance().LoadRegistry();
1787 if (m_pwndBottomView)
1788 m_pwndBottomView->DocumentUpdated();
1789 if (m_pwndLeftView)
1790 m_pwndLeftView->DocumentUpdated();
1791 if (m_pwndRightView)
1792 m_pwndRightView->DocumentUpdated();
1795 void CMainFrame::OnClose()
1797 if (!IsWindowEnabled())
1798 return; // just in case someone sends a WM_CLOSE to the main window while another window (dialog,...) is open
1799 if (CheckForSave(CHFSR_CLOSE)!=IDCANCEL)
1801 WINDOWPLACEMENT wp;
1803 // before it is destroyed, save the position of the window
1804 wp.length = sizeof wp;
1806 if (GetWindowPlacement(&wp))
1809 if (IsIconic())
1810 // never restore to Iconic state
1811 wp.showCmd = SW_SHOW ;
1813 if ((wp.flags & WPF_RESTORETOMAXIMIZED) != 0)
1814 // if maximized and maybe iconic restore maximized state
1815 wp.showCmd = SW_SHOWMAXIMIZED ;
1817 // and write it
1818 WriteWindowPlacement(&wp);
1820 __super::OnClose();
1824 void CMainFrame::OnActivate(UINT nValue, CWnd* /*pwnd*/, BOOL /*bActivated?*/)
1826 if (nValue != 0) // activated
1828 if (IsIconic())
1830 m_bCheckReload = TRUE;
1832 else
1834 // use a timer to give other messages time to get processed
1835 // first, like e.g. the WM_CLOSE message in case the user
1836 // clicked the close button and that brought the window
1837 // to the front - in that case checking for reload wouldn't
1838 // do any good.
1839 SetTimer(IDT_RELOADCHECKTIMER, 300, nullptr);
1844 void CMainFrame::OnViewLinedown()
1846 OnViewLineUpDown(1);
1849 void CMainFrame::OnViewLineup()
1851 OnViewLineUpDown(-1);
1854 void CMainFrame::OnViewLineUpDown(int direction)
1856 if (m_pwndLeftView)
1857 m_pwndLeftView->ScrollToLine(m_pwndLeftView->m_nTopLine+direction);
1858 if (m_pwndRightView)
1859 m_pwndRightView->ScrollToLine(m_pwndRightView->m_nTopLine+direction);
1860 if (m_pwndBottomView)
1861 m_pwndBottomView->ScrollToLine(m_pwndBottomView->m_nTopLine+direction);
1862 m_wndLocatorBar.Invalidate();
1863 m_nMoveMovesToIgnore = MOVESTOIGNORE;
1866 void CMainFrame::OnViewLineleft()
1868 OnViewLineLeftRight(-1);
1871 void CMainFrame::OnViewLineright()
1873 OnViewLineLeftRight(1);
1876 void CMainFrame::OnViewLineLeftRight(int direction)
1878 if (m_pwndLeftView)
1879 m_pwndLeftView->ScrollSide(direction);
1880 if (m_pwndRightView)
1881 m_pwndRightView->ScrollSide(direction);
1882 if (m_pwndBottomView)
1883 m_pwndBottomView->ScrollSide(direction);
1886 void CMainFrame::OnEditUseTheirs()
1888 if (m_pwndBottomView)
1889 m_pwndBottomView->UseTheirTextBlock();
1891 void CMainFrame::OnUpdateEditUsetheirblock(CCmdUI *pCmdUI)
1893 pCmdUI->Enable(m_pwndBottomView && m_pwndBottomView->HasSelection());
1896 void CMainFrame::OnEditUseMine()
1898 if (m_pwndBottomView)
1899 m_pwndBottomView->UseMyTextBlock();
1901 void CMainFrame::OnUpdateEditUsemyblock(CCmdUI *pCmdUI)
1903 OnUpdateEditUsetheirblock(pCmdUI);
1906 void CMainFrame::OnEditUseTheirsThenMine()
1908 if (m_pwndBottomView)
1909 m_pwndBottomView->UseTheirAndYourBlock();
1912 void CMainFrame::OnUpdateEditUsetheirthenmyblock(CCmdUI *pCmdUI)
1914 OnUpdateEditUsetheirblock(pCmdUI);
1917 void CMainFrame::OnEditUseMineThenTheirs()
1919 if (m_pwndBottomView)
1920 m_pwndBottomView->UseYourAndTheirBlock();
1923 void CMainFrame::OnUpdateEditUseminethentheirblock(CCmdUI *pCmdUI)
1925 OnUpdateEditUsetheirblock(pCmdUI);
1928 void CMainFrame::OnEditUseleftblock()
1930 if (m_pwndBottomView->IsWindowVisible())
1931 m_pwndBottomView->UseRightBlock();
1932 else
1933 m_pwndRightView->UseLeftBlock();
1936 void CMainFrame::OnUpdateEditUseleftblock(CCmdUI *pCmdUI)
1938 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget() && m_pwndRightView->HasSelection());
1941 void CMainFrame::OnUpdateUseBlock(CCmdUI *pCmdUI)
1943 pCmdUI->Enable(TRUE);
1946 void CMainFrame::OnEditUseleftfile()
1948 if (m_pwndBottomView->IsWindowVisible())
1949 m_pwndBottomView->UseRightFile();
1950 else
1951 m_pwndRightView->UseLeftFile();
1954 void CMainFrame::OnUpdateEditUseleftfile(CCmdUI *pCmdUI)
1956 pCmdUI->Enable(IsViewGood(m_pwndRightView) && m_pwndRightView->IsTarget());
1959 void CMainFrame::OnEditUseblockfromleftbeforeright()
1961 if (m_pwndRightView)
1962 m_pwndRightView->UseBothLeftFirst();
1965 void CMainFrame::OnUpdateEditUseblockfromleftbeforeright(CCmdUI *pCmdUI)
1967 OnUpdateEditUseleftblock(pCmdUI);
1970 void CMainFrame::OnEditUseblockfromrightbeforeleft()
1972 if (m_pwndRightView)
1973 m_pwndRightView->UseBothRightFirst();
1976 void CMainFrame::OnUpdateEditUseblockfromrightbeforeleft(CCmdUI *pCmdUI)
1978 OnUpdateEditUseleftblock(pCmdUI);
1981 void CMainFrame::OnFileReload()
1983 if (CheckForSave(CHFSR_RELOAD)==IDCANCEL)
1984 return;
1985 CDiffColors::GetInstance().LoadRegistry();
1986 LoadViews(-1);
1989 void CMainFrame::ActivateFrame(int nCmdShow)
1991 // nCmdShow is the normal show mode this frame should be in
1992 // translate default nCmdShow (-1)
1993 if (nCmdShow == -1)
1995 if (!IsWindowVisible())
1996 nCmdShow = SW_SHOWNORMAL;
1997 else if (IsIconic())
1998 nCmdShow = SW_RESTORE;
2001 // bring to top before showing
2002 BringToTop(nCmdShow);
2004 if (nCmdShow != -1)
2006 // show the window as specified
2007 WINDOWPLACEMENT wp;
2009 if ( !ReadWindowPlacement(&wp) )
2011 ShowWindow(nCmdShow);
2013 else
2015 if ( nCmdShow != SW_SHOWNORMAL )
2016 wp.showCmd = nCmdShow;
2018 SetWindowPlacement(&wp);
2021 // and finally, bring to top after showing
2022 BringToTop(nCmdShow);
2026 BOOL CMainFrame::ReadWindowPlacement(WINDOWPLACEMENT * pwp)
2028 CRegString placement = CRegString(L"Software\\TortoiseGitMerge\\WindowPos");
2029 CString sPlacement = placement;
2030 if (sPlacement.IsEmpty())
2031 return FALSE;
2032 int nRead = swscanf_s(sPlacement, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2033 &pwp->flags, &pwp->showCmd,
2034 &pwp->ptMinPosition.x, &pwp->ptMinPosition.y,
2035 &pwp->ptMaxPosition.x, &pwp->ptMaxPosition.y,
2036 &pwp->rcNormalPosition.left, &pwp->rcNormalPosition.top,
2037 &pwp->rcNormalPosition.right, &pwp->rcNormalPosition.bottom);
2038 if ( nRead != 10 )
2039 return FALSE;
2040 pwp->length = sizeof(WINDOWPLACEMENT);
2042 return TRUE;
2045 void CMainFrame::WriteWindowPlacement(WINDOWPLACEMENT * pwp)
2047 CRegString placement(L"Software\\TortoiseGitMerge\\WindowPos");
2048 TCHAR szBuffer[_countof("-32767")*8 + sizeof("65535")*2];
2050 swprintf_s(szBuffer, L"%u,%u,%d,%d,%d,%d,%d,%d,%d,%d",
2051 pwp->flags, pwp->showCmd,
2052 pwp->ptMinPosition.x, pwp->ptMinPosition.y,
2053 pwp->ptMaxPosition.x, pwp->ptMaxPosition.y,
2054 pwp->rcNormalPosition.left, pwp->rcNormalPosition.top,
2055 pwp->rcNormalPosition.right, pwp->rcNormalPosition.bottom);
2056 placement = szBuffer;
2059 void CMainFrame::OnUpdateMergeMarkasresolved(CCmdUI *pCmdUI)
2061 if (!pCmdUI)
2062 return;
2063 BOOL bEnable = FALSE;
2064 if ((!m_bReadOnly)&&(m_Data.m_mergedFile.InUse()))
2066 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2068 bEnable = TRUE;
2071 pCmdUI->Enable(bEnable);
2074 void CMainFrame::OnMergeMarkasresolved()
2076 if(HasConflictsWontKeep())
2077 return;
2079 // now check if the file has already been saved and if not, save it.
2080 if (m_Data.m_mergedFile.InUse())
2082 if (IsViewGood(m_pwndBottomView)&&(m_pwndBottomView->m_pViewData))
2084 if (!FileSave(false))
2085 return;
2086 m_bSaveRequired = false;
2089 MarkAsResolved();
2092 BOOL CMainFrame::MarkAsResolved()
2094 if (m_bReadOnly)
2095 return FALSE;
2096 if (!IsViewGood(m_pwndBottomView))
2097 return FALSE;
2099 CString cmd = L"/command:resolve /path:\"";
2100 cmd += m_Data.m_mergedFile.GetFilename();
2101 cmd += L"\" /closeonend:1 /noquestion /skipcheck /silent";
2102 if (resolveMsgWnd)
2104 CString s;
2105 s.Format(L" /resolvemsghwnd:%I64d /resolvemsgwparam:%I64d /resolvemsglparam:%I64d", (__int64)resolveMsgWnd, (__int64)resolveMsgWParam, (__int64)resolveMsgLParam);
2106 cmd += s;
2108 if(!CAppUtils::RunTortoiseGitProc(cmd))
2109 return FALSE;
2110 m_bSaveRequired = false;
2111 return TRUE;
2114 void CMainFrame::OnUpdateMergeNextconflict(CCmdUI *pCmdUI)
2116 BOOL bShow = FALSE;
2117 if (HasNextConflict(m_pwndBottomView))
2118 bShow = TRUE;
2119 else if (HasNextConflict(m_pwndRightView))
2120 bShow = TRUE;
2121 else if (HasNextConflict(m_pwndLeftView))
2122 bShow = TRUE;
2123 pCmdUI->Enable(bShow);
2126 bool CMainFrame::HasNextConflict(CBaseView* view)
2128 if (view == 0)
2129 return false;
2130 if (!view->IsTarget())
2131 return false;
2132 return view->HasNextConflict();
2135 void CMainFrame::OnUpdateMergePreviousconflict(CCmdUI *pCmdUI)
2137 BOOL bShow = FALSE;
2138 if (HasPrevConflict(m_pwndBottomView))
2139 bShow = TRUE;
2140 else if (HasPrevConflict(m_pwndRightView))
2141 bShow = TRUE;
2142 else if (HasPrevConflict(m_pwndLeftView))
2143 bShow = TRUE;
2144 pCmdUI->Enable(bShow);
2147 bool CMainFrame::HasPrevConflict(CBaseView* view)
2149 if (view == 0)
2150 return false;
2151 if (!view->IsTarget())
2152 return false;
2153 return view->HasPrevConflict();
2156 void CMainFrame::OnUpdateNavigateNextdifference(CCmdUI *pCmdUI)
2158 CBaseView* baseView = GetActiveBaseView();
2159 BOOL bShow = FALSE;
2160 if (baseView != 0)
2161 bShow = baseView->HasNextDiff();
2162 pCmdUI->Enable(bShow);
2165 void CMainFrame::OnUpdateNavigatePreviousdifference(CCmdUI *pCmdUI)
2167 CBaseView* baseView = GetActiveBaseView();
2168 BOOL bShow = FALSE;
2169 if (baseView != 0)
2170 bShow = baseView->HasPrevDiff();
2171 pCmdUI->Enable(bShow);
2174 void CMainFrame::OnUpdateNavigateNextinlinediff(CCmdUI *pCmdUI)
2176 BOOL bShow = FALSE;
2177 if (HasNextInlineDiff(m_pwndBottomView))
2178 bShow = TRUE;
2179 else if (HasNextInlineDiff(m_pwndRightView))
2180 bShow = TRUE;
2181 else if (HasNextInlineDiff(m_pwndLeftView))
2182 bShow = TRUE;
2183 pCmdUI->Enable(bShow);
2186 bool CMainFrame::HasNextInlineDiff(CBaseView* view)
2188 if (view == 0)
2189 return false;
2190 if (!view->IsTarget())
2191 return false;
2192 return view->HasNextInlineDiff();
2195 void CMainFrame::OnUpdateNavigatePrevinlinediff(CCmdUI *pCmdUI)
2197 BOOL bShow = FALSE;
2198 if (HasPrevInlineDiff(m_pwndBottomView))
2199 bShow = TRUE;
2200 else if (HasPrevInlineDiff(m_pwndRightView))
2201 bShow = TRUE;
2202 else if (HasPrevInlineDiff(m_pwndLeftView))
2203 bShow = TRUE;
2204 pCmdUI->Enable(bShow);
2207 bool CMainFrame::HasPrevInlineDiff(CBaseView* view)
2209 if (view == 0)
2210 return false;
2211 if (!view->IsTarget())
2212 return false;
2213 return view->HasPrevInlineDiff();
2216 void CMainFrame::OnMoving(UINT fwSide, LPRECT pRect)
2218 // if the pathfilelist dialog is attached to the mainframe,
2219 // move it along with the mainframe
2220 if (::IsWindow(m_dlgFilePatches.m_hWnd))
2222 RECT patchrect;
2223 m_dlgFilePatches.GetWindowRect(&patchrect);
2224 if (::IsWindow(m_hWnd))
2226 RECT thisrect;
2227 GetWindowRect(&thisrect);
2228 if (patchrect.right == thisrect.left)
2230 m_dlgFilePatches.SetWindowPos(nullptr, patchrect.left - (thisrect.left - pRect->left), patchrect.top - (thisrect.top - pRect->top),
2231 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
2235 __super::OnMoving(fwSide, pRect);
2238 void CMainFrame::OnUpdateEditCopy(CCmdUI *pCmdUI)
2240 BOOL bShow = FALSE;
2241 if ((m_pwndBottomView)&&(m_pwndBottomView->HasSelection()))
2242 bShow = TRUE;
2243 else if ((m_pwndRightView)&&(m_pwndRightView->HasSelection()))
2244 bShow = TRUE;
2245 else if ((m_pwndLeftView)&&(m_pwndLeftView->HasSelection()))
2246 bShow = TRUE;
2247 pCmdUI->Enable(bShow);
2250 void CMainFrame::OnUpdateEditPaste(CCmdUI *pCmdUI)
2252 BOOL bWritable = FALSE;
2253 if ((m_pwndBottomView)&&(m_pwndBottomView->IsWritable()))
2254 bWritable = TRUE;
2255 else if ((m_pwndRightView)&&(m_pwndRightView->IsWritable()))
2256 bWritable = TRUE;
2257 else if ((m_pwndLeftView)&&(m_pwndLeftView->IsWritable()))
2258 bWritable = TRUE;
2259 pCmdUI->Enable(bWritable && ::IsClipboardFormatAvailable(CF_TEXT));
2262 void CMainFrame::OnViewSwitchleft()
2264 if (CheckForSave(CHFSR_SWITCH)!=IDCANCEL)
2266 CWorkingFile file = m_Data.m_baseFile;
2267 m_Data.m_baseFile = m_Data.m_yourFile;
2268 m_Data.m_yourFile = file;
2269 if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_yourFile.GetFilename())==0)
2271 m_Data.m_mergedFile = m_Data.m_baseFile;
2273 else if (m_Data.m_mergedFile.GetFilename().CompareNoCase(m_Data.m_baseFile.GetFilename())==0)
2275 m_Data.m_mergedFile = m_Data.m_yourFile;
2277 LoadViews();
2281 void CMainFrame::OnUpdateViewSwitchleft(CCmdUI *pCmdUI)
2283 BOOL bEnable = !IsViewGood(m_pwndBottomView);
2284 pCmdUI->Enable(bEnable);
2287 void CMainFrame::OnUpdateViewShowfilelist(CCmdUI *pCmdUI)
2289 BOOL bEnable = m_dlgFilePatches.HasFiles();
2290 pCmdUI->Enable(bEnable);
2291 pCmdUI->SetCheck(m_dlgFilePatches.IsWindowVisible());
2294 void CMainFrame::OnViewShowfilelist()
2296 m_dlgFilePatches.ShowWindow(m_dlgFilePatches.IsWindowVisible() ? SW_HIDE : SW_SHOW);
2299 void CMainFrame::OnEditUndo()
2301 if (CUndo::GetInstance().CanUndo())
2303 CUndo::GetInstance().Undo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2307 void CMainFrame::OnUpdateEditUndo(CCmdUI *pCmdUI)
2309 pCmdUI->Enable(CUndo::GetInstance().CanUndo());
2312 void CMainFrame::OnEditRedo()
2314 if (CUndo::GetInstance().CanRedo())
2316 CUndo::GetInstance().Redo(m_pwndLeftView, m_pwndRightView, m_pwndBottomView);
2320 void CMainFrame::OnUpdateEditRedo(CCmdUI *pCmdUI)
2322 pCmdUI->Enable(CUndo::GetInstance().CanRedo());
2325 void CMainFrame::OnEditEnable()
2327 CBaseView * pView = GetActiveBaseView();
2328 if (pView && pView->IsReadonlyChangable())
2330 bool isReadOnly = pView->IsReadonly();
2331 pView->SetReadonly(!isReadOnly);
2335 void CMainFrame::OnUpdateEditEnable(CCmdUI *pCmdUI)
2337 CBaseView * pView = GetActiveBaseView();
2338 if (pView)
2340 pCmdUI->Enable(pView->IsReadonlyChangable() || !pView->IsReadonly());
2341 pCmdUI->SetCheck(!pView->IsReadonly());
2343 else
2345 pCmdUI->Enable(FALSE);
2349 void CMainFrame::OnIndicatorLeftview()
2351 if (m_bUseRibbons)
2352 return;
2353 if (IsViewGood(m_pwndLeftView))
2355 m_pwndLeftView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_LEFTVIEW);
2359 void CMainFrame::OnIndicatorRightview()
2361 if (m_bUseRibbons)
2362 return;
2363 if (IsViewGood(m_pwndRightView))
2365 m_pwndRightView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_RIGHTVIEW);
2369 void CMainFrame::OnIndicatorBottomview()
2371 if (m_bUseRibbons)
2372 return;
2373 if (IsViewGood(m_pwndBottomView))
2375 m_pwndBottomView->AskUserForNewLineEndingsAndTextType(IDS_STATUSBAR_BOTTOMVIEW);
2379 int CMainFrame::CheckForReload()
2381 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
2382 if (bLock)
2384 return IDNO;
2386 bLock = true;
2387 bool bSourceChanged =
2388 m_Data.m_baseFile.HasSourceFileChanged()
2389 || m_Data.m_yourFile.HasSourceFileChanged()
2390 || m_Data.m_theirFile.HasSourceFileChanged()
2391 /*|| m_Data.m_mergedFile.HasSourceFileChanged()*/;
2392 if (!bSourceChanged)
2394 bLock = false;
2395 return IDNO;
2398 CString msg = HasUnsavedEdits() ? CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDELOOSECHANGES)) : CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE));
2399 CTaskDialog taskdlg(msg,
2400 CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK2)),
2401 L"TortoiseGitMerge",
2403 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2404 CString sTask3;
2405 if (HasUnsavedEdits())
2406 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK3);
2407 else
2408 sTask3.LoadString(IDS_WARNMODIFIEDOUTSIDE_TASK4);
2409 taskdlg.AddCommandControl(IDYES, sTask3);
2410 taskdlg.AddCommandControl(IDNO, CString(MAKEINTRESOURCE(IDS_WARNMODIFIEDOUTSIDE_TASK5)));
2411 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2412 taskdlg.SetDefaultCommandControl(IDYES);
2413 taskdlg.SetMainIcon(TD_WARNING_ICON);
2414 UINT ret = (UINT)taskdlg.DoModal(m_hWnd);
2415 if (ret == IDYES)
2417 CDiffColors::GetInstance().LoadRegistry();
2418 LoadViews(-1);
2420 else
2422 if (IsViewGood(m_pwndBottomView)) // three pane view
2424 /*if (m_Data.m_sourceFile.HasSourceFileChanged())
2425 m_pwndBottomView->SetModified();
2426 if (m_Data.m_mergedFile.HasSourceFileChanged())
2427 m_pwndBottomView->SetModified();//*/
2428 if (m_Data.m_yourFile.HasSourceFileChanged())
2429 m_pwndRightView->SetModified();
2430 if (m_Data.m_theirFile.HasSourceFileChanged())
2431 m_pwndLeftView->SetModified();
2433 else if (IsViewGood(m_pwndRightView)) // two pane view
2435 if (m_Data.m_baseFile.HasSourceFileChanged())
2436 m_pwndLeftView->SetModified();
2437 if (m_Data.m_yourFile.HasSourceFileChanged())
2438 m_pwndRightView->SetModified();
2440 else
2442 if (m_Data.m_yourFile.HasSourceFileChanged())
2443 m_pwndLeftView->SetModified();
2446 // no reload just store updated file time
2447 m_Data.m_baseFile.StoreFileAttributes();
2448 m_Data.m_theirFile.StoreFileAttributes();
2449 m_Data.m_yourFile.StoreFileAttributes();
2450 //m_Data.m_mergedFile.StoreFileAttributes();
2452 bLock = false;
2453 return ret;
2456 int CMainFrame::CheckForSave(ECheckForSaveReason eReason)
2458 int idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2459 int idNoSave = IDS_ASKFORSAVE_TASK7;
2460 int idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2461 switch (eReason)
2463 case CHFSR_CLOSE:
2464 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2465 idNoSave = IDS_ASKFORSAVE_TASK4;
2466 idCancelAction = IDS_ASKFORSAVE_TASK5;
2467 break;
2468 case CHFSR_SWITCH:
2469 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2470 //idNoSave = IDS_ASKFORSAVE_TASK7;
2471 idCancelAction = IDS_ASKFORSAVE_TASK8;
2472 break;
2473 case CHFSR_RELOAD:
2474 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2475 //idNoSave = IDS_ASKFORSAVE_TASK7;
2476 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2477 break;
2478 case CHFSR_OPTIONS:
2479 idTitle = IDS_WARNMODIFIEDLOOSECHANGESOPTIONS;
2480 //idNoSave = IDS_ASKFORSAVE_TASK7;
2481 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPTIONS;
2482 break;
2483 case CHFSR_OPEN:
2484 //idTitle = IDS_WARNMODIFIEDLOOSECHANGES;
2485 idNoSave = IDS_ASKFORSAVE_NOSAVE_OPEN;
2486 idCancelAction = IDS_ASKFORSAVE_CANCEL_OPEN;
2487 break;
2490 CString sTitle(MAKEINTRESOURCE(idTitle));
2491 CString sSubTitle(MAKEINTRESOURCE(IDS_ASKFORSAVE_TASK2));
2492 CString sNoSave(MAKEINTRESOURCE(idNoSave));
2493 CString sCancelAction(MAKEINTRESOURCE(idCancelAction));
2494 CString sAppName(MAKEINTRESOURCE(IDS_APPNAME));
2496 // TODO simplify logic, reduce code duplication
2497 if (CBaseView::IsViewGood(m_pwndBottomView))
2499 // three-way diff - by design only bottom can be changed
2500 // use 1.7 way to do that
2502 else if (CBaseView::IsViewGood(m_pwndRightView))
2504 // two-way diff -
2505 // 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
2506 if (HasUnsavedEdits(m_pwndLeftView))
2508 // both views
2509 CTaskDialog taskdlg(sTitle,
2510 sSubTitle,
2511 sAppName,
2513 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2514 CString sTaskTemp;
2515 if (m_pwndLeftView->m_pWorkingFile->InUse() && !m_pwndLeftView->m_pWorkingFile->IsReadonly())
2516 sTaskTemp.Format(IDS_ASKFORSAVE_SAVELEFT, (LPCTSTR)m_pwndLeftView->m_pWorkingFile->GetFilename());
2517 else
2518 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVELEFTAS));
2519 taskdlg.AddCommandControl(201, sTaskTemp); // left
2520 taskdlg.SetDefaultCommandControl(201);
2521 if (HasUnsavedEdits(m_pwndRightView))
2523 if (m_pwndRightView->m_pWorkingFile->InUse() && !m_pwndRightView->m_pWorkingFile->IsReadonly())
2524 sTaskTemp.Format(IDS_ASKFORSAVE_SAVERIGHT, (LPCTSTR)m_pwndRightView->m_pWorkingFile->GetFilename());
2525 else
2526 sTaskTemp = CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVERIGHTAS));
2527 taskdlg.AddCommandControl(202, sTaskTemp); // right
2528 taskdlg.AddCommandControl(203, CString(MAKEINTRESOURCE(IDS_ASKFORSAVE_SAVEALL2))); // both
2529 taskdlg.SetDefaultCommandControl(203);
2531 taskdlg.AddCommandControl(IDNO, sNoSave); // none
2532 taskdlg.AddCommandControl(IDCANCEL, sCancelAction); // cancel
2533 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2534 taskdlg.SetMainIcon(TD_WARNING_ICON);
2535 UINT ret = (UINT)taskdlg.DoModal(m_hWnd);
2536 switch (ret)
2538 case 201: // left
2539 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2540 break;
2541 case 203: // both
2542 m_pwndLeftView->SaveFile(SAVE_REMOVEDLINES);
2543 case 202: // right
2544 m_pwndRightView->SaveFile();
2545 break;
2547 if (ret != IDCANCEL && (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN))
2548 DeleteBaseTheirsMineOnClose();
2549 return ret;
2551 else
2553 // only secondary (left) view
2555 // only right view - 1.7 implementation is used
2557 else if (CBaseView::IsViewGood(m_pwndLeftView))
2559 // only one view - only one to save
2560 // 1.7 FileSave don't support this mode
2561 if (HasUnsavedEdits(m_pwndLeftView))
2563 CTaskDialog taskdlg(sTitle,
2564 sSubTitle,
2565 sAppName,
2567 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2568 CString sTask3;
2569 if (m_Data.m_mergedFile.InUse())
2570 sTask3.Format(IDS_ASKFORSAVE_TASK3, (LPCTSTR)m_Data.m_mergedFile.GetFilename());
2571 else
2572 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2573 taskdlg.AddCommandControl(IDYES, sTask3);
2574 taskdlg.AddCommandControl(IDNO, sNoSave);
2575 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2576 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2577 taskdlg.SetDefaultCommandControl(IDYES);
2578 taskdlg.SetMainIcon(TD_WARNING_ICON);
2579 if ((UINT)taskdlg.DoModal(m_hWnd) == IDYES)
2581 if (m_pwndLeftView->SaveFile()<0)
2582 return IDCANCEL;
2585 if (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN)
2586 DeleteBaseTheirsMineOnClose();
2587 return IDNO;
2589 else
2591 if (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN)
2592 DeleteBaseTheirsMineOnClose();
2593 return IDNO; // nothing to save
2596 // 1.7 implementation
2597 UINT ret = IDNO;
2598 if (HasUnsavedEdits())
2600 CTaskDialog taskdlg(sTitle,
2601 sSubTitle,
2602 sAppName,
2604 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2605 CString sTask3;
2606 if (m_Data.m_mergedFile.InUse())
2607 sTask3.Format(IDS_ASKFORSAVE_TASK3, (LPCTSTR)m_Data.m_mergedFile.GetFilename());
2608 else
2609 sTask3.LoadString(IDS_ASKFORSAVE_TASK6);
2610 taskdlg.AddCommandControl(IDYES, sTask3);
2611 taskdlg.AddCommandControl(IDNO, sNoSave);
2612 taskdlg.AddCommandControl(IDCANCEL, sCancelAction);
2613 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2614 taskdlg.SetDefaultCommandControl(IDYES);
2615 taskdlg.SetMainIcon(TD_WARNING_ICON);
2616 ret = (UINT)taskdlg.DoModal(m_hWnd);
2618 if (ret == IDYES)
2620 if (!FileSave())
2621 ret = IDCANCEL;
2625 if (ret != IDCANCEL && (eReason == CHFSR_CLOSE || eReason == CHFSR_OPEN))
2626 DeleteBaseTheirsMineOnClose();
2628 return ret;
2631 void CMainFrame::DeleteBaseTheirsMineOnClose()
2633 if (!m_bDeleteBaseTheirsMineOnClose)
2634 return;
2636 m_bDeleteBaseTheirsMineOnClose = false;
2638 DeleteFile(m_Data.m_baseFile.GetFilename());
2639 DeleteFile(m_Data.m_theirFile.GetFilename());
2640 DeleteFile(m_Data.m_yourFile.GetFilename());
2643 bool CMainFrame::HasUnsavedEdits() const
2645 return HasUnsavedEdits(m_pwndBottomView) || HasUnsavedEdits(m_pwndRightView) || m_bSaveRequired;
2648 bool CMainFrame::HasUnsavedEdits(const CBaseView* view)
2650 if (!CBaseView::IsViewGood(view))
2651 return false;
2652 return view->IsModified();
2655 bool CMainFrame::HasMarkedBlocks() const
2657 return CBaseView::IsViewGood(m_pwndRightView) && m_pwndRightView->HasMarkedBlocks();
2660 bool CMainFrame::IsViewGood(const CBaseView* view)
2662 return CBaseView::IsViewGood(view);
2665 void CMainFrame::OnViewInlinediffword()
2667 m_bInlineWordDiff = !m_bInlineWordDiff;
2668 if (m_pwndLeftView)
2670 m_pwndLeftView->SetInlineWordDiff(m_bInlineWordDiff);
2671 m_pwndLeftView->BuildAllScreen2ViewVector();
2672 m_pwndLeftView->DocumentUpdated();
2674 if (m_pwndRightView)
2676 m_pwndRightView->SetInlineWordDiff(m_bInlineWordDiff);
2677 m_pwndRightView->BuildAllScreen2ViewVector();
2678 m_pwndRightView->DocumentUpdated();
2680 if (m_pwndBottomView)
2682 m_pwndBottomView->SetInlineWordDiff(m_bInlineWordDiff);
2683 m_pwndBottomView->BuildAllScreen2ViewVector();
2684 m_pwndBottomView->DocumentUpdated();
2686 m_wndLineDiffBar.DocumentUpdated();
2689 void CMainFrame::OnUpdateViewInlinediffword(CCmdUI *pCmdUI)
2691 pCmdUI->Enable(m_bInlineDiff && IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2692 pCmdUI->SetCheck(m_bInlineWordDiff);
2695 void CMainFrame::OnViewInlinediff()
2697 m_bInlineDiff = !m_bInlineDiff;
2698 if (m_pwndLeftView)
2700 m_pwndLeftView->SetInlineDiff(m_bInlineDiff);
2701 m_pwndLeftView->BuildAllScreen2ViewVector();
2702 m_pwndLeftView->DocumentUpdated();
2704 if (m_pwndRightView)
2706 m_pwndRightView->SetInlineDiff(m_bInlineDiff);
2707 m_pwndRightView->BuildAllScreen2ViewVector();
2708 m_pwndRightView->DocumentUpdated();
2710 if (m_pwndBottomView)
2712 m_pwndBottomView->SetInlineDiff(m_bInlineDiff);
2713 m_pwndBottomView->BuildAllScreen2ViewVector();
2714 m_pwndBottomView->DocumentUpdated();
2716 m_wndLineDiffBar.DocumentUpdated();
2719 void CMainFrame::OnUpdateViewInlinediff(CCmdUI *pCmdUI)
2721 pCmdUI->Enable(IsViewGood(m_pwndLeftView) && IsViewGood(m_pwndRightView));
2722 pCmdUI->SetCheck(m_bInlineDiff);
2725 void CMainFrame::OnUpdateEditCreateunifieddifffile(CCmdUI *pCmdUI)
2727 // "create unified diff file" is only available if two files
2728 // are diffed, not three.
2729 bool bEnabled = true;
2730 if (!IsViewGood(m_pwndLeftView))
2731 bEnabled = false;
2732 else if (!IsViewGood(m_pwndRightView))
2733 bEnabled = false;
2734 else if (IsViewGood(m_pwndBottomView)) //no negation here
2735 bEnabled = false;
2736 pCmdUI->Enable(bEnabled);
2739 void CMainFrame::OnEditCreateunifieddifffile()
2741 CString origFile, modifiedFile;
2742 CString origReflected, modifiedReflected;
2743 // the original file is the one on the left
2744 if (m_pwndLeftView)
2746 origFile = m_pwndLeftView->m_sFullFilePath;
2747 origReflected = m_pwndLeftView->m_sReflectedName;
2749 if (m_pwndRightView)
2751 modifiedFile = m_pwndRightView->m_sFullFilePath;
2752 modifiedReflected = m_pwndRightView->m_sReflectedName;
2754 if (origFile.IsEmpty() || modifiedFile.IsEmpty())
2755 return;
2757 CString outputFile;
2758 if(!TryGetFileName(outputFile))
2759 return;
2761 CRegStdDWORD regContextLines(L"Software\\TortoiseGitMerge\\ContextLines", (DWORD)-1);
2762 CAppUtils::CreateUnifiedDiff(origFile, modifiedFile, outputFile, regContextLines, true);
2763 // from here a hacky solution exchanges the paths included in the patch, see issue #2541
2764 if (origReflected.IsEmpty() || modifiedReflected.IsEmpty())
2765 return;
2766 CString projectDir1, projectDir2;
2767 if (!GitAdminDir::HasAdminDir(origReflected, &projectDir1) || !GitAdminDir::HasAdminDir(modifiedReflected, &projectDir2) || projectDir1 != projectDir2)
2768 return;
2769 CStringA origReflectedA = CUnicodeUtils::GetUTF8(origReflected.Mid(projectDir1.GetLength() + 1));
2770 CStringA modifiedReflectedA = CUnicodeUtils::GetUTF8(modifiedReflected.Mid(projectDir1.GetLength() + 1));
2771 origReflectedA.Replace('\\', '/');
2772 modifiedReflectedA.Replace('\\', '/');
2775 CStdioFile file;
2776 // w/o typeBinary for some files \r gets dropped
2777 if (!file.Open(outputFile, CFile::typeBinary | CFile::modeReadWrite | CFile::shareExclusive))
2778 return;
2780 CStringA filecontent;
2781 UINT filelength = (UINT)file.GetLength();
2782 int bytesread = (int)file.Read(filecontent.GetBuffer(filelength), filelength);
2783 filecontent.ReleaseBuffer(bytesread);
2785 if (!CStringUtils::StartsWith(filecontent, "diff --git "))
2786 return;
2787 int lineend = filecontent.Find("\n");
2788 if (lineend <= (int)strlen("diff --git "))
2789 return;
2790 CStringA newStart = "diff --git \"a/" + origReflectedA + "\" \"b/" + modifiedReflectedA + "\"\n";
2791 if (!CStringUtils::StartsWith(filecontent.GetBuffer() + lineend, "\nindex "))
2792 return;
2793 int nextlineend = filecontent.Find("\n", lineend + 1);
2794 if (nextlineend <= lineend)
2795 return;
2796 newStart += filecontent.Mid(lineend + 1, nextlineend - lineend);
2797 lineend = filecontent.Find("\n--- ", nextlineend);
2798 nextlineend = filecontent.Find("\n@@ ", lineend);
2799 if (nextlineend <= lineend)
2800 return;
2801 newStart += "--- \"a/" + origReflectedA + "\"\n+++ \"b/" + modifiedReflectedA + "\"";
2802 filecontent = newStart + filecontent.Mid(nextlineend);
2803 file.SeekToBegin();
2804 file.Write(filecontent, (UINT)filecontent.GetLength());
2805 file.SetLength(file.GetPosition());
2806 file.Close();
2808 catch (CFileException*)
2813 void CMainFrame::OnUpdateViewLinediffbar(CCmdUI *pCmdUI)
2815 pCmdUI->SetCheck(m_bLineDiff);
2816 pCmdUI->Enable();
2819 void CMainFrame::OnViewLinediffbar()
2821 m_bLineDiff = !m_bLineDiff;
2822 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2823 m_wndLineDiffBar.DocumentUpdated();
2824 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2825 m_wndLocatorBar.DocumentUpdated();
2828 void CMainFrame::OnUpdateViewLocatorbar(CCmdUI *pCmdUI)
2830 pCmdUI->SetCheck(m_bLocatorBar);
2831 pCmdUI->Enable();
2834 void CMainFrame::OnUpdateViewBars(CCmdUI * pCmdUI)
2836 pCmdUI->Enable();
2839 void CMainFrame::OnViewLocatorbar()
2841 m_bLocatorBar = !m_bLocatorBar;
2842 m_wndLocatorBar.ShowPane(m_bLocatorBar, false, true);
2843 m_wndLocatorBar.DocumentUpdated();
2844 m_wndLineDiffBar.ShowPane(m_bLineDiff, false, true);
2845 m_wndLineDiffBar.DocumentUpdated();
2848 void CMainFrame::OnViewComparewhitespaces()
2850 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
2851 return;
2852 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2853 regIgnoreWS = 0;
2854 LoadViews(-1);
2857 void CMainFrame::OnUpdateViewComparewhitespaces(CCmdUI *pCmdUI)
2859 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2860 DWORD dwIgnoreWS = regIgnoreWS;
2861 pCmdUI->SetCheck(dwIgnoreWS == 0);
2864 void CMainFrame::OnViewIgnorewhitespacechanges()
2866 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
2867 return;
2868 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2869 regIgnoreWS = 2;
2870 LoadViews(-1);
2873 void CMainFrame::OnUpdateViewIgnorewhitespacechanges(CCmdUI *pCmdUI)
2875 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2876 DWORD dwIgnoreWS = regIgnoreWS;
2877 pCmdUI->SetCheck(dwIgnoreWS == 2);
2880 void CMainFrame::OnViewIgnoreallwhitespacechanges()
2882 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
2883 return;
2884 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2885 regIgnoreWS = 1;
2886 LoadViews(-1);
2889 void CMainFrame::OnUpdateViewIgnoreallwhitespacechanges(CCmdUI *pCmdUI)
2891 CRegDWORD regIgnoreWS(L"Software\\TortoiseGitMerge\\IgnoreWS");
2892 DWORD dwIgnoreWS = regIgnoreWS;
2893 pCmdUI->SetCheck(dwIgnoreWS == 1);
2896 void CMainFrame::OnViewMovedBlocks()
2898 m_bViewMovedBlocks = !(DWORD)m_regViewModedBlocks;
2899 m_regViewModedBlocks = m_bViewMovedBlocks;
2900 LoadViews(-1);
2903 void CMainFrame::OnUpdateViewMovedBlocks(CCmdUI *pCmdUI)
2905 pCmdUI->SetCheck(m_bViewMovedBlocks);
2906 BOOL bEnable = TRUE;
2907 if (IsViewGood(m_pwndBottomView))
2909 bEnable = FALSE;
2911 pCmdUI->Enable(bEnable);
2914 bool CMainFrame::HasConflictsWontKeep()
2916 const int nConflictLine = CheckResolved();
2917 if (nConflictLine < 0)
2918 return false;
2919 if (!m_pwndBottomView)
2920 return false;
2922 CString sTemp;
2923 sTemp.Format(IDS_ERR_MAINFRAME_FILEHASCONFLICTS, m_pwndBottomView->m_pViewData->GetLineNumber(nConflictLine)+1);
2924 CTaskDialog taskdlg(sTemp,
2925 CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK2)),
2926 L"TortoiseGitMerge",
2928 TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW);
2929 taskdlg.AddCommandControl(1, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK3)));
2930 taskdlg.AddCommandControl(2, CString(MAKEINTRESOURCE(IDS_ERR_MAINFRAME_FILEHASCONFLICTS_TASK4)));
2931 taskdlg.SetCommonButtons(TDCBF_CANCEL_BUTTON);
2932 taskdlg.SetDefaultCommandControl(2);
2933 taskdlg.SetMainIcon(TD_ERROR_ICON);
2934 if (taskdlg.DoModal(m_hWnd) == 1)
2935 return false;
2937 m_pwndBottomView->GoToLine(nConflictLine);
2938 return true;
2941 bool CMainFrame::TryGetFileName(CString& result)
2943 return CCommonAppUtils::FileOpenSave(result, nullptr, IDS_SAVEASTITLE, IDS_COMMONFILEFILTER, false, m_hWnd);
2946 CBaseView* CMainFrame::GetActiveBaseView() const
2948 CView* activeView = GetActiveView();
2949 CBaseView* activeBase = dynamic_cast<CBaseView*>( activeView );
2950 return activeBase;
2953 void CMainFrame::SetWindowTitle()
2955 // try to find a suitable window title
2956 CString sYour = m_Data.m_yourFile.GetDescriptiveName();
2957 if (sYour.Find(L" - ") >= 0)
2958 sYour = sYour.Left(sYour.Find(L" - "));
2959 if (sYour.Find(L" : ") >= 0)
2960 sYour = sYour.Left(sYour.Find(L" : "));
2961 CString sTheir = m_Data.m_theirFile.GetDescriptiveName();
2962 if (sTheir.IsEmpty())
2963 sTheir = m_Data.m_baseFile.GetDescriptiveName();
2964 if (sTheir.Find(L" - ") >= 0)
2965 sTheir = sTheir.Left(sTheir.Find(L" - "));
2966 if (sTheir.Find(L" : ") >= 0)
2967 sTheir = sTheir.Left(sTheir.Find(L" : "));
2969 if (!sYour.IsEmpty() && !sTheir.IsEmpty())
2971 if (sYour.CompareNoCase(sTheir)==0)
2972 SetWindowText(sYour + L" - TortoiseGitMerge");
2973 else if ((sYour.GetLength() < 10) &&
2974 (sTheir.GetLength() < 10))
2975 SetWindowText(sYour + L" - " + sTheir + L" - TortoiseGitMerge");
2976 else
2978 // we have two very long descriptive texts here, which
2979 // means we have to find a way to use them as a window
2980 // title in a shorter way.
2981 // for simplicity, we just use the one from "yourfile"
2982 SetWindowText(sYour + L" - TortoiseGitMerge");
2985 else if (!sYour.IsEmpty())
2986 SetWindowText(sYour + L" - TortoiseGitMerge");
2987 else if (!sTheir.IsEmpty())
2988 SetWindowText(sTheir + L" - TortoiseGitMerge");
2989 else
2990 SetWindowText(L"TortoiseGitMerge");
2993 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
2995 switch (nIDEvent)
2997 case IDT_RELOADCHECKTIMER:
2998 KillTimer(nIDEvent);
2999 CheckForReload();
3000 break;
3003 __super::OnTimer(nIDEvent);
3006 void CMainFrame::LoadIgnoreCommentData()
3008 static bool bLoaded = false;
3009 if (bLoaded)
3010 return;
3011 CString sPath = CPathUtils::GetAppDataDirectory() + L"ignorecomments.txt";
3012 if (!PathFileExists(sPath))
3014 // ignore comments file does not exist (yet), so create a default one
3015 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_IGNORECOMMENTSTXT), L"config");
3016 if (hRes)
3018 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3019 if (hResourceLoaded)
3021 char * lpResLock = (char *) LockResource(hResourceLoaded);
3022 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3023 if (lpResLock)
3025 HANDLE hFile = CreateFile(sPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3026 if (hFile != INVALID_HANDLE_VALUE)
3028 DWORD dwWritten = 0;
3029 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3030 CloseHandle(hFile);
3039 CStdioFile file;
3040 if (file.Open(sPath, CFile::modeRead))
3042 CString sLine;
3043 while (file.ReadString(sLine))
3045 int eqpos = sLine.Find('=');
3046 if (eqpos >= 0)
3048 CString sExts = sLine.Left(eqpos);
3049 CString sComments = sLine.Mid(eqpos+1);
3051 int pos = sComments.Find(',');
3052 CString sLineStart = sComments.Left(pos);
3053 pos = sComments.Find(',', pos);
3054 int pos2 = sComments.Find(',', pos+1);
3055 CString sBlockStart = sComments.Mid(pos+1, pos2-pos-1);
3056 CString sBlockEnd = sComments.Mid(pos2+1);
3058 auto commentTuple = std::make_tuple(sLineStart, sBlockStart, sBlockEnd);
3060 pos = 0;
3061 CString temp;
3062 for (;;)
3064 temp = sExts.Tokenize(L",", pos);
3065 if (temp.IsEmpty())
3067 break;
3069 ASSERT(m_IgnoreCommentsMap.find(temp) == m_IgnoreCommentsMap.end());
3070 m_IgnoreCommentsMap[temp] = commentTuple;
3076 catch (CFileException* e)
3078 e->Delete();
3080 bLoaded = true;
3083 void CMainFrame::OnViewIgnorecomments()
3085 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3086 return;
3087 m_regIgnoreComments = !DWORD(m_regIgnoreComments);
3088 LoadViews(-1);
3091 void CMainFrame::OnUpdateViewIgnorecomments(CCmdUI *pCmdUI)
3093 // only enable if we have comments defined for this file extension
3094 CString sExt = CPathUtils::GetFileExtFromPath(m_Data.m_baseFile.GetFilename()).MakeLower();
3095 sExt.TrimLeft(L".");
3096 auto sC = m_IgnoreCommentsMap.find(sExt);
3097 if (sC == m_IgnoreCommentsMap.end())
3099 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_yourFile.GetFilename()).MakeLower();
3100 sExt.TrimLeft(L".");
3101 sC = m_IgnoreCommentsMap.find(sExt);
3102 if (sC == m_IgnoreCommentsMap.end())
3104 sExt = CPathUtils::GetFileExtFromPath(m_Data.m_theirFile.GetFilename()).MakeLower();
3105 sExt.TrimLeft(L".");
3106 sC = m_IgnoreCommentsMap.find(sExt);
3109 pCmdUI->Enable(sC != m_IgnoreCommentsMap.end());
3111 pCmdUI->SetCheck(DWORD(m_regIgnoreComments) != 0);
3115 void CMainFrame::OnRegexfilter(UINT cmd)
3117 if ((cmd == ID_REGEXFILTER)||(cmd == (ID_REGEXFILTER+1)))
3119 CRegexFiltersDlg dlg(this);
3120 dlg.SetIniFile(&m_regexIni);
3121 if (dlg.DoModal() == IDOK)
3123 FILE* pFile = nullptr;
3124 _wfopen_s(&pFile, CPathUtils::GetAppDataDirectory() + L"regexfilters.ini", L"wb");
3125 m_regexIni.SaveFile(pFile);
3126 fclose(pFile);
3128 BuildRegexSubitems();
3130 else
3132 if (cmd == (UINT)m_regexIndex && !m_bUseRibbons)
3134 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3135 return;
3136 m_Data.SetRegexTokens(std::wregex(), L"");
3137 m_regexIndex = -1;
3138 LoadViews(-1);
3140 else if (cmd != (UINT)m_regexIndex)
3142 CSimpleIni::TNamesDepend sections;
3143 m_regexIni.GetAllSections(sections);
3144 int index = ID_REGEXFILTER + 2;
3145 m_regexIndex = -1;
3146 for (const auto& section : sections)
3148 if (cmd == (UINT)index)
3150 if (CheckForSave(CHFSR_OPTIONS)==IDCANCEL)
3151 break;
3154 std::wregex rx(m_regexIni.GetValue(section.pItem, L"regex", L""));
3155 m_Data.SetRegexTokens(rx, m_regexIni.GetValue(section.pItem, L"replace", L""));
3157 catch (std::exception &ex)
3159 MessageBox(L"Regex is invalid!\r\n" + CString(ex.what()));
3161 m_regexIndex = index;
3164 LoadViews(-1);
3166 catch (const std::regex_error& ex)
3168 MessageBox(L"Regexp error caught:\r\n" + CString(ex.what()) + L"\r\nTrying to recover by unsetting it again.");
3169 m_Data.SetRegexTokens(std::wregex(), L"");
3170 m_regexIndex = -1;
3171 LoadViews(-1);
3173 break;
3175 ++index;
3181 void CMainFrame::OnUpdateViewRegexFilter( CCmdUI *pCmdUI )
3183 pCmdUI->Enable();
3184 pCmdUI->SetCheck(pCmdUI->m_nID == (UINT)m_regexIndex);
3187 void CMainFrame::BuildRegexSubitems(CMFCPopupMenu* pMenuPopup)
3189 CString sIniPath = CPathUtils::GetAppDataDirectory() + L"regexfilters.ini";
3190 if (!PathFileExists(sIniPath))
3192 // ini file does not exist (yet), so create a default one
3193 HRSRC hRes = FindResource(nullptr, MAKEINTRESOURCE(IDR_REGEXFILTERINI), L"config");
3194 if (hRes)
3196 HGLOBAL hResourceLoaded = LoadResource(nullptr, hRes);
3197 if (hResourceLoaded)
3199 char * lpResLock = (char *)LockResource(hResourceLoaded);
3200 DWORD dwSizeRes = SizeofResource(nullptr, hRes);
3201 if (lpResLock)
3203 HANDLE hFile = CreateFile(sIniPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
3204 if (hFile != INVALID_HANDLE_VALUE)
3206 DWORD dwWritten = 0;
3207 WriteFile(hFile, lpResLock, dwSizeRes, &dwWritten, nullptr);
3208 CloseHandle(hFile);
3215 m_regexIni.LoadFile(sIniPath);
3216 CSimpleIni::TNamesDepend sections;
3217 m_regexIni.GetAllSections(sections);
3219 if (m_bUseRibbons)
3221 std::list<CNativeRibbonDynamicItemInfo> items;
3222 int cmdIndex = 2;
3223 items.push_back(CNativeRibbonDynamicItemInfo(ID_REGEX_NO_FILTER, CString(MAKEINTRESOURCE(ID_REGEX_NO_FILTER)), IDB_REGEX_FILTER));
3224 for (const auto& section : sections)
3226 items.emplace_back(ID_REGEXFILTER + cmdIndex, section.pItem, IDB_REGEX_FILTER);
3227 cmdIndex++;
3230 m_pRibbonApp->SetItems(ID_REGEXFILTER, items);
3232 else if (pMenuPopup)
3234 int iIndex = -1;
3235 if (!CMFCToolBar::IsCustomizeMode() &&
3236 (iIndex = pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER)) >= 0)
3238 if (!sections.empty())
3239 pMenuPopup->InsertSeparator(iIndex + 1); // insert the separator at the end
3240 int cmdIndex = 2;
3241 for (const auto& section : sections)
3243 pMenuPopup->InsertItem(CMFCToolBarMenuButton(ID_REGEXFILTER + cmdIndex, nullptr, -1, (LPCWSTR)section.pItem), iIndex + cmdIndex);
3244 cmdIndex++;
3250 void CMainFrame::FillEncodingButton( CMFCRibbonButton * pButton, int start )
3252 pButton->SetDefaultCommand(FALSE);
3253 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::ASCII, L"ASCII" ));
3254 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::BINARY, L"BINARY" ));
3255 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_LE, L"UTF-16LE" ));
3256 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_LEBOM, L"UTF-16LE BOM"));
3257 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_BE, L"UTF-16BE" ));
3258 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF16_BEBOM, L"UTF-16BE BOM"));
3259 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF32_LE, L"UTF-32LE" ));
3260 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF32_BE, L"UTF-32BE" ));
3261 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF8, L"UTF-8" ));
3262 pButton->AddSubItem(new CMFCRibbonButton(start + CFileTextLines::UnicodeType::UTF8BOM, L"UTF-8 BOM" ));
3265 void CMainFrame::FillEOLButton( CMFCRibbonButton * pButton, int start )
3267 pButton->SetDefaultCommand(FALSE);
3268 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LF , L"LF" ));
3269 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_CRLF, L"CRLF"));
3270 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LFCR, L"LRCR"));
3271 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_CR , L"CR" ));
3272 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_VT , L"VT" ));
3273 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_FF , L"FF" ));
3274 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_NEL , L"NEL" ));
3275 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_LS , L"LS" ));
3276 pButton->AddSubItem(new CMFCRibbonButton(start + EOL::EOL_PS , L"PS" ));
3279 void CMainFrame::FillTabModeButton(CMFCRibbonButton * pButton, int start)
3281 pButton->SetDefaultCommand(FALSE);
3282 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_NONE , L"Tab"));
3283 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_USESPACES , L"Space"));
3284 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3285 pButton->AddSubItem(new CMFCRibbonButton(start + TABMODE_SMARTINDENT , L"Smart tab char"));
3286 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3287 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON1, L"1"));
3288 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON2, L"2"));
3289 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON4, L"4"));
3290 pButton->AddSubItem(new CMFCRibbonButton(start + TABSIZEBUTTON8, L"8"));
3291 pButton->AddSubItem(new CMFCRibbonSeparator(TRUE));
3292 pButton->AddSubItem(new CMFCRibbonButton(start + ENABLEEDITORCONFIG, L"EditorConfig"));
3295 bool CMainFrame::AdjustUnicodeTypeForLoad(CFileTextLines::UnicodeType& type)
3297 switch (type)
3299 case CFileTextLines::UnicodeType::AUTOTYPE:
3300 case CFileTextLines::UnicodeType::BINARY:
3301 return false;
3303 case CFileTextLines::UnicodeType::ASCII:
3304 case CFileTextLines::UnicodeType::UTF16_LE:
3305 case CFileTextLines::UnicodeType::UTF16_BE:
3306 case CFileTextLines::UnicodeType::UTF32_LE:
3307 case CFileTextLines::UnicodeType::UTF32_BE:
3308 case CFileTextLines::UnicodeType::UTF8:
3309 return true;
3311 case CFileTextLines::UnicodeType::UTF16_LEBOM:
3312 type = CFileTextLines::UnicodeType::UTF16_LE;
3313 return true;
3315 case CFileTextLines::UnicodeType::UTF16_BEBOM:
3316 type = CFileTextLines::UnicodeType::UTF16_BE;
3317 return true;
3319 case CFileTextLines::UnicodeType::UTF8BOM:
3320 type = CFileTextLines::UnicodeType::UTF8;
3321 return true;
3323 return false;
3326 void CMainFrame::OnEncodingLeft( UINT cmd )
3328 if (m_pwndLeftView)
3330 if (GetKeyState(VK_CONTROL) & 0x8000)
3332 // reload with selected encoding
3333 auto saveparams = m_Data.m_arBaseFile.GetSaveParams();
3334 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART);
3335 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3337 m_Data.m_arBaseFile.SetSaveParams(saveparams);
3338 m_Data.m_arBaseFile.KeepEncoding();
3339 LoadViews();
3342 else
3344 m_pwndLeftView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_LEFTENCODINGSTART));
3345 m_pwndLeftView->RefreshViews();
3350 void CMainFrame::OnEncodingRight( UINT cmd )
3352 if (m_pwndRightView)
3354 if (GetKeyState(VK_CONTROL) & 0x8000)
3356 // reload with selected encoding
3357 auto saveparams = m_Data.m_arYourFile.GetSaveParams();
3358 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART);
3359 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3361 m_Data.m_arYourFile.SetSaveParams(saveparams);
3362 m_Data.m_arYourFile.KeepEncoding();
3363 LoadViews();
3366 else
3368 m_pwndRightView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_RIGHTENCODINGSTART));
3369 m_pwndRightView->RefreshViews();
3374 void CMainFrame::OnEncodingBottom( UINT cmd )
3376 if (m_pwndBottomView)
3378 if (GetKeyState(VK_CONTROL) & 0x8000)
3380 // reload with selected encoding
3381 auto saveparams = m_Data.m_arTheirFile.GetSaveParams();
3382 saveparams.m_UnicodeType = CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART);
3383 if (AdjustUnicodeTypeForLoad(saveparams.m_UnicodeType))
3385 m_Data.m_arTheirFile.SetSaveParams(saveparams);
3386 m_Data.m_arTheirFile.KeepEncoding();
3387 LoadViews();
3390 else
3392 m_pwndBottomView->SetTextType(CFileTextLines::UnicodeType(cmd - ID_INDICATOR_BOTTOMENCODINGSTART));
3393 m_pwndBottomView->RefreshViews();
3398 void CMainFrame::OnEOLLeft( UINT cmd )
3400 if (m_pwndLeftView)
3402 m_pwndLeftView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_LEFTEOLSTART));
3403 m_pwndLeftView->RefreshViews();
3407 void CMainFrame::OnEOLRight( UINT cmd )
3409 if (m_pwndRightView)
3411 m_pwndRightView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_RIGHTEOLSTART));
3412 m_pwndRightView->RefreshViews();
3416 void CMainFrame::OnEOLBottom( UINT cmd )
3418 if (m_pwndBottomView)
3420 m_pwndBottomView->ReplaceLineEndings(EOL(cmd-ID_INDICATOR_BOTTOMEOLSTART));
3421 m_pwndBottomView->RefreshViews();
3425 void CMainFrame::OnTabModeLeft( UINT cmd )
3427 OnTabMode(m_pwndLeftView, (int)cmd - ID_INDICATOR_LEFTTABMODESTART);
3430 void CMainFrame::OnTabModeRight( UINT cmd )
3432 OnTabMode(m_pwndRightView, (int)cmd - ID_INDICATOR_RIGHTTABMODESTART);
3435 void CMainFrame::OnTabModeBottom( UINT cmd )
3437 OnTabMode(m_pwndBottomView, (int)cmd - ID_INDICATOR_BOTTOMTABMODESTART);
3440 void CMainFrame::OnTabMode(CBaseView *view, int cmd)
3442 if (!view)
3443 return;
3444 int nTabMode = view->GetTabMode();
3445 if (cmd == TABMODE_NONE || cmd == TABMODE_USESPACES)
3446 view->SetTabMode((nTabMode & (~TABMODE_USESPACES)) | (cmd & TABMODE_USESPACES));
3447 else if (cmd == TABMODE_SMARTINDENT) // Toggle
3448 view->SetTabMode((nTabMode & (~TABMODE_SMARTINDENT)) | ((nTabMode & TABMODE_SMARTINDENT) ? 0 : TABMODE_SMARTINDENT));
3449 else if (cmd == TABSIZEBUTTON1)
3450 view->SetTabSize(1);
3451 else if (cmd == TABSIZEBUTTON2)
3452 view->SetTabSize(2);
3453 else if (cmd == TABSIZEBUTTON4)
3454 view->SetTabSize(4);
3455 else if (cmd == TABSIZEBUTTON8)
3456 view->SetTabSize(8);
3457 else if (cmd == ENABLEEDITORCONFIG)
3458 view->SetEditorConfigEnabled(!view->GetEditorConfigEnabled());
3459 view->RefreshViews();
3462 void CMainFrame::OnUpdateEncodingLeft( CCmdUI *pCmdUI )
3464 if (m_pwndLeftView)
3466 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_LEFTENCODINGSTART) == m_pwndLeftView->GetTextType());
3467 pCmdUI->Enable(m_pwndLeftView->IsWritable() || (GetKeyState(VK_CONTROL)&0x8000));
3469 else
3470 pCmdUI->Enable(FALSE);
3473 void CMainFrame::OnUpdateEncodingRight( CCmdUI *pCmdUI )
3475 if (m_pwndRightView)
3477 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_RIGHTENCODINGSTART) == m_pwndRightView->GetTextType());
3478 pCmdUI->Enable(m_pwndRightView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3480 else
3481 pCmdUI->Enable(FALSE);
3484 void CMainFrame::OnUpdateEncodingBottom( CCmdUI *pCmdUI )
3486 if (m_pwndBottomView)
3488 pCmdUI->SetCheck(CFileTextLines::UnicodeType(pCmdUI->m_nID - ID_INDICATOR_BOTTOMENCODINGSTART) == m_pwndBottomView->GetTextType());
3489 pCmdUI->Enable(m_pwndBottomView->IsWritable() || (GetKeyState(VK_CONTROL) & 0x8000));
3491 else
3492 pCmdUI->Enable(FALSE);
3495 void CMainFrame::OnUpdateEOLLeft( CCmdUI *pCmdUI )
3497 if (m_pwndLeftView)
3499 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_LEFTEOLSTART) == m_pwndLeftView->GetLineEndings());
3500 pCmdUI->Enable(m_pwndLeftView->IsWritable());
3502 else
3503 pCmdUI->Enable(FALSE);
3506 void CMainFrame::OnUpdateEOLRight( CCmdUI *pCmdUI )
3508 if (m_pwndRightView)
3510 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_RIGHTEOLSTART) == m_pwndRightView->GetLineEndings());
3511 pCmdUI->Enable(m_pwndRightView->IsWritable());
3513 else
3514 pCmdUI->Enable(FALSE);
3517 void CMainFrame::OnUpdateEOLBottom( CCmdUI *pCmdUI )
3519 if (m_pwndBottomView)
3521 pCmdUI->SetCheck(EOL(pCmdUI->m_nID - ID_INDICATOR_BOTTOMEOLSTART) == m_pwndBottomView->GetLineEndings());
3522 pCmdUI->Enable(m_pwndBottomView->IsWritable());
3524 else
3525 pCmdUI->Enable(FALSE);
3528 void CMainFrame::OnUpdateTabModeLeft(CCmdUI *pCmdUI)
3530 OnUpdateTabMode(m_pwndLeftView, pCmdUI, ID_INDICATOR_LEFTTABMODESTART);
3533 void CMainFrame::OnUpdateTabModeRight(CCmdUI *pCmdUI)
3535 OnUpdateTabMode(m_pwndRightView, pCmdUI, ID_INDICATOR_RIGHTTABMODESTART);
3538 void CMainFrame::OnUpdateTabModeBottom(CCmdUI *pCmdUI)
3540 OnUpdateTabMode(m_pwndBottomView, pCmdUI, ID_INDICATOR_BOTTOMTABMODESTART);
3543 void CMainFrame::OnUpdateTabMode(CBaseView *view, CCmdUI *pCmdUI, int startid)
3545 if (view)
3547 int cmd = (int)pCmdUI->m_nID - startid;
3548 if (cmd == TABMODE_NONE)
3549 pCmdUI->SetCheck((view->GetTabMode() & TABMODE_USESPACES) == TABMODE_NONE);
3550 else if (cmd == TABMODE_USESPACES)
3551 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_USESPACES);
3552 else if (cmd == TABMODE_SMARTINDENT)
3553 pCmdUI->SetCheck(view->GetTabMode() & TABMODE_SMARTINDENT);
3554 else if (cmd == TABSIZEBUTTON1)
3555 pCmdUI->SetCheck(view->GetTabSize() == 1);
3556 else if (cmd == TABSIZEBUTTON2)
3557 pCmdUI->SetCheck(view->GetTabSize() == 2);
3558 else if (cmd == TABSIZEBUTTON4)
3559 pCmdUI->SetCheck(view->GetTabSize() == 4);
3560 else if (cmd == TABSIZEBUTTON8)
3561 pCmdUI->SetCheck(view->GetTabSize() == 8);
3562 else if (cmd == ENABLEEDITORCONFIG)
3563 pCmdUI->SetCheck(view->GetEditorConfigEnabled());
3564 pCmdUI->Enable(view->IsWritable());
3565 if (cmd == ENABLEEDITORCONFIG)
3566 pCmdUI->Enable(view->IsWritable() && view->GetEditorConfigLoaded());
3568 else
3569 pCmdUI->Enable(FALSE);
3572 BOOL CMainFrame::OnShowPopupMenu(CMFCPopupMenu* pMenuPopup)
3574 __super::OnShowPopupMenu(pMenuPopup);
3576 if (!pMenuPopup)
3577 return TRUE;
3579 int iIndex = -1;
3580 if (!CMFCToolBar::IsCustomizeMode() &&
3581 (iIndex = pMenuPopup->GetMenuBar()->CommandToIndex(ID_REGEXFILTER)) >= 0)
3583 BuildRegexSubitems(pMenuPopup);
3586 return TRUE;
3589 LRESULT CMainFrame::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
3591 __super::OnIdleUpdateCmdUI(wParam, lParam);
3593 if (m_pRibbonApp)
3595 BOOL bDisableIfNoHandler = (BOOL)wParam;
3596 m_pRibbonApp->UpdateCmdUI(bDisableIfNoHandler);
3598 return 0;
3601 void CMainFrame::OnUpdateThreeWayActions(CCmdUI * pCmdUI)
3603 pCmdUI->Enable();
3606 void CMainFrame::OnRegexNoFilter()
3608 if (CheckForSave(CHFSR_OPTIONS) == IDCANCEL)
3609 return;
3610 m_Data.SetRegexTokens(std::wregex(), L"");
3611 m_regexIndex = -1;
3612 LoadViews(-1);
3615 void CMainFrame::OnUpdateRegexNoFilter(CCmdUI * pCmdUI)
3617 pCmdUI->SetCheck(m_regexIndex < 0);