SyncDlg: Make check if local branch fast-fowards to remote branch work again
[TortoiseGit.git] / src / TortoiseProc / FileDiffDlg.cpp
blob64322bea264338a62847877f55596600f070517d
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2012 - 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
51 BOOL CFileDiffDlg::m_bAscending = FALSE;
52 int CFileDiffDlg::m_nSortedColumn = -1;
55 IMPLEMENT_DYNAMIC(CFileDiffDlg, CResizableStandAloneDialog)
56 CFileDiffDlg::CFileDiffDlg(CWnd* pParent /*=NULL*/)
57 : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent),
58 m_bBlame(false),
59 m_pProgDlg(NULL),
60 m_bCancelled(false)
62 m_bLoadingRef=FALSE;
65 CFileDiffDlg::~CFileDiffDlg()
69 void CFileDiffDlg::DoDataExchange(CDataExchange* pDX)
71 CResizableStandAloneDialog::DoDataExchange(pDX);
72 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
73 DDX_Control(pDX, IDC_SWITCHLEFTRIGHT, m_SwitchButton);
74 DDX_Control(pDX, IDC_REV1BTN, m_cRev1Btn);
75 DDX_Control(pDX, IDC_REV2BTN, m_cRev2Btn);
76 DDX_Control(pDX, IDC_FILTER, m_cFilter);
77 DDX_Control(pDX, IDC_REV1EDIT, m_ctrRev1Edit);
78 DDX_Control(pDX, IDC_REV2EDIT, m_ctrRev2Edit);
82 BEGIN_MESSAGE_MAP(CFileDiffDlg, CResizableStandAloneDialog)
83 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
84 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
85 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
86 ON_WM_CONTEXTMENU()
87 ON_WM_SETCURSOR()
88 ON_EN_SETFOCUS(IDC_SECONDURL, &CFileDiffDlg::OnEnSetfocusSecondurl)
89 ON_EN_SETFOCUS(IDC_FIRSTURL, &CFileDiffDlg::OnEnSetfocusFirsturl)
90 ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT, &CFileDiffDlg::OnBnClickedSwitchleftright)
91 ON_NOTIFY(HDN_ITEMCLICK, 0, &CFileDiffDlg::OnHdnItemclickFilelist)
92 ON_BN_CLICKED(IDC_REV1BTN, &CFileDiffDlg::OnBnClickedRev1btn)
93 ON_BN_CLICKED(IDC_REV2BTN, &CFileDiffDlg::OnBnClickedRev2btn)
94 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
95 ON_EN_CHANGE(IDC_FILTER, &CFileDiffDlg::OnEnChangeFilter)
96 ON_WM_TIMER()
97 ON_MESSAGE(ENAC_UPDATE, &CFileDiffDlg::OnEnUpdate)
98 ON_MESSAGE(MSG_REF_LOADED, OnRefLoad)
99 END_MESSAGE_MAP()
102 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1, GitRev rev2)
104 if(path!=NULL)
106 m_path1 = *path;
107 m_path2 = *path;
108 m_sFilter = path->GetGitPathString();
110 m_rev1 = rev1;
111 m_rev2 = rev2;
114 void CFileDiffDlg::SetDiff(CTGitPath * path, CString &hash1, CString &hash2)
116 if(path!=NULL)
118 m_path1 = *path;
119 m_path2 = *path;
120 m_sFilter = path->GetGitPathString();
123 BYTE_VECTOR logout;
125 if(hash1 == GIT_REV_ZERO)
127 m_rev1.m_CommitHash.Empty();
128 m_rev1.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
130 else
132 m_rev1.GetCommit(hash1);
135 logout.clear();
137 if(hash2 == GIT_REV_ZERO)
139 m_rev2.m_CommitHash.Empty();
140 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_git_DEPTH_WORKING));
142 else
144 m_rev2.GetCommit(hash2);
148 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1)
150 if(path!=NULL)
152 m_path1 = *path;
153 m_path2 = *path;
154 m_sFilter = path->GetGitPathString();
156 m_rev1 = rev1;
157 m_rev2.m_CommitHash.Empty();
158 m_rev2.GetSubject() = CString(MAKEINTRESOURCE(IDS_PROC_PREVIOUSVERSION));
160 //this->GetDlgItem()->EnableWindow(FALSE);
163 BOOL CFileDiffDlg::OnInitDialog()
165 CResizableStandAloneDialog::OnInitDialog();
166 CString temp;
168 CString sWindowTitle;
169 GetWindowText(sWindowTitle);
170 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
172 this->m_ctrRev1Edit.Init();
173 this->m_ctrRev2Edit.Init();
175 m_tooltips.Create(this);
176 m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);
178 m_cFileList.SetRedraw(false);
179 m_cFileList.DeleteAllItems();
180 DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
181 m_cFileList.SetExtendedStyle(exStyle);
183 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
184 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
186 m_SwitchButton.SetImage((HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
187 m_SwitchButton.Invalidate();
189 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
190 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
191 temp.LoadString(IDS_FILEDIFF_FILTERCUE);
192 temp = _T(" ")+temp;
193 m_cFilter.SetCueBanner(temp);
194 if (!m_sFilter.IsEmpty())
195 m_cFilter.SetWindowText(m_sFilter);
197 int c = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
198 while (c>=0)
199 m_cFileList.DeleteColumn(c--);
201 temp.LoadString(IDS_FILEDIFF_FILE);
202 m_cFileList.InsertColumn(0, temp);
203 temp.LoadString(IDS_FILEDIFF_ACTION);
204 m_cFileList.InsertColumn(1, temp);
206 temp.LoadString(IDS_FILEDIFF_STATADD);
207 m_cFileList.InsertColumn(2, temp);
208 temp.LoadString(IDS_FILEDIFF_STATDEL);
209 m_cFileList.InsertColumn(3, temp);
211 int mincol = 0;
212 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
213 int col;
214 for (col = mincol; col <= maxcol; col++)
216 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
219 m_cFileList.SetRedraw(true);
221 AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);
222 AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);
223 AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);
224 AddAnchor(IDC_REV1BTN, TOP_RIGHT);
225 //AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);
226 AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);
227 AddAnchor(IDC_REV2BTN, TOP_RIGHT);
228 AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);
229 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
230 AddAnchor(IDC_REV1GROUP,TOP_LEFT,TOP_RIGHT);
231 AddAnchor(IDC_REV2GROUP,TOP_LEFT,TOP_RIGHT);
232 AddAnchor(IDC_REV1EDIT,TOP_LEFT);
233 AddAnchor(IDC_REV2EDIT,TOP_LEFT);
235 EnableSaveRestore(_T("FileDiffDlg"));
237 if(this->m_strRev1.IsEmpty())
238 this->m_ctrRev1Edit.SetWindowText(this->m_rev1.m_CommitHash.ToString());
239 else
241 if(m_rev1.GetCommit(this->m_strRev1))
243 CString msg;
244 msg.Format(IDS_PROC_REFINVALID, this->m_strRev1);
245 this->m_FileListText += msg;
248 this->m_ctrRev1Edit.SetWindowText(m_strRev1);
251 if(this->m_strRev2.IsEmpty())
252 this->m_ctrRev2Edit.SetWindowText(this->m_rev2.m_CommitHash.ToString());
253 else
255 if(m_rev2.GetCommit(this->m_strRev2))
257 CString msg;
258 msg.Format(IDS_PROC_REFINVALID, this->m_strRev2);
259 this->m_FileListText += msg;
262 this->m_ctrRev2Edit.SetWindowText(m_strRev2);
265 SetURLLabels();
267 InterlockedExchange(&m_bThreadRunning, TRUE);
268 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
270 InterlockedExchange(&m_bThreadRunning, FALSE);
271 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
274 InterlockedExchange(&m_bLoadingRef, TRUE);
275 if (AfxBeginThread(LoadRefThreadEntry, this)==NULL)
277 InterlockedExchange(&m_bLoadingRef, FALSE);
278 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
281 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
282 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
283 this->m_cRev1Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
285 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFBROWSE)));
286 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_LOG)));
287 this->m_cRev2Btn.AddEntry(CString(MAKEINTRESOURCE(IDS_REFLOG)));
289 // Start with focus on file list
290 GetDlgItem(IDC_FILELIST)->SetFocus();
292 if(m_rev2.m_CommitHash.IsEmpty())
293 m_SwitchButton.EnableWindow(FALSE);
295 KillTimer(IDT_INPUT);
296 return FALSE;
299 #if 0
300 svn_error_t* CFileDiffDlg::DiffSummarizeCallback(const CTGitPath& path,
301 svn_client_diff_summarize_kind_t kind,
302 bool propchanged, svn_node_kind_t node)
304 CTGitPath* fd;
305 fd.path = path;
306 fd.kind = kind;
307 fd.node = node;
308 fd.propchanged = propchanged;
309 m_arFileList.push_back(fd);
310 return Git_NO_ERROR;
312 #endif
314 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)
316 return ((CFileDiffDlg*)pVoid)->DiffThread();
319 UINT CFileDiffDlg::DiffThread()
321 RefreshCursor();
322 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));
323 m_cFileList.DeleteAllItems();
324 m_arFileList.Clear();
325 EnableInputControl(false);
326 #if 0
327 bool bSuccess = true;
328 if (m_bDoPegDiff)
330 // bSuccess = DiffSummarizePeg(m_path1, m_peg, m_rev1, m_rev2, m_depth, m_bIgnoreancestry);
332 else
334 // bSuccess = DiffSummarize(m_path1, m_rev1, m_path2, m_rev2, m_depth, m_bIgnoreancestry);
336 // if (!bSuccess)
337 // {
338 // m_cFileList.ShowText(GetLastErrorMessage());
339 // InterlockedExchange(&m_bThreadRunning, FALSE);
340 // return 0;
341 // }
342 #endif
344 if( m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty())
345 g_Git.RefreshGitIndex();
347 g_Git.GetCommitDiffList(m_rev1.m_CommitHash.ToString(),m_rev2.m_CommitHash.ToString(),m_arFileList);
349 CString sFilterText;
350 m_cFilter.GetWindowText(sFilterText);
351 m_cFileList.SetRedraw(false);
352 Filter(sFilterText);
353 if (m_arFileList.GetCount()>0)
355 // Highlight first entry in file list
356 m_cFileList.SetSelectionMark(0);
357 m_cFileList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
360 int mincol = 0;
361 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
362 int col;
363 for (col = mincol; col <= maxcol; col++)
365 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
368 m_cFileList.ClearText();
369 m_cFileList.SetRedraw(true);
371 InterlockedExchange(&m_bThreadRunning, FALSE);
372 InvalidateRect(NULL);
373 RefreshCursor();
374 EnableInputControl(true);
375 return 0;
378 int CFileDiffDlg::AddEntry(const CTGitPath * fd)
380 int ret = -1;
381 if (fd)
383 int index = m_cFileList.GetItemCount();
385 int icon_idx = 0;
386 // if (fd->node == svn_node_dir)
387 // icon_idx = m_nIconFolder;
388 // else
390 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());
393 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);
394 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetActionName());
395 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->m_StatAdd);
396 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatDel);
398 return ret;
401 void CFileDiffDlg::EnableInputControl(bool b)
403 this->m_ctrRev1Edit.EnableWindow(b);
404 this->m_ctrRev2Edit.EnableWindow(b);
405 this->m_cRev1Btn.EnableWindow(b);
406 this->m_cRev2Btn.EnableWindow(b);
407 m_cFilter.EnableWindow(b);
408 m_SwitchButton.EnableWindow(b);
411 void CFileDiffDlg::DoDiff(int selIndex, bool blame)
413 CGitDiff diff;
414 CTGitPath* fd2 = m_arFilteredList[selIndex];
415 CTGitPath* fd1 = fd2;
416 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
417 fd1 = new CTGitPath(fd2->GetGitOldPathString());
418 diff.Diff(fd2, fd1, this->m_rev1.m_CommitHash.ToString(), this->m_rev2.m_CommitHash.ToString(), blame, FALSE);
422 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
424 *pResult = 0;
425 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
426 int selIndex = pNMLV->iItem;
427 if (selIndex < 0)
428 return;
429 if (selIndex >= (int)m_arFilteredList.size())
430 return;
432 DoDiff(selIndex, m_bBlame);
435 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
438 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
439 if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())
440 return;
442 CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();
443 if (pGetInfoTip->cchTextMax > path.GetLength())
444 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax);
446 *pResult = 0;
449 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
451 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
452 // Take the default processing unless we set this to something else below.
453 *pResult = CDRF_DODEFAULT;
455 // First thing - check the draw stage. If it's the control's prepaint
456 // stage, then tell Windows we want messages for every item.
458 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
460 *pResult = CDRF_NOTIFYITEMDRAW;
462 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
464 // This is the prepaint stage for an item. Here's where we set the
465 // item's text color. Our return value will tell Windows to draw the
466 // item itself, but it will use the new color we set here.
468 // Tell Windows to paint the control itself.
469 *pResult = CDRF_DODEFAULT;
471 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
473 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)
475 CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];
476 switch (fd->m_Action)
478 case CTGitPath::LOGACTIONS_ADDED:
479 crText = m_colors.GetColor(CColors::Added);
480 break;
481 case CTGitPath::LOGACTIONS_DELETED:
482 crText = m_colors.GetColor(CColors::Deleted);
483 break;
484 case CTGitPath::LOGACTIONS_MODIFIED:
485 crText = m_colors.GetColor(CColors::Modified);
486 break;
487 //case svn_client_diff_summarize_kind_normal:
488 default:
489 //if (fd.propchanged)
490 crText = m_colors.GetColor(CColors::PropertyChanged);
491 break;
494 // Store the color back in the NMLVCUSTOMDRAW struct.
495 pLVCD->clrText = crText;
499 UINT CFileDiffDlg::LoadRefThread()
501 g_Git.GetBranchList(m_Reflist,NULL,CGit::BRANCH_ALL_F);
502 g_Git.GetTagList(m_Reflist);
504 this->PostMessage(MSG_REF_LOADED);
505 InterlockedExchange(&m_bLoadingRef, FALSE);
506 return 0;
509 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)
511 if ((pWnd==0)||(pWnd != &m_cFileList))
512 return;
513 if (m_cFileList.GetSelectedCount() == 0)
514 return;
515 // if the context menu is invoked through the keyboard, we have to use
516 // a calculated position on where to anchor the menu on
517 if ((point.x == -1) && (point.y == -1))
519 CRect rect;
520 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);
521 m_cFileList.ClientToScreen(&rect);
522 point = rect.CenterPoint();
524 CIconMenu popup;
525 if (popup.CreatePopupMenu())
527 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
528 popup.AppendMenuIcon(ID_GNUDIFFCOMPARE, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
529 popup.AppendMenu(MF_SEPARATOR, NULL);
530 popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG);
531 popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME);
532 popup.AppendMenu(MF_SEPARATOR, NULL);
533 popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT);
534 popup.AppendMenu(MF_SEPARATOR, NULL);
535 popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS);
536 popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
537 popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
539 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
540 m_bCancelled = false;
541 switch (cmd)
543 case ID_COMPARE:
545 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
546 while (pos)
548 int index = m_cFileList.GetNextSelectedItem(pos);
549 DoDiff(index, false);
552 break;
553 case ID_GNUDIFFCOMPARE:
555 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
556 while (pos)
558 CTGitPath *fd2 = m_arFilteredList[m_cFileList.GetNextSelectedItem(pos)];
559 CTGitPath *fd1 = fd2;
560 if (fd2->m_Action & CTGitPath::LOGACTIONS_REPLACED)
561 fd1 = new CTGitPath(fd2->GetGitOldPathString());
562 CAppUtils::StartShowUnifiedDiff(m_hWnd, *fd2, m_rev2.m_CommitHash.ToString(), *fd1, m_rev1.m_CommitHash.ToString());
565 break;
566 case ID_BLAME:
568 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
569 while (pos)
571 int index = m_cFileList.GetNextSelectedItem(pos);
572 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString());
575 break;
576 case ID_LOG:
578 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
579 while (pos)
581 int index = m_cFileList.GetNextSelectedItem(pos);
582 CString cmd = _T("/command:log");
583 cmd += _T(" /path:\"")+m_arFilteredList[index]->GetWinPathString()+_T("\" ");
584 cmd += _T(" /endrev:")+m_rev1.m_CommitHash.ToString();
585 CAppUtils::RunTortoiseProc(cmd);
588 break;
589 case ID_SAVEAS:
591 if (m_cFileList.GetSelectedCount() > 0)
593 CString temp;
594 CTGitPath savePath;
595 CString pathSave;
596 if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd))
598 break;
600 savePath = CTGitPath(pathSave);
602 // now open the selected file for writing
605 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
606 // temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString());
607 file.WriteString(temp + _T("\n"));
608 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
609 while (pos)
611 int index = m_cFileList.GetNextSelectedItem(pos);
612 CTGitPath* fd = m_arFilteredList[index];
613 file.WriteString(fd->GetGitPathString());
614 file.WriteString(_T("\n"));
616 file.Close();
618 catch (CFileException* pE)
620 pE->ReportError();
624 break;
625 case ID_CLIPBOARD_PATH:
627 CopySelectionToClipboard();
629 break;
631 case ID_CLIPBOARD_ALL:
633 CopySelectionToClipboard(TRUE);
635 break;
636 case ID_EXPORT:
638 // export all changed files to a folder
639 CBrowseFolder browseFolder;
640 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
641 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK)
643 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
644 while (pos)
646 int index = m_cFileList.GetNextSelectedItem(pos);
647 CTGitPath* fd = m_arFilteredList[index];
648 // we cannot export directories or folders
649 if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory())
650 continue;
651 CAppUtils::CreateMultipleDirectory(m_strExportDir + _T("\\") + fd->GetDirectory().GetWinPathString());
652 CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString();
653 if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO)
655 if(!CopyFile(g_Git.m_CurrentDir + _T("\\") + fd->GetWinPath(), filename, false))
657 MessageBox(CFormatMessageWrapper(), _T("TortoiseGit"), MB_OK | MB_ICONERROR);
658 return;
661 else
663 if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename))
665 CString out;
666 out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, fd->GetGitPathString(), m_rev1.m_CommitHash.ToString(), filename);
667 if (CMessageBox::Show(NULL, out, _T("TortoiseGit"), 2, IDI_WARNING, CString(MAKEINTRESOURCE(IDS_IGNOREBUTTON)), CString(MAKEINTRESOURCE(IDS_ABORTBUTTON))) == 2)
668 return;
675 break;
681 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
683 if (pWnd != &m_cFileList)
684 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
685 if (m_bThreadRunning == 0)
687 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
688 SetCursor(hCur);
689 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
691 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
692 SetCursor(hCur);
693 return TRUE;
696 void CFileDiffDlg::OnEnSetfocusFirsturl()
698 GetDlgItem(IDC_FIRSTURL)->HideCaret();
701 void CFileDiffDlg::OnEnSetfocusSecondurl()
703 GetDlgItem(IDC_SECONDURL)->HideCaret();
707 void CFileDiffDlg::OnBnClickedSwitchleftright()
710 if (m_bThreadRunning)
711 return;
713 #if 0
714 CString sFilterString;
715 m_cFilter.GetWindowText(sFilterString);
717 m_cFileList.SetRedraw(false);
718 m_cFileList.DeleteAllItems();
719 for (int i=0; i<(int)m_arFileList.GetCount(); ++i)
721 CTGitPath fd = m_arFileList[i];
722 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)
723 fd.m_Action = CTGitPath::LOGACTIONS_DELETED;
724 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)
725 fd.m_Action = CTGitPath::LOGACTIONS_ADDED;
726 std::swap(fd.m_StatAdd, fd.m_StatDel);
727 (CTGitPath&)m_arFileList[i] = fd;
729 Filter(sFilterString);
730 #endif
732 m_cFileList.SetRedraw(true);
733 CTGitPath path = m_path1;
734 m_path1 = m_path2;
735 m_path2 = path;
736 GitRev rev = m_rev1;
737 m_rev1 = m_rev2;
738 m_rev2 = rev;
740 CString str1,str2;
741 this->m_ctrRev1Edit.GetWindowText(str1);
742 this->m_ctrRev2Edit.GetWindowText(str2);
744 this->m_ctrRev1Edit.SetWindowText(str2);
745 this->m_ctrRev2Edit.SetWindowText(str1);
747 SetURLLabels();
748 //KillTimer(IDT_INPUT);
751 void CFileDiffDlg::SetURLLabels(int mask)
754 // m_cRev1Btn.SetWindowText(m_rev1.m_CommitHash.ToString().Left(6));
755 // m_cRev2Btn.SetWindowText(m_rev2.m_CommitHash.ToString().Left(6));
757 if(mask &0x1)
759 SetDlgItemText(IDC_FIRSTURL, m_rev1.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev1.GetSubject());
760 m_tooltips.AddTool(IDC_FIRSTURL,
761 CLoglistUtils::FormatDateAndTime(m_rev1.GetAuthorDate(), DATE_SHORTDATE, false) + _T(" ") + m_rev1.GetAuthorName());
765 if(mask &0x2)
767 SetDlgItemText(IDC_SECONDURL,m_rev2.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev2.GetSubject());
769 m_tooltips.AddTool(IDC_SECONDURL,
770 CLoglistUtils::FormatDateAndTime(m_rev2.GetAuthorDate(), DATE_SHORTDATE, false) + _T(" ") + m_rev2.GetAuthorName());
773 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASE)));
774 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1)));
776 if( (mask&0x3) == 0x3)
777 if(m_rev2.GetCommitterDate() > m_rev1.GetCommitterDate())
779 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION2BASENEWER)));
781 else
783 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_FILEDIFF_VERSION1NEWER)));
787 void CFileDiffDlg::ClearURLabels(int mask)
789 if(mask&0x1)
791 SetDlgItemText(IDC_FIRSTURL, _T(""));
792 m_tooltips.AddTool(IDC_FIRSTURL, _T(""));
795 if(mask&0x2)
797 SetDlgItemText(IDC_SECONDURL, _T(""));
798 m_tooltips.AddTool(IDC_SECONDURL, _T(""));
801 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)
803 m_tooltips.RelayEvent(pMsg);
804 if (pMsg->message == WM_KEYDOWN)
806 switch (pMsg->wParam)
808 case 'A':
810 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
812 // select all entries
813 for (int i=0; i<m_cFileList.GetItemCount(); ++i)
815 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
817 return TRUE;
820 break;
821 case 'C':
822 case VK_INSERT:
824 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
826 CopySelectionToClipboard();
827 return TRUE;
830 break;
831 case '\r':
833 if (GetFocus() == GetDlgItem(IDC_FILELIST))
835 // Return pressed in file list. Show diff, as for double click
836 int selIndex = m_cFileList.GetSelectionMark();
837 if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))
838 DoDiff(selIndex, m_bBlame);
839 return TRUE;
842 break;
845 return __super::PreTranslateMessage(pMsg);
848 void CFileDiffDlg::OnCancel()
850 if (m_bThreadRunning)
852 m_bCancelled = true;
853 return;
855 __super::OnCancel();
858 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)
860 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
861 if (m_bThreadRunning)
862 return;
864 if (m_nSortedColumn == phdr->iItem)
865 m_bAscending = !m_bAscending;
866 else
867 m_bAscending = TRUE;
868 m_nSortedColumn = phdr->iItem;
869 Sort();
871 CString temp;
872 m_cFileList.SetRedraw(FALSE);
873 m_cFileList.DeleteAllItems();
874 m_cFilter.GetWindowText(temp);
875 Filter(temp);
877 CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();
878 HDITEM HeaderItem = {0};
879 HeaderItem.mask = HDI_FORMAT;
880 for (int i=0; i<pHeader->GetItemCount(); ++i)
882 pHeader->GetItem(i, &HeaderItem);
883 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
884 pHeader->SetItem(i, &HeaderItem);
886 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
887 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
888 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
890 m_cFileList.SetRedraw(TRUE);
892 *pResult = 0;
895 void CFileDiffDlg::Sort()
897 if(m_arFileList.GetCount() < 2)
899 return;
902 std::sort(m_arFileList.m_paths.begin(), m_arFileList.m_paths.end(), &CFileDiffDlg::SortCompare);
905 bool CFileDiffDlg::SortCompare(const CTGitPath& Data1, const CTGitPath& Data2)
907 int result = 0;
908 int d1, d2;
909 switch (m_nSortedColumn)
911 case 0: //path column
912 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
913 break;
914 case 1: //action column
915 result = Data1.m_Action - Data2.m_Action;
916 break;
917 case 2:
918 d1 = CSorter::A2L(Data1.m_StatAdd);
919 d2 = CSorter::A2L(Data2.m_StatAdd);
920 result = d1 - d2;
921 break;
922 case 3:
923 d1 = CSorter::A2L(Data1.m_StatDel);;
924 d2 = CSorter::A2L(Data2.m_StatDel);
925 result = d1 - d2;
926 break;
927 default:
928 break;
931 if (!m_bAscending)
932 result = -result;
933 return result < 0;
937 void CFileDiffDlg::OnBnClickedRev1btn()
939 ClickRevButton(&this->m_cRev1Btn,&this->m_rev1, &this->m_ctrRev1Edit);
942 void CFileDiffDlg::ClickRevButton(CMenuButton *button, GitRev *rev, CACEdit *edit)
944 INT_PTR entry=button->GetCurrentEntry();
945 if(entry == 0) /* Browse Refence*/
948 CString str = CBrowseRefsDlg::PickRef();
949 if(str.IsEmpty())
950 return;
952 if(FillRevFromString(rev,str))
953 return;
955 edit->SetWindowText(str);
959 if(entry == 1) /*Log*/
961 CLogDlg dlg;
962 dlg.SetSelect(true);
963 if(dlg.DoModal() == IDOK)
965 if( dlg.GetSelectedHash().IsEmpty() )
966 return;
968 if(FillRevFromString(rev,dlg.GetSelectedHash()))
969 return;
971 edit->SetWindowText(dlg.GetSelectedHash());
974 else
975 return;
978 if(entry == 2) /*RefLog*/
980 CRefLogDlg dlg;
981 if(dlg.DoModal() == IDOK)
983 if(FillRevFromString(rev,dlg.m_SelectedHash))
984 return;
986 edit->SetWindowText(dlg.m_SelectedHash);
989 else
990 return;
993 SetURLLabels();
995 InterlockedExchange(&m_bThreadRunning, TRUE);
996 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
998 InterlockedExchange(&m_bThreadRunning, FALSE);
999 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1001 KillTimer(IDT_INPUT);
1004 void CFileDiffDlg::OnBnClickedRev2btn()
1006 ClickRevButton(&this->m_cRev2Btn,&this->m_rev2, &this->m_ctrRev2Edit);
1009 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1011 if (m_bThreadRunning)
1013 SetTimer(IDT_FILTER, 1000, NULL);
1014 return 0L;
1017 KillTimer(IDT_FILTER);
1019 m_cFileList.SetRedraw(FALSE);
1020 m_arFilteredList.clear();
1021 m_cFileList.DeleteAllItems();
1023 Filter(_T(""));
1025 m_cFileList.SetRedraw(TRUE);
1026 return 0L;
1029 void CFileDiffDlg::OnEnChangeFilter()
1031 SetTimer(IDT_FILTER, 1000, NULL);
1034 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)
1036 if (m_bThreadRunning)
1037 return;
1039 if( nIDEvent == IDT_FILTER)
1041 CString sFilterText;
1042 KillTimer(IDT_FILTER);
1043 m_cFilter.GetWindowText(sFilterText);
1045 m_cFileList.SetRedraw(FALSE);
1046 m_cFileList.DeleteAllItems();
1048 Filter(sFilterText);
1050 m_cFileList.SetRedraw(TRUE);
1052 __super::OnTimer(nIDEvent);
1055 if( nIDEvent == IDT_INPUT)
1057 KillTimer(IDT_INPUT);
1058 TRACE(_T("Input Timer\r\n"));
1060 GitRev gitrev;
1061 CString str;
1062 int mask = 0;
1063 this->m_ctrRev1Edit.GetWindowText(str);
1064 if( !gitrev.GetCommit(str) )
1066 this->m_rev1=gitrev;
1067 mask |= 0x1;
1070 this->m_ctrRev2Edit.GetWindowText(str);
1072 if( !gitrev.GetCommit(str) )
1074 this->m_rev2=gitrev;
1075 mask |= 0x2;
1078 this->SetURLLabels(mask);
1080 if(mask == 0x3)
1083 InterlockedExchange(&m_bThreadRunning, TRUE);
1084 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1086 InterlockedExchange(&m_bThreadRunning, FALSE);
1087 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1093 void CFileDiffDlg::Filter(CString sFilterText)
1095 sFilterText.MakeLower();
1097 m_arFilteredList.clear();
1099 for (int i=0;i<m_arFileList.GetCount();i++)
1101 CString sPath = m_arFileList[i].GetGitPathString();
1102 sPath.MakeLower();
1103 if (sPath.Find(sFilterText) >= 0)
1105 m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));
1108 for (std::vector<CTGitPath*>::const_iterator it = m_arFilteredList.begin(); it != m_arFilteredList.end(); ++it)
1110 AddEntry(*it);
1114 void CFileDiffDlg::CopySelectionToClipboard(BOOL isFull)
1116 // copy all selected paths to the clipboard
1117 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1118 int index;
1119 CString sTextForClipboard;
1120 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1122 sTextForClipboard += m_cFileList.GetItemText(index, 0);
1123 sTextForClipboard += _T("\t");
1125 if(!isFull)
1127 sTextForClipboard += _T("\r\n");
1130 else
1132 sTextForClipboard += m_cFileList.GetItemText(index, 1);
1133 sTextForClipboard += _T("\t");
1134 sTextForClipboard += m_cFileList.GetItemText(index, 2);
1135 sTextForClipboard += _T("\t");
1136 sTextForClipboard += m_cFileList.GetItemText(index, 3);
1137 sTextForClipboard += _T("\r\n");
1140 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);
1144 LRESULT CFileDiffDlg::OnRefLoad(WPARAM wParam, LPARAM lParam)
1146 UNREFERENCED_PARAMETER(wParam);
1147 UNREFERENCED_PARAMETER(lParam);
1148 for(int i=0;i<m_Reflist.size();i++)
1150 CString str=m_Reflist[i];
1152 if(str.Find(_T("remotes/")) == 0)
1153 str=str.Mid(8);
1155 m_ctrRev1Edit.AddSearchString(str);
1156 m_ctrRev2Edit.AddSearchString(str);
1158 return 0;
1161 BOOL CFileDiffDlg::DestroyWindow()
1163 return CResizableStandAloneDialog::DestroyWindow();
1166 LRESULT CFileDiffDlg::OnEnUpdate(WPARAM /*wParam*/, LPARAM lParam)
1168 if(lParam == IDC_REV1EDIT)
1170 OnTextUpdate(&this->m_ctrRev1Edit);
1171 ClearURLabels(1);
1173 if(lParam == IDC_REV2EDIT)
1175 OnTextUpdate(&this->m_ctrRev2Edit);
1176 ClearURLabels(1<<1);
1178 return 0;
1181 void CFileDiffDlg::OnTextUpdate(CACEdit * /*pEdit*/)
1183 SetTimer(IDT_INPUT, 1000, NULL);
1184 this->m_cFileList.ShowText(_T("Wait For input validate version"));