1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "TortoiseProc.h"
21 #include "UnicodeUtils.h"
22 #include "MessageBox.h"
25 #include "ProgressDlg.h"
26 #include "SysImageList.h"
27 //#include "GitProperties.h"
28 #include "StringUtils.h"
29 #include "PathUtils.h"
30 #include "BrowseFolder.h"
31 #include "RevisionDlg.h"
32 #include ".\filediffdlg.h"
34 #include "CommonResource.h"
40 #define ID_CLIPBOARD 5
42 BOOL
CFileDiffDlg::m_bAscending
= FALSE
;
43 int CFileDiffDlg::m_nSortedColumn
= -1;
46 IMPLEMENT_DYNAMIC(CFileDiffDlg
, CResizableStandAloneDialog
)
47 CFileDiffDlg::CFileDiffDlg(CWnd
* pParent
/*=NULL*/)
48 : CResizableStandAloneDialog(CFileDiffDlg::IDD
, pParent
),
55 CFileDiffDlg::~CFileDiffDlg()
57 DestroyIcon(m_hSwitchIcon
);
60 void CFileDiffDlg::DoDataExchange(CDataExchange
* pDX
)
62 CResizableStandAloneDialog::DoDataExchange(pDX
);
63 DDX_Control(pDX
, IDC_FILELIST
, m_cFileList
);
64 DDX_Control(pDX
, IDC_SWITCHLEFTRIGHT
, m_SwitchButton
);
65 DDX_Control(pDX
, IDC_REV1BTN
, m_cRev1Btn
);
66 DDX_Control(pDX
, IDC_REV2BTN
, m_cRev2Btn
);
67 DDX_Control(pDX
, IDC_FILTER
, m_cFilter
);
71 BEGIN_MESSAGE_MAP(CFileDiffDlg
, CResizableStandAloneDialog
)
72 ON_NOTIFY(NM_DBLCLK
, IDC_FILELIST
, OnNMDblclkFilelist
)
73 ON_NOTIFY(LVN_GETINFOTIP
, IDC_FILELIST
, OnLvnGetInfoTipFilelist
)
74 ON_NOTIFY(NM_CUSTOMDRAW
, IDC_FILELIST
, OnNMCustomdrawFilelist
)
77 ON_EN_SETFOCUS(IDC_SECONDURL
, &CFileDiffDlg::OnEnSetfocusSecondurl
)
78 ON_EN_SETFOCUS(IDC_FIRSTURL
, &CFileDiffDlg::OnEnSetfocusFirsturl
)
79 ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT
, &CFileDiffDlg::OnBnClickedSwitchleftright
)
80 ON_NOTIFY(HDN_ITEMCLICK
, 0, &CFileDiffDlg::OnHdnItemclickFilelist
)
81 ON_BN_CLICKED(IDC_REV1BTN
, &CFileDiffDlg::OnBnClickedRev1btn
)
82 ON_BN_CLICKED(IDC_REV2BTN
, &CFileDiffDlg::OnBnClickedRev2btn
)
83 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED
, OnClickedCancelFilter
)
84 ON_EN_CHANGE(IDC_FILTER
, &CFileDiffDlg::OnEnChangeFilter
)
89 void CFileDiffDlg::SetDiff(CTGitPath
* path
, GitRev rev1
, GitRev rev2
)
100 void CFileDiffDlg::SetDiff(CTGitPath
* path
, CString
&hash1
, CString
&hash2
)
110 g_Git
.GetLog(logout
,hash1
,path
,1,0);
111 m_rev1
.ParserFromLog(logout
);
115 g_Git
.GetLog(logout
,hash2
,path
,1,0);
116 m_rev2
.ParserFromLog(logout
);
118 void CFileDiffDlg::SetDiff(CTGitPath
* path
, GitRev rev1
)
126 m_rev2
.m_CommitHash
= _T("");
127 m_rev2
.m_Subject
= _T("Previou Version");
129 //this->GetDlgItem()->EnableWindow(FALSE);
134 BOOL
CFileDiffDlg::OnInitDialog()
136 CResizableStandAloneDialog::OnInitDialog();
139 m_tooltips
.Create(this);
140 m_tooltips
.AddTool(IDC_SWITCHLEFTRIGHT
, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT
);
142 m_cFileList
.SetRedraw(false);
143 m_cFileList
.DeleteAllItems();
144 DWORD exStyle
= LVS_EX_FULLROWSELECT
| LVS_EX_DOUBLEBUFFER
| LVS_EX_INFOTIP
;
145 m_cFileList
.SetExtendedStyle(exStyle
);
147 m_nIconFolder
= SYS_IMAGE_LIST().GetDirIconIndex();
148 m_cFileList
.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL
);
150 m_hSwitchIcon
= (HICON
)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
151 m_SwitchButton
.SetIcon(m_hSwitchIcon
);
153 m_cFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
154 m_cFilter
.SetInfoIcon(IDI_FILTEREDIT
);
155 temp
.LoadString(IDS_FILEDIFF_FILTERCUE
);
157 m_cFilter
.SetCueBanner(temp
);
159 int c
= ((CHeaderCtrl
*)(m_cFileList
.GetDlgItem(0)))->GetItemCount()-1;
161 m_cFileList
.DeleteColumn(c
--);
164 temp
.LoadString(IDS_FILEDIFF_FILE
);
165 m_cFileList
.InsertColumn(0, temp
);
166 temp
.LoadString(IDS_FILEDIFF_ACTION
);
167 m_cFileList
.InsertColumn(1, temp
);
169 temp
.LoadString(IDS_FILEDIFF_STATADD
);
170 m_cFileList
.InsertColumn(2, temp
);
171 temp
.LoadString(IDS_FILEDIFF_STATDEL
);
172 m_cFileList
.InsertColumn(3, temp
);
175 int maxcol
= ((CHeaderCtrl
*)(m_cFileList
.GetDlgItem(0)))->GetItemCount()-1;
177 for (col
= mincol
; col
<= maxcol
; col
++)
179 m_cFileList
.SetColumnWidth(col
,LVSCW_AUTOSIZE_USEHEADER
);
182 m_cFileList
.SetRedraw(true);
184 AddAnchor(IDC_DIFFSTATIC1
, TOP_LEFT
, TOP_RIGHT
);
185 AddAnchor(IDC_SWITCHLEFTRIGHT
, TOP_RIGHT
);
186 AddAnchor(IDC_FIRSTURL
, TOP_LEFT
, TOP_RIGHT
);
187 AddAnchor(IDC_REV1BTN
, TOP_RIGHT
);
188 AddAnchor(IDC_DIFFSTATIC2
, TOP_LEFT
, TOP_RIGHT
);
189 AddAnchor(IDC_SECONDURL
, TOP_LEFT
, TOP_RIGHT
);
190 AddAnchor(IDC_REV2BTN
, TOP_RIGHT
);
191 AddAnchor(IDC_FILTER
, TOP_LEFT
, TOP_RIGHT
);
192 AddAnchor(IDC_FILELIST
, TOP_LEFT
, BOTTOM_RIGHT
);
196 EnableSaveRestore(_T("FileDiffDlg"));
198 InterlockedExchange(&m_bThreadRunning
, TRUE
);
199 if (AfxBeginThread(DiffThreadEntry
, this)==NULL
)
201 InterlockedExchange(&m_bThreadRunning
, FALSE
);
202 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
205 // Start with focus on file list
206 GetDlgItem(IDC_FILELIST
)->SetFocus();
208 if(m_rev2
.m_CommitHash
.IsEmpty())
209 m_SwitchButton
.EnableWindow(FALSE
);
214 svn_error_t
* CFileDiffDlg::DiffSummarizeCallback(const CTGitPath
& path
,
215 svn_client_diff_summarize_kind_t kind
,
216 bool propchanged
, svn_node_kind_t node
)
222 fd
.propchanged
= propchanged
;
223 m_arFileList
.push_back(fd
);
228 UINT
CFileDiffDlg::DiffThreadEntry(LPVOID pVoid
)
230 return ((CFileDiffDlg
*)pVoid
)->DiffThread();
233 UINT
CFileDiffDlg::DiffThread()
235 bool bSuccess
= true;
237 m_cFileList
.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT
)));
238 m_arFileList
.Clear();
242 // bSuccess = DiffSummarizePeg(m_path1, m_peg, m_rev1, m_rev2, m_depth, m_bIgnoreancestry);
246 // bSuccess = DiffSummarize(m_path1, m_rev1, m_path2, m_rev2, m_depth, m_bIgnoreancestry);
250 // m_cFileList.ShowText(GetLastErrorMessage());
251 // InterlockedExchange(&m_bThreadRunning, FALSE);
256 CString rev1
=m_rev1
.m_CommitHash
;
257 if(this->m_rev1
.m_CommitHash
== GIT_REV_ZERO
|| this->m_rev2
.m_CommitHash
== GIT_REV_ZERO
)
260 if(this->m_rev1
.m_CommitHash
== GIT_REV_ZERO
)
261 cmd
.Format(_T("git.exe diff -r --raw -C -M --numstat -z %s"),m_rev2
.m_CommitHash
);
263 cmd
.Format(_T("git.exe diff -r -R --raw -C -M --numstat -z %s"),m_rev1
.m_CommitHash
);
266 cmd
.Format(_T("git.exe diff-tree -r --raw -C -M --numstat -z %s %s"),m_rev2
.m_CommitHash
,rev1
);
271 this->m_arFileList
.ParserFromLog(out
);
274 m_cFilter
.GetWindowText(sFilterText
);
275 m_cFileList
.SetRedraw(false);
277 if (m_arFileList
.GetCount()>0)
279 // Highlight first entry in file list
280 m_cFileList
.SetSelectionMark(0);
281 m_cFileList
.SetItemState(0, LVIS_SELECTED
, LVIS_SELECTED
);
285 int maxcol
= ((CHeaderCtrl
*)(m_cFileList
.GetDlgItem(0)))->GetItemCount()-1;
287 for (col
= mincol
; col
<= maxcol
; col
++)
289 m_cFileList
.SetColumnWidth(col
,LVSCW_AUTOSIZE_USEHEADER
);
292 m_cFileList
.ClearText();
293 m_cFileList
.SetRedraw(true);
295 InterlockedExchange(&m_bThreadRunning
, FALSE
);
296 InvalidateRect(NULL
);
301 int CFileDiffDlg::AddEntry(const CTGitPath
* fd
)
306 int index
= m_cFileList
.GetItemCount();
309 // if (fd->node == svn_node_dir)
310 // icon_idx = m_nIconFolder;
313 icon_idx
= SYS_IMAGE_LIST().GetPathIconIndex(fd
->GetGitPathString());
316 ret
= m_cFileList
.InsertItem(index
, fd
->GetGitPathString(), icon_idx
);
317 m_cFileList
.SetItemText(index
, 1, ((CTGitPath
*)fd
)->GetActionName());
318 m_cFileList
.SetItemText(index
, 2, ((CTGitPath
*)fd
)->m_StatAdd
);
319 m_cFileList
.SetItemText(index
, 3, ((CTGitPath
*)fd
)->m_StatDel
);
324 void CFileDiffDlg::DoDiff(int selIndex
, bool blame
)
328 CTGitPath
* fd
= m_arFilteredList
[selIndex
];
329 diff
.Diff(fd
, fd
,this->m_rev1
.m_CommitHash
, this->m_rev2
.m_CommitHash
, blame
, FALSE
);
332 CFileDiffDlg::CTGitPath
* fd
= m_arFilteredList
[selIndex
];
334 CTGitPath url1
= CTGitPath(m_path1
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString());
335 CTGitPath url2
= m_bDoPegDiff
? url1
: CTGitPath(m_path2
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString());
337 if (fd
.kind
== svn_client_diff_summarize_kind_deleted
)
339 if (!PathIsURL(url1
))
340 url1
= CTGitPath(GetURLFromPath(m_path1
) + _T("/") + fd
.path
.GetGitPathString());
341 if (!PathIsURL(url2
))
342 url2
= m_bDoPegDiff
? url1
: CTGitPath(GetURLFromPath(m_path2
) + _T("/") + fd
.path
.GetGitPathString());
349 if (fd
.node
== svn_node_dir
)
352 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, m_path1
, m_rev1
);
354 CProgressDlg progDlg
;
355 progDlg
.SetTitle(IDS_PROGRESSWAIT
);
356 progDlg
.SetAnimation(IDR_DOWNLOAD
);
357 progDlg
.ShowModeless(this);
358 progDlg
.FormatPathLine(1, IDS_PROGRESSGETFILE
, (LPCTSTR
)m_path1
.GetUIPathString());
359 progDlg
.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT
, (LPCTSTR
)m_rev1
.ToString());
361 if ((fd
.kind
!= svn_client_diff_summarize_kind_added
)&&(!blame
)&&(!Cat(url1
, m_bDoPegDiff
? m_peg
: m_rev1
, m_rev1
, tempfile
)))
363 if ((!m_bDoPegDiff
)||(!Cat(url1
, m_rev1
, m_rev1
, tempfile
)))
365 CMessageBox::Show(NULL
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
369 else if ((fd
.kind
!= svn_client_diff_summarize_kind_added
)&&(blame
)&&(!m_blamer
.BlameToFile(url1
, 1, m_rev1
, m_bDoPegDiff
? m_peg
: m_rev1
, tempfile
, _T(""), TRUE
, TRUE
)))
371 if ((!m_bDoPegDiff
)||(!m_blamer
.BlameToFile(url1
, 1, m_rev1
, m_rev1
, tempfile
, _T(""), TRUE
, TRUE
)))
373 CMessageBox::Show(NULL
, m_blamer
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
377 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
378 progDlg
.SetProgress(1, 2);
379 progDlg
.FormatPathLine(1, IDS_PROGRESSGETFILE
, (LPCTSTR
)url2
.GetUIPathString());
380 progDlg
.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT
, (LPCTSTR
)m_rev2
.ToString());
381 CTGitPath tempfile2
= CTempFiles::Instance().GetTempFilePath(false, url2
, m_rev2
);
382 if ((fd
.kind
!= svn_client_diff_summarize_kind_deleted
)&&(!blame
)&&(!Cat(url2
, m_bDoPegDiff
? m_peg
: m_rev2
, m_rev2
, tempfile2
)))
384 if ((!m_bDoPegDiff
)||(!Cat(url2
, m_rev2
, m_rev2
, tempfile2
)))
386 CMessageBox::Show(NULL
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
390 else if ((fd
.kind
!= svn_client_diff_summarize_kind_deleted
)&&(blame
)&&(!m_blamer
.BlameToFile(url2
, 1, m_bDoPegDiff
? m_peg
: m_rev2
, m_rev2
, tempfile2
, _T(""), TRUE
, TRUE
)))
392 if ((!m_bDoPegDiff
)||(!m_blamer
.BlameToFile(url2
, 1, m_rev2
, m_rev2
, tempfile2
, _T(""), TRUE
, TRUE
)))
394 CMessageBox::Show(NULL
, m_blamer
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
398 SetFileAttributes(tempfile2
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
399 progDlg
.SetProgress(2,2);
402 CString rev1name
, rev2name
;
405 rev1name
.Format(_T("%s Revision %ld"), (LPCTSTR
)fd
.path
.GetGitPathString(), (LONG
)m_rev1
);
406 rev2name
.Format(_T("%s Revision %ld"), (LPCTSTR
)fd
.path
.GetGitPathString(), (LONG
)m_rev2
);
410 rev1name
= m_path1
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString();
411 rev2name
= m_path2
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString();
413 CAppUtils::DiffFlags flags
;
414 flags
.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
416 CAppUtils::StartExtDiff(
417 tempfile
, tempfile2
, rev1name
, rev2name
, flags
);
422 void CFileDiffDlg::DiffProps(int selIndex
)
424 CFileDiffDlg::CTGitPath
* fd
= m_arFilteredList
[selIndex
];
426 CTGitPath url1
= CTGitPath(m_path1
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString());
427 CTGitPath url2
= m_bDoPegDiff
? url1
: CTGitPath(m_path2
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString());
429 GitProperties
propsurl1(url1
, m_rev1
, false);
430 GitProperties
propsurl2(url2
, m_rev2
, false);
432 // collect the properties of both revisions in a set
433 std::set
<stdstring
> properties
;
434 for (int wcindex
= 0; wcindex
< propsurl1
.GetCount(); ++wcindex
)
436 stdstring urlname
= propsurl1
.GetItemName(wcindex
);
437 if ( properties
.find(urlname
) == properties
.end() )
439 properties
.insert(urlname
);
442 for (int wcindex
= 0; wcindex
< propsurl2
.GetCount(); ++wcindex
)
444 stdstring urlname
= propsurl2
.GetItemName(wcindex
);
445 if ( properties
.find(urlname
) == properties
.end() )
447 properties
.insert(urlname
);
451 // iterate over all properties and diff the properties
452 for (std::set
<stdstring
>::iterator iter
= properties
.begin(), end
= properties
.end(); iter
!= end
; ++iter
)
454 stdstring url1name
= *iter
;
456 stdstring url1value
= _T(""); // CUnicodeUtils::StdGetUnicode((char *)propsurl1.GetItemValue(wcindex).c_str());
457 for (int url1index
= 0; url1index
< propsurl1
.GetCount(); ++url1index
)
459 if (propsurl1
.GetItemName(url1index
).compare(url1name
)==0)
461 url1value
= CString((char *)propsurl1
.GetItemValue(url1index
).c_str());
465 stdstring url2value
= _T("");
466 for (int url2index
= 0; url2index
< propsurl2
.GetCount(); ++url2index
)
468 if (propsurl2
.GetItemName(url2index
).compare(url1name
)==0)
470 url2value
= CString((char *)propsurl2
.GetItemValue(url2index
).c_str());
474 if (url2value
.compare(url1value
)!=0)
476 // write both property values to temporary files
477 CTGitPath url1propfile
= CTempFiles::Instance().GetTempFilePath(false);
478 CTGitPath url2propfile
= CTempFiles::Instance().GetTempFilePath(false);
480 _tfopen_s(&pFile
, url1propfile
.GetWinPath(), _T("wb"));
483 fputs(CUnicodeUtils::StdGetUTF8(url1value
).c_str(), pFile
);
486 _tfopen_s(&pFile
, url2propfile
.GetWinPath(), _T("wb"));
489 fputs(CUnicodeUtils::StdGetUTF8(url2value
).c_str(), pFile
);
497 SetFileAttributes(url1propfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
498 SetFileAttributes(url2propfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
500 if (m_rev1
.IsWorking())
501 n1
.Format(IDS_DIFF_WCNAME
, url1name
.c_str());
503 n1
.Format(IDS_DIFF_BASENAME
, url1name
.c_str());
504 if (m_rev1
.IsHead() || m_rev1
.IsNumber())
508 n1
.Format(_T("%s : %s Revision %ld"), url1name
.c_str(), (LPCTSTR
)fd
.path
.GetGitPathString(), (LONG
)m_rev1
);
512 CString sTemp
= url1name
.c_str();
514 n1
= sTemp
+ m_path1
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString();
517 if (m_rev2
.IsWorking())
518 n2
.Format(IDS_DIFF_WCNAME
, url1name
.c_str());
520 n2
.Format(IDS_DIFF_BASENAME
, url1name
.c_str());
521 if (m_rev2
.IsHead() || m_rev2
.IsNumber())
525 n2
.Format(_T("%s : %s Revision %ld"), url1name
.c_str(), (LPCTSTR
)fd
.path
.GetGitPathString(), (LONG
)m_rev2
);
529 CString sTemp
= url1name
.c_str();
531 n2
= sTemp
+ m_path2
.GetGitPathString() + _T("/") + fd
.path
.GetGitPathString();
534 CAppUtils::StartExtDiffProps(url1propfile
, url2propfile
, n1
, n2
, TRUE
);
539 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
542 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
543 int selIndex
= pNMLV
->iItem
;
546 if (selIndex
>= (int)m_arFilteredList
.size())
549 DoDiff(selIndex
, m_bBlame
);
552 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
555 LPNMLVGETINFOTIP pGetInfoTip
= reinterpret_cast<LPNMLVGETINFOTIP
>(pNMHDR
);
556 if (pGetInfoTip
->iItem
>= (int)m_arFilteredList
.size())
559 CString path
= m_path1
.GetGitPathString() + _T("/") + m_arFilteredList
[pGetInfoTip
->iItem
]->GetGitPathString();
560 if (pGetInfoTip
->cchTextMax
> path
.GetLength())
561 _tcsncpy_s(pGetInfoTip
->pszText
, pGetInfoTip
->cchTextMax
, path
, pGetInfoTip
->cchTextMax
);
566 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
568 NMLVCUSTOMDRAW
* pLVCD
= reinterpret_cast<NMLVCUSTOMDRAW
*>( pNMHDR
);
569 // Take the default processing unless we set this to something else below.
570 *pResult
= CDRF_DODEFAULT
;
572 // First thing - check the draw stage. If it's the control's prepaint
573 // stage, then tell Windows we want messages for every item.
575 if ( CDDS_PREPAINT
== pLVCD
->nmcd
.dwDrawStage
)
577 *pResult
= CDRF_NOTIFYITEMDRAW
;
579 else if ( CDDS_ITEMPREPAINT
== pLVCD
->nmcd
.dwDrawStage
)
581 // This is the prepaint stage for an item. Here's where we set the
582 // item's text color. Our return value will tell Windows to draw the
583 // item itself, but it will use the new color we set here.
585 // Tell Windows to paint the control itself.
586 *pResult
= CDRF_DODEFAULT
;
588 COLORREF crText
= GetSysColor(COLOR_WINDOWTEXT
);
590 if (m_arFilteredList
.size() > pLVCD
->nmcd
.dwItemSpec
)
592 CTGitPath
* fd
= m_arFilteredList
[pLVCD
->nmcd
.dwItemSpec
];
593 switch (fd
->m_Action
)
595 case CTGitPath::LOGACTIONS_ADDED
:
596 crText
= m_colors
.GetColor(CColors::Added
);
598 case CTGitPath::LOGACTIONS_DELETED
:
599 crText
= m_colors
.GetColor(CColors::Deleted
);
601 case CTGitPath::LOGACTIONS_MODIFIED
:
602 crText
= m_colors
.GetColor(CColors::Modified
);
604 //case svn_client_diff_summarize_kind_normal:
606 //if (fd.propchanged)
607 crText
= m_colors
.GetColor(CColors::PropertyChanged
);
611 // Store the color back in the NMLVCUSTOMDRAW struct.
612 pLVCD
->clrText
= crText
;
616 void CFileDiffDlg::OnContextMenu(CWnd
* pWnd
, CPoint point
)
618 if ((pWnd
==0)||(pWnd
!= &m_cFileList
))
620 if (m_cFileList
.GetSelectedCount() == 0)
622 // if the context menu is invoked through the keyboard, we have to use
623 // a calculated position on where to anchor the menu on
624 if ((point
.x
== -1) && (point
.y
== -1))
627 m_cFileList
.GetItemRect(m_cFileList
.GetSelectionMark(), &rect
, LVIR_LABEL
);
628 m_cFileList
.ClientToScreen(&rect
);
629 point
= rect
.CenterPoint();
632 if (popup
.CreatePopupMenu())
635 temp
.LoadString(IDS_LOG_POPUP_COMPARETWO
);
636 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, ID_COMPARE
, temp
);
637 temp
.LoadString(IDS_FILEDIFF_POPBLAME
);
638 //popup.AppendMenu(MF_STRING | MF_ENABLED, ID_BLAME, temp);
639 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
640 temp
.LoadString(IDS_FILEDIFF_POPSAVELIST
);
641 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, ID_SAVEAS
, temp
);
642 temp
.LoadString(IDS_FILEDIFF_POPCLIPBOARD
);
643 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, ID_CLIPBOARD
, temp
);
644 temp
.LoadString(IDS_FILEDIFF_POPEXPORT
);
645 //popup.AppendMenu(MF_STRING | MF_ENABLED, ID_EXPORT, temp);
646 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
647 m_bCancelled
= false;
652 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
655 int index
= m_cFileList
.GetNextSelectedItem(pos
);
656 DoDiff(index
, false);
662 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
665 int index
= m_cFileList
.GetNextSelectedItem(pos
);
672 if (m_cFileList
.GetSelectedCount() > 0)
677 if (!CAppUtils::FileOpenSave(pathSave
, NULL
, IDS_REPOBROWSE_SAVEAS
, IDS_COMMONFILEFILTER
, false, m_hWnd
))
681 savePath
= CTGitPath(pathSave
);
683 // now open the selected file for writing
686 CStdioFile
file(savePath
.GetWinPathString(), CFile::typeBinary
| CFile::modeReadWrite
| CFile::modeCreate
);
687 // temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString());
688 file
.WriteString(temp
+ _T("\n"));
689 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
692 int index
= m_cFileList
.GetNextSelectedItem(pos
);
693 CTGitPath
* fd
= m_arFilteredList
[index
];
694 file
.WriteString(fd
->GetGitPathString());
695 file
.WriteString(_T("\n"));
699 catch (CFileException
* pE
)
708 CopySelectionToClipboard();
713 #if 0 //this funcation seem no useful
714 // export all changed files to a folder
715 CBrowseFolder browseFolder
;
716 browseFolder
.m_style
= BIF_EDITBOX
| BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
717 if (browseFolder
.Show(GetSafeHwnd(), m_strExportDir
) == CBrowseFolder::OK
)
719 m_arSelectedFileList
.RemoveAll();
720 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
723 int index
= m_cFileList
.GetNextSelectedItem(pos
);
724 CTGitPath
* fd
= m_arFilteredList
[index
];
725 m_arSelectedFileList
.Add(fd
);
727 m_pProgDlg
= new CProgressDlg();
728 InterlockedExchange(&m_bThreadRunning
, TRUE
);
729 if (AfxBeginThread(ExportThreadEntry
, this)==NULL
)
731 InterlockedExchange(&m_bThreadRunning
, FALSE
);
732 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
744 UINT
CFileDiffDlg::ExportThreadEntry(LPVOID pVoid
)
746 return ((CFileDiffDlg
*)pVoid
)->ExportThread();
749 UINT
CFileDiffDlg::ExportThread()
753 // if (m_pProgDlg == NULL)
756 // SetAndClearProgressInfo(m_pProgDlg, false);
757 m_pProgDlg
->SetTitle(IDS_PROGRESSWAIT
);
758 m_pProgDlg
->SetAnimation(AfxGetResourceHandle(), IDR_DOWNLOAD
);
759 m_pProgDlg
->ShowModeless(this);
760 for (INT_PTR i
=0; (i
<m_arSelectedFileList
.GetCount())&&(!m_pProgDlg
->HasUserCancelled()); ++i
)
762 CTGitPath
* fd
= m_arSelectedFileList
[i
];
763 // CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
764 // CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
765 // if ((fd.node == svn_node_dir)&&(fd.kind != svn_client_diff_summarize_kind_added))
767 // just create the directory
768 // CreateDirectoryEx(NULL, m_strExportDir+_T("\\")+CPathUtils::PathUnescape(fd.path.GetWinPathString()), NULL);
773 m_pProgDlg
->FormatPathLine(1, IDS_PROGRESSGETFILE
, (LPCTSTR
)url1
.GetGitPathString());
775 CTGitPath savepath
= CTGitPath(m_strExportDir
);
776 savepath
.AppendPathString(_T("\\") + CPathUtils::PathUnescape(fd
.path
.GetWinPathString()));
777 CPathUtils::MakeSureDirectoryPathExists(fd
.node
== svn_node_file
? savepath
.GetContainingDirectory().GetWinPath() : savepath
.GetDirectory().GetWinPath());
778 if (fd
.node
== svn_node_dir
)
780 // exporting a folder requires calling Git::Export() so we also export all
781 // children of that added folder.
782 if ((fd
.kind
== svn_client_diff_summarize_kind_added
)&&(!Export(url2
, savepath
, m_bDoPegDiff
? m_peg
: m_rev2
, m_rev2
, true, true)))
784 if ((!m_bDoPegDiff
)||(!Export(url2
, savepath
, m_rev2
, m_rev2
, true, true)))
788 CMessageBox::Show(NULL
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
789 InterlockedExchange(&m_bThreadRunning
, FALSE
);
797 // exporting a file requires calling Git::Cat(), since Git::Export() only works
799 if ((fd
.kind
!= svn_client_diff_summarize_kind_deleted
)&&(!Cat(url2
, m_bDoPegDiff
? m_peg
: m_rev2
, m_rev2
, savepath
)))
801 if ((!m_bDoPegDiff
)||(!Cat(url2
, m_rev2
, m_rev2
, savepath
)))
805 CMessageBox::Show(NULL
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
806 InterlockedExchange(&m_bThreadRunning
, FALSE
);
813 m_pProgDlg
->SetProgress (count
, static_cast<DWORD
>(m_arSelectedFileList
.GetCount()));
816 SetAndClearProgressInfo(NULL
, false);
819 InterlockedExchange(&m_bThreadRunning
, FALSE
);
825 BOOL
CFileDiffDlg::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
827 if (pWnd
!= &m_cFileList
)
828 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
829 if (m_bThreadRunning
== 0)
831 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
));
833 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
835 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_WAIT
));
840 void CFileDiffDlg::OnEnSetfocusFirsturl()
842 GetDlgItem(IDC_FIRSTURL
)->HideCaret();
845 void CFileDiffDlg::OnEnSetfocusSecondurl()
847 GetDlgItem(IDC_SECONDURL
)->HideCaret();
851 void CFileDiffDlg::OnBnClickedSwitchleftright()
854 if (m_bThreadRunning
)
856 CString sFilterString
;
857 m_cFilter
.GetWindowText(sFilterString
);
859 m_cFileList
.SetRedraw(false);
860 m_cFileList
.DeleteAllItems();
861 for (int i
=0; i
<(int)m_arFileList
.GetCount(); ++i
)
863 CTGitPath fd
= m_arFileList
[i
];
864 if (fd
.m_Action
== CTGitPath::LOGACTIONS_ADDED
)
865 fd
.m_Action
= CTGitPath::LOGACTIONS_DELETED
;
866 else if (fd
.m_Action
== CTGitPath::LOGACTIONS_DELETED
)
867 fd
.m_Action
= CTGitPath::LOGACTIONS_ADDED
;
868 ( CTGitPath
)m_arFileList
[i
] = ( CTGitPath
)fd
;
870 Filter(sFilterString
);
872 m_cFileList
.SetRedraw(true);
873 CTGitPath path
= m_path1
;
883 void CFileDiffDlg::SetURLLabels()
886 m_cRev1Btn
.SetWindowText(m_rev1
.m_CommitHash
.Left(6));
887 m_cRev2Btn
.SetWindowText(m_rev2
.m_CommitHash
.Left(6));
889 SetDlgItemText(IDC_FIRSTURL
, m_rev1
.m_Subject
+CString(_T("\r\n"))+m_rev1
.m_CommitHash
);
890 SetDlgItemText(IDC_SECONDURL
,m_rev2
.m_Subject
+CString(_T("\r\n"))+m_rev2
.m_CommitHash
);
892 m_tooltips
.AddTool(IDC_FIRSTURL
,
893 CAppUtils::FormatDateAndTime( m_rev1
.m_AuthorDate
, DATE_SHORTDATE
, false )+_T(" ")+m_rev1
.m_AuthorName
);
894 m_tooltips
.AddTool(IDC_SECONDURL
,
895 CAppUtils::FormatDateAndTime( m_rev2
.m_AuthorDate
, DATE_SHORTDATE
, false )+_T(" ")+m_rev2
.m_AuthorName
);
899 BOOL
CFileDiffDlg::PreTranslateMessage(MSG
* pMsg
)
901 m_tooltips
.RelayEvent(pMsg
);
902 if (pMsg
->message
== WM_KEYDOWN
)
904 switch (pMsg
->wParam
)
908 if (GetAsyncKeyState(VK_CONTROL
)&0x8000)
910 // select all entries
911 for (int i
=0; i
<m_cFileList
.GetItemCount(); ++i
)
913 m_cFileList
.SetItemState(i
, LVIS_SELECTED
, LVIS_SELECTED
);
922 if (GetAsyncKeyState(VK_CONTROL
)&0x8000)
924 CopySelectionToClipboard();
931 if (GetFocus() == GetDlgItem(IDC_FILELIST
))
933 // Return pressed in file list. Show diff, as for double click
934 int selIndex
= m_cFileList
.GetSelectionMark();
935 if ((selIndex
>= 0) && (selIndex
< (int)m_arFileList
.GetCount()))
936 DoDiff(selIndex
, m_bBlame
);
943 return __super::PreTranslateMessage(pMsg
);
946 void CFileDiffDlg::OnCancel()
948 if (m_bThreadRunning
)
956 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
958 LPNMHEADER phdr
= reinterpret_cast<LPNMHEADER
>(pNMHDR
);
959 if (m_bThreadRunning
)
962 if (m_nSortedColumn
== phdr
->iItem
)
963 m_bAscending
= !m_bAscending
;
966 m_nSortedColumn
= phdr
->iItem
;
967 m_arSelectedFileList
.RemoveAll();
971 m_cFileList
.SetRedraw(FALSE
);
972 m_cFileList
.DeleteAllItems();
973 m_cFilter
.GetWindowText(temp
);
976 CHeaderCtrl
* pHeader
= m_cFileList
.GetHeaderCtrl();
977 HDITEM HeaderItem
= {0};
978 HeaderItem
.mask
= HDI_FORMAT
;
979 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
981 pHeader
->GetItem(i
, &HeaderItem
);
982 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
983 pHeader
->SetItem(i
, &HeaderItem
);
985 pHeader
->GetItem(m_nSortedColumn
, &HeaderItem
);
986 HeaderItem
.fmt
|= (m_bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
987 pHeader
->SetItem(m_nSortedColumn
, &HeaderItem
);
989 m_cFileList
.SetRedraw(TRUE
);
994 void CFileDiffDlg::Sort()
996 if(m_arFileList
.GetCount() < 2)
1001 // std::sort(m_arFileList.begin(), m_arFileList.end(), &CFileDiffDlg::SortCompare);
1004 bool CFileDiffDlg::SortCompare(const CTGitPath
*& Data1
, const CTGitPath
*& Data2
)
1007 switch (m_nSortedColumn
)
1009 case 0: //path column
1010 result
= Data1
.path
.GetWinPathString().Compare(Data2
.path
.GetWinPathString());
1012 case 1: //action column
1013 result
= Data1
.kind
- Data2
.kind
;
1025 void CFileDiffDlg::OnBnClickedRev1btn()
1028 if (m_bThreadRunning
)
1029 return; // do nothing as long as the thread is still running
1031 // show a dialog where the user can enter a revision
1032 CRevisionDlg
dlg(this);
1033 dlg
.AllowWCRevs(false);
1034 *((GitRev
*)&dlg
) = m_rev1
;
1036 if (dlg
.DoModal() == IDOK
)
1039 m_cRev1Btn
.SetWindowText(m_rev1
.ToString());
1040 m_cFileList
.DeleteAllItems();
1041 // start a new thread to re-fetch the diff
1042 InterlockedExchange(&m_bThreadRunning
, TRUE
);
1043 if (AfxBeginThread(DiffThreadEntry
, this)==NULL
)
1045 InterlockedExchange(&m_bThreadRunning
, FALSE
);
1046 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1052 void CFileDiffDlg::OnBnClickedRev2btn()
1055 if (m_bThreadRunning
)
1056 return; // do nothing as long as the thread is still running
1058 // show a dialog where the user can enter a revision
1059 CRevisionDlg
dlg(this);
1060 dlg
.AllowWCRevs(false);
1061 *((GitRev
*)&dlg
) = m_rev2
;
1063 if (dlg
.DoModal() == IDOK
)
1066 m_cRev2Btn
.SetWindowText(m_rev2
.ToString());
1067 m_cFileList
.DeleteAllItems();
1068 // start a new thread to re-fetch the diff
1069 InterlockedExchange(&m_bThreadRunning
, TRUE
);
1070 if (AfxBeginThread(DiffThreadEntry
, this)==NULL
)
1072 InterlockedExchange(&m_bThreadRunning
, FALSE
);
1073 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
1079 LRESULT
CFileDiffDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
1081 if (m_bThreadRunning
)
1083 SetTimer(IDT_FILTER
, 1000, NULL
);
1087 KillTimer(IDT_FILTER
);
1089 m_cFileList
.SetRedraw(FALSE
);
1090 m_arFilteredList
.clear();
1091 m_cFileList
.DeleteAllItems();
1095 m_cFileList
.SetRedraw(TRUE
);
1099 void CFileDiffDlg::OnEnChangeFilter()
1101 SetTimer(IDT_FILTER
, 1000, NULL
);
1104 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent
)
1106 if (m_bThreadRunning
)
1109 CString sFilterText
;
1110 KillTimer(IDT_FILTER
);
1111 m_cFilter
.GetWindowText(sFilterText
);
1113 m_cFileList
.SetRedraw(FALSE
);
1114 m_cFileList
.DeleteAllItems();
1116 Filter(sFilterText
);
1118 m_cFileList
.SetRedraw(TRUE
);
1120 __super::OnTimer(nIDEvent
);
1123 void CFileDiffDlg::Filter(CString sFilterText
)
1126 sFilterText
.MakeLower();
1128 m_arFilteredList
.clear();
1130 for (int i
=0;i
<m_arFileList
.GetCount();i
++)
1132 CString sPath
= m_arFileList
[i
].GetGitPathString();
1134 if (sPath
.Find(sFilterText
) >= 0)
1136 m_arFilteredList
.push_back((CTGitPath
*)&(m_arFileList
[i
]));
1139 for (std::vector
<CTGitPath
*>::const_iterator it
= m_arFilteredList
.begin(); it
!= m_arFilteredList
.end(); ++it
)
1146 void CFileDiffDlg::CopySelectionToClipboard()
1148 // copy all selected paths to the clipboard
1149 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
1151 CString sTextForClipboard
;
1152 while ((index
= m_cFileList
.GetNextSelectedItem(pos
)) >= 0)
1154 sTextForClipboard
+= m_cFileList
.GetItemText(index
, 0);
1155 sTextForClipboard
+= _T("\t");
1156 sTextForClipboard
+= m_cFileList
.GetItemText(index
, 1);
1157 sTextForClipboard
+= _T("\r\n");
1159 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard
);