TortoiseSVN -> TortoiseGit
[TortoiseGit.git] / src / TortoiseProc / FileDiffDlg.cpp
blob09b1e6f38cb8ff62d60083aaef92932366c216c7
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - 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 "ProgressDlg.h"
27 #include "SysImageList.h"
28 #include "IconMenu.h"
29 //#include "GitProperties.h"
30 #include "StringUtils.h"
31 #include "PathUtils.h"
32 #include "BrowseFolder.h"
33 #include "RevisionDlg.h"
34 #include ".\filediffdlg.h"
35 #include "gitdiff.h"
36 #include "CommonResource.h"
37 #include "BrowseRefsDlg.h"
38 #include "LogDlg.h"
39 #include "RefLogDlg.h"
40 #include "GitStatusListCtrl.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
50 BOOL CFileDiffDlg::m_bAscending = FALSE;
51 int CFileDiffDlg::m_nSortedColumn = -1;
54 IMPLEMENT_DYNAMIC(CFileDiffDlg, CResizableStandAloneDialog)
55 CFileDiffDlg::CFileDiffDlg(CWnd* pParent /*=NULL*/)
56 : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent),
57 m_bBlame(false),
58 m_pProgDlg(NULL),
59 m_bCancelled(false)
61 m_bLoadingRef=FALSE;
64 CFileDiffDlg::~CFileDiffDlg()
66 DestroyIcon(m_hSwitchIcon);
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;
109 m_rev1 = rev1;
110 m_rev2 = rev2;
113 void CFileDiffDlg::SetDiff(CTGitPath * path, CString &hash1, CString &hash2)
115 if(path!=NULL)
117 m_path1 = *path;
118 m_path2 = *path;
121 BYTE_VECTOR logout;
123 if(hash1 == GIT_REV_ZERO)
125 m_rev1.m_CommitHash.Empty();
126 m_rev1.GetSubject()=_T("Working Copy");
128 else
130 m_rev1.GetCommit(hash1);
133 logout.clear();
135 if(hash2 == GIT_REV_ZERO)
137 m_rev2.m_CommitHash.Empty();
138 m_rev2.GetSubject()=_T("Working Copy");
140 else
142 m_rev2.GetCommit(hash2);
146 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1)
148 if(path!=NULL)
150 m_path1 = *path;
151 m_path2 = *path;
153 m_rev1 = rev1;
154 m_rev2.m_CommitHash.Empty();
155 m_rev2.GetSubject() = _T("Previous Version");
157 //this->GetDlgItem()->EnableWindow(FALSE);
160 BOOL CFileDiffDlg::OnInitDialog()
162 CResizableStandAloneDialog::OnInitDialog();
163 CString temp;
165 this->m_ctrRev1Edit.Init();
166 this->m_ctrRev2Edit.Init();
168 m_tooltips.Create(this);
169 m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);
171 m_cFileList.SetRedraw(false);
172 m_cFileList.DeleteAllItems();
173 DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;
174 m_cFileList.SetExtendedStyle(exStyle);
176 m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();
177 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
179 m_hSwitchIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
180 m_SwitchButton.SetIcon(m_hSwitchIcon);
182 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
183 m_cFilter.SetInfoIcon(IDI_FILTEREDIT);
184 temp.LoadString(IDS_FILEDIFF_FILTERCUE);
185 temp = _T(" ")+temp;
186 m_cFilter.SetCueBanner(temp);
188 int c = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
189 while (c>=0)
190 m_cFileList.DeleteColumn(c--);
192 temp.LoadString(IDS_FILEDIFF_FILE);
193 m_cFileList.InsertColumn(0, temp);
194 temp.LoadString(IDS_FILEDIFF_ACTION);
195 m_cFileList.InsertColumn(1, temp);
197 temp.LoadString(IDS_FILEDIFF_STATADD);
198 m_cFileList.InsertColumn(2, temp);
199 temp.LoadString(IDS_FILEDIFF_STATDEL);
200 m_cFileList.InsertColumn(3, temp);
202 int mincol = 0;
203 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
204 int col;
205 for (col = mincol; col <= maxcol; col++)
207 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
210 m_cFileList.SetRedraw(true);
212 AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);
213 AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);
214 AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);
215 AddAnchor(IDC_REV1BTN, TOP_RIGHT);
216 //AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);
217 AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);
218 AddAnchor(IDC_REV2BTN, TOP_RIGHT);
219 AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);
220 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
221 AddAnchor(IDC_REV1GROUP,TOP_LEFT,TOP_RIGHT);
222 AddAnchor(IDC_REV2GROUP,TOP_LEFT,TOP_RIGHT);
223 AddAnchor(IDC_REV1EDIT,TOP_LEFT);
224 AddAnchor(IDC_REV2EDIT,TOP_LEFT);
226 EnableSaveRestore(_T("FileDiffDlg"));
228 if(this->m_strRev1.IsEmpty())
229 this->m_ctrRev1Edit.SetWindowText(this->m_rev1.m_CommitHash.ToString());
230 else
232 if(m_rev1.GetCommit(this->m_strRev1))
233 this->m_FileListText+=this->m_strRev1 + _T(" is wrong");
235 this->m_ctrRev1Edit.SetWindowText(m_strRev1);
238 if(this->m_strRev2.IsEmpty())
239 this->m_ctrRev2Edit.SetWindowText(this->m_rev2.m_CommitHash.ToString());
240 else
242 if(m_rev2.GetCommit(this->m_strRev2))
243 this->m_FileListText+=this->m_strRev2 + _T(" is wrong");
245 this->m_ctrRev2Edit.SetWindowText(m_strRev2);
248 SetURLLabels();
250 InterlockedExchange(&m_bThreadRunning, TRUE);
251 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
253 InterlockedExchange(&m_bThreadRunning, FALSE);
254 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
257 InterlockedExchange(&m_bLoadingRef, TRUE);
258 if (AfxBeginThread(LoadRefThreadEntry, this)==NULL)
260 InterlockedExchange(&m_bLoadingRef, FALSE);
261 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
264 this->m_cRev1Btn.AddEntry(_T("RefBrowse"));
265 this->m_cRev1Btn.AddEntry(_T("Log"));
266 this->m_cRev1Btn.AddEntry(_T("RefLog"));
268 this->m_cRev2Btn.AddEntry(_T("RefBrowse"));
269 this->m_cRev2Btn.AddEntry(_T("Log"));
270 this->m_cRev2Btn.AddEntry(_T("RefLog"));
272 // Start with focus on file list
273 GetDlgItem(IDC_FILELIST)->SetFocus();
275 if(m_rev2.m_CommitHash.IsEmpty())
276 m_SwitchButton.EnableWindow(FALSE);
278 KillTimer(IDT_INPUT);
279 return FALSE;
282 #if 0
283 svn_error_t* CFileDiffDlg::DiffSummarizeCallback(const CTGitPath& path,
284 svn_client_diff_summarize_kind_t kind,
285 bool propchanged, svn_node_kind_t node)
287 CTGitPath* fd;
288 fd.path = path;
289 fd.kind = kind;
290 fd.node = node;
291 fd.propchanged = propchanged;
292 m_arFileList.push_back(fd);
293 return Git_NO_ERROR;
295 #endif
297 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)
299 return ((CFileDiffDlg*)pVoid)->DiffThread();
302 UINT CFileDiffDlg::DiffThread()
304 RefreshCursor();
305 m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));
306 m_cFileList.DeleteAllItems();
307 m_arFileList.Clear();
308 EnableInputControl(false);
309 #if 0
310 bool bSuccess = true;
311 if (m_bDoPegDiff)
313 // bSuccess = DiffSummarizePeg(m_path1, m_peg, m_rev1, m_rev2, m_depth, m_bIgnoreancestry);
315 else
317 // bSuccess = DiffSummarize(m_path1, m_rev1, m_path2, m_rev2, m_depth, m_bIgnoreancestry);
319 // if (!bSuccess)
320 // {
321 // m_cFileList.ShowText(GetLastErrorMessage());
322 // InterlockedExchange(&m_bThreadRunning, FALSE);
323 // return 0;
324 // }
325 #endif
327 if( m_rev1.m_CommitHash.IsEmpty() || m_rev2.m_CommitHash.IsEmpty())
328 g_Git.RefreshGitIndex();
330 g_Git.GetCommitDiffList(m_rev1.m_CommitHash.ToString(),m_rev2.m_CommitHash.ToString(),m_arFileList);
332 CString sFilterText;
333 m_cFilter.GetWindowText(sFilterText);
334 m_cFileList.SetRedraw(false);
335 Filter(sFilterText);
336 if (m_arFileList.GetCount()>0)
338 // Highlight first entry in file list
339 m_cFileList.SetSelectionMark(0);
340 m_cFileList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
343 int mincol = 0;
344 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
345 int col;
346 for (col = mincol; col <= maxcol; col++)
348 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
351 m_cFileList.ClearText();
352 m_cFileList.SetRedraw(true);
354 InterlockedExchange(&m_bThreadRunning, FALSE);
355 InvalidateRect(NULL);
356 RefreshCursor();
357 EnableInputControl(true);
358 return 0;
361 int CFileDiffDlg::AddEntry(const CTGitPath * fd)
363 int ret = -1;
364 if (fd)
366 int index = m_cFileList.GetItemCount();
368 int icon_idx = 0;
369 // if (fd->node == svn_node_dir)
370 // icon_idx = m_nIconFolder;
371 // else
373 icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());
376 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);
377 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetActionName());
378 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->m_StatAdd);
379 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatDel);
381 return ret;
384 void CFileDiffDlg::EnableInputControl(bool b)
386 this->m_ctrRev1Edit.EnableWindow(b);
387 this->m_ctrRev2Edit.EnableWindow(b);
388 this->m_cRev1Btn.EnableWindow(b);
389 this->m_cRev2Btn.EnableWindow(b);
390 m_cFilter.EnableWindow(b);
391 m_SwitchButton.EnableWindow(b);
394 void CFileDiffDlg::DoDiff(int selIndex, bool blame)
396 CGitDiff diff;
397 CTGitPath* fd = m_arFilteredList[selIndex];
398 diff.Diff(fd, fd,this->m_rev1.m_CommitHash.ToString(), this->m_rev2.m_CommitHash.ToString(), blame, FALSE);
400 #if 0
401 CFileDiffDlg::CTGitPath* fd = m_arFilteredList[selIndex];
403 CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
404 CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
406 if (fd.kind == svn_client_diff_summarize_kind_deleted)
408 if (!PathIsURL(url1))
409 url1 = CTGitPath(GetURLFromPath(m_path1) + _T("/") + fd.path.GetGitPathString());
410 if (!PathIsURL(url2))
411 url2 = m_bDoPegDiff ? url1 : CTGitPath(GetURLFromPath(m_path2) + _T("/") + fd.path.GetGitPathString());
414 if (fd.propchanged)
416 DiffProps(selIndex);
418 if (fd.node == svn_node_dir)
419 return;
421 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path1, m_rev1);
422 CString sTemp;
423 CProgressDlg progDlg;
424 progDlg.SetTitle(IDS_PROGRESSWAIT);
425 progDlg.SetAnimation(IDR_DOWNLOAD);
426 progDlg.ShowModeless(this);
427 progDlg.FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)m_path1.GetUIPathString());
428 progDlg.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT, (LPCTSTR)m_rev1.ToString());
430 if ((fd.kind != svn_client_diff_summarize_kind_added)&&(!blame)&&(!Cat(url1, m_bDoPegDiff ? m_peg : m_rev1, m_rev1, tempfile)))
432 if ((!m_bDoPegDiff)||(!Cat(url1, m_rev1, m_rev1, tempfile)))
434 CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
435 return;
438 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)))
440 if ((!m_bDoPegDiff)||(!m_blamer.BlameToFile(url1, 1, m_rev1, m_rev1, tempfile, _T(""), TRUE, TRUE)))
442 CMessageBox::Show(NULL, m_blamer.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
443 return;
446 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
447 progDlg.SetProgress(1, 2);
448 progDlg.FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)url2.GetUIPathString());
449 progDlg.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT, (LPCTSTR)m_rev2.ToString());
450 CTGitPath tempfile2 = CTempFiles::Instance().GetTempFilePath(false, url2, m_rev2);
451 if ((fd.kind != svn_client_diff_summarize_kind_deleted)&&(!blame)&&(!Cat(url2, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, tempfile2)))
453 if ((!m_bDoPegDiff)||(!Cat(url2, m_rev2, m_rev2, tempfile2)))
455 CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
456 return;
459 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)))
461 if ((!m_bDoPegDiff)||(!m_blamer.BlameToFile(url2, 1, m_rev2, m_rev2, tempfile2, _T(""), TRUE, TRUE)))
463 CMessageBox::Show(NULL, m_blamer.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
464 return;
467 SetFileAttributes(tempfile2.GetWinPath(), FILE_ATTRIBUTE_READONLY);
468 progDlg.SetProgress(2,2);
469 progDlg.Stop();
471 CString rev1name, rev2name;
472 if (m_bDoPegDiff)
474 rev1name.Format(_T("%s Revision %ld"), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev1);
475 rev2name.Format(_T("%s Revision %ld"), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev2);
477 else
479 rev1name = m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString();
480 rev2name = m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString();
482 CAppUtils::DiffFlags flags;
483 flags.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
484 flags.Blame(blame);
485 CAppUtils::StartExtDiff(
486 tempfile, tempfile2, rev1name, rev2name, flags);
487 #endif
490 #if 0
491 void CFileDiffDlg::DiffProps(int selIndex)
493 CFileDiffDlg::CTGitPath* fd = m_arFilteredList[selIndex];
495 CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
496 CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
498 GitProperties propsurl1(url1, m_rev1, false);
499 GitProperties propsurl2(url2, m_rev2, false);
501 // collect the properties of both revisions in a set
502 std::set<stdstring> properties;
503 for (int wcindex = 0; wcindex < propsurl1.GetCount(); ++wcindex)
505 stdstring urlname = propsurl1.GetItemName(wcindex);
506 if ( properties.find(urlname) == properties.end() )
508 properties.insert(urlname);
511 for (int wcindex = 0; wcindex < propsurl2.GetCount(); ++wcindex)
513 stdstring urlname = propsurl2.GetItemName(wcindex);
514 if ( properties.find(urlname) == properties.end() )
516 properties.insert(urlname);
520 // iterate over all properties and diff the properties
521 for (std::set<stdstring>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)
523 stdstring url1name = *iter;
525 stdstring url1value = _T(""); // CUnicodeUtils::StdGetUnicode((char *)propsurl1.GetItemValue(wcindex).c_str());
526 for (int url1index = 0; url1index < propsurl1.GetCount(); ++url1index)
528 if (propsurl1.GetItemName(url1index).compare(url1name)==0)
530 url1value = CString((char *)propsurl1.GetItemValue(url1index).c_str());
534 stdstring url2value = _T("");
535 for (int url2index = 0; url2index < propsurl2.GetCount(); ++url2index)
537 if (propsurl2.GetItemName(url2index).compare(url1name)==0)
539 url2value = CString((char *)propsurl2.GetItemValue(url2index).c_str());
543 if (url2value.compare(url1value)!=0)
545 // write both property values to temporary files
546 CTGitPath url1propfile = CTempFiles::Instance().GetTempFilePath(false);
547 CTGitPath url2propfile = CTempFiles::Instance().GetTempFilePath(false);
548 FILE * pFile;
549 _tfopen_s(&pFile, url1propfile.GetWinPath(), _T("wb"));
550 if (pFile)
552 fputs(CUnicodeUtils::StdGetUTF8(url1value).c_str(), pFile);
553 fclose(pFile);
554 FILE * pFile;
555 _tfopen_s(&pFile, url2propfile.GetWinPath(), _T("wb"));
556 if (pFile)
558 fputs(CUnicodeUtils::StdGetUTF8(url2value).c_str(), pFile);
559 fclose(pFile);
561 else
562 return;
564 else
565 return;
566 SetFileAttributes(url1propfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
567 SetFileAttributes(url2propfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
568 CString n1, n2;
569 if (m_rev1.IsWorking())
570 n1.Format(IDS_DIFF_WCNAME, url1name.c_str());
571 if (m_rev1.IsBase())
572 n1.Format(IDS_DIFF_BASENAME, url1name.c_str());
573 if (m_rev1.IsHead() || m_rev1.IsNumber())
575 if (m_bDoPegDiff)
577 n1.Format(_T("%s : %s Revision %ld"), url1name.c_str(), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev1);
579 else
581 CString sTemp = url1name.c_str();
582 sTemp += _T(" : ");
583 n1 = sTemp + m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString();
586 if (m_rev2.IsWorking())
587 n2.Format(IDS_DIFF_WCNAME, url1name.c_str());
588 if (m_rev2.IsBase())
589 n2.Format(IDS_DIFF_BASENAME, url1name.c_str());
590 if (m_rev2.IsHead() || m_rev2.IsNumber())
592 if (m_bDoPegDiff)
594 n2.Format(_T("%s : %s Revision %ld"), url1name.c_str(), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev2);
596 else
598 CString sTemp = url1name.c_str();
599 sTemp += _T(" : ");
600 n2 = sTemp + m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString();
603 CAppUtils::StartExtDiffProps(url1propfile, url2propfile, n1, n2, TRUE);
607 #endif
608 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
610 *pResult = 0;
611 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
612 int selIndex = pNMLV->iItem;
613 if (selIndex < 0)
614 return;
615 if (selIndex >= (int)m_arFilteredList.size())
616 return;
618 DoDiff(selIndex, m_bBlame);
621 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
624 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
625 if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())
626 return;
628 CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();
629 if (pGetInfoTip->cchTextMax > path.GetLength())
630 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax);
632 *pResult = 0;
635 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
637 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
638 // Take the default processing unless we set this to something else below.
639 *pResult = CDRF_DODEFAULT;
641 // First thing - check the draw stage. If it's the control's prepaint
642 // stage, then tell Windows we want messages for every item.
644 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
646 *pResult = CDRF_NOTIFYITEMDRAW;
648 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
650 // This is the prepaint stage for an item. Here's where we set the
651 // item's text color. Our return value will tell Windows to draw the
652 // item itself, but it will use the new color we set here.
654 // Tell Windows to paint the control itself.
655 *pResult = CDRF_DODEFAULT;
657 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
659 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)
661 CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];
662 switch (fd->m_Action)
664 case CTGitPath::LOGACTIONS_ADDED:
665 crText = m_colors.GetColor(CColors::Added);
666 break;
667 case CTGitPath::LOGACTIONS_DELETED:
668 crText = m_colors.GetColor(CColors::Deleted);
669 break;
670 case CTGitPath::LOGACTIONS_MODIFIED:
671 crText = m_colors.GetColor(CColors::Modified);
672 break;
673 //case svn_client_diff_summarize_kind_normal:
674 default:
675 //if (fd.propchanged)
676 crText = m_colors.GetColor(CColors::PropertyChanged);
677 break;
680 // Store the color back in the NMLVCUSTOMDRAW struct.
681 pLVCD->clrText = crText;
685 UINT CFileDiffDlg::LoadRefThread()
687 g_Git.GetBranchList(m_Reflist,NULL,CGit::BRANCH_ALL_F);
688 g_Git.GetTagList(m_Reflist);
690 this->PostMessage(MSG_REF_LOADED);
691 InterlockedExchange(&m_bLoadingRef, FALSE);
692 return 0;
695 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)
697 if ((pWnd==0)||(pWnd != &m_cFileList))
698 return;
699 if (m_cFileList.GetSelectedCount() == 0)
700 return;
701 // if the context menu is invoked through the keyboard, we have to use
702 // a calculated position on where to anchor the menu on
703 if ((point.x == -1) && (point.y == -1))
705 CRect rect;
706 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);
707 m_cFileList.ClientToScreen(&rect);
708 point = rect.CenterPoint();
710 CIconMenu popup;
711 if (popup.CreatePopupMenu())
713 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
714 popup.AppendMenuIcon(ID_BLAME, IDS_FILEDIFF_POPBLAME, IDI_BLAME);
715 popup.AppendMenuIcon(ID_LOG, IDS_FILEDIFF_LOG, IDI_LOG);
716 popup.AppendMenu(MF_SEPARATOR, NULL);
717 popup.AppendMenuIcon(ID_EXPORT, IDS_FILEDIFF_POPEXPORT, IDI_EXPORT);
718 popup.AppendMenu(MF_SEPARATOR, NULL);
719 popup.AppendMenuIcon(ID_SAVEAS, IDS_FILEDIFF_POPSAVELIST, IDI_SAVEAS);
720 popup.AppendMenuIcon(ID_CLIPBOARD_PATH, IDS_STATUSLIST_CONTEXT_COPY, IDI_COPYCLIP);
721 popup.AppendMenuIcon(ID_CLIPBOARD_ALL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
723 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
724 m_bCancelled = false;
725 switch (cmd)
727 case ID_COMPARE:
729 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
730 while (pos)
732 int index = m_cFileList.GetNextSelectedItem(pos);
733 DoDiff(index, false);
736 break;
737 case ID_BLAME:
739 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
740 while (pos)
742 int index = m_cFileList.GetNextSelectedItem(pos);
743 CAppUtils::LaunchTortoiseBlame(m_arFilteredList[index]->GetWinPathString(), m_rev1.m_CommitHash.ToString());
746 break;
747 case ID_LOG:
749 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
750 while (pos)
752 int index = m_cFileList.GetNextSelectedItem(pos);
753 CString cmd;
754 cmd = CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe");
755 cmd += _T(" /command:log");
756 cmd += _T(" /path:\"")+m_arFilteredList[index]->GetWinPathString()+_T("\" ");
757 cmd += _T(" /endrev:")+m_rev1.m_CommitHash.ToString();
758 CAppUtils::LaunchApplication(cmd,IDS_ERR_PROC,false);
761 break;
762 case ID_SAVEAS:
764 if (m_cFileList.GetSelectedCount() > 0)
766 CString temp;
767 CTGitPath savePath;
768 CString pathSave;
769 if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd))
771 break;
773 savePath = CTGitPath(pathSave);
775 // now open the selected file for writing
778 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);
779 // temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString());
780 file.WriteString(temp + _T("\n"));
781 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
782 while (pos)
784 int index = m_cFileList.GetNextSelectedItem(pos);
785 CTGitPath* fd = m_arFilteredList[index];
786 file.WriteString(fd->GetGitPathString());
787 file.WriteString(_T("\n"));
789 file.Close();
791 catch (CFileException* pE)
793 pE->ReportError();
797 break;
798 case ID_CLIPBOARD_PATH:
800 CopySelectionToClipboard();
802 break;
804 case ID_CLIPBOARD_ALL:
806 CopySelectionToClipboard(TRUE);
808 break;
809 case ID_EXPORT:
811 // export all changed files to a folder
812 CBrowseFolder browseFolder;
813 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
814 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK)
816 m_arSelectedFileList.RemoveAll();
817 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
818 while (pos)
820 int index = m_cFileList.GetNextSelectedItem(pos);
821 CTGitPath* fd = m_arFilteredList[index];
822 #if 0
823 m_arSelectedFileList.Add(fd);
824 #endif
825 // we cannot export directories or folders
826 if (fd->m_Action == CTGitPath::LOGACTIONS_DELETED || fd->IsDirectory())
827 continue;
828 CAppUtils::CreateMultipleDirectory(m_strExportDir + _T("\\") + fd->GetDirectory().GetWinPathString());
829 CString filename = m_strExportDir + _T("\\") + fd->GetWinPathString();
830 if(m_rev1.m_CommitHash.ToString() == GIT_REV_ZERO)
832 if(!CopyFile(g_Git.m_CurrentDir + _T("\\") + fd->GetWinPath(), filename, false))
834 LPVOID lpMsgBuf=NULL;
835 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
836 NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
837 (LPTSTR)&lpMsgBuf,
838 0, NULL);
839 CMessageBox::Show(NULL,(TCHAR *)lpMsgBuf, _T("TortoiseGit"), MB_OK|MB_ICONERROR);
840 LocalFree(lpMsgBuf);
841 return;
844 else
846 if(g_Git.GetOneFile(m_rev1.m_CommitHash, *fd, filename))
848 CString out;
849 out.Format(_T("Fail checkout one file %s;%s"), m_rev1.m_CommitHash.ToString(), fd->GetWinPath());
850 CMessageBox::Show(NULL, out, _T("TortoiseGit"), MB_OK);
851 return;
855 #if 0
856 m_pProgDlg = new CProgressDlg();
857 InterlockedExchange(&m_bThreadRunning, TRUE);
858 if (AfxBeginThread(ExportThreadEntry, this)==NULL)
860 InterlockedExchange(&m_bThreadRunning, FALSE);
861 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
863 #endif
867 break;
873 UINT CFileDiffDlg::ExportThreadEntry(LPVOID pVoid)
875 return ((CFileDiffDlg*)pVoid)->ExportThread();
878 UINT CFileDiffDlg::ExportThread()
880 #if 0
881 RefreshCursor();
882 // if (m_pProgDlg == NULL)
883 // return 1;
884 long count = 0;
885 // SetAndClearProgressInfo(m_pProgDlg, false);
886 m_pProgDlg->SetTitle(IDS_PROGRESSWAIT);
887 m_pProgDlg->SetAnimation(AfxGetResourceHandle(), IDR_DOWNLOAD);
888 m_pProgDlg->ShowModeless(this);
889 for (INT_PTR i=0; (i<m_arSelectedFileList.GetCount())&&(!m_pProgDlg->HasUserCancelled()); ++i)
891 CTGitPath* fd = m_arSelectedFileList[i];
892 // CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
893 // CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());
894 // if ((fd.node == svn_node_dir)&&(fd.kind != svn_client_diff_summarize_kind_added))
895 // {
896 // just create the directory
897 // CreateDirectoryEx(NULL, m_strExportDir+_T("\\")+CPathUtils::PathUnescape(fd.path.GetWinPathString()), NULL);
898 // continue;
899 // }
901 CString sTemp;
902 m_pProgDlg->FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)url1.GetGitPathString());
904 CTGitPath savepath = CTGitPath(m_strExportDir);
905 savepath.AppendPathString(_T("\\") + CPathUtils::PathUnescape(fd.path.GetWinPathString()));
906 CPathUtils::MakeSureDirectoryPathExists(fd.node == svn_node_file ? savepath.GetContainingDirectory().GetWinPath() : savepath.GetDirectory().GetWinPath());
907 if (fd.node == svn_node_dir)
909 // exporting a folder requires calling Git::Export() so we also export all
910 // children of that added folder.
911 if ((fd.kind == svn_client_diff_summarize_kind_added)&&(!Export(url2, savepath, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, true, true)))
913 if ((!m_bDoPegDiff)||(!Export(url2, savepath, m_rev2, m_rev2, true, true)))
915 delete m_pProgDlg;
916 m_pProgDlg = NULL;
917 CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
918 InterlockedExchange(&m_bThreadRunning, FALSE);
919 RefreshCursor();
920 return 1;
924 else
926 // exporting a file requires calling Git::Cat(), since Git::Export() only works
927 // with folders.
928 if ((fd.kind != svn_client_diff_summarize_kind_deleted)&&(!Cat(url2, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, savepath)))
930 if ((!m_bDoPegDiff)||(!Cat(url2, m_rev2, m_rev2, savepath)))
932 delete m_pProgDlg;
933 m_pProgDlg = NULL;
934 CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
935 InterlockedExchange(&m_bThreadRunning, FALSE);
936 RefreshCursor();
937 return 1;
941 count++;
942 m_pProgDlg->SetProgress (count, static_cast<DWORD>(m_arSelectedFileList.GetCount()));
944 m_pProgDlg->Stop();
945 SetAndClearProgressInfo(NULL, false);
946 delete m_pProgDlg;
947 m_pProgDlg = NULL;
948 InterlockedExchange(&m_bThreadRunning, FALSE);
949 RefreshCursor();
950 #endif
951 return 0;
954 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
956 if (pWnd != &m_cFileList)
957 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
958 if (m_bThreadRunning == 0)
960 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
961 SetCursor(hCur);
962 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
964 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
965 SetCursor(hCur);
966 return TRUE;
969 void CFileDiffDlg::OnEnSetfocusFirsturl()
971 GetDlgItem(IDC_FIRSTURL)->HideCaret();
974 void CFileDiffDlg::OnEnSetfocusSecondurl()
976 GetDlgItem(IDC_SECONDURL)->HideCaret();
980 void CFileDiffDlg::OnBnClickedSwitchleftright()
983 if (m_bThreadRunning)
984 return;
986 #if 0
987 CString sFilterString;
988 m_cFilter.GetWindowText(sFilterString);
990 m_cFileList.SetRedraw(false);
991 m_cFileList.DeleteAllItems();
992 for (int i=0; i<(int)m_arFileList.GetCount(); ++i)
994 CTGitPath fd = m_arFileList[i];
995 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)
996 fd.m_Action = CTGitPath::LOGACTIONS_DELETED;
997 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)
998 fd.m_Action = CTGitPath::LOGACTIONS_ADDED;
999 std::swap(fd.m_StatAdd, fd.m_StatDel);
1000 (CTGitPath&)m_arFileList[i] = fd;
1002 Filter(sFilterString);
1003 #endif
1005 m_cFileList.SetRedraw(true);
1006 CTGitPath path = m_path1;
1007 m_path1 = m_path2;
1008 m_path2 = path;
1009 GitRev rev = m_rev1;
1010 m_rev1 = m_rev2;
1011 m_rev2 = rev;
1013 CString str1,str2;
1014 this->m_ctrRev1Edit.GetWindowText(str1);
1015 this->m_ctrRev2Edit.GetWindowText(str2);
1017 this->m_ctrRev1Edit.SetWindowText(str2);
1018 this->m_ctrRev2Edit.SetWindowText(str1);
1020 SetURLLabels();
1021 //KillTimer(IDT_INPUT);
1024 void CFileDiffDlg::SetURLLabels(int mask)
1027 // m_cRev1Btn.SetWindowText(m_rev1.m_CommitHash.ToString().Left(6));
1028 // m_cRev2Btn.SetWindowText(m_rev2.m_CommitHash.ToString().Left(6));
1030 if(mask &0x1)
1032 SetDlgItemText(IDC_FIRSTURL, m_rev1.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev1.GetSubject());
1033 m_tooltips.AddTool(IDC_FIRSTURL,
1034 CAppUtils::FormatDateAndTime( m_rev1.GetAuthorDate(), DATE_SHORTDATE, false )+_T(" ")+m_rev1.GetAuthorName());
1038 if(mask &0x2)
1040 SetDlgItemText(IDC_SECONDURL,m_rev2.m_CommitHash.ToString().Left(8)+_T(": ")+m_rev2.GetSubject());
1042 m_tooltips.AddTool(IDC_SECONDURL,
1043 CAppUtils::FormatDateAndTime( m_rev2.GetAuthorDate(), DATE_SHORTDATE, false )+_T(" ")+m_rev2.GetAuthorName());
1046 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(_T("Version 2 (Base)"));
1047 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(_T("Version 1"));
1049 if( (mask&0x3) == 0x3)
1050 if(m_rev2.GetCommitterDate() > m_rev1.GetCommitterDate())
1052 this->GetDlgItem(IDC_REV2GROUP)->SetWindowText(_T("Version 2 (Base) (Commit Date New)"));
1054 else
1056 this->GetDlgItem(IDC_REV1GROUP)->SetWindowText(_T("Version 1 (Commit Data New)"));
1060 void CFileDiffDlg::ClearURLabels(int mask)
1062 if(mask&0x1)
1064 SetDlgItemText(IDC_FIRSTURL, _T(""));
1065 m_tooltips.AddTool(IDC_FIRSTURL, _T(""));
1068 if(mask&0x2)
1070 SetDlgItemText(IDC_SECONDURL, _T(""));
1071 m_tooltips.AddTool(IDC_SECONDURL, _T(""));
1074 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)
1076 m_tooltips.RelayEvent(pMsg);
1077 if (pMsg->message == WM_KEYDOWN)
1079 switch (pMsg->wParam)
1081 case 'A':
1083 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1085 // select all entries
1086 for (int i=0; i<m_cFileList.GetItemCount(); ++i)
1088 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1090 return TRUE;
1093 break;
1094 case 'C':
1095 case VK_INSERT:
1097 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1099 CopySelectionToClipboard();
1100 return TRUE;
1103 break;
1104 case '\r':
1106 if (GetFocus() == GetDlgItem(IDC_FILELIST))
1108 // Return pressed in file list. Show diff, as for double click
1109 int selIndex = m_cFileList.GetSelectionMark();
1110 if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))
1111 DoDiff(selIndex, m_bBlame);
1112 return TRUE;
1115 break;
1118 return __super::PreTranslateMessage(pMsg);
1121 void CFileDiffDlg::OnCancel()
1123 if (m_bThreadRunning)
1125 m_bCancelled = true;
1126 return;
1128 __super::OnCancel();
1131 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)
1133 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
1134 if (m_bThreadRunning)
1135 return;
1137 if (m_nSortedColumn == phdr->iItem)
1138 m_bAscending = !m_bAscending;
1139 else
1140 m_bAscending = TRUE;
1141 m_nSortedColumn = phdr->iItem;
1142 m_arSelectedFileList.RemoveAll();
1143 Sort();
1145 CString temp;
1146 m_cFileList.SetRedraw(FALSE);
1147 m_cFileList.DeleteAllItems();
1148 m_cFilter.GetWindowText(temp);
1149 Filter(temp);
1151 CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();
1152 HDITEM HeaderItem = {0};
1153 HeaderItem.mask = HDI_FORMAT;
1154 for (int i=0; i<pHeader->GetItemCount(); ++i)
1156 pHeader->GetItem(i, &HeaderItem);
1157 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
1158 pHeader->SetItem(i, &HeaderItem);
1160 pHeader->GetItem(m_nSortedColumn, &HeaderItem);
1161 HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);
1162 pHeader->SetItem(m_nSortedColumn, &HeaderItem);
1164 m_cFileList.SetRedraw(TRUE);
1166 *pResult = 0;
1169 void CFileDiffDlg::Sort()
1171 if(m_arFileList.GetCount() < 2)
1173 return;
1176 std::sort(m_arFileList.m_paths.begin(), m_arFileList.m_paths.end(), &CFileDiffDlg::SortCompare);
1179 bool CFileDiffDlg::SortCompare(const CTGitPath& Data1, const CTGitPath& Data2)
1181 int result = 0;
1182 int d1, d2;
1183 switch (m_nSortedColumn)
1185 case 0: //path column
1186 result = Data1.GetWinPathString().Compare(Data2.GetWinPathString());
1187 break;
1188 case 1: //action column
1189 result = Data1.m_Action - Data2.m_Action;
1190 break;
1191 case 2:
1192 d1 = CSorter::A2L(Data1.m_StatAdd);
1193 d2 = CSorter::A2L(Data2.m_StatAdd);
1194 result = d1 - d2;
1195 break;
1196 case 3:
1197 d1 = CSorter::A2L(Data1.m_StatDel);;
1198 d2 = CSorter::A2L(Data2.m_StatDel);
1199 result = d1 - d2;
1200 break;
1201 default:
1202 break;
1205 if (!m_bAscending)
1206 result = -result;
1207 return result < 0;
1211 void CFileDiffDlg::OnBnClickedRev1btn()
1213 ClickRevButton(&this->m_cRev1Btn,&this->m_rev1, &this->m_ctrRev1Edit);
1216 void CFileDiffDlg::ClickRevButton(CMenuButton *button, GitRev *rev, CACEdit *edit)
1218 int entry=button->GetCurrentEntry();
1219 if(entry == 0) /* Browse Refence*/
1222 CString str = CBrowseRefsDlg::PickRef();
1223 if(str.IsEmpty())
1224 return;
1226 if(FillRevFromString(rev,str))
1227 return;
1229 edit->SetWindowText(str);
1233 if(entry == 1) /*Log*/
1235 CLogDlg dlg;
1236 dlg.SetSelect(true);
1237 if(dlg.DoModal() == IDOK)
1239 if( dlg.GetSelectedHash().IsEmpty() )
1240 return;
1242 if(FillRevFromString(rev,dlg.GetSelectedHash()))
1243 return;
1245 edit->SetWindowText(dlg.GetSelectedHash());
1248 else
1249 return;
1252 if(entry == 2) /*RefLog*/
1254 CRefLogDlg dlg;
1255 if(dlg.DoModal() == IDOK)
1257 if(FillRevFromString(rev,dlg.m_SelectedHash))
1258 return;
1260 edit->SetWindowText(dlg.m_SelectedHash);
1263 else
1264 return;
1267 SetURLLabels();
1269 InterlockedExchange(&m_bThreadRunning, TRUE);
1270 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1272 InterlockedExchange(&m_bThreadRunning, FALSE);
1273 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1275 KillTimer(IDT_INPUT);
1278 void CFileDiffDlg::OnBnClickedRev2btn()
1280 ClickRevButton(&this->m_cRev2Btn,&this->m_rev2, &this->m_ctrRev2Edit);
1281 #if 0
1282 if (m_bThreadRunning)
1283 return; // do nothing as long as the thread is still running
1285 // show a dialog where the user can enter a revision
1286 CRevisionDlg dlg(this);
1287 dlg.AllowWCRevs(false);
1288 *((GitRev*)&dlg) = m_rev2;
1290 if (dlg.DoModal() == IDOK)
1292 m_rev2 = dlg;
1293 m_cRev2Btn.SetWindowText(m_rev2.ToString());
1294 m_cFileList.DeleteAllItems();
1295 // start a new thread to re-fetch the diff
1296 InterlockedExchange(&m_bThreadRunning, TRUE);
1297 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1299 InterlockedExchange(&m_bThreadRunning, FALSE);
1300 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1303 #endif
1306 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1308 if (m_bThreadRunning)
1310 SetTimer(IDT_FILTER, 1000, NULL);
1311 return 0L;
1314 KillTimer(IDT_FILTER);
1316 m_cFileList.SetRedraw(FALSE);
1317 m_arFilteredList.clear();
1318 m_cFileList.DeleteAllItems();
1320 Filter(_T(""));
1322 m_cFileList.SetRedraw(TRUE);
1323 return 0L;
1326 void CFileDiffDlg::OnEnChangeFilter()
1328 SetTimer(IDT_FILTER, 1000, NULL);
1331 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)
1333 if (m_bThreadRunning)
1334 return;
1336 if( nIDEvent == IDT_FILTER)
1338 CString sFilterText;
1339 KillTimer(IDT_FILTER);
1340 m_cFilter.GetWindowText(sFilterText);
1342 m_cFileList.SetRedraw(FALSE);
1343 m_cFileList.DeleteAllItems();
1345 Filter(sFilterText);
1347 m_cFileList.SetRedraw(TRUE);
1349 __super::OnTimer(nIDEvent);
1352 if( nIDEvent == IDT_INPUT)
1354 KillTimer(IDT_INPUT);
1355 TRACE(_T("Input Timer\r\n"));
1357 GitRev gitrev;
1358 CString str;
1359 int mask = 0;
1360 this->m_ctrRev1Edit.GetWindowText(str);
1361 if( !gitrev.GetCommit(str) )
1363 this->m_rev1=gitrev;
1364 mask |= 0x1;
1367 this->m_ctrRev2Edit.GetWindowText(str);
1369 if( !gitrev.GetCommit(str) )
1371 this->m_rev2=gitrev;
1372 mask |= 0x2;
1375 this->SetURLLabels(mask);
1377 if(mask == 0x3)
1380 InterlockedExchange(&m_bThreadRunning, TRUE);
1381 if (AfxBeginThread(DiffThreadEntry, this)==NULL)
1383 InterlockedExchange(&m_bThreadRunning, FALSE);
1384 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1390 void CFileDiffDlg::Filter(CString sFilterText)
1392 sFilterText.MakeLower();
1394 m_arFilteredList.clear();
1396 for (int i=0;i<m_arFileList.GetCount();i++)
1398 CString sPath = m_arFileList[i].GetGitPathString();
1399 sPath.MakeLower();
1400 if (sPath.Find(sFilterText) >= 0)
1402 m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));
1405 for (std::vector<CTGitPath*>::const_iterator it = m_arFilteredList.begin(); it != m_arFilteredList.end(); ++it)
1407 AddEntry(*it);
1411 void CFileDiffDlg::CopySelectionToClipboard(BOOL isFull)
1413 // copy all selected paths to the clipboard
1414 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
1415 int index;
1416 CString sTextForClipboard;
1417 while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)
1419 sTextForClipboard += m_cFileList.GetItemText(index, 0);
1420 sTextForClipboard += _T("\t");
1422 if(!isFull)
1424 sTextForClipboard += _T("\r\n");
1426 }else
1428 sTextForClipboard += m_cFileList.GetItemText(index, 1);
1429 sTextForClipboard += _T("\t");
1430 sTextForClipboard += m_cFileList.GetItemText(index, 2);
1431 sTextForClipboard += _T("\t");
1432 sTextForClipboard += m_cFileList.GetItemText(index, 3);
1433 sTextForClipboard += _T("\r\n");
1436 CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);
1440 LRESULT CFileDiffDlg::OnRefLoad(WPARAM wParam, LPARAM lParam)
1442 UNREFERENCED_PARAMETER(wParam);
1443 UNREFERENCED_PARAMETER(lParam);
1444 for(int i=0;i<m_Reflist.size();i++)
1446 CString str=m_Reflist[i];
1448 if(str.Find(_T("remotes/")) == 0)
1449 str=str.Mid(8);
1451 m_ctrRev1Edit.AddSearchString(str);
1452 m_ctrRev2Edit.AddSearchString(str);
1454 return 0;
1457 BOOL CFileDiffDlg::DestroyWindow()
1459 return CResizableStandAloneDialog::DestroyWindow();
1462 LRESULT CFileDiffDlg::OnEnUpdate(WPARAM /*wParam*/, LPARAM lParam)
1464 if(lParam == IDC_REV1EDIT)
1466 OnTextUpdate(&this->m_ctrRev1Edit);
1467 ClearURLabels(1);
1469 if(lParam == IDC_REV2EDIT)
1471 OnTextUpdate(&this->m_ctrRev2Edit);
1472 ClearURLabels(1<<1);
1474 return 0;
1477 void CFileDiffDlg::OnTextUpdate(CACEdit * /*pEdit*/)
1479 SetTimer(IDT_INPUT, 1000, NULL);
1480 this->m_cFileList.ShowText(_T("Wait For input validate version"));