Add refresh (press F5) function in Changed Files Dialog
[TortoiseGit.git] / src / TortoiseProc / FileDiffDlg.cpp
blob131d014e53dc7a9a252f50853e9c8abc189cdc70
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - 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 "TempFile.h"
26 #include "SysImageList.h"
27 #include "IconMenu.h"
28 //#include "GitProperties.h"
29 #include "StringUtils.h"
30 #include "PathUtils.h"
31 #include "BrowseFolder.h"
32 #include ".\filediffdlg.h"
33 #include "gitdiff.h"
34 #include "LoglistCommonResource.h"
35 #include "LoglistUtils.h"
36 #include "BrowseRefsDlg.h"
37 #include "LogDlg.h"
38 #include "RefLogDlg.h"
39 #include "GitStatusListCtrl.h"
40 #include "FormatMessageWrapper.h"
42 #define ID_COMPARE 1
43 #define ID_BLAME 2
44 #define ID_SAVEAS 3
45 #define ID_EXPORT 4
46 #define ID_CLIPBOARD_PATH 5
47 #define ID_CLIPBOARD_ALL 6
48 #define ID_LOG 7
49 #define ID_GNUDIFFCOMPARE 8
50 #define ID_REVERT1 9
51 #define ID_REVERT2 10
53 BOOL CFileDiffDlg::m_bAscending = FALSE;
54 int CFileDiffDlg::m_nSortedColumn = -1;
57 IMPLEMENT_DYNAMIC(CFileDiffDlg, CResizableStandAloneDialog)
58 CFileDiffDlg::CFileDiffDlg(CWnd* pParent /*=NULL*/)
59 : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent),
60 m_bBlame(false),
61 m_pProgDlg(NULL),
62 m_bCancelled(false)
64 m_bLoadingRef=FALSE;
67 CFileDiffDlg::~CFileDiffDlg()
71 void CFileDiffDlg::DoDataExchange(CDataExchange* pDX)
73 CResizableStandAloneDialog::DoDataExchange(pDX);
74 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
75 DDX_Control(pDX, IDC_SWITCHLEFTRIGHT, m_SwitchButton);
76 DDX_Control(pDX, IDC_REV1BTN, m_cRev1Btn);
77 DDX_Control(pDX, IDC_REV2BTN, m_cRev2Btn);
78 DDX_Control(pDX, IDC_FILTER, m_cFilter);
79 DDX_Control(pDX, IDC_REV1EDIT, m_ctrRev1Edit);
80 DDX_Control(pDX, IDC_REV2EDIT, m_ctrRev2Edit);
84 BEGIN_MESSAGE_MAP(CFileDiffDlg, CResizableStandAloneDialog)
85 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
86 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
87 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
88 ON_WM_CONTEXTMENU()
89 ON_WM_SETCURSOR()
90 ON_EN_SETFOCUS(IDC_SECONDURL, &CFileDiffDlg::OnEnSetfocusSecondurl)
91 ON_EN_SETFOCUS(IDC_FIRSTURL, &CFileDiffDlg::OnEnSetfocusFirsturl)
92 ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT, &CFileDiffDlg::OnBnClickedSwitchleftright)
93 ON_NOTIFY(HDN_ITEMCLICK, 0, &CFileDiffDlg::OnHdnItemclickFilelist)
94 ON_BN_CLICKED(IDC_REV1BTN, &CFileDiffDlg::OnBnClickedRev1btn)
95 ON_BN_CLICKED(IDC_REV2BTN, &CFileDiffDlg::OnBnClickedRev2btn)
96 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
97 ON_EN_CHANGE(IDC_FILTER, &CFileDiffDlg::OnEnChangeFilter)
98 ON_WM_TIMER()
99 ON_MESSAGE(ENAC_UPDATE, &CFileDiffDlg::OnEnUpdate)
100 ON_MESSAGE(MSG_REF_LOADED, OnRefLoad)
101 END_MESSAGE_MAP()
104 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1, GitRev rev2)
106 if(path!=NULL)
108 m_path1 = *path;
109 m_path2 = *path;
110 m_sFilter = path->GetGitPathString();
112 m_rev1 = rev1;
113 m_rev2 = rev2;
116 void CFileDiffDlg::SetDiff(CTGitPath * path, CString &hash1, CString &hash2)
118 if(path!=NULL)
120 m_path1 = *path;
121 m_path2 = *path;
122 m_sFilter = path->GetGitPathString();
125 BYTE_VECTOR logout;
127 if(hash1 == GIT_REV_ZERO)
129 m_rev1.m_CommitHash.Empty();
130 m_rev1.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
132 else
134 m_rev1.GetCommit(hash1);
137 logout.clear();
139 if(hash2 == GIT_REV_ZERO)
141 m_rev2.m_CommitHash.Empty();
142 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
144 else
146 m_rev2.GetCommit(hash2);
150 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1)
152 if(path!=NULL)
154 m_path1 = *path;
155 m_path2 = *path;
156 m_sFilter = path->GetGitPathString();
158 m_rev1 = rev1;
159 m_rev2.m_CommitHash.Empty();
160 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_PROC_PREVIOUSVERSION));
162 //this->GetDlgItem()->EnableWindow(FALSE);
165 BOOL CFileDiffDlg::OnInitDialog()
167 CResizableStandAloneDialog::OnInitDialog();
168 CString temp;
170 CString sWindowTitle;
171 GetWindowText(sWindowTitle);
172 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
174 this->m_ctrRev1Edit.Init();
175 this->m_ctrRev2Edit.Init();
177 m_tooltips.Create(this);
178 m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);
180 m_cFileList.SetRedraw(false);
181 m_cFileList.DeleteAllItems();
182 DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
183 m_cFileList.SetExtendedStyle(exStyle);
185 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
186 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
188 m_SwitchButton.SetImage((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
189 m_SwitchButton.Invalidate();
191 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
192 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
193 temp.LoadString(IDS_FILEDIFF_FILTERCUE);
194 temp = _T(" ")+temp;
195 m_cFilter.SetCueBanner(temp);
196 if (!m_sFilter.IsEmpty())
197 m_cFilter.SetWindowText(m_sFilter);
199 int c = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
200 while (c>=0)
201 m_cFileList.DeleteColumn(c--);
203 temp.LoadString(IDS_FILEDIFF_FILE);
204 m_cFileList.InsertColumn(0, temp);
205 temp.LoadString(IDS_FILEDIFF_EXT);
206 m_cFileList.InsertColumn(1, temp);
207 temp.LoadString(IDS_FILEDIFF_ACTION);
208 m_cFileList.InsertColumn(2, temp);
210 temp.LoadString(IDS_FILEDIFF_STATADD);
211 m_cFileList.InsertColumn(3, temp);
212 temp.LoadString(IDS_FILEDIFF_STATDEL);
213 m_cFileList.InsertColumn(4, temp);
215 int mincol = 0;
216 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
217 int col;
218 for (col = mincol; col <= maxcol; col++)
220 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
223 m_cFileList.SetRedraw(true);
225 AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);
226 AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);
227 AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);
228 AddAnchor(IDC_REV1BTN, TOP_RIGHT);
229 //AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);
230 AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);
231 AddAnchor(IDC_REV2BTN, TOP_RIGHT);
232 AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);
233 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
234 AddAnchor(IDC_REV1GROUP,TOP_LEFT,TOP_RIGHT);
235 AddAnchor(IDC_REV2GROUP,TOP_LEFT,TOP_RIGHT);
236 AddAnchor(IDC_REV1EDIT,TOP_LEFT);
237 AddAnchor(IDC_REV2EDIT,TOP_LEFT);
239 EnableSaveRestore(_T("FileDiffDlg"));
241 if(this->m_strRev1.IsEmpty())
242 this->m_ctrRev1Edit.SetWindowText(this->m_rev1.m_CommitHash.ToString());
243 else
245 if(m_rev1.GetCommit(this->m_strRev1))
247 CString msg;
248 msg.Format(IDS_PROC_REFINVALID, this->m_strRev1);
249 this->m_FileListText += msg;
252 this->m_ctrRev1Edit.SetWindowText(m_strRev1);
255 if(this->m_strRev2.IsEmpty())
256 this->m_ctrRev2Edit.SetWindowText(this->m_rev2.m_CommitHash.ToString());
257 else
259 if(m_rev2.GetCommit(this->m_strRev2))
261 CString msg;
262 msg.Format(IDS_PROC_REFINVALID, this->m_strRev2);
263 this->m_FileListText += msg;
266 this->m_ctrRev2Edit.SetWindowText(m_strRev2);
269 SetURLLabels();
271 InterlockedExchange(&m_bThreadRunning, TRUE);
272 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
274 InterlockedExchange(&m_bThreadRunning, FALSE);
275 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
278 InterlockedExchange(&m_bLoadingRef, TRUE);
279 if (AfxBeginThread(LoadRefThreadEntry, this)==NULL)
281 InterlockedExchange(&m_bLoadingRef, FALSE);
282 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
285 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
286 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
287 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
289 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
290 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
291 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
293 // Start with focus on file list
294 GetDlgItem(IDC_FILELIST)->SetFocus();
296 if(m_rev2.m_CommitHash.IsEmpty())
297 m_SwitchButton.EnableWindow(FALSE);
299 KillTimer(IDT_INPUT);
300 return FALSE;
303 #if 0
304 svn_error_t* CFileDiffDlg::DiffSummarizeCallback(const CTGitPath& path,
305 svn_client_diff_summarize_kind_t kind,
306 bool propchanged, svn_node_kind_t node)
308 CTGitPath* fd;
309 fd.path = path;
310 fd.kind = kind;
311 fd.node = node;
312 fd.propchanged = propchanged;
313 m_arFileList.push_back(fd);
314 return Git_NO_ERROR;
316 #endif
318 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)
320 return ((CFileDiffDlg*)pVoid)->DiffThread();
323 UINT CFileDiffDlg::DiffThread()
325 RefreshCursor();
326 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));
327 m_cFileList.DeleteAllItems();
328 m_arFileList.Clear();
329 EnableInputControl(false);
330 #if 0
331 bool bSuccess = true;
332 if (m_bDoPegDiff)
334 // bSuccess = DiffSummarizePeg(m_path1, m_peg, m_rev1, m_rev2, m_depth, m_bIgnoreancestry);
336 else
338 // bSuccess = DiffSummarize(m_path1, m_rev1, m_path2, m_rev2, m_depth, m_bIgnoreancestry);
340 // if (!bSuccess)
341 // {
342 // m_cFileList.ShowText(GetLastErrorMessage());
343 // InterlockedExchange(&m_bThreadRunning, FALSE);
344 // return 0;
345 // }
346 #endif
348 if( m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty())
349 g_Git.RefreshGitIndex();
351 g_Git.GetCommitDiffList(m_rev1.m_CommitHash.ToString(),m_rev2.m_CommitHash.ToString(),m_arFileList);
353 CString sFilterText;
354 m_cFilter.GetWindowText(sFilterText);
355 m_cFileList.SetRedraw(false);
356 Filter(sFilterText);
357 if (m_arFileList.GetCount()>0)
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 = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
366 int col;
367 for (col = mincol; col <= maxcol; col++)
369 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
372 m_cFileList.ClearText();
373 m_cFileList.SetRedraw(true);
375 InterlockedExchange(&m_bThreadRunning, FALSE);
376 InvalidateRect(NULL);
377 RefreshCursor();
378 EnableInputControl(true);
379 return 0;
382 int CFileDiffDlg::AddEntry(const CTGitPath * fd)
384 int ret = -1;
385 if (fd)
387 int index = m_cFileList.GetItemCount();
389 int icon_idx = 0;
390 if (fd->IsDirectory())
391 icon_idx = m_nIconFolder;
392 else
393 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());
395 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);
396 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetFileExtension());
397 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->GetActionName());
398 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatAdd);
399 m_cFileList.SetItemText(index, 4, ((CTGitPath*)fd)->m_StatDel);
401 return ret;
404 void CFileDiffDlg::EnableInputControl(bool b)
406 this->m_ctrRev1Edit.EnableWindow(b);
407 this->m_ctrRev2Edit.EnableWindow(b);
408 this->m_cRev1Btn.EnableWindow(b);
409 this->m_cRev2Btn.EnableWindow(b);
410 m_cFilter.EnableWindow(b);
411 m_SwitchButton.EnableWindow(b);
414 void CFileDiffDlg::DoDiff(int selIndex, bool blame)
416 CGitDiff diff;
417 CTGitPath* fd2 = m_arFilteredList[selIndex];
418 CTGitPath* fd1 = fd2;
419 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
420 fd1 = new CTGitPath(fd2->GetGitOldPathString());
421 diff.Diff(fd2, fd1, this->m_rev1.m_CommitHash.ToString(), this->m_rev2.m_CommitHash.ToString(), blame, FALSE);
425 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
427 *pResult = 0;
428 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
429 int selIndex = pNMLV->iItem;
430 if (selIndex < 0)
431 return;
432 if (selIndex >= (int)m_arFilteredList.size())
433 return;
435 DoDiff(selIndex, m_bBlame);
438 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
441 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
442 if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())
443 return;
445 CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();
446 if (pGetInfoTip->cchTextMax > path.GetLength())
447 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax);
449 *pResult = 0;
452 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
454 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
455 // Take the default processing unless we set this to something else below.
456 *pResult = CDRF_DODEFAULT;
458 // First thing - check the draw stage. If it's the control's prepaint
459 // stage, then tell Windows we want messages for every item.
461 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
463 *pResult = CDRF_NOTIFYITEMDRAW;
465 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
467 // This is the prepaint stage for an item. Here's where we set the
468 // item's text color. Our return value will tell Windows to draw the
469 // item itself, but it will use the new color we set here.
471 // Tell Windows to paint the control itself.
472 *pResult = CDRF_DODEFAULT;
474 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
476 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)
478 CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];
479 switch (fd->m_Action)
481 case CTGitPath::LOGACTIONS_ADDED:
482 crText = m_colors.GetColor(CColors::Added);
483 break;
484 case CTGitPath::LOGACTIONS_DELETED:
485 crText = m_colors.GetColor(CColors::Deleted);
486 break;
487 case CTGitPath::LOGACTIONS_MODIFIED:
488 crText = m_colors.GetColor(CColors::Modified);
489 break;
490 //case svn_client_diff_summarize_kind_normal:
491 default:
492 //if (fd.propchanged)
493 crText = m_colors.GetColor(CColors::PropertyChanged);
494 break;
497 // Store the color back in the NMLVCUSTOMDRAW struct.
498 pLVCD->clrText = crText;
502 UINT CFileDiffDlg::LoadRefThread()
504 g_Git.GetBranchList(m_Reflist,NULL,CGit::BRANCH_ALL_F);
505 g_Git.GetTagList(m_Reflist);
507 this->PostMessage(MSG_REF_LOADED);
508 InterlockedExchange(&m_bLoadingRef, FALSE);
509 return 0;
512 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)
514 if ((pWnd==0)||(pWnd != &m_cFileList))
515 return;
516 if (m_cFileList.GetSelectedCount() == 0)
517 return;
518 // if the context menu is invoked through the keyboard, we have to use
519 // a calculated position on where to anchor the menu on
520 if ((point.x == -1) && (point.y == -1))
522 CRect rect;
523 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);
524 m_cFileList.ClientToScreen(&rect);
525 point = rect.CenterPoint();
527 CIconMenu popup;
528 if (popup.CreatePopupMenu())
530 CString menuText;
531 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
532 popup.AppendMenuIcon(ID_GNUDIFFCOMPARE, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
533 popup.AppendMenu(MF_SEPARATOR, NULL);
534 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, m_rev1.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
535 popup.AppendMenuIcon(ID_REVERT1, menuText, IDI_REVERT);
536 menuText.Format(IDS_FILEDIFF_POPREVERTTOREV, m_rev2.m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()));
537 popup.AppendMenuIcon(ID_REVERT2, menuText, IDI_REVERT);
538 popup.AppendMenu(MF_SEPARATOR, NULL);
539 popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG);
540 popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME);
541 popup.AppendMenu(MF_SEPARATOR, NULL);
542 popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT);
543 popup.AppendMenu(MF_SEPARATOR, NULL);
544 popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS);
545 popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
546 popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
548 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
549 m_bCancelled = false;
550 switch (cmd)
552 case ID_COMPARE:
554 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
555 while (pos)
557 int index = m_cFileList.GetNextSelectedItem(pos);
558 DoDiff(index, false);
561 break;
562 case ID_GNUDIFFCOMPARE:
564 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
565 while (pos)
567 CTGitPath *fd2 = m_arFilteredList[m_cFileList.GetNextSelectedItem(pos)];
568 CTGitPath *fd1 = fd2;
569 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
570 fd1 = new CTGitPath(fd2->GetGitOldPathString());
571 CAppUtils::StartShowUnifiedDiff(m_hWnd, *fd2, m_rev2.m_CommitHash.ToString(), *fd1, m_rev1.m_CommitHash.ToString());
574 break;
575 case ID_REVERT1:
576 RevertSelectedItemToVersion(m_rev1.m_CommitHash.ToString());
577 break;
578 case ID_REVERT2:
579 RevertSelectedItemToVersion(m_rev2.m_CommitHash.ToString());
580 break;
581 case ID_BLAME:
583 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
584 while (pos)
586 int index = m_cFileList.GetNextSelectedItem(pos);
587 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString());
590 break;
591 case ID_LOG:
593 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
594 while (pos)
596 int index = m_cFileList.GetNextSelectedItem(pos);
597 CString cmd = _T("/command:log");
598 cmd += _T(" /path:\"")+m_arFilteredList[index]->GetWinPathString()+_T("\" ");
599 cmd += _T(" /endrev:")+m_rev1.m_CommitHash.ToString();
600 CAppUtils::RunTortoiseGitProc(cmd);
603 break;
604 case ID_SAVEAS:
606 if (m_cFileList.GetSelectedCount() > 0)
608 CString temp;
609 CTGitPath savePath;
610 CString pathSave;
611 if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd))
613 break;
615 savePath = CTGitPath(pathSave);
617 // now open the selected file for writing
620 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
621 // temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString());
622 file.WriteString(temp + _T("\n"));
623 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
624 while (pos)
626 int index = m_cFileList.GetNextSelectedItem(pos);
627 CTGitPath* fd = m_arFilteredList[index];
628 file.WriteString(fd->GetGitPathString());
629 file.WriteString(_T("\n"));
631 file.Close();
633 catch (CFileException* pE)
635 pE->ReportError();
639 break;
640 case ID_CLIPBOARD_PATH:
642 CopySelectionToClipboard();
644 break;
646 case ID_CLIPBOARD_ALL:
648 CopySelectionToClipboard(TRUE);
650 break;
651 case ID_EXPORT:
653 // export all changed files to a folder
654 CBrowseFolder browseFolder;
655 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
656 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK)
658 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
659 while (pos)
661 int index = m_cFileList.GetNextSelectedItem(pos);
662 CTGitPath* fd = m_arFilteredList[index];
663 // we cannot export directories or folders
664 if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory())
665 continue;
666 CAppUtils::CreateMultipleDirectory(m_strExportDir + _T("\\") + fd->GetDirectory().GetWinPathString());
667 CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString();
668 if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO)
670 if(!CopyFile(g_Git.m_CurrentDir + _T("\\") + fd->GetWinPath(), filename, false))
672 MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
673 return;
676 else
678 if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename))
680 CString out;
681 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, fd->GetGitPathString(), m_rev1.m_CommitHash.ToString(), filename);
682 if (CMessageBox::Show(NULL, out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
683 return;
690 break;
696 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
698 if (pWnd != &m_cFileList)
699 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
700 if (m_bThreadRunning == 0)
702 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
703 SetCursor(hCur);
704 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
706 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
707 SetCursor(hCur);
708 return TRUE;
711 void CFileDiffDlg::OnEnSetfocusFirsturl()
713 GetDlgItem(IDC_FIRSTURL)->HideCaret();
716 void CFileDiffDlg::OnEnSetfocusSecondurl()
718 GetDlgItem(IDC_SECONDURL)->HideCaret();
722 void CFileDiffDlg::OnBnClickedSwitchleftright()
725 if (m_bThreadRunning)
726 return;
728 #if 0
729 CString sFilterString;
730 m_cFilter.GetWindowText(sFilterString);
732 m_cFileList.SetRedraw(false);
733 m_cFileList.DeleteAllItems();
734 for (int i=0; i<(int)m_arFileList.GetCount(); ++i)
736 CTGitPath fd = m_arFileList[i];
737 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)
738 fd.m_Action = CTGitPath::LOGACTIONS_DELETED;
739 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)
740 fd.m_Action = CTGitPath::LOGACTIONS_ADDED;
741 std::swap(fd.m_StatAdd, fd.m_StatDel);
742 (CTGitPath&)m_arFileList[i] = fd;
744 Filter(sFilterString);
745 #endif
747 m_cFileList.SetRedraw(true);
748 CTGitPath path = m_path1;
749 m_path1 = m_path2;
750 m_path2 = path;
751 GitRev rev = m_rev1;
752 m_rev1 = m_rev2;
753 m_rev2 = rev;
755 CString str1,str2;
756 this->m_ctrRev1Edit.GetWindowText(str1);
757 this->m_ctrRev2Edit.GetWindowText(str2);
759 this->m_ctrRev1Edit.SetWindowText(str2);
760 this->m_ctrRev2Edit.SetWindowText(str1);
762 SetURLLabels();
763 //KillTimer(IDT_INPUT);
766 void CFileDiffDlg::SetURLLabels(int mask)
769 // m_cRev1Btn.SetWindowText(m_rev1.m_CommitHash.ToString().Left(6));
770 // m_cRev2Btn.SetWindowText(m_rev2.m_CommitHash.ToString().Left(6));
772 if(mask &0x1)
774 SetDlgItemText(IDC_FIRSTURL, m_rev1.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev1.GetSubject());
775 m_tooltips.AddTool(IDC_FIRSTURL,
776 CLoglistUtils::FormatDateAndTime(m_rev1.GetAuthorDate(), DATE_SHORTDATE, false) + _T(" ") + m_rev1.GetAuthorName());
780 if(mask &0x2)
782 SetDlgItemText(IDC_SECONDURL,m_rev2.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev2.GetSubject());
784 m_tooltips.AddTool(IDC_SECONDURL,
785 CLoglistUtils::FormatDateAndTime(m_rev2.GetAuthorDate(), DATE_SHORTDATE, false) + _T(" ") + m_rev2.GetAuthorName());
788 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASE)));
789 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1)));
791 if( (mask&0x3) == 0x3)
792 if(m_rev2.GetCommitterDate() > m_rev1.GetCommitterDate())
794 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASENEWER)));
796 else
798 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1NEWER)));
802 void CFileDiffDlg::ClearURLabels(int mask)
804 if(mask&0x1)
806 SetDlgItemText(IDC_FIRSTURL, _T(""));
807 m_tooltips.AddTool(IDC_FIRSTURL, _T(""));
810 if(mask&0x2)
812 SetDlgItemText(IDC_SECONDURL, _T(""));
813 m_tooltips.AddTool(IDC_SECONDURL, _T(""));
816 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)
818 m_tooltips.RelayEvent(pMsg);
819 if (pMsg->message == WM_KEYDOWN)
821 switch (pMsg->wParam)
823 case 'A':
825 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
827 // select all entries
828 for (int i=0; i<m_cFileList.GetItemCount(); ++i)
830 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
832 return TRUE;
835 break;
836 case 'C':
837 case VK_INSERT:
839 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
841 CopySelectionToClipboard();
842 return TRUE;
845 break;
846 case '\r':
848 if (GetFocus() == GetDlgItem(IDC_FILELIST))
850 // Return pressed in file list. Show diff, as for double click
851 int selIndex = m_cFileList.GetSelectionMark();
852 if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))
853 DoDiff(selIndex, m_bBlame);
854 return TRUE;
857 break;
858 case VK_F5:
860 OnTimer(IDT_INPUT);
862 break;
865 return __super::PreTranslateMessage(pMsg);
868 void CFileDiffDlg::OnCancel()
870 if (m_bThreadRunning)
872 m_bCancelled = true;
873 return;
875 __super::OnCancel();
878 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)
880 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
881 if (m_bThreadRunning)
882 return;
884 if (m_nSortedColumn == phdr->iItem)
885 m_bAscending = !m_bAscending;
886 else
887 m_bAscending = TRUE;
888 m_nSortedColumn = phdr->iItem;
889 Sort();
891 CString temp;
892 m_cFileList.SetRedraw(FALSE);
893 m_cFileList.DeleteAllItems();
894 m_cFilter.GetWindowText(temp);
895 Filter(temp);
897 CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();
898 HDITEM HeaderItem = {0};
899 HeaderItem.mask = HDI_FORMAT;
900 for (int i=0; i<pHeader->GetItemCount(); ++i)
902 pHeader->GetItem(i, &HeaderItem);
903 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
904 pHeader->SetItem(i, &HeaderItem);
906 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
907 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
908 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
910 m_cFileList.SetRedraw(TRUE);
912 *pResult = 0;
915 void CFileDiffDlg::Sort()
917 if(m_arFileList.GetCount() < 2)
919 return;
922 std::sort(m_arFileList.m_paths.begin(), m_arFileList.m_paths.end(), &CFileDiffDlg::SortCompare);
925 bool CFileDiffDlg::SortCompare(const CTGitPath& Data1, const CTGitPath& Data2)
927 int result = 0;
928 int d1, d2;
929 switch (m_nSortedColumn)
931 case 0: //path column
932 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
933 break;
934 case 1: //extension column
935 result = Data1.GetFileExtension().Compare(Data2.GetFileExtension());
936 break;
937 case 2: //action column
938 result = Data1.m_Action - Data2.m_Action;
939 break;
940 case 3:
941 d1 = CSorter::A2L(Data1.m_StatAdd);
942 d2 = CSorter::A2L(Data2.m_StatAdd);
943 result = d1 - d2;
944 break;
945 case 4:
946 d1 = CSorter::A2L(Data1.m_StatDel);;
947 d2 = CSorter::A2L(Data2.m_StatDel);
948 result = d1 - d2;
949 break;
950 default:
951 break;
954 if (!m_bAscending)
955 result = -result;
956 return result < 0;
960 void CFileDiffDlg::OnBnClickedRev1btn()
962 ClickRevButton(&this->m_cRev1Btn,&this->m_rev1, &this->m_ctrRev1Edit);
965 void CFileDiffDlg::ClickRevButton(CMenuButton *button, GitRev *rev, CACEdit *edit)
967 INT_PTR entry=button->GetCurrentEntry();
968 if(entry == 0) /* Browse Refence*/
971 CString str = CBrowseRefsDlg::PickRef();
972 if(str.IsEmpty())
973 return;
975 if(FillRevFromString(rev,str))
976 return;
978 edit->SetWindowText(str);
982 if(entry == 1) /*Log*/
984 CLogDlg dlg;
985 dlg.SetSelect(true);
986 if(dlg.DoModal() == IDOK)
988 if( dlg.GetSelectedHash().IsEmpty() )
989 return;
991 if(FillRevFromString(rev,dlg.GetSelectedHash()))
992 return;
994 edit->SetWindowText(dlg.GetSelectedHash());
997 else
998 return;
1001 if(entry == 2) /*RefLog*/
1003 CRefLogDlg dlg;
1004 if(dlg.DoModal() == IDOK)
1006 if(FillRevFromString(rev,dlg.m_SelectedHash))
1007 return;
1009 edit->SetWindowText(dlg.m_SelectedHash);
1012 else
1013 return;
1016 SetURLLabels();
1018 InterlockedExchange(&m_bThreadRunning, TRUE);
1019 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1021 InterlockedExchange(&m_bThreadRunning, FALSE);
1022 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1024 KillTimer(IDT_INPUT);
1027 void CFileDiffDlg::OnBnClickedRev2btn()
1029 ClickRevButton(&this->m_cRev2Btn,&this->m_rev2, &this->m_ctrRev2Edit);
1032 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1034 if (m_bThreadRunning)
1036 SetTimer(IDT_FILTER, 1000, NULL);
1037 return 0L;
1040 KillTimer(IDT_FILTER);
1042 m_cFileList.SetRedraw(FALSE);
1043 m_arFilteredList.clear();
1044 m_cFileList.DeleteAllItems();
1046 Filter(_T(""));
1048 m_cFileList.SetRedraw(TRUE);
1049 return 0L;
1052 void CFileDiffDlg::OnEnChangeFilter()
1054 SetTimer(IDT_FILTER, 1000, NULL);
1057 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)
1059 if (m_bThreadRunning)
1060 return;
1062 if( nIDEvent == IDT_FILTER)
1064 CString sFilterText;
1065 KillTimer(IDT_FILTER);
1066 m_cFilter.GetWindowText(sFilterText);
1068 m_cFileList.SetRedraw(FALSE);
1069 m_cFileList.DeleteAllItems();
1071 Filter(sFilterText);
1073 m_cFileList.SetRedraw(TRUE);
1075 __super::OnTimer(nIDEvent);
1078 if( nIDEvent == IDT_INPUT)
1080 KillTimer(IDT_INPUT);
1081 TRACE(_T("Input Timer\r\n"));
1083 GitRev gitrev;
1084 CString str;
1085 int mask = 0;
1086 this->m_ctrRev1Edit.GetWindowText(str);
1087 if( !gitrev.GetCommit(str) )
1089 this->m_rev1=gitrev;
1090 mask |= 0x1;
1093 this->m_ctrRev2Edit.GetWindowText(str);
1095 if( !gitrev.GetCommit(str) )
1097 this->m_rev2=gitrev;
1098 mask |= 0x2;
1101 this->SetURLLabels(mask);
1103 if(mask == 0x3)
1106 InterlockedExchange(&m_bThreadRunning, TRUE);
1107 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1109 InterlockedExchange(&m_bThreadRunning, FALSE);
1110 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1116 void CFileDiffDlg::Filter(CString sFilterText)
1118 sFilterText.MakeLower();
1120 m_arFilteredList.clear();
1122 for (int i=0;i<m_arFileList.GetCount();i++)
1124 CString sPath = m_arFileList[i].GetGitPathString();
1125 sPath.MakeLower();
1126 if (sPath.Find(sFilterText) >= 0)
1128 m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));
1131 for (std::vector<CTGitPath*>::const_iterator it = m_arFilteredList.begin(); it != m_arFilteredList.end(); ++it)
1133 AddEntry(*it);
1137 void CFileDiffDlg::CopySelectionToClipboard(BOOL isFull)
1139 // copy all selected paths to the clipboard
1140 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1141 int index;
1142 CString sTextForClipboard;
1143 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1145 sTextForClipboard += m_cFileList.GetItemText(index, 0);
1146 sTextForClipboard += _T("\t");
1148 if(!isFull)
1150 sTextForClipboard += _T("\r\n");
1153 else
1155 sTextForClipboard += m_cFileList.GetItemText(index, 1);
1156 sTextForClipboard += _T("\t");
1157 sTextForClipboard += m_cFileList.GetItemText(index, 2);
1158 sTextForClipboard += _T("\t");
1159 sTextForClipboard += m_cFileList.GetItemText(index, 3);
1160 sTextForClipboard += _T("\t");
1161 sTextForClipboard += m_cFileList.GetItemText(index, 4);
1162 sTextForClipboard += _T("\r\n");
1165 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);
1169 LRESULT CFileDiffDlg::OnRefLoad(WPARAM /*wParam*/, LPARAM /*lParam*/)
1171 for(int i=0;i<m_Reflist.size();i++)
1173 CString str=m_Reflist[i];
1175 if(str.Find(_T("remotes/")) == 0)
1176 str=str.Mid(8);
1178 m_ctrRev1Edit.AddSearchString(str);
1179 m_ctrRev2Edit.AddSearchString(str);
1181 return 0;
1184 BOOL CFileDiffDlg::DestroyWindow()
1186 return CResizableStandAloneDialog::DestroyWindow();
1189 LRESULT CFileDiffDlg::OnEnUpdate(WPARAM /*wParam*/, LPARAM lParam)
1191 if(lParam == IDC_REV1EDIT)
1193 OnTextUpdate(&this->m_ctrRev1Edit);
1194 ClearURLabels(1);
1196 if(lParam == IDC_REV2EDIT)
1198 OnTextUpdate(&this->m_ctrRev2Edit);
1199 ClearURLabels(1<<1);
1201 return 0;
1204 void CFileDiffDlg::OnTextUpdate(CACEdit * /*pEdit*/)
1206 SetTimer(IDT_INPUT, 1000, NULL);
1207 this->m_cFileList.ShowText(_T("Wait For input validate version"));
1210 int CFileDiffDlg::RevertSelectedItemToVersion(CString rev)
1212 if (rev.IsEmpty() || rev == GIT_REV_ZERO)
1213 return 0;
1215 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1216 int index;
1217 int count = 0;
1218 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1220 CString cmd, out;
1221 CTGitPath *fentry = (CTGitPath *)m_arFilteredList[index];
1222 cmd.Format(_T("git.exe checkout %s -- \"%s\""), rev, fentry->GetGitPathString());
1223 if (g_Git.Run(cmd, &out, CP_UTF8))
1225 if (CMessageBox::Show(NULL, out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
1226 break;
1228 else
1229 count++;
1232 CString out;
1233 out.Format(IDS_STATUSLIST_FILESREVERTED, count, rev);
1234 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK);
1235 return 0;