OSDN Git Service

Git Blame Add context menu Action
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / FileDiffDlg.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "stdafx.h"\r
20 #include "TortoiseProc.h"\r
21 #include "UnicodeUtils.h"\r
22 #include "MessageBox.h"\r
23 #include "AppUtils.h"\r
24 #include "TempFile.h"\r
25 #include "ProgressDlg.h"\r
26 #include "SysImageList.h"\r
27 //#include "GitProperties.h"\r
28 #include "StringUtils.h"\r
29 #include "PathUtils.h"\r
30 #include "BrowseFolder.h"\r
31 #include "RevisionDlg.h"\r
32 #include ".\filediffdlg.h"\r
33 #include "gitdiff.h"\r
34 #include "CommonResource.h"\r
35 \r
36 #define ID_COMPARE 1\r
37 #define ID_BLAME 2\r
38 #define ID_SAVEAS 3\r
39 #define ID_EXPORT 4\r
40 #define ID_CLIPBOARD 5\r
41 \r
42 BOOL    CFileDiffDlg::m_bAscending = FALSE;\r
43 int             CFileDiffDlg::m_nSortedColumn = -1;\r
44 \r
45 \r
46 IMPLEMENT_DYNAMIC(CFileDiffDlg, CResizableStandAloneDialog)\r
47 CFileDiffDlg::CFileDiffDlg(CWnd* pParent /*=NULL*/)\r
48         : CResizableStandAloneDialog(CFileDiffDlg::IDD, pParent),\r
49         m_bBlame(false),\r
50         m_pProgDlg(NULL),\r
51         m_bCancelled(false)\r
52 {\r
53 }\r
54 \r
55 CFileDiffDlg::~CFileDiffDlg()\r
56 {\r
57         DestroyIcon(m_hSwitchIcon);\r
58 }\r
59 \r
60 void CFileDiffDlg::DoDataExchange(CDataExchange* pDX)\r
61 {\r
62         CResizableStandAloneDialog::DoDataExchange(pDX);\r
63         DDX_Control(pDX, IDC_FILELIST, m_cFileList);\r
64         DDX_Control(pDX, IDC_SWITCHLEFTRIGHT, m_SwitchButton);\r
65         DDX_Control(pDX, IDC_REV1BTN, m_cRev1Btn);\r
66         DDX_Control(pDX, IDC_REV2BTN, m_cRev2Btn);\r
67         DDX_Control(pDX, IDC_FILTER, m_cFilter);\r
68 }\r
69 \r
70 \r
71 BEGIN_MESSAGE_MAP(CFileDiffDlg, CResizableStandAloneDialog)\r
72         ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)\r
73         ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)\r
74         ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)\r
75         ON_WM_CONTEXTMENU()\r
76         ON_WM_SETCURSOR()\r
77         ON_EN_SETFOCUS(IDC_SECONDURL, &CFileDiffDlg::OnEnSetfocusSecondurl)\r
78         ON_EN_SETFOCUS(IDC_FIRSTURL, &CFileDiffDlg::OnEnSetfocusFirsturl)\r
79         ON_BN_CLICKED(IDC_SWITCHLEFTRIGHT, &CFileDiffDlg::OnBnClickedSwitchleftright)\r
80         ON_NOTIFY(HDN_ITEMCLICK, 0, &CFileDiffDlg::OnHdnItemclickFilelist)\r
81         ON_BN_CLICKED(IDC_REV1BTN, &CFileDiffDlg::OnBnClickedRev1btn)\r
82         ON_BN_CLICKED(IDC_REV2BTN, &CFileDiffDlg::OnBnClickedRev2btn)\r
83         ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)\r
84         ON_EN_CHANGE(IDC_FILTER, &CFileDiffDlg::OnEnChangeFilter)\r
85         ON_WM_TIMER()\r
86 END_MESSAGE_MAP()\r
87 \r
88 \r
89 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1, GitRev rev2)\r
90 {\r
91         if(path!=NULL)\r
92         {\r
93                 m_path1 = *path;\r
94                 m_path2 = *path;\r
95         }\r
96         m_rev1 = rev1;\r
97         m_rev2 = rev2;\r
98         \r
99 }\r
100 void CFileDiffDlg::SetDiff(CTGitPath * path, CString &hash1, CString &hash2)\r
101 {\r
102         if(path!=NULL)\r
103         {\r
104                 m_path1 = *path;\r
105                 m_path2 = *path;\r
106         }\r
107         m_rev1.m_CommitHash = hash1;\r
108         m_rev2.m_CommitHash = hash2;\r
109 }\r
110 void CFileDiffDlg::SetDiff(CTGitPath * path, GitRev rev1)\r
111 {\r
112         if(path!=NULL)\r
113         {\r
114                 m_path1 = *path;\r
115                 m_path2 = *path;\r
116         }\r
117         m_rev1 = rev1;\r
118         m_rev2.m_CommitHash = _T("");\r
119         m_rev2.m_Subject = _T("Previou Version");\r
120 \r
121         //this->GetDlgItem()->EnableWindow(FALSE);\r
122         \r
123         \r
124 }\r
125 \r
126 BOOL CFileDiffDlg::OnInitDialog()\r
127 {\r
128         CResizableStandAloneDialog::OnInitDialog();\r
129         CString temp;\r
130 \r
131         m_tooltips.Create(this);\r
132         m_tooltips.AddTool(IDC_SWITCHLEFTRIGHT, IDS_FILEDIFF_SWITCHLEFTRIGHT_TT);\r
133 \r
134         m_cFileList.SetRedraw(false);\r
135         m_cFileList.DeleteAllItems();\r
136         DWORD exStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP;\r
137         m_cFileList.SetExtendedStyle(exStyle);\r
138 \r
139         m_nIconFolder = SYS_IMAGE_LIST().GetDirIconIndex();\r
140         m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);\r
141 \r
142         m_hSwitchIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_SWITCHLEFTRIGHT), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
143         m_SwitchButton.SetIcon(m_hSwitchIcon);\r
144 \r
145         m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);\r
146         m_cFilter.SetInfoIcon(IDI_FILTEREDIT);\r
147         temp.LoadString(IDS_FILEDIFF_FILTERCUE);\r
148         temp = _T("   ")+temp;\r
149         m_cFilter.SetCueBanner(temp);\r
150 \r
151         int c = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;\r
152         while (c>=0)\r
153                 m_cFileList.DeleteColumn(c--);\r
154 \r
155         \r
156         temp.LoadString(IDS_FILEDIFF_FILE);\r
157         m_cFileList.InsertColumn(0, temp);\r
158         temp.LoadString(IDS_FILEDIFF_ACTION);\r
159         m_cFileList.InsertColumn(1, temp);\r
160 \r
161         temp.LoadString(IDS_FILEDIFF_STATADD);\r
162         m_cFileList.InsertColumn(2, temp);\r
163         temp.LoadString(IDS_FILEDIFF_STATDEL);\r
164         m_cFileList.InsertColumn(3, temp);\r
165         \r
166         int mincol = 0;\r
167         int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;\r
168         int col;\r
169         for (col = mincol; col <= maxcol; col++)\r
170         {\r
171                 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);\r
172         }\r
173         \r
174         m_cFileList.SetRedraw(true);\r
175         \r
176         AddAnchor(IDC_DIFFSTATIC1, TOP_LEFT, TOP_RIGHT);\r
177         AddAnchor(IDC_SWITCHLEFTRIGHT, TOP_RIGHT);\r
178         AddAnchor(IDC_FIRSTURL, TOP_LEFT, TOP_RIGHT);\r
179         AddAnchor(IDC_REV1BTN, TOP_RIGHT);\r
180         AddAnchor(IDC_DIFFSTATIC2, TOP_LEFT, TOP_RIGHT);\r
181         AddAnchor(IDC_SECONDURL, TOP_LEFT, TOP_RIGHT);\r
182         AddAnchor(IDC_REV2BTN, TOP_RIGHT);\r
183         AddAnchor(IDC_FILTER, TOP_LEFT, TOP_RIGHT);\r
184         AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);\r
185         \r
186         SetURLLabels();\r
187 \r
188         EnableSaveRestore(_T("FileDiffDlg"));\r
189 \r
190         InterlockedExchange(&m_bThreadRunning, TRUE);\r
191         if (AfxBeginThread(DiffThreadEntry, this)==NULL)\r
192         {\r
193                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
194                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
195         }\r
196 \r
197         // Start with focus on file list\r
198         GetDlgItem(IDC_FILELIST)->SetFocus();\r
199 \r
200         if(m_rev2.m_CommitHash.IsEmpty())\r
201                 m_SwitchButton.EnableWindow(FALSE);\r
202         return FALSE;\r
203 }\r
204 \r
205 #if 0\r
206 svn_error_t* CFileDiffDlg::DiffSummarizeCallback(const CTGitPath& path, \r
207                                                                                                  svn_client_diff_summarize_kind_t kind, \r
208                                                                                                  bool propchanged, svn_node_kind_t node)\r
209 {\r
210         CTGitPath* fd;\r
211         fd.path = path;\r
212         fd.kind = kind;\r
213         fd.node = node;\r
214         fd.propchanged = propchanged;\r
215         m_arFileList.push_back(fd);\r
216         return Git_NO_ERROR;\r
217 }\r
218 #endif\r
219 \r
220 UINT CFileDiffDlg::DiffThreadEntry(LPVOID pVoid)\r
221 {\r
222         return ((CFileDiffDlg*)pVoid)->DiffThread();\r
223 }\r
224 \r
225 UINT CFileDiffDlg::DiffThread()\r
226 {\r
227         bool bSuccess = true;\r
228         RefreshCursor();\r
229         m_cFileList.ShowText(CString(MAKEINTRESOURCE(IDS_FILEDIFF_WAIT)));\r
230         m_arFileList.Clear();\r
231 #if 0\r
232         if (m_bDoPegDiff)\r
233         {\r
234 //              bSuccess = DiffSummarizePeg(m_path1, m_peg, m_rev1, m_rev2, m_depth, m_bIgnoreancestry);\r
235         }\r
236         else\r
237         {\r
238 //              bSuccess = DiffSummarize(m_path1, m_rev1, m_path2, m_rev2, m_depth, m_bIgnoreancestry);\r
239         }\r
240 //      if (!bSuccess)\r
241 //      {\r
242 //              m_cFileList.ShowText(GetLastErrorMessage());\r
243 //              InterlockedExchange(&m_bThreadRunning, FALSE);\r
244 //              return 0;\r
245 //      }\r
246 #endif\r
247         CString cmd;\r
248         CString rev1=m_rev1.m_CommitHash;\r
249         if(this->m_rev1.m_CommitHash == GIT_REV_ZERO || this->m_rev2.m_CommitHash == GIT_REV_ZERO)\r
250         {\r
251                 rev1=+_T("");\r
252                 if(this->m_rev1.m_CommitHash == GIT_REV_ZERO)\r
253                         cmd.Format(_T("git.exe diff -r --raw -C -M --numstat  %s"),m_rev2.m_CommitHash);\r
254                 else\r
255                         cmd.Format(_T("git.exe diff -r -R --raw -C -M --numstat  %s"),m_rev1.m_CommitHash);\r
256         }else\r
257         {\r
258                 cmd.Format(_T("git.exe diff-tree -r --raw -C -M --numstat %s %s"),rev1,m_rev2.m_CommitHash);\r
259         }\r
260 \r
261         CString out;\r
262         g_Git.Run(cmd,&out);\r
263         this->m_arFileList.ParserFromLog(out);\r
264         \r
265         CString sFilterText;\r
266         m_cFilter.GetWindowText(sFilterText);\r
267         m_cFileList.SetRedraw(false);\r
268         Filter(sFilterText);\r
269         if (m_arFileList.GetCount()>0)\r
270         {\r
271                 // Highlight first entry in file list\r
272                 m_cFileList.SetSelectionMark(0);\r
273                 m_cFileList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);\r
274         }\r
275 \r
276         int mincol = 0;\r
277         int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;\r
278         int col;\r
279         for (col = mincol; col <= maxcol; col++)\r
280         {\r
281                 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);\r
282         }\r
283 \r
284         m_cFileList.ClearText();\r
285         m_cFileList.SetRedraw(true);\r
286 \r
287         InterlockedExchange(&m_bThreadRunning, FALSE);\r
288         InvalidateRect(NULL);\r
289         RefreshCursor();\r
290         return 0;\r
291 }\r
292 \r
293 int CFileDiffDlg::AddEntry(const CTGitPath * fd)\r
294 {\r
295         int ret = -1;\r
296         if (fd)\r
297         {\r
298                 int index = m_cFileList.GetItemCount();\r
299 \r
300                 int icon_idx = 0;\r
301 //              if (fd->node == svn_node_dir)\r
302 //                              icon_idx = m_nIconFolder;\r
303 //              else\r
304                 {\r
305                         icon_idx = SYS_IMAGE_LIST().GetPathIconIndex(fd->GetGitPathString());\r
306                 }\r
307 \r
308                 ret = m_cFileList.InsertItem(index, fd->GetGitPathString(), icon_idx);\r
309                 m_cFileList.SetItemText(index, 1, ((CTGitPath*)fd)->GetActionName());\r
310                 m_cFileList.SetItemText(index, 2, ((CTGitPath*)fd)->m_StatAdd);\r
311                 m_cFileList.SetItemText(index, 3, ((CTGitPath*)fd)->m_StatDel);\r
312         }\r
313         return ret;\r
314 }\r
315 \r
316 void CFileDiffDlg::DoDiff(int selIndex, bool blame)\r
317 {\r
318 \r
319         CGitDiff diff;\r
320         CTGitPath* fd = m_arFilteredList[selIndex];\r
321         diff.Diff(fd, this->m_rev1.m_CommitHash, this->m_rev2.m_CommitHash, blame, FALSE);\r
322 \r
323 #if 0\r
324         CFileDiffDlg::CTGitPath* fd = m_arFilteredList[selIndex];\r
325 \r
326         CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
327         CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
328 \r
329         if (fd.kind == svn_client_diff_summarize_kind_deleted)\r
330         {\r
331                 if (!PathIsURL(url1))\r
332                         url1 = CTGitPath(GetURLFromPath(m_path1) + _T("/") + fd.path.GetGitPathString());\r
333                 if (!PathIsURL(url2))\r
334                         url2 = m_bDoPegDiff ? url1 : CTGitPath(GetURLFromPath(m_path2) + _T("/") + fd.path.GetGitPathString());\r
335         }\r
336 \r
337         if (fd.propchanged)\r
338         {\r
339                 DiffProps(selIndex);\r
340         }\r
341         if (fd.node == svn_node_dir)\r
342                 return;\r
343 \r
344         CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path1, m_rev1);\r
345         CString sTemp;\r
346         CProgressDlg progDlg;\r
347         progDlg.SetTitle(IDS_PROGRESSWAIT);\r
348         progDlg.SetAnimation(IDR_DOWNLOAD);\r
349         progDlg.ShowModeless(this);\r
350         progDlg.FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)m_path1.GetUIPathString());\r
351         progDlg.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT, (LPCTSTR)m_rev1.ToString());\r
352 \r
353         if ((fd.kind != svn_client_diff_summarize_kind_added)&&(!blame)&&(!Cat(url1, m_bDoPegDiff ? m_peg : m_rev1, m_rev1, tempfile)))\r
354         {\r
355                 if ((!m_bDoPegDiff)||(!Cat(url1, m_rev1, m_rev1, tempfile)))\r
356                 {\r
357                         CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
358                         return;\r
359                 }\r
360         }\r
361         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)))\r
362         {\r
363                 if ((!m_bDoPegDiff)||(!m_blamer.BlameToFile(url1, 1, m_rev1, m_rev1, tempfile, _T(""), TRUE, TRUE)))\r
364                 {\r
365                         CMessageBox::Show(NULL, m_blamer.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
366                         return;\r
367                 }\r
368         }\r
369         SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
370         progDlg.SetProgress(1, 2);\r
371         progDlg.FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)url2.GetUIPathString());\r
372         progDlg.FormatNonPathLine(2, IDS_PROGRESSREVISIONTEXT, (LPCTSTR)m_rev2.ToString());\r
373         CTGitPath tempfile2 = CTempFiles::Instance().GetTempFilePath(false, url2, m_rev2);\r
374         if ((fd.kind != svn_client_diff_summarize_kind_deleted)&&(!blame)&&(!Cat(url2, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, tempfile2)))\r
375         {\r
376                 if ((!m_bDoPegDiff)||(!Cat(url2, m_rev2, m_rev2, tempfile2)))\r
377                 {\r
378                         CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
379                         return;\r
380                 }\r
381         }\r
382         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)))\r
383         {\r
384                 if ((!m_bDoPegDiff)||(!m_blamer.BlameToFile(url2, 1, m_rev2, m_rev2, tempfile2, _T(""), TRUE, TRUE)))\r
385                 {\r
386                         CMessageBox::Show(NULL, m_blamer.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
387                         return;\r
388                 }\r
389         }\r
390         SetFileAttributes(tempfile2.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
391         progDlg.SetProgress(2,2);\r
392         progDlg.Stop();\r
393 \r
394         CString rev1name, rev2name;\r
395         if (m_bDoPegDiff)\r
396         {\r
397                 rev1name.Format(_T("%s Revision %ld"), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev1);\r
398                 rev2name.Format(_T("%s Revision %ld"), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev2);\r
399         }\r
400         else\r
401         {\r
402                 rev1name = m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString();\r
403                 rev2name = m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString();\r
404         }\r
405         CAppUtils::DiffFlags flags;\r
406         flags.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
407         flags.Blame(blame);\r
408         CAppUtils::StartExtDiff(\r
409                 tempfile, tempfile2, rev1name, rev2name, flags);\r
410 #endif\r
411 }\r
412 \r
413 #if 0\r
414 void CFileDiffDlg::DiffProps(int selIndex)\r
415 {\r
416         CFileDiffDlg::CTGitPath* fd = m_arFilteredList[selIndex];\r
417 \r
418         CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
419         CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
420 \r
421         GitProperties propsurl1(url1, m_rev1, false);\r
422         GitProperties propsurl2(url2, m_rev2, false);\r
423         \r
424         // collect the properties of both revisions in a set\r
425         std::set<stdstring> properties;\r
426         for (int wcindex = 0; wcindex < propsurl1.GetCount(); ++wcindex)\r
427         {\r
428                 stdstring urlname = propsurl1.GetItemName(wcindex);\r
429                 if ( properties.find(urlname) == properties.end() )\r
430                 {\r
431                         properties.insert(urlname);\r
432                 }\r
433         }\r
434         for (int wcindex = 0; wcindex < propsurl2.GetCount(); ++wcindex)\r
435         {\r
436                 stdstring urlname = propsurl2.GetItemName(wcindex);\r
437                 if ( properties.find(urlname) == properties.end() )\r
438                 {\r
439                         properties.insert(urlname);\r
440                 }\r
441         }\r
442 \r
443         // iterate over all properties and diff the properties\r
444         for (std::set<stdstring>::iterator iter = properties.begin(), end = properties.end(); iter != end; ++iter)\r
445         {\r
446                 stdstring url1name = *iter;\r
447                 \r
448                 stdstring url1value = _T(""); // CUnicodeUtils::StdGetUnicode((char *)propsurl1.GetItemValue(wcindex).c_str());\r
449                 for (int url1index = 0; url1index < propsurl1.GetCount(); ++url1index)\r
450                 {\r
451                         if (propsurl1.GetItemName(url1index).compare(url1name)==0)\r
452                         {\r
453                                 url1value = CString((char *)propsurl1.GetItemValue(url1index).c_str());\r
454                         }\r
455                 }               \r
456                 \r
457                 stdstring url2value = _T("");\r
458                 for (int url2index = 0; url2index < propsurl2.GetCount(); ++url2index)\r
459                 {\r
460                         if (propsurl2.GetItemName(url2index).compare(url1name)==0)\r
461                         {\r
462                                 url2value = CString((char *)propsurl2.GetItemValue(url2index).c_str());\r
463                         }\r
464                 }\r
465 \r
466                 if (url2value.compare(url1value)!=0)\r
467                 {\r
468                         // write both property values to temporary files\r
469                         CTGitPath url1propfile = CTempFiles::Instance().GetTempFilePath(false);\r
470                         CTGitPath url2propfile = CTempFiles::Instance().GetTempFilePath(false);\r
471                         FILE * pFile;\r
472                         _tfopen_s(&pFile, url1propfile.GetWinPath(), _T("wb"));\r
473                         if (pFile)\r
474                         {\r
475                                 fputs(CUnicodeUtils::StdGetUTF8(url1value).c_str(), pFile);\r
476                                 fclose(pFile);\r
477                                 FILE * pFile;\r
478                                 _tfopen_s(&pFile, url2propfile.GetWinPath(), _T("wb"));\r
479                                 if (pFile)\r
480                                 {\r
481                                         fputs(CUnicodeUtils::StdGetUTF8(url2value).c_str(), pFile);\r
482                                         fclose(pFile);\r
483                                 }\r
484                                 else\r
485                                         return;\r
486                         }\r
487                         else\r
488                                 return;\r
489                         SetFileAttributes(url1propfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
490                         SetFileAttributes(url2propfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
491                         CString n1, n2;\r
492                         if (m_rev1.IsWorking())\r
493                                 n1.Format(IDS_DIFF_WCNAME, url1name.c_str());\r
494                         if (m_rev1.IsBase())\r
495                                 n1.Format(IDS_DIFF_BASENAME, url1name.c_str());\r
496                         if (m_rev1.IsHead() || m_rev1.IsNumber())\r
497                         {\r
498                                 if (m_bDoPegDiff)\r
499                                 {\r
500                                         n1.Format(_T("%s : %s Revision %ld"), url1name.c_str(), (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev1);\r
501                                 }\r
502                                 else\r
503                                 {\r
504                                         CString sTemp = url1name.c_str();\r
505                                         sTemp += _T(" : ");\r
506                                         n1 = sTemp + m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString();\r
507                                 }\r
508                         }\r
509                         if (m_rev2.IsWorking())\r
510                                 n2.Format(IDS_DIFF_WCNAME, url1name.c_str());\r
511                         if (m_rev2.IsBase())\r
512                                 n2.Format(IDS_DIFF_BASENAME, url1name.c_str());\r
513                         if (m_rev2.IsHead() || m_rev2.IsNumber())\r
514                         {\r
515                                 if (m_bDoPegDiff)\r
516                                 {\r
517                                         n2.Format(_T("%s : %s Revision %ld"), url1name.c_str(),  (LPCTSTR)fd.path.GetGitPathString(), (LONG)m_rev2);\r
518                                 }\r
519                                 else\r
520                                 {\r
521                                         CString sTemp = url1name.c_str();\r
522                                         sTemp += _T(" : ");\r
523                                         n2 = sTemp + m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString();\r
524                                 }\r
525                         }\r
526                         CAppUtils::StartExtDiffProps(url1propfile, url2propfile, n1, n2, TRUE);\r
527                 }\r
528         }\r
529 }\r
530 #endif\r
531 void CFileDiffDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)\r
532 {\r
533         *pResult = 0;\r
534         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
535         int selIndex = pNMLV->iItem;\r
536         if (selIndex < 0)\r
537                 return;\r
538         if (selIndex >= (int)m_arFilteredList.size())\r
539                 return; \r
540         \r
541         DoDiff(selIndex, m_bBlame);\r
542 }\r
543 \r
544 void CFileDiffDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)\r
545 {\r
546 \r
547         LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);\r
548         if (pGetInfoTip->iItem >= (int)m_arFilteredList.size())\r
549                 return;\r
550 \r
551         CString path = m_path1.GetGitPathString() + _T("/") + m_arFilteredList[pGetInfoTip->iItem]->GetGitPathString();\r
552         if (pGetInfoTip->cchTextMax > path.GetLength())\r
553                         _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, path, pGetInfoTip->cchTextMax);\r
554 \r
555         *pResult = 0;\r
556 }\r
557 \r
558 void CFileDiffDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)\r
559 {\r
560         NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );\r
561         // Take the default processing unless we set this to something else below.\r
562         *pResult = CDRF_DODEFAULT;\r
563 \r
564         // First thing - check the draw stage. If it's the control's prepaint\r
565         // stage, then tell Windows we want messages for every item.\r
566 \r
567         if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )\r
568         {\r
569                 *pResult = CDRF_NOTIFYITEMDRAW;\r
570         }\r
571         else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )\r
572         {\r
573                 // This is the prepaint stage for an item. Here's where we set the\r
574                 // item's text color. Our return value will tell Windows to draw the\r
575                 // item itself, but it will use the new color we set here.\r
576 \r
577                 // Tell Windows to paint the control itself.\r
578                 *pResult = CDRF_DODEFAULT;\r
579 \r
580                 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);\r
581 \r
582                 if (m_arFilteredList.size() > pLVCD->nmcd.dwItemSpec)\r
583                 {\r
584                         CTGitPath * fd = m_arFilteredList[pLVCD->nmcd.dwItemSpec];\r
585                         switch (fd->m_Action)\r
586                         {\r
587                         case CTGitPath::LOGACTIONS_ADDED:\r
588                                 crText = m_colors.GetColor(CColors::Added);\r
589                                 break;\r
590                         case CTGitPath::LOGACTIONS_DELETED:\r
591                                 crText = m_colors.GetColor(CColors::Deleted);\r
592                                 break;\r
593                         case CTGitPath::LOGACTIONS_MODIFIED:\r
594                                 crText = m_colors.GetColor(CColors::Modified);\r
595                                 break;\r
596                         //case svn_client_diff_summarize_kind_normal:\r
597                         default:\r
598                         //if (fd.propchanged)\r
599                                 crText = m_colors.GetColor(CColors::PropertyChanged);\r
600                                 break;\r
601                         }\r
602                 }\r
603                 // Store the color back in the NMLVCUSTOMDRAW struct.\r
604                 pLVCD->clrText = crText;\r
605         }\r
606 }\r
607 \r
608 void CFileDiffDlg::OnContextMenu(CWnd* pWnd, CPoint point)\r
609 {\r
610         if ((pWnd==0)||(pWnd != &m_cFileList))\r
611                 return;\r
612         if (m_cFileList.GetSelectedCount() == 0)\r
613                 return;\r
614         // if the context menu is invoked through the keyboard, we have to use\r
615         // a calculated position on where to anchor the menu on\r
616         if ((point.x == -1) && (point.y == -1))\r
617         {\r
618                 CRect rect;\r
619                 m_cFileList.GetItemRect(m_cFileList.GetSelectionMark(), &rect, LVIR_LABEL);\r
620                 m_cFileList.ClientToScreen(&rect);\r
621                 point = rect.CenterPoint();\r
622         }\r
623         CMenu popup;\r
624         if (popup.CreatePopupMenu())\r
625         {\r
626                 CString temp;\r
627                 temp.LoadString(IDS_LOG_POPUP_COMPARETWO);\r
628                 popup.AppendMenu(MF_STRING | MF_ENABLED, ID_COMPARE, temp);\r
629                 temp.LoadString(IDS_FILEDIFF_POPBLAME);\r
630                 //popup.AppendMenu(MF_STRING | MF_ENABLED, ID_BLAME, temp);\r
631                 popup.AppendMenu(MF_SEPARATOR, NULL);\r
632                 temp.LoadString(IDS_FILEDIFF_POPSAVELIST);\r
633                 popup.AppendMenu(MF_STRING | MF_ENABLED, ID_SAVEAS, temp);\r
634                 temp.LoadString(IDS_FILEDIFF_POPCLIPBOARD);\r
635                 popup.AppendMenu(MF_STRING | MF_ENABLED, ID_CLIPBOARD, temp);\r
636                 temp.LoadString(IDS_FILEDIFF_POPEXPORT);\r
637                 //popup.AppendMenu(MF_STRING | MF_ENABLED, ID_EXPORT, temp);\r
638                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
639                 m_bCancelled = false;\r
640                 switch (cmd)\r
641                 {\r
642                 case ID_COMPARE:\r
643                         {\r
644                                 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();\r
645                                 while (pos)\r
646                                 {\r
647                                         int index = m_cFileList.GetNextSelectedItem(pos);\r
648                                         DoDiff(index, false);\r
649                                 }                                       \r
650                         }\r
651                         break;\r
652                 case ID_BLAME:\r
653                         {\r
654                                 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();\r
655                                 while (pos)\r
656                                 {\r
657                                         int index = m_cFileList.GetNextSelectedItem(pos);\r
658                                         DoDiff(index, true);\r
659                                 }                                       \r
660                         }\r
661                         break;\r
662                 case ID_SAVEAS:\r
663                         {\r
664                                 if (m_cFileList.GetSelectedCount() > 0)\r
665                                 {\r
666                                         CString temp;\r
667                                         CTGitPath savePath;\r
668                                         CString pathSave;\r
669                                         if (!CAppUtils::FileOpenSave(pathSave, NULL, IDS_REPOBROWSE_SAVEAS, IDS_COMMONFILEFILTER, false, m_hWnd))\r
670                                         {\r
671                                                 break;\r
672                                         }\r
673                                         savePath = CTGitPath(pathSave);\r
674 \r
675                                         // now open the selected file for writing\r
676                                         try\r
677                                         {\r
678                                                 CStdioFile file(savePath.GetWinPathString(), CFile::typeBinary | CFile::modeReadWrite | CFile::modeCreate);\r
679 //                                              temp.Format(IDS_FILEDIFF_CHANGEDLISTINTRO, (LPCTSTR)m_path1.GetGitPathString(), (LPCTSTR)m_rev1.ToString(), (LPCTSTR)m_path2.GetGitPathString(), (LPCTSTR)m_rev2.ToString());\r
680                                                 file.WriteString(temp + _T("\n"));\r
681                                                 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();\r
682                                                 while (pos)\r
683                                                 {\r
684                                                         int index = m_cFileList.GetNextSelectedItem(pos);\r
685                                                         CTGitPath* fd = m_arFilteredList[index];\r
686                                                         file.WriteString(fd->GetGitPathString());\r
687                                                         file.WriteString(_T("\n"));\r
688                                                 }\r
689                                                 file.Close();\r
690                                         } \r
691                                         catch (CFileException* pE)\r
692                                         {\r
693                                                 pE->ReportError();\r
694                                         }\r
695                                 }\r
696                         }\r
697                         break;\r
698                 case ID_CLIPBOARD:\r
699                         {\r
700                                 CopySelectionToClipboard();\r
701                         }\r
702                         break;\r
703                 case ID_EXPORT:\r
704                         {\r
705 #if 0 //this funcation seem no useful\r
706                                 // export all changed files to a folder\r
707                                 CBrowseFolder browseFolder;\r
708                                 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;\r
709                                 if (browseFolder.Show(GetSafeHwnd(), m_strExportDir) == CBrowseFolder::OK) \r
710                                 {\r
711                                         m_arSelectedFileList.RemoveAll();\r
712                                         POSITION pos = m_cFileList.GetFirstSelectedItemPosition();\r
713                                         while (pos)\r
714                                         {\r
715                                                 int index = m_cFileList.GetNextSelectedItem(pos);\r
716                                                 CTGitPath* fd = m_arFilteredList[index];\r
717                                                 m_arSelectedFileList.Add(fd);\r
718                                         }\r
719                                         m_pProgDlg = new CProgressDlg();\r
720                                         InterlockedExchange(&m_bThreadRunning, TRUE);\r
721                                         if (AfxBeginThread(ExportThreadEntry, this)==NULL)\r
722                                         {\r
723                                                 InterlockedExchange(&m_bThreadRunning, FALSE);\r
724                                                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
725                                         }\r
726                                 }\r
727 #endif;                         \r
728                         }\r
729 \r
730                         break;\r
731 \r
732                 }\r
733         }\r
734 }\r
735 \r
736 UINT CFileDiffDlg::ExportThreadEntry(LPVOID pVoid)\r
737 {\r
738         return ((CFileDiffDlg*)pVoid)->ExportThread();\r
739 }\r
740 \r
741 UINT CFileDiffDlg::ExportThread()\r
742 {\r
743 #if 0\r
744         RefreshCursor();\r
745 //      if (m_pProgDlg == NULL)\r
746 //              return 1;\r
747         long count = 0;\r
748 //      SetAndClearProgressInfo(m_pProgDlg, false);\r
749         m_pProgDlg->SetTitle(IDS_PROGRESSWAIT);\r
750         m_pProgDlg->SetAnimation(AfxGetResourceHandle(), IDR_DOWNLOAD);\r
751         m_pProgDlg->ShowModeless(this);\r
752         for (INT_PTR i=0; (i<m_arSelectedFileList.GetCount())&&(!m_pProgDlg->HasUserCancelled()); ++i)\r
753         {\r
754                 CTGitPath* fd = m_arSelectedFileList[i];\r
755 //              CTGitPath url1 = CTGitPath(m_path1.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
756 //              CTGitPath url2 = m_bDoPegDiff ? url1 : CTGitPath(m_path2.GetGitPathString() + _T("/") + fd.path.GetGitPathString());\r
757 //              if ((fd.node == svn_node_dir)&&(fd.kind != svn_client_diff_summarize_kind_added))\r
758 //              {\r
759                         // just create the directory\r
760 //                      CreateDirectoryEx(NULL, m_strExportDir+_T("\\")+CPathUtils::PathUnescape(fd.path.GetWinPathString()), NULL);\r
761 //                      continue;\r
762 //              }\r
763 \r
764                 CString sTemp;\r
765                 m_pProgDlg->FormatPathLine(1, IDS_PROGRESSGETFILE, (LPCTSTR)url1.GetGitPathString());\r
766 \r
767                 CTGitPath savepath = CTGitPath(m_strExportDir);\r
768                 savepath.AppendPathString(_T("\\") + CPathUtils::PathUnescape(fd.path.GetWinPathString()));\r
769                 CPathUtils::MakeSureDirectoryPathExists(fd.node == svn_node_file ? savepath.GetContainingDirectory().GetWinPath() : savepath.GetDirectory().GetWinPath());\r
770                 if (fd.node == svn_node_dir)\r
771                 {\r
772                         // exporting a folder requires calling Git::Export() so we also export all\r
773                         // children of that added folder.\r
774                         if ((fd.kind == svn_client_diff_summarize_kind_added)&&(!Export(url2, savepath, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, true, true)))\r
775                         {\r
776                                 if ((!m_bDoPegDiff)||(!Export(url2, savepath, m_rev2, m_rev2, true, true)))\r
777                                 {\r
778                                         delete m_pProgDlg;\r
779                                         m_pProgDlg = NULL;\r
780                                         CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
781                                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
782                                         RefreshCursor();\r
783                                         return 1;\r
784                                 }\r
785                         }\r
786                 }\r
787                 else\r
788                 {\r
789                         // exporting a file requires calling Git::Cat(), since Git::Export() only works\r
790                         // with folders.\r
791                         if ((fd.kind != svn_client_diff_summarize_kind_deleted)&&(!Cat(url2, m_bDoPegDiff ? m_peg : m_rev2, m_rev2, savepath)))\r
792                         {\r
793                                 if ((!m_bDoPegDiff)||(!Cat(url2, m_rev2, m_rev2, savepath)))\r
794                                 {\r
795                                         delete m_pProgDlg;\r
796                                         m_pProgDlg = NULL;\r
797                                         CMessageBox::Show(NULL, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
798                                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
799                                         RefreshCursor();\r
800                                         return 1;\r
801                                 }\r
802                         }\r
803                 }\r
804                 count++;\r
805                 m_pProgDlg->SetProgress (count, static_cast<DWORD>(m_arSelectedFileList.GetCount()));\r
806         }                                       \r
807         m_pProgDlg->Stop();\r
808         SetAndClearProgressInfo(NULL, false);\r
809         delete m_pProgDlg;\r
810         m_pProgDlg = NULL;\r
811         InterlockedExchange(&m_bThreadRunning, FALSE);\r
812         RefreshCursor();\r
813 #endif\r
814         return 0;\r
815 }\r
816 \r
817 BOOL CFileDiffDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)\r
818 {\r
819         if (pWnd != &m_cFileList)\r
820                 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);\r
821         if (m_bThreadRunning == 0)\r
822         {\r
823                 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));\r
824                 SetCursor(hCur);\r
825                 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);\r
826         }\r
827         HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));\r
828         SetCursor(hCur);\r
829         return TRUE;\r
830 }\r
831 \r
832 void CFileDiffDlg::OnEnSetfocusFirsturl()\r
833 {\r
834         GetDlgItem(IDC_FIRSTURL)->HideCaret();\r
835 }\r
836 \r
837 void CFileDiffDlg::OnEnSetfocusSecondurl()\r
838 {\r
839         GetDlgItem(IDC_SECONDURL)->HideCaret();\r
840 }\r
841 \r
842 \r
843 void CFileDiffDlg::OnBnClickedSwitchleftright()\r
844 {\r
845 \r
846         if (m_bThreadRunning)\r
847                 return;\r
848         CString sFilterString;\r
849         m_cFilter.GetWindowText(sFilterString);\r
850 \r
851         m_cFileList.SetRedraw(false);\r
852         m_cFileList.DeleteAllItems();\r
853         for (int i=0; i<(int)m_arFileList.GetCount(); ++i)\r
854         {\r
855                 CTGitPath fd = m_arFileList[i];\r
856                 if (fd.m_Action == CTGitPath::LOGACTIONS_ADDED)\r
857                         fd.m_Action = CTGitPath::LOGACTIONS_DELETED;\r
858                 else if (fd.m_Action == CTGitPath::LOGACTIONS_DELETED)\r
859                         fd.m_Action = CTGitPath::LOGACTIONS_ADDED;\r
860                 ( CTGitPath)m_arFileList[i] = ( CTGitPath)fd;\r
861         }\r
862         Filter(sFilterString);\r
863 \r
864         m_cFileList.SetRedraw(true);\r
865         CTGitPath path = m_path1;\r
866         m_path1 = m_path2;\r
867         m_path2 = path;\r
868         GitRev rev = m_rev1;\r
869         m_rev1 = m_rev2;\r
870         m_rev2 = rev;\r
871         SetURLLabels();\r
872 \r
873 }\r
874 \r
875 void CFileDiffDlg::SetURLLabels()\r
876 {\r
877 \r
878         m_cRev1Btn.SetWindowText(m_rev1.m_CommitHash.Left(6));\r
879         m_cRev2Btn.SetWindowText(m_rev2.m_CommitHash.Left(6));\r
880 \r
881         SetDlgItemText(IDC_FIRSTURL, m_rev1.m_Subject+CString(_T("\r\n"))+m_rev1.m_CommitHash);\r
882         SetDlgItemText(IDC_SECONDURL,m_rev2.m_Subject+CString(_T("\r\n"))+m_rev2.m_CommitHash);\r
883 \r
884         m_tooltips.AddTool(IDC_FIRSTURL,  m_rev1.m_AuthorDate.Format(_T("%Y-%m-%d  "))+m_rev1.m_AuthorName);\r
885         m_tooltips.AddTool(IDC_SECONDURL, m_rev2.m_AuthorDate.Format(_T("%Y-%m-%d  "))+m_rev2.m_AuthorName);\r
886 \r
887 }\r
888 \r
889 BOOL CFileDiffDlg::PreTranslateMessage(MSG* pMsg)\r
890 {\r
891         m_tooltips.RelayEvent(pMsg);\r
892         if (pMsg->message == WM_KEYDOWN)\r
893         {\r
894                 switch (pMsg->wParam)\r
895                 {\r
896                 case 'A':\r
897                         {\r
898                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
899                                 {\r
900                                         // select all entries\r
901                                         for (int i=0; i<m_cFileList.GetItemCount(); ++i)\r
902                                         {\r
903                                                 m_cFileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);\r
904                                         }\r
905                                         return TRUE;\r
906                                 }\r
907                         }\r
908                         break;\r
909                 case 'C':\r
910                 case VK_INSERT:\r
911                         {\r
912                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
913                                 {\r
914                                         CopySelectionToClipboard();\r
915                                         return TRUE;\r
916                                 }\r
917                         }\r
918                         break;\r
919                 case '\r':\r
920                         {\r
921                                 if (GetFocus() == GetDlgItem(IDC_FILELIST))\r
922                                 {\r
923                                         // Return pressed in file list. Show diff, as for double click\r
924                                         int selIndex = m_cFileList.GetSelectionMark();\r
925                                         if ((selIndex >= 0) && (selIndex < (int)m_arFileList.GetCount()))\r
926                                                 DoDiff(selIndex, m_bBlame);\r
927                                         return TRUE;\r
928                                 }\r
929                         }\r
930                         break;\r
931                 }\r
932         }\r
933         return __super::PreTranslateMessage(pMsg);\r
934 }\r
935 \r
936 void CFileDiffDlg::OnCancel()\r
937 {\r
938         if (m_bThreadRunning)\r
939         {\r
940                 m_bCancelled = true;\r
941                 return;\r
942         }\r
943         __super::OnCancel();\r
944 }\r
945 \r
946 void CFileDiffDlg::OnHdnItemclickFilelist(NMHDR *pNMHDR, LRESULT *pResult)\r
947 {\r
948         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
949         if (m_bThreadRunning)\r
950                 return;\r
951 \r
952         if (m_nSortedColumn == phdr->iItem)\r
953                 m_bAscending = !m_bAscending;\r
954         else\r
955                 m_bAscending = TRUE;\r
956         m_nSortedColumn = phdr->iItem;\r
957         m_arSelectedFileList.RemoveAll();\r
958         Sort();\r
959 \r
960         CString temp;\r
961         m_cFileList.SetRedraw(FALSE);\r
962         m_cFileList.DeleteAllItems();\r
963         m_cFilter.GetWindowText(temp);\r
964         Filter(temp);\r
965 \r
966         CHeaderCtrl * pHeader = m_cFileList.GetHeaderCtrl();\r
967         HDITEM HeaderItem = {0};\r
968         HeaderItem.mask = HDI_FORMAT;\r
969         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
970         {\r
971                 pHeader->GetItem(i, &HeaderItem);\r
972                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
973                 pHeader->SetItem(i, &HeaderItem);\r
974         }\r
975         pHeader->GetItem(m_nSortedColumn, &HeaderItem);\r
976         HeaderItem.fmt |= (m_bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
977         pHeader->SetItem(m_nSortedColumn, &HeaderItem);\r
978 \r
979         m_cFileList.SetRedraw(TRUE);\r
980 \r
981         *pResult = 0;\r
982 }\r
983 \r
984 void CFileDiffDlg::Sort()\r
985 {\r
986         if(m_arFileList.GetCount() < 2)\r
987         {\r
988                 return;\r
989         }\r
990 \r
991 //      std::sort(m_arFileList.begin(), m_arFileList.end(), &CFileDiffDlg::SortCompare);\r
992 }\r
993 #if 0\r
994 bool CFileDiffDlg::SortCompare(const CTGitPath*& Data1, const CTGitPath*& Data2)\r
995 {\r
996         int result = 0;\r
997         switch (m_nSortedColumn)\r
998         {\r
999         case 0:         //path column\r
1000                 result = Data1.path.GetWinPathString().Compare(Data2.path.GetWinPathString());\r
1001                 break;\r
1002         case 1:         //action column\r
1003                 result = Data1.kind - Data2.kind;\r
1004                 break;\r
1005         default:\r
1006                 break;\r
1007         }\r
1008 \r
1009         if (!m_bAscending)\r
1010                 result = -result;\r
1011         return result < 0;\r
1012 }\r
1013 #endif\r
1014 \r
1015 void CFileDiffDlg::OnBnClickedRev1btn()\r
1016 {\r
1017 #if 0\r
1018         if (m_bThreadRunning)\r
1019                 return; // do nothing as long as the thread is still running\r
1020 \r
1021         // show a dialog where the user can enter a revision\r
1022         CRevisionDlg dlg(this);\r
1023         dlg.AllowWCRevs(false);\r
1024         *((GitRev*)&dlg) = m_rev1;\r
1025 \r
1026         if (dlg.DoModal() == IDOK)\r
1027         {\r
1028                 m_rev1 = dlg;\r
1029                 m_cRev1Btn.SetWindowText(m_rev1.ToString());\r
1030                 m_cFileList.DeleteAllItems();\r
1031                 // start a new thread to re-fetch the diff\r
1032                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
1033                 if (AfxBeginThread(DiffThreadEntry, this)==NULL)\r
1034                 {\r
1035                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
1036                         CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
1037                 }\r
1038         }\r
1039 #endif\r
1040 }\r
1041 \r
1042 void CFileDiffDlg::OnBnClickedRev2btn()\r
1043 {\r
1044 #if 0\r
1045         if (m_bThreadRunning)\r
1046                 return; // do nothing as long as the thread is still running\r
1047 \r
1048         // show a dialog where the user can enter a revision\r
1049         CRevisionDlg dlg(this);\r
1050         dlg.AllowWCRevs(false);\r
1051         *((GitRev*)&dlg) = m_rev2;\r
1052 \r
1053         if (dlg.DoModal() == IDOK)\r
1054         {\r
1055                 m_rev2 = dlg;\r
1056                 m_cRev2Btn.SetWindowText(m_rev2.ToString());\r
1057                 m_cFileList.DeleteAllItems();\r
1058                 // start a new thread to re-fetch the diff\r
1059                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
1060                 if (AfxBeginThread(DiffThreadEntry, this)==NULL)\r
1061                 {\r
1062                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
1063                         CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
1064                 }\r
1065         }\r
1066 #endif\r
1067 }\r
1068 \r
1069 LRESULT CFileDiffDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)\r
1070 {\r
1071         if (m_bThreadRunning)\r
1072         {\r
1073                 SetTimer(IDT_FILTER, 1000, NULL);\r
1074                 return 0L;\r
1075         }\r
1076 \r
1077         KillTimer(IDT_FILTER);\r
1078 \r
1079         m_cFileList.SetRedraw(FALSE);\r
1080         m_arFilteredList.clear();\r
1081         m_cFileList.DeleteAllItems();\r
1082 \r
1083         Filter(_T(""));\r
1084 \r
1085         m_cFileList.SetRedraw(TRUE);\r
1086         return 0L;\r
1087 }\r
1088 \r
1089 void CFileDiffDlg::OnEnChangeFilter()\r
1090 {\r
1091         SetTimer(IDT_FILTER, 1000, NULL);\r
1092 }\r
1093 \r
1094 void CFileDiffDlg::OnTimer(UINT_PTR nIDEvent)\r
1095 {\r
1096         if (m_bThreadRunning)\r
1097                 return;\r
1098 \r
1099         CString sFilterText;\r
1100         KillTimer(IDT_FILTER);\r
1101         m_cFilter.GetWindowText(sFilterText);\r
1102 \r
1103         m_cFileList.SetRedraw(FALSE);\r
1104         m_cFileList.DeleteAllItems();\r
1105 \r
1106         Filter(sFilterText);\r
1107 \r
1108         m_cFileList.SetRedraw(TRUE);\r
1109 \r
1110         __super::OnTimer(nIDEvent);\r
1111 }\r
1112 \r
1113 void CFileDiffDlg::Filter(CString sFilterText)\r
1114 {\r
1115 \r
1116         sFilterText.MakeLower();\r
1117 \r
1118         m_arFilteredList.clear();\r
1119         \r
1120         for (int i=0;i<m_arFileList.GetCount();i++)\r
1121         {\r
1122                 CString sPath = m_arFileList[i].GetGitPathString();\r
1123                 sPath.MakeLower();\r
1124                 if (sPath.Find(sFilterText) >= 0)\r
1125                 {\r
1126                         m_arFilteredList.push_back((CTGitPath*)&(m_arFileList[i]));\r
1127                 }\r
1128         }\r
1129         for (std::vector<CTGitPath*>::const_iterator it = m_arFilteredList.begin(); it != m_arFilteredList.end(); ++it)\r
1130         {\r
1131                 AddEntry(*it);\r
1132         }\r
1133 \r
1134 }\r
1135 \r
1136 void CFileDiffDlg::CopySelectionToClipboard()\r
1137 {\r
1138         // copy all selected paths to the clipboard\r
1139         POSITION pos = m_cFileList.GetFirstSelectedItemPosition();\r
1140         int index;\r
1141         CString sTextForClipboard;\r
1142         while ((index = m_cFileList.GetNextSelectedItem(pos)) >= 0)\r
1143         {\r
1144                 sTextForClipboard += m_cFileList.GetItemText(index, 0);\r
1145                 sTextForClipboard += _T("\t");\r
1146                 sTextForClipboard += m_cFileList.GetItemText(index, 1);\r
1147                 sTextForClipboard += _T("\r\n");\r
1148         }\r
1149         CStringUtils::WriteAsciiStringToClipboard(sTextForClipboard);\r
1150 }\r
1151 \r