Fixed issue #1571: Warn author not set when editing notes
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob6769237cecae4454a78f5565706b757c0247dfb1
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - 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 // BrowseRefsDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
25 #include "LogDlg.h"
26 #include "AddRemoteDlg.h"
27 #include "AppUtils.h"
28 #include "Settings\SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
32 #include "IconMenu.h"
33 #include "FileDiffDlg.h"
34 #include "DeleteRemoteTagDlg.h"
35 #include "git2.h"
36 #include "UnicodeUtils.h"
37 #include "InputDlg.h"
39 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
41 if (control == NULL)
42 return;
43 // set the sort arrow
44 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
45 HDITEM HeaderItem = {0};
46 HeaderItem.mask = HDI_FORMAT;
47 for (int i=0; i<pHeader->GetItemCount(); ++i)
49 pHeader->GetItem(i, &HeaderItem);
50 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
51 pHeader->SetItem(i, &HeaderItem);
53 if (nColumn >= 0)
55 pHeader->GetItem(nColumn, &HeaderItem);
56 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
57 pHeader->SetItem(nColumn, &HeaderItem);
61 class CRefLeafListCompareFunc
63 public:
64 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){
65 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_CURRENT_USER);
66 if (m_bSortLogical)
67 m_bSortLogical = !CRegDWORD(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\NoStrCmpLogical", 0, false, HKEY_LOCAL_MACHINE);
70 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
72 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
75 int Compare(LPARAM lParam1, LPARAM lParam2)
77 return Compare(
78 (CShadowTree*)m_pList->GetItemData((int)lParam1),
79 (CShadowTree*)m_pList->GetItemData((int)lParam2));
82 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
84 int result=CompareNoDesc(pLeft,pRight);
85 if(m_desc)
86 return -result;
87 return result;
90 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
92 switch(m_col)
94 case CBrowseRefsDlg::eCol_Name: return SortStrCmp(pLeft->GetRefName(), pRight->GetRefName());
95 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
96 case CBrowseRefsDlg::eCol_Msg: return SortStrCmp(pLeft->m_csSubject, pRight->m_csSubject);
97 case CBrowseRefsDlg::eCol_LastAuthor: return SortStrCmp(pLeft->m_csAuthor, pRight->m_csAuthor);
98 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
99 case CBrowseRefsDlg::eCol_Description: return SortStrCmp(pLeft->m_csDescription, pRight->m_csDescription);
101 return 0;
103 int SortStrCmp(CString left, CString right)
105 if (m_bSortLogical)
106 return StrCmpLogicalW(left, right);
107 return StrCmpI(left, right);
110 int m_col;
111 bool m_desc;
112 CListCtrl* m_pList;
113 bool m_bSortLogical;
116 // CBrowseRefsDlg dialog
118 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
120 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
121 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
122 m_cmdPath(cmdPath),
123 m_currSortCol(0),
124 m_currSortDesc(false),
125 m_initialRef(L"HEAD"),
126 m_pickRef_Kind(gPickRef_All),
127 m_pListCtrlRoot(NULL),
128 m_bHasWC(true),
129 m_SelectedFilters(LOGFILTER_ALL),
130 m_bPickOne(false)
135 CBrowseRefsDlg::~CBrowseRefsDlg()
139 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
141 CDialog::DoDataExchange(pDX);
142 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
143 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
144 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
148 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
149 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
150 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
151 ON_WM_CONTEXTMENU()
152 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
153 ON_WM_DESTROY()
154 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
155 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
156 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
157 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
158 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
159 ON_WM_TIMER()
160 END_MESSAGE_MAP()
163 // CBrowseRefsDlg message handlers
165 void CBrowseRefsDlg::OnBnClickedOk()
167 OnOK();
170 BOOL CBrowseRefsDlg::OnInitDialog()
172 CResizableStandAloneDialog::OnInitDialog();
173 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
175 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
176 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
177 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
178 SetFilterCueText();
180 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
181 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
182 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, TOP_LEFT);
183 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, TOP_LEFT, TOP_RIGHT);
184 AddAnchor(IDHELP, BOTTOM_RIGHT);
186 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
187 CString temp;
188 temp.LoadString(IDS_BRANCHNAME);
189 m_ListRefLeafs.InsertColumn(eCol_Name, temp, 0, 150);
190 temp.LoadString(IDS_DATELASTCOMMIT);
191 m_ListRefLeafs.InsertColumn(eCol_Date, temp, 0, 100);
192 temp.LoadString(IDS_LASTCOMMIT);
193 m_ListRefLeafs.InsertColumn(eCol_Msg, temp, 0, 300);
194 temp.LoadString(IDS_LASTAUTHOR);
195 m_ListRefLeafs.InsertColumn(eCol_LastAuthor, temp, 0, 100);
196 temp.LoadString(IDS_HASH);
197 m_ListRefLeafs.InsertColumn(eCol_Hash, temp, 0, 80);
198 temp.LoadString(IDS_DESCRIPTION);
199 m_ListRefLeafs.InsertColumn(eCol_Description, temp, 0, 80);
201 AddAnchor(IDOK,BOTTOM_RIGHT);
202 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
204 Refresh(m_initialRef);
206 EnableSaveRestore(L"BrowseRefs");
208 CString sWindowTitle;
209 GetWindowText(sWindowTitle);
210 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
212 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
214 if (m_bPickOne)
215 m_ListRefLeafs.ModifyStyle(0, LVS_SINGLESEL);
217 m_ListRefLeafs.SetFocus();
218 return FALSE;
221 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
223 int posSlash=nameLeft.Find('/');
224 CString nameSub;
225 if(posSlash<0)
227 nameSub=nameLeft;
228 nameLeft.Empty();//Nothing left
230 else
232 nameSub=nameLeft.Left(posSlash);
233 nameLeft=nameLeft.Mid(posSlash+1);
235 if(nameSub.IsEmpty())
236 return NULL;
238 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
239 return NULL;
241 CShadowTree& nextNode=m_ShadowTree[nameSub];
242 nextNode.m_csRefName=nameSub;
243 nextNode.m_pParent=this;
244 return &nextNode;
247 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
249 if(IsLeaf())
251 if(m_csRefName.GetLength() > partialRefName.GetLength())
252 return NULL;
253 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
255 //Match of leaf name. Try match on total name.
256 CString totalRefName = GetRefName();
257 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
258 return this; //Also match. Found.
261 else
263 //Not a leaf. Search all nodes.
264 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
266 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
267 if(pSubtree != NULL)
268 return pSubtree; //Found
271 return NULL;//Not found
275 typedef std::map<CString,CString> MAP_STRING_STRING;
277 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
279 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
280 //List ctrl selection?
281 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
283 //A leaf is selected
284 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
285 m_ListRefLeafs.GetNextSelectedItem(pos));
286 return pTree->GetRefName();
288 else if(!onlyIfLeaf)
290 //Tree ctrl selection?
291 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
292 if(hTree!=NULL)
294 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
295 return pTree->GetRefName();
298 return CString();//None
301 static int GetBranchDescriptionsCallback(const git_config_entry *entry, void *data)
303 MAP_STRING_STRING *descriptions = (MAP_STRING_STRING *) data;
304 CString key = CUnicodeUtils::GetUnicode(entry->name, CP_UTF8);
305 CString val = CUnicodeUtils::GetUnicode(entry->value, CP_UTF8);
306 descriptions->insert(make_pair(key.Mid(7, key.GetLength() - 7 - 12), val)); // 7: branch., 12: .description
307 return 0;
310 MAP_STRING_STRING GetBranchDescriptions()
312 MAP_STRING_STRING descriptions;
313 git_config * config;
314 git_config_new(&config);
315 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
316 git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 3, FALSE);
317 projectConfigA.ReleaseBuffer();
318 git_config_foreach_match(config, "branch\\..*\\.description", GetBranchDescriptionsCallback, &descriptions);
319 git_config_free(config);
320 return descriptions;
323 void CBrowseRefsDlg::Refresh(CString selectRef)
325 // m_RefMap.clear();
326 // g_Git.GetMapHashToFriendName(m_RefMap);
328 if(!selectRef.IsEmpty())
330 if(selectRef == "HEAD")
332 selectRef = g_Git.GetSymbolicRef(selectRef, false);
335 else
337 selectRef = GetSelectedRef(false, true);
340 m_RefTreeCtrl.DeleteAllItems();
341 m_ListRefLeafs.DeleteAllItems();
342 m_TreeRoot.m_ShadowTree.clear();
343 m_TreeRoot.m_csRefName = "refs";
344 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
345 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
347 CString allRefs;
348 g_Git.Run(L"git for-each-ref --format="
349 L"%(refname)%04"
350 L"%(objectname)%04"
351 L"%(authordate:relative)%04"
352 L"%(subject)%04"
353 L"%(authorname)%04"
354 L"%(authordate:iso8601)%03",
355 &allRefs, NULL, CP_UTF8);
357 int linePos=0;
358 CString singleRef;
360 MAP_STRING_STRING refMap;
362 //First sort on ref name
363 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
365 singleRef.TrimLeft(L"\r\n");
366 int valuePos=0;
367 CString refName=singleRef.Tokenize(L"\04",valuePos);
368 if(refName.IsEmpty())
369 continue;
370 CString refRest=singleRef.Mid(valuePos);
373 //Use ref based on m_pickRef_Kind
374 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
375 continue; //Skip
376 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
377 continue; //Skip
378 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
379 continue; //Skip
381 refMap[refName] = refRest; //Use
384 MAP_STRING_STRING descriptions = GetBranchDescriptions();
386 //Populate ref tree
387 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
389 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
390 CString values=iterRefMap->second;
391 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
393 int valuePos=0;
394 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
395 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
396 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
397 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
398 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
400 if (wcsncmp(iterRefMap->first, L"refs/heads", 10) == 0)
401 treeLeaf.m_csDescription = descriptions[treeLeaf.m_csRefName];
405 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
406 //Probably not on a branch. Select root node.
407 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
411 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
413 if(!bExactMatch)
415 CString newRefName = GetFullRefName(refName);
416 if(!newRefName.IsEmpty())
417 refName = newRefName;
418 //else refName is not a valid ref. Try to select as good as possible.
420 if(_wcsnicmp(refName, L"refs/", 5) != 0)
421 return false; // Not a ref name
423 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
424 if(treeLeafHead.m_hTree != NULL)
426 //Not a leaf. Select tree node and return
427 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
428 return true;
431 if(treeLeafHead.m_pParent==NULL)
432 return false; //Weird... should not occur.
434 //This is the current head.
435 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
437 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
439 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
440 if(pCurrShadowTree == &treeLeafHead)
442 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
443 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
447 return true;
450 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
452 if(pTreePos==NULL)
454 if(_wcsnicmp(refName, L"refs/", 5) == 0)
455 refName=refName.Mid(5);
456 pTreePos=&m_TreeRoot;
458 if(refName.IsEmpty())
459 return *pTreePos;//Found leaf
461 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
462 if(pNextTree==NULL)
464 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
465 ASSERT(!bCreateIfNotExist);
466 return *pTreePos;
469 if(!refName.IsEmpty())
471 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
472 //Leafs are for the list control.
473 if(pNextTree->m_hTree==NULL)
475 //New tree. Create node in control.
476 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
477 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
481 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
485 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
487 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
488 *pResult = 0;
490 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
493 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
495 m_ListRefLeafs.DeleteAllItems();
497 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
498 if(pTree==NULL)
500 ASSERT(FALSE);
501 return;
503 FillListCtrlForShadowTree(pTree,L"",true);
506 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
508 if(pTree->IsLeaf())
510 CString filter;
511 m_ctrlFilter.GetWindowText(filter);
512 filter.MakeLower();
513 CString ref = refNamePrefix + pTree->m_csRefName;
514 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
516 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
518 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
519 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
520 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
521 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
522 m_ListRefLeafs.SetItemText(indexItem,eCol_LastAuthor, pTree->m_csAuthor);
523 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
524 int pos = 0;
525 m_ListRefLeafs.SetItemText(indexItem,eCol_Description, pTree->m_csDescription.Tokenize(_T("\n"), pos));
528 else
531 CString csThisName;
532 if(!isFirstLevel)
533 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
534 else
535 m_pListCtrlRoot = pTree;
536 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
538 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
541 if (isFirstLevel)
543 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
544 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
546 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
550 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
552 if (m_SelectedFilters & LOGFILTER_REFNAME)
554 CString msg = ref;
555 msg = msg.MakeLower();
557 if (msg.Find(filter) >= 0)
558 return true;
561 if (m_SelectedFilters & LOGFILTER_SUBJECT)
563 CString msg = pTree->m_csSubject;
564 msg = msg.MakeLower();
566 if (msg.Find(filter) >= 0)
567 return true;
570 if (m_SelectedFilters & LOGFILTER_AUTHORS)
572 CString msg = pTree->m_csAuthor;
573 msg = msg.MakeLower();
575 if (msg.Find(filter) >= 0)
576 return true;
579 if (m_SelectedFilters & LOGFILTER_REVS)
581 CString msg = pTree->m_csRefHash;
582 msg = msg.MakeLower();
584 if (msg.Find(filter) >= 0)
585 return true;
587 return false;
590 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
592 ASSERT(!leafs.empty());
594 CString csMessage;
595 UINT mbIcon=MB_ICONQUESTION;
597 bool bIsRemoteBranch = false;
598 bool bIsBranch = false;
599 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
600 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
602 if(bIsBranch)
604 if(leafs.size() == 1)
606 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
607 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
609 //Check if branch is fully merged in HEAD
610 if (!g_Git.IsFastForward(leafs[0]->GetRefName(), _T("HEAD")))
612 csMessage += L"\r\n\r\n";
613 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
614 mbIcon = MB_ICONWARNING;
617 if(bIsRemoteBranch)
619 csMessage += L"\r\n\r\n";
620 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
621 mbIcon = MB_ICONWARNING;
624 else
626 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
628 csMessage += L"\r\n\r\n";
629 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
630 mbIcon = MB_ICONWARNING;
632 if(bIsRemoteBranch)
634 csMessage += L"\r\n\r\n";
635 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
636 mbIcon = MB_ICONWARNING;
641 else if(leafs[0]->IsFrom(L"refs/tags"))
643 if(leafs.size() == 1)
645 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
646 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
648 else
650 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
654 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
658 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
660 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
661 if(!DoDeleteRef((*i)->GetRefName(), bForce))
662 return false;
663 return true;
666 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
668 bool bIsRemoteBranch = false;
669 bool bIsBranch = false;
670 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
671 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
673 if(bIsBranch)
675 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
676 CString cmd;
677 if(bIsRemoteBranch)
679 int slash = branchToDelete.Find(L'/');
680 if(slash < 0)
681 return false;
682 CString remoteName = branchToDelete.Left(slash);
683 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
685 if(CAppUtils::IsSSHPutty())
687 CAppUtils::LaunchPAgent(NULL, &remoteName);
690 cmd.Format(L"git.exe push \"%s\" :refs/heads/%s", remoteName, remoteBranchToDelete);
692 else
693 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
694 CString errorMsg;
695 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
697 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
698 return false;
701 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
703 CString tagToDelete = completeRefName.Mid(10);
704 CString cmd;
705 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
706 CString errorMsg;
707 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
709 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
710 return false;
713 return true;
716 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
718 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
719 if(pLeaf == NULL)
720 return CString();
721 return pLeaf->GetRefName();
725 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
727 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
728 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
731 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
733 CPoint clientPoint=point;
734 m_RefTreeCtrl.ScreenToClient(&clientPoint);
736 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
737 if(hTreeItem!=NULL)
738 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
740 VectorPShadowTree tree;
741 ShowContextMenu(point,hTreeItem,tree);
745 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
747 std::vector<CShadowTree*> selectedLeafs;
748 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
749 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
750 while(pos)
752 selectedLeafs.push_back(
753 (CShadowTree*)m_ListRefLeafs.GetItemData(
754 m_ListRefLeafs.GetNextSelectedItem(pos)));
757 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
760 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
762 CIconMenu popupMenu;
763 popupMenu.CreatePopupMenu();
765 bool bAddSeparator = false;
766 CString remoteName;
768 if(selectedLeafs.size()==1)
770 bAddSeparator = true;
772 bool bShowReflogOption = false;
773 bool bShowFetchOption = false;
774 bool bShowSwitchOption = false;
775 bool bShowRenameOption = false;
776 bool bShowCreateBranchOption = false;
777 bool bShowEditBranchDescriptionOption = false;
779 CString fetchFromCmd;
781 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
783 bShowReflogOption = true;
784 bShowSwitchOption = true;
785 bShowRenameOption = true;
786 bShowEditBranchDescriptionOption = true;
788 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
790 bShowReflogOption = true;
791 bShowFetchOption = true;
792 bShowCreateBranchOption = true;
794 int dummy = 0;//Needed for tokenize
795 remoteName = selectedLeafs[0]->GetRefName();
796 remoteName = remoteName.Mid(13);
797 remoteName = remoteName.Tokenize(L"/", dummy);
798 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
800 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
804 CString temp;
805 temp.LoadString(IDS_MENULOG);
806 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
807 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
808 if(bShowReflogOption)
810 temp.LoadString(IDS_MENUREFLOG);
811 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
814 popupMenu.AppendMenu(MF_SEPARATOR);
815 bAddSeparator = false;
817 if(bShowFetchOption)
819 bAddSeparator = true;
820 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
823 if(bAddSeparator)
824 popupMenu.AppendMenu(MF_SEPARATOR);
826 bAddSeparator = false;
827 if (m_bHasWC)
829 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
830 popupMenu.AppendMenu(MF_SEPARATOR);
833 if(bShowCreateBranchOption)
835 bAddSeparator = true;
836 temp.LoadString(IDS_MENUBRANCH);
837 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
840 if (bShowEditBranchDescriptionOption)
842 bAddSeparator = true;
843 popupMenu.AppendMenuIcon(eCmd_EditBranchDescription, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION)), IDI_RENAME);
845 if(bShowRenameOption)
847 bAddSeparator = true;
848 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
851 else if(selectedLeafs.size() == 2)
853 bAddSeparator = true;
854 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
857 if(!selectedLeafs.empty())
859 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
861 if(bAddSeparator)
862 popupMenu.AppendMenu(MF_SEPARATOR);
863 CString menuItemName;
864 if(selectedLeafs.size() == 1)
865 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
866 else
867 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
869 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
870 bAddSeparator = true;
872 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
874 if(bAddSeparator)
875 popupMenu.AppendMenu(MF_SEPARATOR);
876 CString menuItemName;
877 if(selectedLeafs.size() == 1)
878 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
879 else
880 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
882 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
883 bAddSeparator = true;
885 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
887 if(bAddSeparator)
888 popupMenu.AppendMenu(MF_SEPARATOR);
889 CString menuItemName;
890 if(selectedLeafs.size() == 1)
891 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
892 else
893 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
895 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
896 bAddSeparator = true;
901 if(hTreePos!=NULL && selectedLeafs.empty())
903 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
904 if(pTree->IsFrom(L"refs/remotes"))
906 if(bAddSeparator)
907 popupMenu.AppendMenu(MF_SEPARATOR);
908 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
909 bAddSeparator = true;
910 if(selectedLeafs.empty())
912 int dummy = 0;//Needed for tokenize
913 remoteName = pTree->GetRefName();
914 remoteName = remoteName.Mid(13);
915 remoteName = remoteName.Tokenize(L"/", dummy);
916 if(!remoteName.IsEmpty())
918 CString temp;
919 temp.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
920 popupMenu.AppendMenuIcon(eCmd_Fetch, temp, IDI_PULL);
922 temp.LoadString(IDS_DELETEREMOTETAG);
923 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteTag, temp, IDI_DELETE);
927 if(pTree->IsFrom(L"refs/heads"))
929 if(bAddSeparator)
930 popupMenu.AppendMenu(MF_SEPARATOR);
931 CString temp;
932 temp.LoadString(IDS_MENUBRANCH);
933 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
935 if(pTree->IsFrom(L"refs/tags"))
937 if(bAddSeparator)
938 popupMenu.AppendMenu(MF_SEPARATOR);
939 CString temp;
940 temp.LoadString(IDS_MENUTAG);
941 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
946 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
947 switch(cmd)
949 case eCmd_ViewLog:
951 CLogDlg dlg;
952 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
953 dlg.DoModal();
955 break;
956 case eCmd_RepoBrowser:
957 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
958 break;
959 case eCmd_DeleteBranch:
960 case eCmd_DeleteRemoteBranch:
962 if(ConfirmDeleteRef(selectedLeafs))
963 DoDeleteRefs(selectedLeafs, true);
964 Refresh();
966 break;
967 case eCmd_DeleteTag:
969 if(ConfirmDeleteRef(selectedLeafs))
970 DoDeleteRefs(selectedLeafs, true);
971 Refresh();
973 break;
974 case eCmd_ShowReflog:
976 CRefLogDlg refLogDlg(this);
977 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
978 refLogDlg.DoModal();
980 break;
981 case eCmd_Fetch:
983 CAppUtils::Fetch(remoteName);
984 Refresh();
986 break;
987 case eCmd_DeleteRemoteTag:
989 CDeleteRemoteTagDlg deleteRemoteTagDlg;
990 deleteRemoteTagDlg.m_sRemote = remoteName;
991 deleteRemoteTagDlg.DoModal();
993 break;
994 case eCmd_Switch:
996 CAppUtils::Switch(selectedLeafs[0]->GetRefName());
998 break;
999 case eCmd_Rename:
1001 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1002 if(pos != NULL)
1003 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1005 break;
1006 case eCmd_AddRemote:
1008 CAddRemoteDlg(this).DoModal();
1009 Refresh();
1011 break;
1012 case eCmd_ManageRemotes:
1014 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
1015 // CSettingGitRemote W_Remotes(m_cmdPath);
1016 // W_Remotes.DoModal();
1017 Refresh();
1019 break;
1020 case eCmd_CreateBranch:
1022 CString *commitHash = NULL;
1023 if (selectedLeafs.size() == 1)
1024 commitHash = &(selectedLeafs[0]->m_csRefHash);
1025 CAppUtils::CreateBranchTag(false, commitHash);
1026 Refresh();
1028 break;
1029 case eCmd_CreateTag:
1031 CAppUtils::CreateBranchTag(true);
1032 Refresh();
1034 break;
1035 case eCmd_Diff:
1037 CFileDiffDlg dlg;
1038 dlg.SetDiff(
1039 NULL,
1040 selectedLeafs[0]->m_csRefHash,
1041 selectedLeafs[1]->m_csRefHash);
1042 dlg.DoModal();
1044 break;
1045 case eCmd_EditBranchDescription:
1047 CInputDlg dlg;
1048 dlg.m_sHintText = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1049 dlg.m_sInputText = selectedLeafs[0]->m_csDescription;
1050 dlg.m_sTitle = CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_EDITDESCRIPTION));
1051 dlg.m_bUseLogWidth = true;
1052 if(dlg.DoModal() == IDOK)
1054 CString key;
1055 key.Format(_T("branch.%s.description"), selectedLeafs[0]->m_csRefName);
1056 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1057 dlg.m_sInputText.Trim();
1058 if (dlg.m_sInputText.IsEmpty())
1059 g_Git.UnsetConfigValue(key);
1060 else
1061 g_Git.SetConfigValue(key, dlg.m_sInputText);
1062 Refresh();
1065 break;
1069 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
1071 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
1072 if(!(*i)->IsFrom(from))
1073 return false;
1074 return true;
1077 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
1079 if (pMsg->message == WM_KEYDOWN)
1081 switch (pMsg->wParam)
1083 /* case VK_RETURN:
1085 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
1087 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
1089 PostMessage(WM_COMMAND, IDOK);
1091 return TRUE;
1094 break;
1095 */ case VK_F2:
1097 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
1099 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
1100 if(pos != NULL)
1101 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
1104 break;
1106 case VK_F5:
1108 Refresh();
1110 break;
1115 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
1118 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1120 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1121 *pResult = 0;
1123 if(m_currSortCol == pNMLV->iSubItem)
1124 m_currSortDesc = !m_currSortDesc;
1125 else
1127 m_currSortCol = pNMLV->iSubItem;
1128 m_currSortDesc = false;
1131 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1132 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1134 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1137 void CBrowseRefsDlg::OnDestroy()
1139 m_pickedRef = GetSelectedRef(true, false);
1141 CResizableStandAloneDialog::OnDestroy();
1144 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1146 UNREFERENCED_PARAMETER(pNMHDR);
1147 *pResult = 0;
1149 EndDialog(IDOK);
1152 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1154 CBrowseRefsDlg dlg(CString(),NULL);
1156 if(initialRef.IsEmpty())
1157 initialRef = L"HEAD";
1158 dlg.m_initialRef = initialRef;
1159 dlg.m_pickRef_Kind = pickRef_Kind;
1160 dlg.m_bPickOne = true;
1162 if(dlg.DoModal() != IDOK)
1163 return CString();
1165 return dlg.m_pickedRef;
1168 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1170 CString origRef;
1171 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1172 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1173 if(resultRef.IsEmpty())
1174 return false;
1175 if(wcsncmp(resultRef,L"refs/",5)==0)
1176 resultRef = resultRef.Mid(5);
1177 // if(wcsncmp(resultRef,L"heads/",6)==0)
1178 // resultRef = resultRef.Mid(6);
1180 //Find closest match of choice in combobox
1181 int ixFound = -1;
1182 int matchLength = 0;
1183 CString comboRefName;
1184 for(int i = 0; i < pComboBox->GetCount(); ++i)
1186 pComboBox->GetLBText(i, comboRefName);
1187 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1188 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1189 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1191 matchLength = comboRefName.GetLength();
1192 ixFound = i;
1195 if(ixFound >= 0)
1196 pComboBox->SetCurSel(ixFound);
1197 else
1198 ASSERT(FALSE);//No match found. So either pickRef_Kind is wrong or the combobox does not contain the ref specified in the picker (which it should unless the repo has changed before creating the CBrowseRef dialog)
1200 return true;
1203 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1205 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1206 *pResult = FALSE;
1208 if(pDispInfo->item.pszText == NULL)
1209 return; //User canceled changing
1211 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1213 if(!pTree->IsFrom(L"refs/heads"))
1215 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1216 return;
1219 CString origName = pTree->GetRefName().Mid(11);
1221 CString newName;
1222 if(m_pListCtrlRoot != NULL)
1223 newName = m_pListCtrlRoot->GetRefName() + L'/';
1224 newName += pDispInfo->item.pszText;
1226 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1228 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1229 return;
1232 CString newNameTrunced = newName.Mid(11);
1234 CString errorMsg;
1235 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1237 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1238 return;
1240 //Do as if it failed to rename. Let Refresh() do the job.
1241 //*pResult = TRUE;
1243 Refresh(newName);
1245 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1247 // AfxMessageBox(W_csPopup);
1251 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1253 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1254 *pResult = FALSE;
1256 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1258 if(!pTree->IsFrom(L"refs/heads"))
1260 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1261 return;
1265 void CBrowseRefsDlg::OnEnChangeEditFilter()
1267 SetTimer(IDT_FILTER, 1000, NULL);
1270 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1272 if (nIDEvent == IDT_FILTER)
1274 KillTimer(IDT_FILTER);
1275 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1278 CResizableStandAloneDialog::OnTimer(nIDEvent);
1281 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1283 // FIXME: x64 version would get this function called with unexpected parameters.
1284 if (!lParam)
1285 return 0;
1287 RECT * rect = (LPRECT)lParam;
1288 CPoint point;
1289 CString temp;
1290 point = CPoint(rect->left, rect->bottom);
1291 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1292 CMenu popup;
1293 if (popup.CreatePopupMenu())
1295 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1296 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1298 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1299 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1301 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1302 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1304 temp.LoadString(IDS_LOG_FILTER_REVS);
1305 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1307 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1308 if (selection != 0)
1310 m_SelectedFilters ^= selection;
1311 SetFilterCueText();
1312 SetTimer(IDT_FILTER, 1000, NULL);
1315 return 0L;
1318 void CBrowseRefsDlg::SetFilterCueText()
1320 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1321 temp += _T(" ");
1323 if (m_SelectedFilters & LOGFILTER_REFNAME)
1324 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1326 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1328 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1329 temp += _T(", ");
1330 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1333 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1335 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1336 temp += _T(", ");
1337 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1340 if (m_SelectedFilters & LOGFILTER_REVS)
1342 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1343 temp += _T(", ");
1344 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1347 // to make the cue banner text appear more to the right of the edit control
1348 temp = _T(" ") + temp;
1349 m_ctrlFilter.SetCueBanner(temp.TrimRight());