No need to beep after displaying Commit Not Visible message
[TortoiseGit.git] / src / TortoiseProc / FileDiffDlg.cpp
blob7ba9cdf6431a90de472b2a650bba146d2272cc7b
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2014 - 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 /*=NULL*/)
60 : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent)
61 , m_bBlame(false)
62 , m_pProgDlg(NULL)
63 , m_bCancelled(false)
64 , m_nIconFolder(0)
65 , m_bThreadRunning(FALSE)
66 , m_bIgnoreSpaceAtEol(false)
67 , m_bIgnoreSpaceChange(false)
68 , m_bIgnoreAllSpace(false)
69 , m_bIgnoreBlankLines(false)
70 , m_bIsBare(false)
72 m_bLoadingRef=FALSE;
75 CFileDiffDlg::~CFileDiffDlg()
79 void CFileDiffDlg::DoDataExchange(CDataExchange* pDX)
81 CResizableStandAloneDialog::DoDataExchange(pDX);
82 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
83 DDX_Control(pDX, IDC_SWITCHLEFTRIGHT, m_SwitchButton);
84 DDX_Control(pDX, IDC_REV1BTN, m_cRev1Btn);
85 DDX_Control(pDX, IDC_REV2BTN, m_cRev2Btn);
86 DDX_Control(pDX, IDC_FILTER, m_cFilter);
87 DDX_Control(pDX, IDC_REV1EDIT, m_ctrRev1Edit);
88 DDX_Control(pDX, IDC_REV2EDIT, m_ctrRev2Edit);
92 BEGIN_MESSAGE_MAP(CFileDiffDlg, CResizableStandAloneDialog)
93 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
94 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
95 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
96 ON_WM_CONTEXTMENU()
97 ON_WM_SETCURSOR()
98 ON_EN_SETFOCUS(IDC_SECONDURL, &CFileDiffDlg::OnEnSetfocusSecondurl)
99 ON_EN_SETFOCUS(IDC_FIRSTURL, &CFileDiffDlg::OnEnSetfocusFirsturl)
100 ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT, &CFileDiffDlg::OnBnClickedSwitchleftright)
101 ON_NOTIFY(HDN_ITEMCLICK, 0, &CFileDiffDlg::OnHdnItemclickFilelist)
102 ON_BN_CLICKED(IDC_REV1BTN, &CFileDiffDlg::OnBnClickedRev1btn)
103 ON_BN_CLICKED(IDC_REV2BTN, &CFileDiffDlg::OnBnClickedRev2btn)
104 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
105 ON_EN_CHANGE(IDC_FILTER, &CFileDiffDlg::OnEnChangeFilter)
106 ON_WM_TIMER()
107 ON_MESSAGE(ENAC_UPDATE, &CFileDiffDlg::OnEnUpdate)
108 ON_MESSAGE(MSG_REF_LOADED, OnRefLoad)
109 ON_REGISTERED_MESSAGE(WM_DISABLEBUTTONS, OnDisableButtons)
110 ON_REGISTERED_MESSAGE(WM_DIFFFINISHED, OnDiffFinished)
111 ON_BN_CLICKED(IDC_DIFFOPTION, OnBnClickedDiffoption)
112 ON_BN_CLICKED(IDC_LOG, &CFileDiffDlg::OnBnClickedLog)
113 END_MESSAGE_MAP()
116 void CFileDiffDlg::SetDiff(const CTGitPath * path, const GitRev &rev1, const GitRev &rev2)
118 if(path!=NULL)
120 m_path1 = *path;
121 m_path2 = *path;
122 m_sFilter = path->GetGitPathString();
124 m_rev1 = rev1;
125 m_rev2 = rev2;
128 void CFileDiffDlg::SetDiff(const CTGitPath * path, const CString &hash1, const CString &hash2)
130 if(path!=NULL)
132 m_path1 = *path;
133 m_path2 = *path;
134 m_sFilter = path->GetGitPathString();
137 BYTE_VECTOR logout;
139 if(hash1 == GIT_REV_ZERO)
141 m_rev1.m_CommitHash.Empty();
142 m_rev1.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
144 else
148 m_rev1.GetCommit(hash1);
150 catch (const char *msg)
152 MessageBox(_T("Could not get commit ") + hash1 + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
156 logout.clear();
158 if(hash2 == GIT_REV_ZERO)
160 m_rev2.m_CommitHash.Empty();
161 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
163 else
167 m_rev2.GetCommit(hash2);
169 catch (const char *msg)
171 MessageBox(_T("Could not get commit ") + hash2 + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
176 void CFileDiffDlg::SetDiff(const CTGitPath * path, const GitRev &rev1)
178 if(path!=NULL)
180 m_path1 = *path;
181 m_path2 = *path;
182 m_sFilter = path->GetGitPathString();
184 m_rev1 = rev1;
185 m_rev2.m_CommitHash.Empty();
186 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_PROC_PREVIOUSVERSION));
188 //this->GetDlgItem()->EnableWindow(FALSE);
191 BOOL CFileDiffDlg::OnInitDialog()
193 CResizableStandAloneDialog::OnInitDialog();
194 CString temp;
196 CString sWindowTitle;
197 GetWindowText(sWindowTitle);
198 CString pathText = g_Git.m_CurrentDir;
199 if (!m_path1.IsEmpty())
200 pathText = g_Git.CombinePath(m_path1);
201 CAppUtils::SetWindowTitle(m_hWnd, pathText, sWindowTitle);
203 this->m_ctrRev1Edit.Init();
204 this->m_ctrRev2Edit.Init();
206 m_tooltips.Create(this);
207 m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);
209 m_cFileList.SetRedraw(false);
210 m_cFileList.DeleteAllItems();
211 DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
212 m_cFileList.SetExtendedStyle(exStyle);
214 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
215 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
217 m_SwitchButton.SetImage((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
218 m_SwitchButton.Invalidate();
220 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
221 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
222 temp.LoadString(IDS_FILEDIFF_FILTERCUE);
223 temp = _T(" ")+temp;
224 m_cFilter.SetCueBanner(temp);
225 if (!m_sFilter.IsEmpty())
226 m_cFilter.SetWindowText(m_sFilter);
228 int c = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
229 while (c>=0)
230 m_cFileList.DeleteColumn(c--);
232 temp.LoadString(IDS_FILEDIFF_FILE);
233 m_cFileList.InsertColumn(0, temp);
234 temp.LoadString(IDS_FILEDIFF_EXT);
235 m_cFileList.InsertColumn(1, temp);
236 temp.LoadString(IDS_FILEDIFF_ACTION);
237 m_cFileList.InsertColumn(2, temp);
239 temp.LoadString(IDS_FILEDIFF_STATADD);
240 m_cFileList.InsertColumn(3, temp);
241 temp.LoadString(IDS_FILEDIFF_STATDEL);
242 m_cFileList.InsertColumn(4, temp);
244 int mincol = 0;
245 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
246 int col;
247 for (col = mincol; col <= maxcol; col++)
249 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
252 m_cFileList.SetRedraw(true);
254 AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);
255 AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);
256 AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);
257 AddAnchor(IDC_REV1BTN, TOP_RIGHT);
258 //AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);
259 AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);
260 AddAnchor(IDC_REV2BTN, TOP_RIGHT);
261 AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);
262 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
263 AddAnchor(IDC_REV1GROUP,TOP_LEFT,TOP_RIGHT);
264 AddAnchor(IDC_REV2GROUP,TOP_LEFT,TOP_RIGHT);
265 AddAnchor(IDC_REV1EDIT,TOP_LEFT);
266 AddAnchor(IDC_REV2EDIT,TOP_LEFT);
267 AddAnchor(IDC_DIFFOPTION, TOP_RIGHT);
268 AddAnchor(IDC_LOG, TOP_RIGHT);
270 EnableSaveRestore(_T("FileDiffDlg"));
272 m_bIsBare = GitAdminDir().IsBareRepo(g_Git.m_CurrentDir);
274 if(this->m_strRev1.IsEmpty())
275 this->m_ctrRev1Edit.SetWindowText(this->m_rev1.m_CommitHash.ToString());
276 else
278 bool rev1fail = false;
281 rev1fail = !!m_rev1.GetCommit(m_strRev1);
283 catch (const char *msg)
285 rev1fail = true;
286 MessageBox(_T("Could not get commit ") + m_strRev1 + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
288 if (rev1fail)
290 CString msg;
291 msg.Format(IDS_PROC_REFINVALID, m_strRev1);
292 this->m_FileListText += msg;
295 this->m_ctrRev1Edit.SetWindowText(m_strRev1);
298 if(this->m_strRev2.IsEmpty())
299 this->m_ctrRev2Edit.SetWindowText(this->m_rev2.m_CommitHash.ToString());
300 else
302 bool rev2fail = false;
305 rev2fail = !!m_rev2.GetCommit(m_strRev2);
307 catch (const char *msg)
309 rev2fail = true;
310 MessageBox(_T("Could not get commit ") + m_strRev2 + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
312 if (rev2fail)
314 CString msg;
315 msg.Format(IDS_PROC_REFINVALID, m_strRev2);
316 this->m_FileListText += msg;
319 this->m_ctrRev2Edit.SetWindowText(m_strRev2);
322 SetURLLabels();
324 InterlockedExchange(&m_bThreadRunning, TRUE);
325 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
327 InterlockedExchange(&m_bThreadRunning, FALSE);
328 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
331 InterlockedExchange(&m_bLoadingRef, TRUE);
332 if (AfxBeginThread(LoadRefThreadEntry, this)==NULL)
334 InterlockedExchange(&m_bLoadingRef, FALSE);
335 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
338 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
339 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
340 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
342 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
343 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
344 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
346 // Start with focus on file list
347 GetDlgItem(IDC_FILELIST)->SetFocus();
349 if(m_rev2.m_CommitHash.IsEmpty())
350 m_SwitchButton.EnableWindow(FALSE);
352 KillTimer(IDT_INPUT);
353 return FALSE;
356 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)
358 return ((CFileDiffDlg*)pVoid)->DiffThread();
361 UINT CFileDiffDlg::DiffThread()
363 SendMessage(WM_DISABLEBUTTONS);
365 if( m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty())
366 g_Git.RefreshGitIndex();
368 g_Git.GetCommitDiffList(m_rev1.m_CommitHash.ToString(), m_rev2.m_CommitHash.ToString(), m_arFileList, m_bIgnoreSpaceAtEol, m_bIgnoreSpaceChange, m_bIgnoreAllSpace, m_bIgnoreBlankLines);
369 Sort();
371 SendMessage(WM_DIFFFINISHED);
373 InterlockedExchange(&m_bThreadRunning, FALSE);
374 return 0;
377 LRESULT CFileDiffDlg::OnDisableButtons(WPARAM, LPARAM)
379 RefreshCursor();
380 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));
381 m_cFileList.DeleteAllItems();
382 m_arFileList.Clear();
383 EnableInputControl(false);
384 return 0;
387 LRESULT CFileDiffDlg::OnDiffFinished(WPARAM, LPARAM)
389 CString sFilterText;
390 m_cFilter.GetWindowText(sFilterText);
391 m_cFileList.SetRedraw(false);
392 Filter(sFilterText);
393 if (!m_arFileList.IsEmpty())
395 // Highlight first entry in file list
396 m_cFileList.SetSelectionMark(0);
397 m_cFileList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
400 int mincol = 0;
401 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
402 int col;
403 for (col = mincol; col <= maxcol; ++col)
405 m_cFileList.SetColumnWidth(col, LVSCW_AUTOSIZE_USEHEADER);
408 m_cFileList.ClearText();
409 if (m_arFileList.IsEmpty())
410 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_COMPAREREV_NODIFF)));
411 m_cFileList.SetRedraw(true);
413 InvalidateRect(NULL);
414 RefreshCursor();
415 EnableInputControl(true);
416 return 0;
419 int CFileDiffDlg::AddEntry(const CTGitPath * fd)
421 int ret = -1;
422 if (fd)
424 int index = m_cFileList.GetItemCount();
426 int icon_idx = 0;
427 if (fd->IsDirectory())
428 icon_idx = m_nIconFolder;
429 else
430 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());
432 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);
433 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetFileExtension());
434 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->GetActionName());
435 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatAdd);
436 m_cFileList.SetItemText(index, 4, ((CTGitPath*)fd)->m_StatDel);
438 return ret;
441 void CFileDiffDlg::EnableInputControl(bool b)
443 this->m_ctrRev1Edit.EnableWindow(b);
444 this->m_ctrRev2Edit.EnableWindow(b);
445 this->m_cRev1Btn.EnableWindow(b);
446 this->m_cRev2Btn.EnableWindow(b);
447 m_cFilter.EnableWindow(b);
448 m_SwitchButton.EnableWindow(b);
449 GetDlgItem(IDC_LOG)->EnableWindow(b && !(m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty()));
452 void CFileDiffDlg::DoDiff(int selIndex, bool blame)
454 CGitDiff diff;
455 CTGitPath* fd2 = m_arFilteredList[selIndex];
456 CTGitPath* fd1 = fd2;
457 if (m_rev1.m_CommitHash.IsEmpty() && g_Git.IsInitRepos())
459 CGitDiff::DiffNull(fd2, GIT_REV_ZERO);
460 return;
462 if (fd2->m_Action & CTGitPath::LOGACTIONS_ADDED)
464 CGitDiff::DiffNull(fd2, m_rev1.m_CommitHash.ToString(), true);
465 return;
467 if (fd2->m_Action & CTGitPath::LOGACTIONS_DELETED)
469 CGitDiff::DiffNull(fd2, m_rev2.m_CommitHash.ToString(), false);
470 return;
472 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
473 fd1 = new CTGitPath(fd2->GetGitOldPathString());
474 diff.Diff(fd2, fd1, this->m_rev1.m_CommitHash.ToString(), this->m_rev2.m_CommitHash.ToString(), blame, FALSE);
475 if (fd1 != fd2)
476 delete fd1;
480 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
482 *pResult = 0;
483 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
484 int selIndex = pNMLV->iItem;
485 if (selIndex < 0)
486 return;
487 if (selIndex >= (int)m_arFilteredList.size())
488 return;
490 DoDiff(selIndex, m_bBlame);
493 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
496 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
497 if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())
498 return;
500 CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();
501 if (pGetInfoTip->cchTextMax > path.GetLength())
502 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax);
504 *pResult = 0;
507 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
509 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
510 // Take the default processing unless we set this to something else below.
511 *pResult = CDRF_DODEFAULT;
513 // First thing - check the draw stage. If it's the control's prepaint
514 // stage, then tell Windows we want messages for every item.
516 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
518 *pResult = CDRF_NOTIFYITEMDRAW;
520 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
522 // This is the prepaint stage for an item. Here's where we set the
523 // item's text color. Our return value will tell Windows to draw the
524 // item itself, but it will use the new color we set here.
526 // Tell Windows to paint the control itself.
527 *pResult = CDRF_DODEFAULT;
529 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
531 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)
533 CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];
534 switch (fd->m_Action)
536 case CTGitPath::LOGACTIONS_ADDED:
537 crText = m_colors.GetColor(CColors::Added);
538 break;
539 case CTGitPath::LOGACTIONS_DELETED:
540 crText = m_colors.GetColor(CColors::Deleted);
541 break;
542 case CTGitPath::LOGACTIONS_MODIFIED:
543 crText = m_colors.GetColor(CColors::Modified);
544 break;
545 default:
546 crText = m_colors.GetColor(CColors::PropertyChanged);
547 break;
550 // Store the color back in the NMLVCUSTOMDRAW struct.
551 pLVCD->clrText = crText;
555 UINT CFileDiffDlg::LoadRefThread()
557 g_Git.GetBranchList(m_Reflist,NULL,CGit::BRANCH_ALL_F);
558 g_Git.GetTagList(m_Reflist);
560 this->PostMessage(MSG_REF_LOADED);
561 InterlockedExchange(&m_bLoadingRef, FALSE);
562 return 0;
565 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)
567 if ((pWnd==0)||(pWnd != &m_cFileList))
568 return;
569 if (m_cFileList.GetSelectedCount() == 0)
570 return;
571 // if the context menu is invoked through the keyboard, we have to use
572 // a calculated position on where to anchor the menu on
573 if ((point.x == -1) && (point.y == -1))
575 CRect rect;
576 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);
577 m_cFileList.ClientToScreen(&rect);
578 point = rect.CenterPoint();
580 CIconMenu popup;
581 if (popup.CreatePopupMenu())
583 int firstEntry = -1;
584 POSITION firstPos = m_cFileList.GetFirstSelectedItemPosition();
585 if (firstPos)
586 firstEntry = m_cFileList.GetNextSelectedItem(firstPos);
588 CString menuText;
589 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
590 popup.AppendMenuIcon(ID_GNUDIFFCOMPARE, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
591 popup.AppendMenu(MF_SEPARATOR, NULL);
592 if (!m_bIsBare)
594 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, m_rev1.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
595 popup.AppendMenuIcon(ID_REVERT1, menuText, IDI_REVERT);
596 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, m_rev2.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
597 popup.AppendMenuIcon(ID_REVERT2, menuText, IDI_REVERT);
598 popup.AppendMenu(MF_SEPARATOR, NULL);
600 popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG);
601 if (firstEntry >= 0 && !m_arFilteredList[firstEntry]->IsDirectory())
603 if (!m_bIsBare)
605 popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME);
606 popup.AppendMenu(MF_SEPARATOR, NULL);
608 popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT);
610 else if (firstEntry >= 0)
611 popup.AppendMenuIcon(ID_LOGSUBMODULE, IDS_MENULOGSUBMODULE, IDI_LOG);
612 popup.AppendMenu(MF_SEPARATOR, NULL);
613 popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS);
614 popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
615 popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
617 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
618 m_bCancelled = false;
619 switch (cmd)
621 case ID_COMPARE:
623 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
624 while (pos)
626 int index = m_cFileList.GetNextSelectedItem(pos);
627 DoDiff(index, false);
630 break;
631 case ID_GNUDIFFCOMPARE:
633 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
634 while (pos)
636 CTGitPath *fd2 = m_arFilteredList[m_cFileList.GetNextSelectedItem(pos)];
637 CTGitPath *fd1 = fd2;
638 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
639 fd1 = new CTGitPath(fd2->GetGitOldPathString());
640 CAppUtils::StartShowUnifiedDiff(m_hWnd, *fd2, m_rev2.m_CommitHash.ToString(), *fd1, m_rev1.m_CommitHash.ToString());
641 if (fd1 != fd2)
642 delete fd1;
645 break;
646 case ID_REVERT1:
647 RevertSelectedItemToVersion(m_rev1.m_CommitHash.ToString());
648 break;
649 case ID_REVERT2:
650 RevertSelectedItemToVersion(m_rev2.m_CommitHash.ToString());
651 break;
652 case ID_BLAME:
654 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
655 while (pos)
657 int index = m_cFileList.GetNextSelectedItem(pos);
658 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString());
661 break;
662 case ID_LOG:
663 case ID_LOGSUBMODULE:
665 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
666 while (pos)
668 int index = m_cFileList.GetNextSelectedItem(pos);
669 CString cmd = _T("/command:log");
670 if (cmd == ID_LOGSUBMODULE)
671 cmd += _T(" /submodule");
672 cmd += _T(" /path:\"")+m_arFilteredList[index]->GetWinPathString()+_T("\" ");
673 cmd += _T(" /endrev:")+m_rev1.m_CommitHash.ToString();
674 CAppUtils::RunTortoiseGitProc(cmd);
677 break;
678 case ID_SAVEAS:
680 if (m_cFileList.GetSelectedCount() > 0)
682 CString temp;
683 CTGitPath savePath;
684 CString pathSave;
685 if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_TEXTFILEFILTER, false, m_hWnd, _T(".txt")))
687 break;
689 savePath = CTGitPath(pathSave);
691 // now open the selected file for writing
694 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
695 if (m_path1.IsEmpty() && m_path2.IsEmpty())
696 temp.Format(IDS_FILEDIFF_CHANGEDLISTINTROROOT, m_rev1.m_CommitHash.ToString(), m_rev2.m_CommitHash.ToString());
697 else
698 temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, m_path1.GetGitPathString(), m_rev1.m_CommitHash.ToString(), m_path2.GetGitPathString(), m_rev2.m_CommitHash.ToString());
699 file.WriteString(temp + _T("\r\n"));
700 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
701 while (pos)
703 int index = m_cFileList.GetNextSelectedItem(pos);
704 CTGitPath* fd = m_arFilteredList[index];
705 file.WriteString(fd->GetGitPathString());
706 file.WriteString(_T("\r\n"));
708 file.Close();
710 catch (CFileException* pE)
712 pE->ReportError();
716 break;
717 case ID_CLIPBOARD_PATH:
719 CopySelectionToClipboard();
721 break;
723 case ID_CLIPBOARD_ALL:
725 CopySelectionToClipboard(TRUE);
727 break;
728 case ID_EXPORT:
730 // export all changed files to a folder
731 CBrowseFolder browseFolder;
732 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
733 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK)
735 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
736 while (pos)
738 int index = m_cFileList.GetNextSelectedItem(pos);
739 CTGitPath* fd = m_arFilteredList[index];
740 // we cannot export directories or folders
741 if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory())
742 continue;
743 CAppUtils::CreateMultipleDirectory(m_strExportDir + _T("\\") + fd->GetContainingDirectory().GetWinPathString());
744 CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString();
745 if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO)
747 if(!CopyFile(g_Git.CombinePath(fd), filename, false))
749 MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
750 return;
753 else
755 if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename))
757 CString out;
758 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, fd->GetGitPathString(), m_rev1.m_CommitHash.ToString(), filename);
759 if (CMessageBox::Show(nullptr, g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
760 return;
767 break;
773 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
775 if (pWnd != &m_cFileList)
776 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
777 if (m_bThreadRunning == 0)
779 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
780 SetCursor(hCur);
781 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
783 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
784 SetCursor(hCur);
785 return TRUE;
788 void CFileDiffDlg::OnEnSetfocusFirsturl()
790 GetDlgItem(IDC_FIRSTURL)->HideCaret();
793 void CFileDiffDlg::OnEnSetfocusSecondurl()
795 GetDlgItem(IDC_SECONDURL)->HideCaret();
799 void CFileDiffDlg::OnBnClickedSwitchleftright()
802 if (m_bThreadRunning)
803 return;
805 #if 0
806 CString sFilterString;
807 m_cFilter.GetWindowText(sFilterString);
809 m_cFileList.SetRedraw(false);
810 m_cFileList.DeleteAllItems();
811 for (int i=0; i<(int)m_arFileList.GetCount(); ++i)
813 CTGitPath fd = m_arFileList[i];
814 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)
815 fd.m_Action = CTGitPath::LOGACTIONS_DELETED;
816 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)
817 fd.m_Action = CTGitPath::LOGACTIONS_ADDED;
818 std::swap(fd.m_StatAdd, fd.m_StatDel);
819 (CTGitPath&)m_arFileList[i] = fd;
821 Filter(sFilterString);
822 #endif
824 m_cFileList.SetRedraw(true);
825 CTGitPath path = m_path1;
826 m_path1 = m_path2;
827 m_path2 = path;
828 GitRev rev = m_rev1;
829 m_rev1 = m_rev2;
830 m_rev2 = rev;
832 CString str1,str2;
833 this->m_ctrRev1Edit.GetWindowText(str1);
834 this->m_ctrRev2Edit.GetWindowText(str2);
836 this->m_ctrRev1Edit.SetWindowText(str2);
837 this->m_ctrRev2Edit.SetWindowText(str1);
839 SetURLLabels();
840 //KillTimer(IDT_INPUT);
843 void CFileDiffDlg::SetURLLabels(int mask)
846 // m_cRev1Btn.SetWindowText(m_rev1.m_CommitHash.ToString().Left(6));
847 // m_cRev2Btn.SetWindowText(m_rev2.m_CommitHash.ToString().Left(6));
849 if(mask &0x1)
851 SetDlgItemText(IDC_FIRSTURL, m_rev1.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev1.GetSubject());
852 if (!m_rev1.m_CommitHash.IsEmpty())
853 m_tooltips.AddTool(IDC_FIRSTURL,
854 CLoglistUtils::FormatDateAndTime(m_rev1.GetAuthorDate(), DATE_SHORTDATE) + _T(" ") + m_rev1.GetAuthorName());
858 if(mask &0x2)
860 SetDlgItemText(IDC_SECONDURL,m_rev2.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev2.GetSubject());
861 if (!m_rev2.m_CommitHash.IsEmpty())
862 m_tooltips.AddTool(IDC_SECONDURL,
863 CLoglistUtils::FormatDateAndTime(m_rev2.GetAuthorDate(), DATE_SHORTDATE) + _T(" ") + m_rev2.GetAuthorName());
866 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASE)));
867 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1)));
869 if( (mask&0x3) == 0x3 && !m_rev1.m_CommitHash.IsEmpty() && !m_rev2.m_CommitHash.IsEmpty())
870 if(m_rev2.GetCommitterDate() > m_rev1.GetCommitterDate())
872 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASENEWER)));
874 else if (m_rev2.GetCommitterDate() < m_rev1.GetCommitterDate())
876 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1NEWER)));
880 void CFileDiffDlg::ClearURLabels(int mask)
882 if(mask&0x1)
884 SetDlgItemText(IDC_FIRSTURL, _T(""));
885 m_tooltips.AddTool(IDC_FIRSTURL, _T(""));
888 if(mask&0x2)
890 SetDlgItemText(IDC_SECONDURL, _T(""));
891 m_tooltips.AddTool(IDC_SECONDURL, _T(""));
894 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)
896 m_tooltips.RelayEvent(pMsg);
897 if (pMsg->message == WM_KEYDOWN)
899 switch (pMsg->wParam)
901 case 'A':
903 if (GetFocus() != GetDlgItem(IDC_FILELIST))
904 break;
905 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
907 // select all entries
908 for (int i=0; i<m_cFileList.GetItemCount(); ++i)
910 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
912 return TRUE;
915 break;
916 case 'C':
917 case VK_INSERT:
919 if (GetFocus() != GetDlgItem(IDC_FILELIST))
920 break;
921 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
923 CopySelectionToClipboard();
924 return TRUE;
927 break;
928 case '\r':
930 if (GetFocus() == GetDlgItem(IDC_FILELIST))
932 // Return pressed in file list. Show diff, as for double click
933 int selIndex = m_cFileList.GetSelectionMark();
934 if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))
935 DoDiff(selIndex, m_bBlame);
936 return TRUE;
939 break;
940 case VK_F5:
942 OnTimer(IDT_INPUT);
944 break;
947 return __super::PreTranslateMessage(pMsg);
950 void CFileDiffDlg::OnCancel()
952 if (m_bThreadRunning)
954 m_bCancelled = true;
955 return;
957 __super::OnCancel();
960 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)
962 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
963 if (m_bThreadRunning)
964 return;
966 if (m_nSortedColumn == phdr->iItem)
967 m_bAscending = !m_bAscending;
968 else
969 m_bAscending = TRUE;
970 m_nSortedColumn = phdr->iItem;
971 Sort();
973 CString temp;
974 m_cFileList.SetRedraw(FALSE);
975 m_cFileList.DeleteAllItems();
976 m_cFilter.GetWindowText(temp);
977 Filter(temp);
979 CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();
980 HDITEM HeaderItem = {0};
981 HeaderItem.mask = HDI_FORMAT;
982 for (int i=0; i<pHeader->GetItemCount(); ++i)
984 pHeader->GetItem(i, &HeaderItem);
985 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
986 pHeader->SetItem(i, &HeaderItem);
988 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
989 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
990 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
992 m_cFileList.SetRedraw(TRUE);
994 *pResult = 0;
997 void CFileDiffDlg::Sort()
999 if(m_arFileList.GetCount() < 2)
1001 return;
1004 std::sort(m_arFileList.m_paths.begin(), m_arFileList.m_paths.end(), &CFileDiffDlg::SortCompare);
1007 bool CFileDiffDlg::SortCompare(const CTGitPath& Data1, const CTGitPath& Data2)
1009 int result = 0;
1010 int d1, d2;
1011 switch (m_nSortedColumn)
1013 case 0: //path column
1014 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
1015 break;
1016 case 1: //extension column
1017 result = Data1.GetFileExtension().Compare(Data2.GetFileExtension());
1018 break;
1019 case 2: //action column
1020 result = Data1.m_Action - Data2.m_Action;
1021 break;
1022 case 3:
1023 d1 = CSorter::A2L(Data1.m_StatAdd);
1024 d2 = CSorter::A2L(Data2.m_StatAdd);
1025 result = d1 - d2;
1026 break;
1027 case 4:
1028 d1 = CSorter::A2L(Data1.m_StatDel);;
1029 d2 = CSorter::A2L(Data2.m_StatDel);
1030 result = d1 - d2;
1031 break;
1032 default:
1033 break;
1035 // sort by path name as second priority
1036 if (m_nSortedColumn != 0 && result == 0)
1037 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
1039 if (!m_bAscending)
1040 result = -result;
1041 return result < 0;
1045 void CFileDiffDlg::OnBnClickedRev1btn()
1047 ClickRevButton(&this->m_cRev1Btn,&this->m_rev1, &this->m_ctrRev1Edit);
1050 void CFileDiffDlg::ClickRevButton(CMenuButton *button, GitRev *rev, CACEdit *edit)
1052 INT_PTR entry=button->GetCurrentEntry();
1053 if(entry == 0) /* Browse Refence*/
1056 CString str = CBrowseRefsDlg::PickRef();
1057 if(str.IsEmpty())
1058 return;
1060 if(FillRevFromString(rev,str))
1061 return;
1063 edit->SetWindowText(str);
1067 if(entry == 1) /*Log*/
1069 CLogDlg dlg;
1070 dlg.SetSelect(true);
1071 if(dlg.DoModal() == IDOK)
1073 if( dlg.GetSelectedHash().IsEmpty() )
1074 return;
1076 if(FillRevFromString(rev,dlg.GetSelectedHash()))
1077 return;
1079 edit->SetWindowText(dlg.GetSelectedHash());
1082 else
1083 return;
1086 if(entry == 2) /*RefLog*/
1088 CRefLogDlg dlg;
1089 if(dlg.DoModal() == IDOK)
1091 if(FillRevFromString(rev,dlg.m_SelectedHash))
1092 return;
1094 edit->SetWindowText(dlg.m_SelectedHash);
1097 else
1098 return;
1101 SetURLLabels();
1103 InterlockedExchange(&m_bThreadRunning, TRUE);
1104 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1106 InterlockedExchange(&m_bThreadRunning, FALSE);
1107 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1109 KillTimer(IDT_INPUT);
1112 void CFileDiffDlg::OnBnClickedRev2btn()
1114 ClickRevButton(&this->m_cRev2Btn,&this->m_rev2, &this->m_ctrRev2Edit);
1117 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1119 if (m_bThreadRunning)
1121 SetTimer(IDT_FILTER, 1000, NULL);
1122 return 0L;
1125 KillTimer(IDT_FILTER);
1127 m_cFileList.SetRedraw(FALSE);
1128 m_arFilteredList.clear();
1129 m_cFileList.DeleteAllItems();
1131 Filter(_T(""));
1133 m_cFileList.SetRedraw(TRUE);
1134 return 0L;
1137 void CFileDiffDlg::OnEnChangeFilter()
1139 SetTimer(IDT_FILTER, 1000, NULL);
1142 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)
1144 if (m_bThreadRunning)
1145 return;
1147 if( nIDEvent == IDT_FILTER)
1149 CString sFilterText;
1150 KillTimer(IDT_FILTER);
1151 m_cFilter.GetWindowText(sFilterText);
1153 m_cFileList.SetRedraw(FALSE);
1154 m_cFileList.DeleteAllItems();
1156 Filter(sFilterText);
1158 m_cFileList.SetRedraw(TRUE);
1160 __super::OnTimer(nIDEvent);
1163 if( nIDEvent == IDT_INPUT)
1165 KillTimer(IDT_INPUT);
1166 TRACE(_T("Input Timer\r\n"));
1168 GitRev gitrev;
1169 CString str;
1170 int mask = 0;
1171 this->m_ctrRev1Edit.GetWindowText(str);
1174 if (!gitrev.GetCommit(str))
1176 m_rev1 = gitrev;
1177 mask |= 0x1;
1180 catch (const char *msg)
1182 CMessageBox::Show(m_hWnd, _T("Could not get commit ") + str + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
1185 this->m_ctrRev2Edit.GetWindowText(str);
1189 if (!gitrev.GetCommit(str))
1191 m_rev2 = gitrev;
1192 mask |= 0x2;
1195 catch (const char *msg)
1197 CMessageBox::Show(m_hWnd, _T("Could not get commit ") + str + _T("\nlibgit reports:\n") + CString(msg), _T("TortoiseGit"), MB_ICONERROR);
1200 this->SetURLLabels(mask);
1202 if(mask == 0x3)
1205 InterlockedExchange(&m_bThreadRunning, TRUE);
1206 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1208 InterlockedExchange(&m_bThreadRunning, FALSE);
1209 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1215 void CFileDiffDlg::Filter(CString sFilterText)
1217 sFilterText.MakeLower();
1219 m_arFilteredList.clear();
1221 for (int i=0;i<m_arFileList.GetCount();i++)
1223 CString sPath = m_arFileList[i].GetGitPathString();
1224 sPath.MakeLower();
1225 if (sPath.Find(sFilterText) >= 0)
1227 m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));
1230 for (std::vector<CTGitPath*>::const_iterator it = m_arFilteredList.begin(); it != m_arFilteredList.end(); ++it)
1232 AddEntry(*it);
1236 void CFileDiffDlg::CopySelectionToClipboard(BOOL isFull)
1238 // copy all selected paths to the clipboard
1239 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1240 int index;
1241 CString sTextForClipboard;
1242 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1244 sTextForClipboard += m_cFileList.GetItemText(index, 0);
1245 sTextForClipboard += _T("\t");
1247 if(!isFull)
1249 sTextForClipboard += _T("\r\n");
1252 else
1254 sTextForClipboard += m_cFileList.GetItemText(index, 1);
1255 sTextForClipboard += _T("\t");
1256 sTextForClipboard += m_cFileList.GetItemText(index, 2);
1257 sTextForClipboard += _T("\t");
1258 sTextForClipboard += m_cFileList.GetItemText(index, 3);
1259 sTextForClipboard += _T("\t");
1260 sTextForClipboard += m_cFileList.GetItemText(index, 4);
1261 sTextForClipboard += _T("\r\n");
1264 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);
1268 LRESULT CFileDiffDlg::OnRefLoad(WPARAM /*wParam*/, LPARAM /*lParam*/)
1270 for (size_t i = 0; i < m_Reflist.size(); ++i)
1272 CString str=m_Reflist[i];
1274 if(str.Find(_T("remotes/")) == 0)
1275 str=str.Mid(8);
1277 m_ctrRev1Edit.AddSearchString(str);
1278 m_ctrRev2Edit.AddSearchString(str);
1280 return 0;
1283 BOOL CFileDiffDlg::DestroyWindow()
1285 return CResizableStandAloneDialog::DestroyWindow();
1288 LRESULT CFileDiffDlg::OnEnUpdate(WPARAM /*wParam*/, LPARAM lParam)
1290 if(lParam == IDC_REV1EDIT)
1292 OnTextUpdate(&this->m_ctrRev1Edit);
1293 ClearURLabels(1);
1295 if(lParam == IDC_REV2EDIT)
1297 OnTextUpdate(&this->m_ctrRev2Edit);
1298 ClearURLabels(1<<1);
1300 return 0;
1303 void CFileDiffDlg::OnTextUpdate(CACEdit * /*pEdit*/)
1305 SetTimer(IDT_INPUT, 1000, NULL);
1306 this->m_cFileList.ShowText(_T("Wait For input validate version"));
1309 int CFileDiffDlg::RevertSelectedItemToVersion(CString rev)
1311 if (rev.IsEmpty() || rev == GIT_REV_ZERO)
1312 return 0;
1314 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1315 int index;
1316 int count = 0;
1317 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1319 CString cmd, out;
1320 CTGitPath *fentry = (CTGitPath *)m_arFilteredList[index];
1321 cmd.Format(_T("git.exe checkout %s -- \"%s\""), rev, fentry->GetGitPathString());
1322 if (g_Git.Run(cmd, &out, CP_UTF8))
1324 if (CMessageBox::Show(NULL, out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
1325 break;
1327 else
1328 count++;
1331 CString out;
1332 out.Format(IDS_STATUSLIST_FILESREVERTED, count, rev);
1333 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK);
1334 return 0;
1337 static void AppendMenuChecked(CMenu &menu, UINT nTextID, UINT_PTR nItemID, BOOL checked = FALSE, BOOL enabled = TRUE)
1339 CString text;
1340 text.LoadString(nTextID);
1341 menu.AppendMenu(MF_STRING | (enabled ? MF_ENABLED : MF_DISABLED) | (checked ? MF_CHECKED : MF_UNCHECKED), nItemID, text);
1344 #define DIFFOPTION_IGNORESPACEATEOL 1
1345 #define DIFFOPTION_IGNORESPACECHANGE 2
1346 #define DIFFOPTION_IGNOREALLSPACE 3
1347 #define DIFFOPTION_IGNORBLANKLINES 4
1349 void CFileDiffDlg::OnBnClickedDiffoption()
1351 CMenu popup;
1352 if (popup.CreatePopupMenu())
1354 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORESPACEATEOL, DIFFOPTION_IGNORESPACEATEOL, m_bIgnoreSpaceAtEol);
1355 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORESPACECHANGE, DIFFOPTION_IGNORESPACECHANGE, m_bIgnoreSpaceChange);
1356 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNOREALLSPACE, DIFFOPTION_IGNOREALLSPACE, m_bIgnoreAllSpace);
1357 AppendMenuChecked(popup, IDS_DIFFOPTION_IGNORBLANKLINES, DIFFOPTION_IGNORBLANKLINES, m_bIgnoreBlankLines);
1359 m_tooltips.Pop();
1360 RECT rect;
1361 GetDlgItem(IDC_DIFFOPTION)->GetWindowRect(&rect);
1362 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.bottom, this, 0);
1363 switch (selection)
1365 case DIFFOPTION_IGNORESPACEATEOL:
1366 m_bIgnoreSpaceAtEol = !m_bIgnoreSpaceAtEol;
1367 OnTimer(IDT_INPUT);
1368 break;
1369 case DIFFOPTION_IGNORESPACECHANGE:
1370 m_bIgnoreSpaceChange = !m_bIgnoreSpaceChange;
1371 OnTimer(IDT_INPUT);
1372 break;
1373 case DIFFOPTION_IGNOREALLSPACE:
1374 m_bIgnoreAllSpace = !m_bIgnoreAllSpace;
1375 OnTimer(IDT_INPUT);
1376 break;
1377 case DIFFOPTION_IGNORBLANKLINES:
1378 m_bIgnoreBlankLines = !m_bIgnoreBlankLines;
1379 OnTimer(IDT_INPUT);
1380 break;
1381 default:
1382 break;
1384 UpdateData(FALSE);
1388 void CFileDiffDlg::OnBnClickedLog()
1390 CLogDlg dlg;
1391 dlg.SetRange(m_rev2.m_CommitHash.ToString() + _T("..") + m_rev1.m_CommitHash.ToString());
1392 dlg.DoModal();