Use CUnicodeUtils::GetUnicode instead of CGit::StringAppend if possible
[TortoiseGit.git] / src / TortoiseProc / GitRefCompareList.cpp
blob16040b670e9fae20dc8abbe22bcab65d520e2b75
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2014 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // GitRefCompareList.cpp : implementation file
21 #include "stdafx.h"
22 #include "resource.h"
23 #include "GitRefCompareList.h"
24 #include "registry.h"
25 #include "UnicodeUtils.h"
26 #include "MessageBox.h"
27 #include "IconMenu.h"
28 #include "AppUtils.h"
29 #include "..\TortoiseShell\resource.h"
30 #include "LoglistCommonResource.h"
32 IMPLEMENT_DYNAMIC(CGitRefCompareList, CHintListCtrl)
34 BEGIN_MESSAGE_MAP(CGitRefCompareList, CHintListCtrl)
35 ON_WM_CONTEXTMENU()
36 END_MESSAGE_MAP()
38 BOOL CGitRefCompareList::m_bSortLogical = FALSE;
40 enum IDGITRCL
42 IDGITRCL_DUMMY,
43 IDGITRCL_OLDLOG,
44 IDGITRCL_NEWLOG,
45 IDGITRCL_COMPARE,
46 IDGITRCL_REFLOG,
49 enum IDGITRCLH
51 IDGITRCLH_DUMMY,
52 IDGITRCLH_HIDEUNCHANGED,
55 CGitRefCompareList::CGitRefCompareList()
56 : CHintListCtrl()
57 , colRef(0)
58 , colChange(0)
59 , colOldHash(0)
60 , colOldMessage(0)
61 , colNewHash(0)
62 , colNewMessage(0)
64 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
65 if (m_bSortLogical)
66 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
67 m_bHideUnchanged = CRegDWORD(_T("Software\\TortoiseGit\\RefCompareHideUnchanged"), FALSE);
70 void CGitRefCompareList::Init()
72 int index = 0;
73 colRef = InsertColumn(index++, CString(MAKEINTRESOURCE(IDS_REF)));
74 colChange = InsertColumn(index++, CString(MAKEINTRESOURCE(IDS_CHANGETYPE)));
75 colOldHash = InsertColumn(index++, CString(MAKEINTRESOURCE(IDS_OLDHASH)));
76 colOldMessage = InsertColumn(index++, CString(MAKEINTRESOURCE(IDS_OLDMESSAGE)));
77 colNewHash = InsertColumn(index++, CString(MAKEINTRESOURCE(IDS_NEWHASH)));
78 colNewMessage = InsertColumn(index++,CString(MAKEINTRESOURCE(IDS_NEWMESSAGE)));
79 for (int i = 0; i < index; ++i)
80 SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
81 SetColumnWidth(colRef, 130);
83 CImageList *imagelist = new CImageList();
84 imagelist->Create(IDB_BITMAP_REFTYPE, 16, 3, RGB(255, 255, 255));
85 SetImageList(imagelist, LVSIL_SMALL);
87 SetWindowTheme(m_hWnd, L"Explorer", NULL);
90 int CGitRefCompareList::AddEntry(git_repository *repo, CString ref, CGitHash *oldHash, CGitHash *newHash)
92 RefEntry entry;
93 entry.fullName = ref;
94 entry.shortName = CGit::GetShortName(ref, &entry.refType);
95 if (oldHash)
96 entry.oldHash = oldHash->ToString().Left(g_Git.GetShortHASHLength());
97 if (newHash)
98 entry.newHash = newHash->ToString().Left(g_Git.GetShortHASHLength());
100 CAutoCommit oldCommit;
101 if (oldHash)
103 if (!git_commit_lookup(oldCommit.GetPointer(), repo, (const git_oid *)&oldHash->m_hash))
104 entry.oldMessage = GetCommitMessage(oldCommit);
107 CAutoCommit newCommit;
108 if (newHash)
110 if (!git_commit_lookup(newCommit.GetPointer(), repo, (const git_oid *)&newHash->m_hash))
111 entry.newMessage = GetCommitMessage(newCommit);
114 if (oldHash && newHash)
116 if (*oldHash == *newHash)
118 entry.change = CString(MAKEINTRESOURCE(IDS_SAME));
119 entry.changeType = ChangeType::Same;
121 else
123 size_t ahead = 0, behind = 0;
124 if (!git_graph_ahead_behind(&ahead, &behind, repo, (const git_oid *)&newHash->m_hash, (const git_oid *)&oldHash->m_hash))
126 CString change;
127 if (ahead > 0 && behind == 0)
129 entry.change.Format(IDS_FORWARDN, ahead);
130 entry.changeType = ChangeType::FastForward;
132 else if (ahead == 0 && behind > 0)
134 entry.change.Format(IDS_REWINDN, behind);
135 entry.changeType = ChangeType::Rewind;
137 else
139 git_time_t oldTime = git_commit_committer(oldCommit)->when.time;
140 git_time_t newTime = git_commit_committer(newCommit)->when.time;
141 if (oldTime < newTime)
143 entry.change = CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_NEWERTIME));
144 entry.changeType = ChangeType::NewerTime;
146 else if (oldTime > newTime)
148 entry.change = CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_OLDERTIME));
149 entry.changeType = ChangeType::OlderTime;
151 else
153 entry.change = CString(MAKEINTRESOURCE(IDS_SUBMODULEDIFF_SAMETIME));
154 entry.changeType = ChangeType::SameTime;
160 else if (oldHash)
162 entry.change = CString(MAKEINTRESOURCE(IDS_DELETED));
163 entry.changeType = ChangeType::Deleted;
165 else if (newHash)
167 entry.change = CString(MAKEINTRESOURCE(IDS_NEW));
168 entry.changeType = ChangeType::New;
171 m_RefList.push_back(entry);
172 return (int)m_RefList.size() - 1;
175 void CGitRefCompareList::Show()
177 DeleteAllItems();
178 std::sort(m_RefList.begin(), m_RefList.end(), SortPredicate);
179 int index = 0;
180 for (auto entry = m_RefList.begin(); entry != m_RefList.end(); ++entry)
182 if (entry->changeType == ChangeType::Same && m_bHideUnchanged)
183 continue;
185 int nImage = -1;
186 if (entry->refType == CGit::REF_TYPE::LOCAL_BRANCH)
187 nImage = 1;
188 else if (entry->refType == CGit::REF_TYPE::REMOTE_BRANCH)
189 nImage = 2;
190 else if (entry->refType == CGit::REF_TYPE::TAG)
191 nImage = 0;
192 InsertItem(index, entry->shortName, nImage);
193 SetItemText(index, colChange, entry->change);
194 SetItemText(index, colOldHash, entry->oldHash);
195 SetItemText(index, colOldMessage, entry->oldMessage);
196 SetItemText(index, colNewHash, entry->newHash);
197 SetItemText(index, colNewMessage, entry->newMessage);
198 index++;
202 void CGitRefCompareList::Clear()
204 m_RefList.clear();
205 DeleteAllItems();
208 void CGitRefCompareList::OnContextMenu(CWnd *pWnd, CPoint point)
210 if (pWnd == this)
212 OnContextMenuList(pWnd, point);
214 else if (pWnd == GetHeaderCtrl())
216 OnContextMenuHeader(pWnd, point);
220 void CGitRefCompareList::OnContextMenuList(CWnd * /*pWnd*/, CPoint point)
222 int selIndex = GetSelectionMark();
223 if (selIndex < 0 || selIndex >= m_RefList.size())
224 return;
226 CString refName = m_RefList[selIndex].fullName;
227 CString oldHash = m_RefList[selIndex].oldHash;
228 CString newHash = m_RefList[selIndex].newHash;
229 CIconMenu popup;
230 popup.CreatePopupMenu();
231 CString logStr;
232 if (!oldHash.IsEmpty())
234 logStr.Format(IDS_SHOWLOG_OF, oldHash);
235 popup.AppendMenuIcon(IDGITRCL_OLDLOG, logStr, IDI_LOG);
237 if (!newHash.IsEmpty() && oldHash != newHash)
239 logStr.Format(IDS_SHOWLOG_OF, newHash);
240 popup.AppendMenuIcon(IDGITRCL_NEWLOG, logStr, IDI_LOG);
242 if (!oldHash.IsEmpty() && !newHash.IsEmpty() && oldHash != newHash)
243 popup.AppendMenuIcon(IDGITRCL_COMPARE, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
244 popup.AppendMenuIcon(IDGITRCL_REFLOG, IDS_MENUREFLOG, IDI_LOG);
246 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
247 AfxGetApp()->DoWaitCursor(1);
248 switch (cmd)
250 case IDGITRCL_OLDLOG:
251 case IDGITRCL_NEWLOG:
253 CString sCmd;
254 sCmd.Format(_T("/command:log /path:\"%s\" /endrev:\"%s\""), g_Git.m_CurrentDir, cmd == IDGITRCL_OLDLOG ? oldHash : newHash);
255 CAppUtils::RunTortoiseGitProc(sCmd);
256 break;
258 case IDGITRCL_COMPARE:
260 CString sCmd;
261 sCmd.Format(_T("/command:showcompare /path:\"%s\" /revision1:\"%s\" /revision2:\"%s\""), g_Git.m_CurrentDir, oldHash, newHash);
262 CAppUtils::RunTortoiseGitProc(sCmd);
263 break;
265 case IDGITRCL_REFLOG:
267 CString sCmd;
268 sCmd.Format(_T("/command:reflog /path:\"%s\" /ref:\"%s\""), g_Git.m_CurrentDir, refName);
269 CAppUtils::RunTortoiseGitProc(sCmd);
270 break;
273 AfxGetApp()->DoWaitCursor(-1);
276 static void AppendMenuChecked(CMenu &menu, UINT nTextID, UINT_PTR nItemID, BOOL checked = FALSE, BOOL enabled = TRUE)
278 CString text;
279 text.LoadString(nTextID);
280 menu.AppendMenu(MF_STRING | (enabled ? MF_ENABLED : MF_DISABLED) | (checked ? MF_CHECKED : MF_UNCHECKED), nItemID, text);
283 void CGitRefCompareList::OnContextMenuHeader(CWnd * /*pWnd*/, CPoint point)
285 CMenu popup;
286 if (popup.CreatePopupMenu())
288 AppendMenuChecked(popup, IDS_HIDEUNCHANGED, IDGITRCLH_HIDEUNCHANGED, m_bHideUnchanged);
290 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
291 switch (selection)
293 case IDGITRCLH_HIDEUNCHANGED:
294 m_bHideUnchanged = !m_bHideUnchanged;
295 Show();
296 break;
301 CString CGitRefCompareList::GetCommitMessage(git_commit *commit)
303 int encode = CP_UTF8;
304 const char *encodingString = git_commit_message_encoding(commit);
305 if (encodingString != nullptr)
306 encode = CUnicodeUtils::GetCPCode(CUnicodeUtils::GetUnicode(encodingString));
308 CString message = CUnicodeUtils::GetUnicode(git_commit_message(commit), encode);
309 int start = 0;
310 message = message.Tokenize(_T("\n"), start);
311 return message;
314 bool CGitRefCompareList::SortPredicate(const RefEntry &e1, const RefEntry &e2)
316 if (e1.changeType < e2.changeType)
317 return true;
318 if (e1.changeType > e2.changeType)
319 return false;
320 if (m_bSortLogical)
321 return StrCmpLogicalW(e1.fullName, e2.fullName) < 0;
322 return e1.fullName.Compare(e2.fullName) < 0;