Allow to clear filters with escape
[TortoiseGit.git] / src / TortoiseProc / FileDiffDlg.cpp
blob90a8de9d600e0bd2fae3ad8f0e1fc43c3be9ed46
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
4 // Copyright (C) 2003-2008 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "UnicodeUtils.h"
23 #include "MessageBox.h"
24 #include "AppUtils.h"
25 #include "SysImageList.h"
26 #include "IconMenu.h"
27 #include "StringUtils.h"
28 #include "PathUtils.h"
29 #include "BrowseFolder.h"
30 #include "FileDiffDlg.h"
31 #include "GitDiff.h"
32 #include "LoglistCommonResource.h"
33 #include "LoglistUtils.h"
34 #include "BrowseRefsDlg.h"
35 #include "LogDlg.h"
36 #include "RefLogDlg.h"
37 #include "GitStatusListCtrl.h"
38 #include "FormatMessageWrapper.h"
40 #define ID_COMPARE 1
41 #define ID_BLAME 2
42 #define ID_SAVEAS 3
43 #define ID_EXPORT 4
44 #define ID_CLIPBOARD_PATH 5
45 #define ID_CLIPBOARD_ALL 6
46 #define ID_LOG 7
47 #define ID_GNUDIFFCOMPARE 8
48 #define ID_REVERT1 9
49 #define ID_REVERT2 10
50 #define ID_LOGSUBMODULE 11
52 BOOL CFileDiffDlg::m_bAscending = TRUE;
53 int CFileDiffDlg::m_nSortedColumn = -1;
55 UINT CFileDiffDlg::WM_DISABLEBUTTONS = RegisterWindowMessage(_T("TORTOISEGIT_FILEDIFF_DISABLEBUTTONS"));
56 UINT CFileDiffDlg::WM_DIFFFINISHED = RegisterWindowMessage(_T("TORTOISEGIT_FILEDIFF_DIFFFINISHED"));
58 IMPLEMENT_DYNAMIC(CFileDiffDlg, CResizableStandAloneDialog)
59 CFileDiffDlg::CFileDiffDlg(CWnd* pParent /*=nullptr*/)
60 : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent)
61 , m_bBlame(false)
62 , m_bCancelled(false)
63 , m_nIconFolder(0)
64 , m_bThreadRunning(FALSE)
65 , m_bIgnoreSpaceAtEol(false)
66 , m_bIgnoreSpaceChange(false)
67 , m_bIgnoreAllSpace(false)
68 , m_bIgnoreBlankLines(false)
69 , m_bIsBare(false)
71 m_bLoadingRef=FALSE;
74 CFileDiffDlg::~CFileDiffDlg()
78 void CFileDiffDlg::DoDataExchange(CDataExchange* pDX)
80 CResizableStandAloneDialog::DoDataExchange(pDX);
81 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
82 DDX_Control(pDX, IDC_SWITCHLEFTRIGHT, m_SwitchButton);
83 DDX_Control(pDX, IDC_REV1BTN, m_cRev1Btn);
84 DDX_Control(pDX, IDC_REV2BTN, m_cRev2Btn);
85 DDX_Control(pDX, IDC_FILTER, m_cFilter);
86 DDX_Control(pDX, IDC_REV1EDIT, m_ctrRev1Edit);
87 DDX_Control(pDX, IDC_REV2EDIT, m_ctrRev2Edit);
91 BEGIN_MESSAGE_MAP(CFileDiffDlg, CResizableStandAloneDialog)
92 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
93 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
94 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
95 ON_WM_CONTEXTMENU()
96 ON_WM_SETCURSOR()
97 ON_EN_SETFOCUS(IDC_SECONDURL, &CFileDiffDlg::OnEnSetfocusSecondurl)
98 ON_EN_SETFOCUS(IDC_FIRSTURL, &CFileDiffDlg::OnEnSetfocusFirsturl)
99 ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT, &CFileDiffDlg::OnBnClickedSwitchleftright)
100 ON_NOTIFY(HDN_ITEMCLICK, 0, &CFileDiffDlg::OnHdnItemclickFilelist)
101 ON_BN_CLICKED(IDC_REV1BTN, &CFileDiffDlg::OnBnClickedRev1btn)
102 ON_BN_CLICKED(IDC_REV2BTN, &CFileDiffDlg::OnBnClickedRev2btn)
103 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
104 ON_EN_CHANGE(IDC_FILTER, &CFileDiffDlg::OnEnChangeFilter)
105 ON_WM_TIMER()
106 ON_MESSAGE(ENAC_UPDATE, &CFileDiffDlg::OnEnUpdate)
107 ON_MESSAGE(MSG_REF_LOADED, OnRefLoad)
108 ON_REGISTERED_MESSAGE(WM_DISABLEBUTTONS, OnDisableButtons)
109 ON_REGISTERED_MESSAGE(WM_DIFFFINISHED, OnDiffFinished)
110 ON_BN_CLICKED(IDC_DIFFOPTION, OnBnClickedDiffoption)
111 ON_BN_CLICKED(IDC_LOG, &CFileDiffDlg::OnBnClickedLog)
112 END_MESSAGE_MAP()
115 void CFileDiffDlg::SetDiff(const CTGitPath* path, const GitRev& baseRev1, const GitRev& rev2)
117 if (path)
119 m_path1 = *path;
120 m_path2 = *path;
121 m_sFilter = path->GetGitPathString();
123 m_rev1 = baseRev1;
124 m_rev2 = rev2;
127 void CFileDiffDlg::SetDiff(const CTGitPath* path, const CString &baseRev1, const CString& hash2)
129 if (path)
131 m_path1 = *path;
132 m_path2 = *path;
133 m_sFilter = path->GetGitPathString();
136 BYTE_VECTOR logout;
138 if (baseRev1 == GIT_REV_ZERO)
140 m_rev1.m_CommitHash.Empty();
141 m_rev1.GetSubject().LoadString(IDS_git_DEPTH_WORKING);
143 else
145 if (m_rev1.GetCommit(baseRev1))
146 MessageBox(m_rev1.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
149 logout.clear();
151 if(hash2 == GIT_REV_ZERO)
153 m_rev2.m_CommitHash.Empty();
154 m_rev2.GetSubject().LoadString(IDS_git_DEPTH_WORKING);
156 else
158 if (m_rev2.GetCommit(hash2))
159 MessageBox(m_rev2.GetLastErr(), _T("TortoiseGit"), MB_ICONERROR);
163 void CFileDiffDlg::SetDiff(const CTGitPath* path, const GitRev &baseRev1)
165 if (path)
167 m_path1 = *path;
168 m_path2 = *path;
169 m_sFilter = path->GetGitPathString();
171 m_rev1 = baseRev1;
172 m_rev2.m_CommitHash.Empty();
173 m_rev2.GetSubject().LoadString(IDS_PROC_PREVIOUSVERSION);
175 //this->GetDlgItem()->EnableWindow(FALSE);
178 BOOL CFileDiffDlg::OnInitDialog()
180 CResizableStandAloneDialog::OnInitDialog();
181 CString temp;
183 CString sWindowTitle;
184 GetWindowText(sWindowTitle);
185 CString pathText = g_Git.m_CurrentDir;
186 if (!m_path1.IsEmpty())
187 pathText = g_Git.CombinePath(m_path1);
188 CAppUtils::SetWindowTitle(m_hWnd, pathText, sWindowTitle);
190 this->m_ctrRev1Edit.Init();
191 this->m_ctrRev2Edit.Init();
193 m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);
195 m_cFileList.SetRedraw(false);
196 m_cFileList.DeleteAllItems();
197 DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
198 m_cFileList.SetExtendedStyle(exStyle);
200 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
201 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
203 m_SwitchButton.SetImage((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
204 m_SwitchButton.Invalidate();
206 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
207 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
208 temp.LoadString(IDS_FILEDIFF_FILTERCUE);
209 temp = _T(" ")+temp;
210 m_cFilter.SetCueBanner(temp);
211 if (!m_sFilter.IsEmpty())
212 m_cFilter.SetWindowText(m_sFilter);
214 int c = m_cFileList.GetHeaderCtrl()->GetItemCount() - 1;
215 while (c>=0)
216 m_cFileList.DeleteColumn(c--);
218 temp.LoadString(IDS_FILEDIFF_FILE);
219 m_cFileList.InsertColumn(0, temp);
220 temp.LoadString(IDS_FILEDIFF_EXT);
221 m_cFileList.InsertColumn(1, temp);
222 temp.LoadString(IDS_FILEDIFF_ACTION);
223 m_cFileList.InsertColumn(2, temp);
225 temp.LoadString(IDS_FILEDIFF_STATADD);
226 m_cFileList.InsertColumn(3, temp);
227 temp.LoadString(IDS_FILEDIFF_STATDEL);
228 m_cFileList.InsertColumn(4, temp);
230 int mincol = 0;
231 int maxcol = m_cFileList.GetHeaderCtrl()->GetItemCount() - 1;
232 int col;
233 for (col = mincol; col <= maxcol; col++)
234 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
236 m_cFileList.SetRedraw(true);
238 AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);
239 AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);
240 AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);
241 AddAnchor(IDC_REV1BTN, TOP_RIGHT);
242 //AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);
243 AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);
244 AddAnchor(IDC_REV2BTN, TOP_RIGHT);
245 AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);
246 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
247 AddAnchor(IDC_REV1GROUP,TOP_LEFT,TOP_RIGHT);
248 AddAnchor(IDC_REV2GROUP,TOP_LEFT,TOP_RIGHT);
249 AddAnchor(IDC_REV1EDIT,TOP_LEFT);
250 AddAnchor(IDC_REV2EDIT,TOP_LEFT);
251 AddAnchor(IDC_DIFFOPTION, TOP_RIGHT);
252 AddAnchor(IDC_LOG, TOP_RIGHT);
254 EnableSaveRestore(_T("FileDiffDlg"));
256 m_bIsBare = GitAdminDir::IsBareRepo(g_Git.m_CurrentDir);
258 if(this->m_strRev1.IsEmpty())
259 this->m_ctrRev1Edit.SetWindowText(this->m_rev1.m_CommitHash.ToString());
260 else
262 if (m_rev1.GetCommit(m_strRev1))
264 CString msg;
265 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)m_strRev1);
266 m_cFileList.ShowText(msg + _T("\n") + m_rev1.GetLastErr());
269 this->m_ctrRev1Edit.SetWindowText(m_strRev1);
272 if(this->m_strRev2.IsEmpty())
273 this->m_ctrRev2Edit.SetWindowText(this->m_rev2.m_CommitHash.ToString());
274 else
276 if (m_rev2.GetCommit(m_strRev2))
278 CString msg;
279 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)m_strRev2);
280 m_cFileList.ShowText(msg + _T("\n") + m_rev1.GetLastErr());
283 this->m_ctrRev2Edit.SetWindowText(m_strRev2);
286 SetURLLabels();
288 InterlockedExchange(&m_bThreadRunning, TRUE);
289 if (!AfxBeginThread(DiffThreadEntry, this))
291 InterlockedExchange(&m_bThreadRunning, FALSE);
292 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
295 InterlockedExchange(&m_bLoadingRef, TRUE);
296 if (!AfxBeginThread(LoadRefThreadEntry, this))
298 InterlockedExchange(&m_bLoadingRef, FALSE);
299 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
302 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
303 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
304 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
306 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
307 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
308 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
310 // Start with focus on file list
311 GetDlgItem(IDC_FILELIST)->SetFocus();
313 if(m_rev2.m_CommitHash.IsEmpty())
314 m_SwitchButton.EnableWindow(FALSE);
316 KillTimer(IDT_INPUT);
317 return FALSE;
320 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)
322 return ((CFileDiffDlg*)pVoid)->DiffThread();
325 UINT CFileDiffDlg::DiffThread()
327 SendMessage(WM_DISABLEBUTTONS);
329 if( m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty())
330 g_Git.RefreshGitIndex();
332 g_Git.GetCommitDiffList(m_rev2.m_CommitHash.ToString(), m_rev1.m_CommitHash.ToString(), m_arFileList, m_bIgnoreSpaceAtEol, m_bIgnoreSpaceChange, m_bIgnoreAllSpace, m_bIgnoreBlankLines);
333 Sort();
335 SendMessage(WM_DIFFFINISHED);
337 InterlockedExchange(&m_bThreadRunning, FALSE);
338 return 0;
341 LRESULT CFileDiffDlg::OnDisableButtons(WPARAM, LPARAM)
343 RefreshCursor();
344 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));
345 m_cFileList.DeleteAllItems();
346 m_arFileList.Clear();
347 EnableInputControl(false);
348 return 0;
351 LRESULT CFileDiffDlg::OnDiffFinished(WPARAM, LPARAM)
353 CString sFilterText;
354 m_cFilter.GetWindowText(sFilterText);
355 m_cFileList.SetRedraw(false);
356 Filter(sFilterText);
357 if (!m_arFileList.IsEmpty())
359 // Highlight first entry in file list
360 m_cFileList.SetSelectionMark(0);
361 m_cFileList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
364 int mincol = 0;
365 int maxcol = m_cFileList.GetHeaderCtrl()->GetItemCount() - 1;
366 int col;
367 for (col = mincol; col <= maxcol; ++col)
368 m_cFileList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);
370 m_cFileList.ClearText();
371 if (m_arFileList.IsEmpty())
372 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_COMPAREREV_NODIFF)));
373 m_cFileList.SetRedraw(true);
375 InvalidateRect(nullptr);
376 RefreshCursor();
377 EnableInputControl(true);
378 return 0;
381 int CFileDiffDlg::AddEntry(const CTGitPath * fd)
383 int ret = -1;
384 if (fd)
386 int index = m_cFileList.GetItemCount();
388 int icon_idx = 0;
389 if (fd->IsDirectory())
390 icon_idx = m_nIconFolder;
391 else
392 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());
394 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);
395 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetFileExtension());
396 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->GetActionName());
397 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatAdd);
398 m_cFileList.SetItemText(index, 4, ((CTGitPath*)fd)->m_StatDel);
400 return ret;
403 void CFileDiffDlg::EnableInputControl(bool b)
405 this->m_ctrRev1Edit.EnableWindow(b);
406 this->m_ctrRev2Edit.EnableWindow(b);
407 this->m_cRev1Btn.EnableWindow(b);
408 this->m_cRev2Btn.EnableWindow(b);
409 m_cFilter.EnableWindow(b);
410 m_SwitchButton.EnableWindow(b);
411 GetDlgItem(IDC_LOG)->EnableWindow(b && !(m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty()));
414 void CFileDiffDlg::DoDiff(int selIndex, bool blame)
416 CGitDiff diff;
417 CTGitPath* fd2 = m_arFilteredList[selIndex];
418 CTGitPath* fd1 = fd2;
419 if (m_rev2.m_CommitHash.IsEmpty() && g_Git.IsInitRepos())
421 CGitDiff::DiffNull(fd2, GIT_REV_ZERO);
422 return;
424 if (fd1->m_Action & CTGitPath::LOGACTIONS_ADDED)
426 CGitDiff::DiffNull(fd1, m_rev2.m_CommitHash.ToString(), true);
427 return;
429 if (fd1->m_Action & CTGitPath::LOGACTIONS_DELETED)
431 CGitDiff::DiffNull(fd1, m_rev1.m_CommitHash.ToString(), false);
432 return;
434 if (fd1->m_Action & CTGitPath::LOGACTIONS_REPLACED)
435 fd2 = new CTGitPath(fd1->GetGitOldPathString());
436 diff.Diff(fd1, fd2, m_rev2.m_CommitHash.ToString(), m_rev1.m_CommitHash.ToString(), blame, FALSE);
437 if (fd1 != fd2)
438 delete fd2;
442 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
444 *pResult = 0;
445 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
446 int selIndex = pNMLV->iItem;
447 if (selIndex < 0)
448 return;
449 if (selIndex >= (int)m_arFilteredList.size())
450 return;
452 DoDiff(selIndex, m_bBlame);
455 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
457 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
458 if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())
459 return;
461 CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();
462 if (pGetInfoTip->cchTextMax > path.GetLength())
463 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax - 1);
465 *pResult = 0;
468 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
470 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
471 // Take the default processing unless we set this to something else below.
472 *pResult = CDRF_DODEFAULT;
474 // First thing - check the draw stage. If it's the control's prepaint
475 // stage, then tell Windows we want messages for every item.
477 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
479 *pResult = CDRF_NOTIFYITEMDRAW;
481 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
483 // This is the prepaint stage for an item. Here's where we set the
484 // item's text color. Our return value will tell Windows to draw the
485 // item itself, but it will use the new color we set here.
487 // Tell Windows to paint the control itself.
488 *pResult = CDRF_DODEFAULT;
490 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
492 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)
494 CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];
495 switch (fd->m_Action)
497 case CTGitPath::LOGACTIONS_ADDED:
498 crText = m_colors.GetColor(CColors::Added);
499 break;
500 case CTGitPath::LOGACTIONS_DELETED:
501 crText = m_colors.GetColor(CColors::Deleted);
502 break;
503 case CTGitPath::LOGACTIONS_MODIFIED:
504 crText = m_colors.GetColor(CColors::Modified);
505 break;
506 default:
507 crText = m_colors.GetColor(CColors::PropertyChanged);
508 break;
511 // Store the color back in the NMLVCUSTOMDRAW struct.
512 pLVCD->clrText = crText;
516 UINT CFileDiffDlg::LoadRefThread()
518 g_Git.GetBranchList(m_Reflist, nullptr, CGit::BRANCH_ALL_F);
519 g_Git.GetTagList(m_Reflist);
521 this->PostMessage(MSG_REF_LOADED);
522 InterlockedExchange(&m_bLoadingRef, FALSE);
523 return 0;
526 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)
528 if (!pWnd || pWnd != &m_cFileList)
529 return;
530 if (m_cFileList.GetSelectedCount() == 0)
531 return;
532 // if the context menu is invoked through the keyboard, we have to use
533 // a calculated position on where to anchor the menu on
534 if ((point.x == -1) && (point.y == -1))
536 CRect rect;
537 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);
538 m_cFileList.ClientToScreen(&rect);
539 point = rect.CenterPoint();
541 CIconMenu popup;
542 if (popup.CreatePopupMenu())
544 int firstEntry = -1;
545 POSITION firstPos = m_cFileList.GetFirstSelectedItemPosition();
546 if (firstPos)
547 firstEntry = m_cFileList.GetNextSelectedItem(firstPos);
549 CString menuText;
550 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
551 popup.AppendMenuIcon(ID_GNUDIFFCOMPARE, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
552 popup.AppendMenu(MF_SEPARATOR, NULL);
553 if (!m_bIsBare)
555 if (!m_rev1.m_CommitHash.IsEmpty())
557 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, (LPCTSTR)m_rev1.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
558 popup.AppendMenuIcon(ID_REVERT1, menuText, IDI_REVERT);
560 if (!m_rev2.m_CommitHash.IsEmpty())
562 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, (LPCTSTR)m_rev2.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
563 popup.AppendMenuIcon(ID_REVERT2, menuText, IDI_REVERT);
565 popup.AppendMenu(MF_SEPARATOR, NULL);
567 popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG);
568 if (firstEntry >= 0 && !m_arFilteredList[firstEntry]->IsDirectory())
570 if (!m_bIsBare)
572 popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME);
573 popup.AppendMenu(MF_SEPARATOR, NULL);
575 popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT);
577 else if (firstEntry >= 0)
578 popup.AppendMenuIcon(ID_LOGSUBMODULE, IDS_MENULOGSUBMODULE, IDI_LOG);
579 popup.AppendMenu(MF_SEPARATOR, NULL);
580 popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS);
581 popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
582 popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
584 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this);
585 m_bCancelled = false;
586 switch (cmd)
588 case ID_COMPARE:
590 if (!CheckMultipleDiffs())
591 break;
592 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
593 while (pos)
595 int index = m_cFileList.GetNextSelectedItem(pos);
596 DoDiff(index, false);
599 break;
600 case ID_GNUDIFFCOMPARE:
602 if (!CheckMultipleDiffs())
603 break;
604 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
605 while (pos)
607 CTGitPath *fd2 = m_arFilteredList[m_cFileList.GetNextSelectedItem(pos)];
608 CTGitPath *fd1 = fd2;
609 if (fd1->m_Action & CTGitPath::LOGACTIONS_REPLACED)
610 fd2 = new CTGitPath(fd2->GetGitOldPathString());
611 CAppUtils::StartShowUnifiedDiff(m_hWnd, *fd1, m_rev1.m_CommitHash.ToString(), *fd2, m_rev2.m_CommitHash.ToString(), !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
612 if (fd1 != fd2)
613 delete fd2;
616 break;
617 case ID_REVERT1:
618 RevertSelectedItemToVersion(m_rev1.m_CommitHash.ToString());
619 break;
620 case ID_REVERT2:
621 RevertSelectedItemToVersion(m_rev2.m_CommitHash.ToString());
622 break;
623 case ID_BLAME:
625 if (!CheckMultipleDiffs())
626 break;
627 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
628 while (pos)
630 int index = m_cFileList.GetNextSelectedItem(pos);
631 if (m_arFilteredList[index]->m_Action & CTGitPath::LOGACTIONS_DELETED)
633 if (!m_rev1.m_CommitHash.IsEmpty())
634 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString());
635 continue;
637 if (m_rev2.m_CommitHash.IsEmpty() && (m_arFilteredList[index]->m_Action & CTGitPath::LOGACTIONS_ADDED))
638 continue;
639 if (m_rev2.m_CommitHash.IsEmpty() && (m_arFilteredList[index]->m_Action & CTGitPath::LOGACTIONS_REPLACED))
641 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetGitOldPathString(), m_rev1.m_CommitHash.ToString());
642 continue;
644 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev2.m_CommitHash.ToString());
647 break;
648 case ID_LOG:
649 case ID_LOGSUBMODULE:
651 if (!CheckMultipleDiffs())
652 break;
653 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
654 while (pos)
656 int index = m_cFileList.GetNextSelectedItem(pos);
657 CString sCmd = _T("/command:log");
658 if (sCmd == ID_LOGSUBMODULE)
659 sCmd += _T(" /submodule");
660 sCmd += _T(" /path:\"") + m_arFilteredList[index]->GetWinPathString() + _T("\" ");
661 sCmd += L" /endrev:" + m_rev2.m_CommitHash.ToString();
662 CAppUtils::RunTortoiseGitProc(sCmd);
665 break;
666 case ID_SAVEAS:
668 if (m_cFileList.GetSelectedCount() > 0)
670 CString temp;
671 CTGitPath savePath;
672 CString pathSave;
673 if (!CAppUtils::FileOpenSave(pathSave, nullptr, IDS_REPOBROWSE_SAVEAS, IDS_TEXTFILEFILTER, false, m_hWnd, _T(".txt")))
675 break;
677 savePath = CTGitPath(pathSave);
679 // now open the selected file for writing
682 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
683 if (m_path1.IsEmpty() && m_path2.IsEmpty())
684 temp.Format(IDS_FILEDIFF_CHANGEDLISTINTROROOT, (LPCTSTR)m_rev1.m_CommitHash.ToString(), (LPCTSTR)m_rev2.m_CommitHash.ToString());
685 else
686 temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.m_CommitHash.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.m_CommitHash.ToString());
687 file.WriteString(temp + _T("\r\n"));
688 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
689 while (pos)
691 int index = m_cFileList.GetNextSelectedItem(pos);
692 CTGitPath* fd = m_arFilteredList[index];
693 file.WriteString(fd->GetGitPathString());
694 file.WriteString(_T("\r\n"));
696 file.Close();
698 catch (CFileException* pE)
700 pE->ReportError();
704 break;
705 case ID_CLIPBOARD_PATH:
707 CopySelectionToClipboard();
709 break;
711 case ID_CLIPBOARD_ALL:
713 CopySelectionToClipboard(TRUE);
715 break;
716 case ID_EXPORT:
718 // export all changed files to a folder
719 CBrowseFolder browseFolder;
720 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
721 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK)
723 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
724 while (pos)
726 int index = m_cFileList.GetNextSelectedItem(pos);
727 CTGitPath* fd = m_arFilteredList[index];
728 // we cannot export directories or folders
729 if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory())
730 continue;
731 CPathUtils::MakeSureDirectoryPathExists(m_strExportDir + _T("\\") + fd->GetContainingDirectory().GetWinPathString());
732 CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString();
733 if (m_rev2.m_CommitHash.ToString() == GIT_REV_ZERO)
735 if(!CopyFile(g_Git.CombinePath(fd), filename, false))
737 MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
738 return;
741 else
743 if (g_Git.GetOneFile(m_rev2.m_CommitHash, *fd, filename))
745 CString out;
746 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)fd->GetGitPathString(), (LPCTSTR)m_rev2.m_CommitHash.ToString(), (LPCTSTR)filename);
747 if (CMessageBox::Show(GetSafeHwnd(), g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), L"TortoiseGit", 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
748 return;
755 break;
761 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
763 if (pWnd != &m_cFileList)
764 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
765 if (m_bThreadRunning == 0)
767 HCURSOR hCur = LoadCursor(nullptr, IDC_ARROW);
768 SetCursor(hCur);
769 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
771 HCURSOR hCur = LoadCursor(nullptr, IDC_WAIT);
772 SetCursor(hCur);
773 return TRUE;
776 void CFileDiffDlg::OnEnSetfocusFirsturl()
778 GetDlgItem(IDC_FIRSTURL)->HideCaret();
781 void CFileDiffDlg::OnEnSetfocusSecondurl()
783 GetDlgItem(IDC_SECONDURL)->HideCaret();
786 void CFileDiffDlg::OnBnClickedSwitchleftright()
788 if (m_bThreadRunning)
789 return;
791 #if 0
792 CString sFilterString;
793 m_cFilter.GetWindowText(sFilterString);
795 m_cFileList.SetRedraw(false);
796 m_cFileList.DeleteAllItems();
797 for (int i=0; i<(int)m_arFileList.GetCount(); ++i)
799 CTGitPath fd = m_arFileList[i];
800 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)
801 fd.m_Action = CTGitPath::LOGACTIONS_DELETED;
802 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)
803 fd.m_Action = CTGitPath::LOGACTIONS_ADDED;
804 std::swap(fd.m_StatAdd, fd.m_StatDel);
805 (CTGitPath&)m_arFileList[i] = fd;
807 Filter(sFilterString);
808 #endif
810 m_cFileList.SetRedraw(true);
811 CTGitPath path = m_path1;
812 m_path1 = m_path2;
813 m_path2 = path;
814 GitRev rev = m_rev1;
815 m_rev1 = m_rev2;
816 m_rev2 = rev;
818 CString str1,str2;
819 this->m_ctrRev1Edit.GetWindowText(str1);
820 this->m_ctrRev2Edit.GetWindowText(str2);
822 this->m_ctrRev1Edit.SetWindowText(str2);
823 this->m_ctrRev2Edit.SetWindowText(str1);
825 SetURLLabels();
826 //KillTimer(IDT_INPUT);
829 void CFileDiffDlg::SetURLLabels(int mask)
831 if(mask &0x1)
833 SetDlgItemText(IDC_FIRSTURL, m_rev1.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev1.GetSubject());
834 if (!m_rev1.m_CommitHash.IsEmpty())
835 m_tooltips.AddTool(IDC_FIRSTURL,
836 CLoglistUtils::FormatDateAndTime(m_rev1.GetAuthorDate(), DATE_SHORTDATE) + _T(" ") + m_rev1.GetAuthorName());
839 if(mask &0x2)
841 SetDlgItemText(IDC_SECONDURL,m_rev2.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev2.GetSubject());
842 if (!m_rev2.m_CommitHash.IsEmpty())
843 m_tooltips.AddTool(IDC_SECONDURL,
844 CLoglistUtils::FormatDateAndTime(m_rev2.GetAuthorDate(), DATE_SHORTDATE) + _T(" ") + m_rev2.GetAuthorName());
847 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1BASE)));
848 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2)));
850 if ((mask & 0x3) == 0x3 && !m_rev1.m_CommitHash.IsEmpty() && !m_rev2.m_CommitHash.IsEmpty())
851 if(m_rev1.GetCommitterDate() > m_rev2.GetCommitterDate())
852 GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1BASENEWER)));
853 else if (m_rev1.GetCommitterDate() < m_rev2.GetCommitterDate())
854 GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2NEWER)));
857 void CFileDiffDlg::ClearURLabels(int mask)
859 if(mask&0x1)
861 SetDlgItemText(IDC_FIRSTURL, _T(""));
862 m_tooltips.AddTool(IDC_FIRSTURL, _T(""));
865 if(mask&0x2)
867 SetDlgItemText(IDC_SECONDURL, _T(""));
868 m_tooltips.AddTool(IDC_SECONDURL, _T(""));
871 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)
873 if (pMsg->message == WM_KEYDOWN)
875 switch (pMsg->wParam)
877 case 'A':
879 if (GetFocus() != GetDlgItem(IDC_FILELIST))
880 break;
881 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
883 // select all entries
884 for (int i=0; i<m_cFileList.GetItemCount(); ++i)
885 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
886 return TRUE;
889 break;
890 case 'C':
891 case VK_INSERT:
893 if (GetFocus() != GetDlgItem(IDC_FILELIST))
894 break;
895 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
897 CopySelectionToClipboard();
898 return TRUE;
901 break;
902 case '\r':
904 if (GetFocus() == GetDlgItem(IDC_FILELIST))
906 // Return pressed in file list. Show diff, as for double click
907 int selIndex = m_cFileList.GetSelectionMark();
908 if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))
909 DoDiff(selIndex, m_bBlame);
910 return TRUE;
913 break;
914 case VK_F5:
916 OnTimer(IDT_INPUT);
918 break;
919 case VK_ESCAPE:
920 if (GetFocus() == GetDlgItem(IDC_FILTER) && m_cFilter.GetWindowTextLength())
922 m_cFilter.SetWindowText(L"");
923 OnClickedCancelFilter(NULL, NULL);
924 return TRUE;
926 break;
929 return __super::PreTranslateMessage(pMsg);
932 void CFileDiffDlg::OnCancel()
934 if (m_bThreadRunning)
936 m_bCancelled = true;
937 return;
939 __super::OnCancel();
942 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)
944 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
945 if (m_bThreadRunning)
946 return;
948 if (m_nSortedColumn == phdr->iItem)
949 m_bAscending = !m_bAscending;
950 else
951 m_bAscending = TRUE;
952 m_nSortedColumn = phdr->iItem;
953 Sort();
955 CString temp;
956 m_cFileList.SetRedraw(FALSE);
957 m_cFileList.DeleteAllItems();
958 m_cFilter.GetWindowText(temp);
959 Filter(temp);
961 CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();
962 HDITEM HeaderItem = {0};
963 HeaderItem.mask = HDI_FORMAT;
964 for (int i=0; i<pHeader->GetItemCount(); ++i)
966 pHeader->GetItem(i, &HeaderItem);
967 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
968 pHeader->SetItem(i, &HeaderItem);
970 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
971 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
972 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
974 m_cFileList.SetRedraw(TRUE);
976 *pResult = 0;
979 void CFileDiffDlg::Sort()
981 if(m_arFileList.GetCount() < 2)
982 return;
984 std::sort(m_arFileList.m_paths.begin(), m_arFileList.m_paths.end(), &CFileDiffDlg::SortCompare);
987 bool CFileDiffDlg::SortCompare(const CTGitPath& Data1, const CTGitPath& Data2)
989 int result = 0;
990 int d1, d2;
991 switch (m_nSortedColumn)
993 case 0: //path column
994 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
995 break;
996 case 1: //extension column
997 result = Data1.GetFileExtension().Compare(Data2.GetFileExtension());
998 break;
999 case 2: //action column
1000 result = Data1.m_Action - Data2.m_Action;
1001 break;
1002 case 3:
1003 d1 = CSorter::A2L(Data1.m_StatAdd);
1004 d2 = CSorter::A2L(Data2.m_StatAdd);
1005 result = d1 - d2;
1006 break;
1007 case 4:
1008 d1 = CSorter::A2L(Data1.m_StatDel);;
1009 d2 = CSorter::A2L(Data2.m_StatDel);
1010 result = d1 - d2;
1011 break;
1012 default:
1013 break;
1015 // sort by path name as second priority
1016 if (m_nSortedColumn != 0 && result == 0)
1017 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
1019 if (!m_bAscending)
1020 result = -result;
1021 return result < 0;
1025 void CFileDiffDlg::OnBnClickedRev1btn()
1027 ClickRevButton(&this->m_cRev1Btn,&this->m_rev1, &this->m_ctrRev1Edit);
1030 void CFileDiffDlg::ClickRevButton(CMenuButton *button, GitRev *rev, CACEdit *edit)
1032 INT_PTR entry=button->GetCurrentEntry();
1033 if(entry == 0) /* Browse Refence*/
1036 CString str = CBrowseRefsDlg::PickRef();
1037 if(str.IsEmpty())
1038 return;
1040 if(FillRevFromString(rev,str))
1041 return;
1043 edit->SetWindowText(str);
1047 if(entry == 1) /*Log*/
1049 CLogDlg dlg;
1050 CString revision;
1051 edit->GetWindowText(revision);
1052 dlg.SetParams(CTGitPath(), CTGitPath(), revision, revision, 0);
1053 dlg.SetSelect(true);
1054 if(dlg.DoModal() == IDOK)
1056 if (dlg.GetSelectedHash().empty())
1057 return;
1059 if (FillRevFromString(rev, dlg.GetSelectedHash().at(0).ToString()))
1060 return;
1062 edit->SetWindowText(dlg.GetSelectedHash().at(0).ToString());
1064 else
1065 return;
1068 if(entry == 2) /*RefLog*/
1070 CRefLogDlg dlg;
1071 if(dlg.DoModal() == IDOK)
1073 if(FillRevFromString(rev,dlg.m_SelectedHash))
1074 return;
1076 edit->SetWindowText(dlg.m_SelectedHash);
1078 else
1079 return;
1082 SetURLLabels();
1084 InterlockedExchange(&m_bThreadRunning, TRUE);
1085 if (!AfxBeginThread(DiffThreadEntry, this))
1087 InterlockedExchange(&m_bThreadRunning, FALSE);
1088 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1090 KillTimer(IDT_INPUT);
1093 void CFileDiffDlg::OnBnClickedRev2btn()
1095 ClickRevButton(&this->m_cRev2Btn,&this->m_rev2, &this->m_ctrRev2Edit);
1098 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1100 if (m_bThreadRunning)
1102 SetTimer(IDT_FILTER, 1000, nullptr);
1103 return 0L;
1106 KillTimer(IDT_FILTER);
1108 m_cFileList.SetRedraw(FALSE);
1109 m_arFilteredList.clear();
1110 m_cFileList.DeleteAllItems();
1112 Filter(_T(""));
1114 m_cFileList.SetRedraw(TRUE);
1115 return 0L;
1118 void CFileDiffDlg::OnEnChangeFilter()
1120 SetTimer(IDT_FILTER, 1000, nullptr);
1123 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)
1125 if (m_bThreadRunning)
1126 return;
1128 if( nIDEvent == IDT_FILTER)
1130 CString sFilterText;
1131 KillTimer(IDT_FILTER);
1132 m_cFilter.GetWindowText(sFilterText);
1134 m_cFileList.SetRedraw(FALSE);
1135 m_cFileList.DeleteAllItems();
1137 Filter(sFilterText);
1139 m_cFileList.SetRedraw(TRUE);
1141 __super::OnTimer(nIDEvent);
1144 if( nIDEvent == IDT_INPUT)
1146 KillTimer(IDT_INPUT);
1147 TRACE(_T("Input Timer\r\n"));
1149 GitRev gitrev;
1150 CString str;
1151 int mask = 0;
1152 this->m_ctrRev1Edit.GetWindowText(str);
1153 if (!gitrev.GetCommit(str))
1155 m_rev1 = gitrev;
1156 mask |= 0x1;
1158 else
1160 CString msg;
1161 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)str);
1162 m_cFileList.ShowText(msg + _T("\n") + gitrev.GetLastErr());
1165 this->m_ctrRev2Edit.GetWindowText(str);
1167 if (!gitrev.GetCommit(str))
1169 m_rev2 = gitrev;
1170 mask |= 0x2;
1172 else
1174 CString msg;
1175 msg.Format(IDS_PROC_REFINVALID, (LPCTSTR)str);
1176 m_cFileList.ShowText(msg + _T("\n") + gitrev.GetLastErr());
1179 this->SetURLLabels(mask);
1181 if(mask == 0x3)
1183 InterlockedExchange(&m_bThreadRunning, TRUE);
1184 if (!AfxBeginThread(DiffThreadEntry, this))
1186 InterlockedExchange(&m_bThreadRunning, FALSE);
1187 CMessageBox::Show(GetSafeHwnd(), IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1193 void CFileDiffDlg::Filter(CString sFilterText)
1195 sFilterText.MakeLower();
1197 m_arFilteredList.clear();
1199 for (int i=0;i<m_arFileList.GetCount();i++)
1201 CString sPath = m_arFileList[i].GetGitPathString();
1202 sPath.MakeLower();
1203 if (sPath.Find(sFilterText) >= 0)
1204 m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));
1206 for (const auto path : m_arFilteredList)
1207 AddEntry(path);
1210 void CFileDiffDlg::CopySelectionToClipboard(BOOL isFull)
1212 // copy all selected paths to the clipboard
1213 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1214 int index;
1215 CString sTextForClipboard;
1216 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1218 sTextForClipboard += m_cFileList.GetItemText(index, 0);
1219 sTextForClipboard += _T("\t");
1221 if(!isFull)
1222 sTextForClipboard += _T("\r\n");
1223 else
1225 sTextForClipboard += m_cFileList.GetItemText(index, 1);
1226 sTextForClipboard += _T("\t");
1227 sTextForClipboard += m_cFileList.GetItemText(index, 2);
1228 sTextForClipboard += _T("\t");
1229 sTextForClipboard += m_cFileList.GetItemText(index, 3);
1230 sTextForClipboard += _T("\t");
1231 sTextForClipboard += m_cFileList.GetItemText(index, 4);
1232 sTextForClipboard += _T("\r\n");
1235 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);
1239 LRESULT CFileDiffDlg::OnRefLoad(WPARAM /*wParam*/, LPARAM /*lParam*/)
1241 for (size_t i = 0; i < m_Reflist.size(); ++i)
1243 CString str=m_Reflist[i];
1245 if (CStringUtils::StartsWith(str, L"remotes/"))
1246 str=str.Mid(8);
1248 m_ctrRev1Edit.AddSearchString(str);
1249 m_ctrRev2Edit.AddSearchString(str);
1251 return 0;
1254 BOOL CFileDiffDlg::DestroyWindow()
1256 return CResizableStandAloneDialog::DestroyWindow();
1259 LRESULT CFileDiffDlg::OnEnUpdate(WPARAM /*wParam*/, LPARAM lParam)
1261 if(lParam == IDC_REV1EDIT)
1263 OnTextUpdate(&this->m_ctrRev1Edit);
1264 ClearURLabels(1);
1266 if(lParam == IDC_REV2EDIT)
1268 OnTextUpdate(&this->m_ctrRev2Edit);
1269 ClearURLabels(1<<1);
1271 return 0;
1274 void CFileDiffDlg::OnTextUpdate(CACEdit * /*pEdit*/)
1276 SetTimer(IDT_INPUT, 1000, nullptr);
1277 this->m_cFileList.ShowText(_T("Wait For input validate version"));
1280 int CFileDiffDlg::RevertSelectedItemToVersion(CString rev)
1282 if (rev.IsEmpty() || rev == GIT_REV_ZERO)
1283 return 0;
1285 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1286 int index;
1287 int count = 0;
1288 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1290 CString cmd, out;
1291 CTGitPath *fentry = (CTGitPath *)m_arFilteredList[index];
1292 cmd.Format(_T("git.exe checkout %s -- \"%s\""), (LPCTSTR)rev, (LPCTSTR)fentry->GetGitPathString());
1293 if (g_Git.Run(cmd, &out, CP_UTF8))
1295 if (CMessageBox::Show(GetSafeHwnd(), out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
1296 break;
1298 else
1299 count++;
1302 CString out;
1303 out.Format(IDS_STATUSLIST_FILESREVERTED, count, (LPCTSTR)rev);
1304 CMessageBox::Show(GetSafeHwnd(), out, _T("TortoiseGit"), MB_OK);
1305 return 0;
1308 static void AppendMenuChecked(CMenu &menu, UINT nTextID, UINT_PTR nItemID, BOOL checked = FALSE, BOOL enabled = TRUE)
1310 CString text;
1311 text.LoadString(nTextID);
1312 menu.AppendMenu(MF_STRING | (enabled ? MF_ENABLED : MF_DISABLED) | (checked ? MF_CHECKED : MF_UNCHECKED), nItemID, text);
1315 #define DIFFOPTION_IGNORESPACEATEOL 1
1316 #define DIFFOPTION_IGNORESPACECHANGE 2
1317 #define DIFFOPTION_IGNOREALLSPACE 3
1318 #define DIFFOPTION_IGNORBLANKLINES 4
1320 void CFileDiffDlg::OnBnClickedDiffoption()
1322 CMenu popup;
1323 if (popup.CreatePopupMenu())
1325 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORESPACEATEOL, DIFFOPTION_IGNORESPACEATEOL, m_bIgnoreSpaceAtEol);
1326 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORESPACECHANGE, DIFFOPTION_IGNORESPACECHANGE, m_bIgnoreSpaceChange);
1327 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNOREALLSPACE, DIFFOPTION_IGNOREALLSPACE, m_bIgnoreAllSpace);
1328 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORBLANKLINES, DIFFOPTION_IGNORBLANKLINES, m_bIgnoreBlankLines);
1330 m_tooltips.Pop();
1331 RECT rect;
1332 GetDlgItem(IDC_DIFFOPTION)->GetWindowRect(&rect);
1333 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.bottom, this);
1334 switch (selection)
1336 case DIFFOPTION_IGNORESPACEATEOL:
1337 m_bIgnoreSpaceAtEol = !m_bIgnoreSpaceAtEol;
1338 OnTimer(IDT_INPUT);
1339 break;
1340 case DIFFOPTION_IGNORESPACECHANGE:
1341 m_bIgnoreSpaceChange = !m_bIgnoreSpaceChange;
1342 OnTimer(IDT_INPUT);
1343 break;
1344 case DIFFOPTION_IGNOREALLSPACE:
1345 m_bIgnoreAllSpace = !m_bIgnoreAllSpace;
1346 OnTimer(IDT_INPUT);
1347 break;
1348 case DIFFOPTION_IGNORBLANKLINES:
1349 m_bIgnoreBlankLines = !m_bIgnoreBlankLines;
1350 OnTimer(IDT_INPUT);
1351 break;
1352 default:
1353 break;
1355 UpdateData(FALSE);
1359 void CFileDiffDlg::OnBnClickedLog()
1361 CLogDlg dlg;
1362 dlg.SetRange(m_rev1.m_CommitHash.ToString() + L".." + m_rev2.m_CommitHash.ToString());
1363 dlg.DoModal();
1366 bool CFileDiffDlg::CheckMultipleDiffs()
1368 UINT selCount = m_cFileList.GetSelectedCount();
1369 if (selCount > max(3, (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\NumDiffWarning"), 10)))
1371 CString message;
1372 message.Format(IDS_STATUSLIST_WARN_MAXDIFF, selCount);
1373 return ::MessageBox(GetSafeHwnd(), message, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES;
1375 return true;