Clean up warning
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob29de12d60b1f05ae4fa5bcf57db430f4445bca99
1 // BrowseRefsDlg.cpp : implementation file
2 //
4 #include "stdafx.h"
5 #include "TortoiseProc.h"
6 #include "BrowseRefsDlg.h"
7 #include "LogDlg.h"
8 #include "AddRemoteDlg.h"
9 #include "AppUtils.h"
10 #include "Settings\SettingGitRemote.h"
11 #include "SinglePropSheetDlg.h"
12 #include "MessageBox.h"
13 #include "RefLogDlg.h"
14 #include "IconMenu.h"
15 #include "FileDiffDlg.h"
17 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
19 if (control == NULL)
20 return;
21 // set the sort arrow
22 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
23 HDITEM HeaderItem = {0};
24 HeaderItem.mask = HDI_FORMAT;
25 for (int i=0; i<pHeader->GetItemCount(); ++i)
27 pHeader->GetItem(i, &HeaderItem);
28 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
29 pHeader->SetItem(i, &HeaderItem);
31 if (nColumn >= 0)
33 pHeader->GetItem(nColumn, &HeaderItem);
34 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
35 pHeader->SetItem(nColumn, &HeaderItem);
39 // CBrowseRefsDlg dialog
41 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
43 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
44 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
45 m_cmdPath(cmdPath),
46 m_currSortCol(-1),
47 m_currSortDesc(false),
48 m_initialRef(L"HEAD"),
49 m_pickRef_Kind(gPickRef_All)
54 CBrowseRefsDlg::~CBrowseRefsDlg()
58 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
60 CDialog::DoDataExchange(pDX);
61 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
62 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
66 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
67 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
68 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
69 ON_WM_CONTEXTMENU()
70 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
71 ON_WM_DESTROY()
72 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
73 END_MESSAGE_MAP()
76 // CBrowseRefsDlg message handlers
78 void CBrowseRefsDlg::OnBnClickedOk()
80 OnOK();
83 BOOL CBrowseRefsDlg::OnInitDialog()
85 CResizableStandAloneDialog::OnInitDialog();
87 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
88 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
89 AddAnchor(IDHELP, BOTTOM_RIGHT);
91 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
92 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
93 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
94 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
95 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
97 AddAnchor(IDOK,BOTTOM_RIGHT);
98 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
100 Refresh(m_initialRef);
102 EnableSaveRestore(L"BrowseRefs");
105 m_ListRefLeafs.SetFocus();
106 return FALSE;
109 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
111 int posSlash=nameLeft.Find('/');
112 CString nameSub;
113 if(posSlash<0)
115 nameSub=nameLeft;
116 nameLeft.Empty();//Nothing left
118 else
120 nameSub=nameLeft.Left(posSlash);
121 nameLeft=nameLeft.Mid(posSlash+1);
123 if(nameSub.IsEmpty())
124 return NULL;
126 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
127 return NULL;
129 CShadowTree& nextNode=m_ShadowTree[nameSub];
130 nextNode.m_csRefName=nameSub;
131 nextNode.m_pParent=this;
132 return &nextNode;
135 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
137 if(IsLeaf())
139 if(m_csRefName.GetLength() > partialRefName.GetLength())
140 return NULL;
141 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
143 //Match of leaf name. Try match on total name.
144 CString totalRefName = GetRefName();
145 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
146 return this; //Also match. Found.
149 else
151 //Not a leaf. Search all nodes.
152 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
154 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
155 if(pSubtree != NULL)
156 return pSubtree; //Found
159 return NULL;//Not found
163 typedef std::map<CString,CString> MAP_STRING_STRING;
165 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
167 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
168 //List ctrl selection?
169 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
171 //A leaf is selected
172 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
173 m_ListRefLeafs.GetNextSelectedItem(pos));
174 return pTree->GetRefName();
176 else if(!onlyIfLeaf)
178 //Tree ctrl selection?
179 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
180 if(hTree!=NULL)
182 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
183 return pTree->GetRefName();
186 return CString();//None
189 void CBrowseRefsDlg::Refresh(CString selectRef)
191 // m_RefMap.clear();
192 // g_Git.GetMapHashToFriendName(m_RefMap);
194 if(!selectRef.IsEmpty())
196 if(selectRef == "HEAD")
198 selectRef = g_Git.GetSymbolicRef(selectRef, false);
201 else
203 selectRef = GetSelectedRef(false, true);
206 m_RefTreeCtrl.DeleteAllItems();
207 m_ListRefLeafs.DeleteAllItems();
208 m_TreeRoot.m_ShadowTree.clear();
209 m_TreeRoot.m_csRefName="refs";
210 // m_TreeRoot.m_csShowName="Refs";
211 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
212 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
214 CString allRefs;
215 g_Git.Run(L"git for-each-ref --format="
216 L"%(refname)%04"
217 L"%(objectname)%04"
218 L"%(authordate:relative)%04"
219 L"%(subject)%04"
220 L"%(authorname)%04"
221 L"%(authordate:iso8601)%03",
222 &allRefs,CP_UTF8);
224 int linePos=0;
225 CString singleRef;
227 MAP_STRING_STRING refMap;
229 //First sort on ref name
230 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
232 singleRef.TrimLeft(L"\r\n");
233 int valuePos=0;
234 CString refName=singleRef.Tokenize(L"\04",valuePos);
235 if(refName.IsEmpty())
236 continue;
237 CString refRest=singleRef.Mid(valuePos);
240 //Use ref based on m_pickRef_Kind
241 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
242 continue; //Skip
243 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
244 continue; //Skip
245 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
246 continue; //Skip
248 refMap[refName] = refRest; //Use
253 //Populate ref tree
254 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
256 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
257 CString values=iterRefMap->second;
258 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
260 int valuePos=0;
261 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
262 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
263 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
264 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
265 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
269 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
270 //Probably not on a branch. Select root node.
271 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
275 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
277 if(!bExactMatch)
279 CString newRefName = GetFullRefName(refName);
280 if(!newRefName.IsEmpty())
281 refName = newRefName;
282 //else refName is not a valid ref. Try to select as good as possible.
284 if(wcsnicmp(refName,L"refs/",5)!=0)
285 return false; // Not a ref name
287 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
288 if(treeLeafHead.m_hTree != NULL)
290 //Not a leaf. Select tree node and return
291 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
292 return true;
295 if(treeLeafHead.m_pParent==NULL)
296 return false; //Weird... should not occur.
298 //This is the current head.
299 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
301 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
303 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
304 if(pCurrShadowTree == &treeLeafHead)
306 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
307 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
311 return true;
314 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
316 if(pTreePos==NULL)
318 if(wcsnicmp(refName,L"refs/",5)==0)
319 refName=refName.Mid(5);
320 pTreePos=&m_TreeRoot;
322 if(refName.IsEmpty())
323 return *pTreePos;//Found leaf
325 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
326 if(pNextTree==NULL)
328 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
329 ASSERT(!bCreateIfNotExist);
330 return *pTreePos;
333 if(!refName.IsEmpty())
335 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
336 //Leafs are for the list control.
337 if(pNextTree->m_hTree==NULL)
339 //New tree. Create node in control.
340 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
341 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
345 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
349 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
351 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
352 *pResult = 0;
354 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
357 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
359 m_ListRefLeafs.DeleteAllItems();
360 m_currSortCol = -1;
361 m_currSortDesc = false;
362 SetSortArrow(&m_ListRefLeafs,-1,false);
364 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
365 if(pTree==NULL)
367 ASSERT(FALSE);
368 return;
370 FillListCtrlForShadowTree(pTree,L"",true);
373 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
375 if(pTree->IsLeaf())
377 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
379 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
380 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
381 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
382 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
383 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
385 else
388 CString csThisName;
389 if(!isFirstLevel)
390 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
391 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
393 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
398 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
400 ASSERT(!leafs.empty());
402 CString csMessage;
403 CString csTitle;
405 UINT mbIcon=MB_ICONQUESTION;
406 csMessage = L"Are you sure you want to delete ";
408 bool bIsRemoteBranch = false;
409 bool bIsBranch = false;
410 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
411 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
413 if(bIsBranch)
415 if(leafs.size() == 1)
417 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
418 csTitle.Format(L"Confirm deletion of %sbranch %s",
419 bIsRemoteBranch? L"remote ": L"",
420 branchToDelete);
422 csMessage += "the ";
423 if(bIsRemoteBranch)
424 csMessage += L"<ct=0x0000FF><i>remote</i></ct> ";
425 csMessage += L"branch:\r\n\r\n<b>";
426 csMessage += branchToDelete;
427 csMessage += L"</b>";
429 //Check if branch is fully merged in HEAD
430 CGitHash branchHash = g_Git.GetHash(leafs[0]->GetRefName());
431 CGitHash commonAncestor;
432 CString commonAncestorstr;
433 CString cmd;
434 cmd.Format(L"git.exe merge-base HEAD %s", leafs[0]->GetRefName());
435 g_Git.Run(cmd,&commonAncestorstr,CP_UTF8);
437 commonAncestor=commonAncestorstr;
439 if(commonAncestor != branchHash)
441 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
442 mbIcon = MB_ICONWARNING;
444 if(bIsRemoteBranch)
446 csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branch on the remote.</b>";
447 mbIcon = MB_ICONWARNING;
450 else
452 csTitle.Format(L"Confirm deletion of %d %sbranches",
453 leafs.size(),
454 bIsRemoteBranch? L"remote ": L"");
456 CString csMoreMsgText;
457 csMoreMsgText.Format(L"<b>%d</b> ", leafs.size());
458 csMessage += csMoreMsgText;
459 if(bIsRemoteBranch)
460 csMessage += L"<ct=0x0000FF><i>remote</i></ct> ";
461 csMessage += L"branches";
463 csMessage += L"\r\n\r\n<b>Warning:\r\nIt has not been checked if these branches have been fully merged into HEAD.</b>";
464 mbIcon = MB_ICONWARNING;
466 if(bIsRemoteBranch)
468 csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branches on the remote.</b>";
469 mbIcon = MB_ICONWARNING;
474 else if(leafs[0]->IsFrom(L"refs/tags"))
476 if(leafs.size() == 1)
478 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
479 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
480 csMessage += "the tag:\r\n\r\n<b>";
481 csMessage += tagToDelete;
482 csMessage += "</b>";
484 else
486 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
487 csTitle.Format(L"Confirm deletion of %d tags", leafs.size());
488 CString csMoreMsgText;
489 csMoreMsgText.Format(L"<b>%d</b> ", leafs.size());
490 csMessage += csMoreMsgText;
491 csMessage += L"tags";
495 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
499 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
501 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
502 if(!DoDeleteRef((*i)->GetRefName(), bForce))
503 return false;
504 return true;
507 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
509 bool bIsRemoteBranch = false;
510 bool bIsBranch = false;
511 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
512 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
514 if(bIsBranch)
516 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
517 CString cmd;
518 if(bIsRemoteBranch)
520 int slash = branchToDelete.Find(L'/');
521 if(slash < 0)
522 return false;
523 CString remoteName = branchToDelete.Left(slash);
524 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
525 cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);
527 else
528 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
529 CString resultDummy;
530 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
532 CString errorMsg;
533 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
534 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
535 return false;
538 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
540 CString tagToDelete = completeRefName.Mid(10);
541 CString cmd;
542 cmd.Format(L"git.exe tag -d %s",tagToDelete);
543 CString resultDummy;
544 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
546 CString errorMsg;
547 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
548 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
549 return false;
552 return true;
555 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
557 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
558 if(pLeaf == NULL)
559 return CString();
560 return pLeaf->GetRefName();
564 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
566 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
567 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
570 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
572 CPoint clientPoint=point;
573 m_RefTreeCtrl.ScreenToClient(&clientPoint);
575 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
576 if(hTreeItem!=NULL)
577 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
579 VectorPShadowTree tree;
580 ShowContextMenu(point,hTreeItem,tree);
584 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
586 std::vector<CShadowTree*> selectedLeafs;
587 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
588 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
589 while(pos)
591 selectedLeafs.push_back(
592 (CShadowTree*)m_ListRefLeafs.GetItemData(
593 m_ListRefLeafs.GetNextSelectedItem(pos)));
596 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
599 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
601 CIconMenu popupMenu;
602 popupMenu.CreatePopupMenu();
604 bool bAddSeparator = false;
605 CString remoteName;
607 if(selectedLeafs.size()==1)
609 bAddSeparator = true;
611 bool bShowReflogOption = false;
612 bool bShowFetchOption = false;
613 bool bShowSwitchOption = false;
615 CString fetchFromCmd;
617 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
619 bShowReflogOption = true;
620 bShowSwitchOption = true;
622 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
624 bShowReflogOption = true;
625 bShowFetchOption = true;
627 int dummy = 0;//Needed for tokenize
628 remoteName = selectedLeafs[0]->GetRefName();
629 remoteName = remoteName.Mid(13);
630 remoteName = remoteName.Tokenize(L"/", dummy);
631 fetchFromCmd.Format(L"Fetch from %s", remoteName);
633 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
637 popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);
638 if(bShowReflogOption) popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);
639 if(bShowFetchOption) popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
640 if(bShowSwitchOption) popupMenu.AppendMenuIcon(eCmd_Switch, L"Switch to this Ref", IDI_SWITCH);
643 else if(selectedLeafs.size() == 2)
645 bAddSeparator = true;
647 popupMenu.AppendMenuIcon(eCmd_Diff, L"Compare These Refs", IDI_DIFF);
650 if(!selectedLeafs.empty())
652 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
654 CString menuItemName;
655 if(selectedLeafs.size() == 1)
656 menuItemName = L"Delete Remote Branch";
657 else
658 menuItemName.Format(L"Delete %d Remote Branches", selectedLeafs.size());
660 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
663 if(AreAllFrom(selectedLeafs, L"refs/heads/"))
665 CString menuItemName;
666 if(selectedLeafs.size() == 1)
667 menuItemName = L"Delete Branch";
668 else
669 menuItemName.Format(L"Delete %d Branches", selectedLeafs.size());
671 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
674 if(AreAllFrom(selectedLeafs, L"refs/tags/"))
676 CString menuItemName;
677 if(selectedLeafs.size() == 1)
678 menuItemName = L"Delete Tag";
679 else
680 menuItemName.Format(L"Delete %d Tags", selectedLeafs.size());
682 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
686 if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);
688 if(hTreePos!=NULL)
690 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
691 if(pTree->IsFrom(L"refs/remotes"))
693 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
694 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);
695 if(selectedLeafs.empty())
697 int dummy = 0;//Needed for tokenize
698 remoteName = pTree->GetRefName();
699 remoteName = remoteName.Mid(13);
700 remoteName = remoteName.Tokenize(L"/", dummy);
701 if(!remoteName.IsEmpty())
703 CString fetchFromCmd;
704 fetchFromCmd.Format(L"Fetch from %s", remoteName);
705 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
709 else if(pTree->IsFrom(L"refs/heads"))
710 popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);
711 else if(pTree->IsFrom(L"refs/tags"))
712 popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);
716 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
717 switch(cmd)
719 case eCmd_ViewLog:
721 CLogDlg dlg;
722 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
723 dlg.DoModal();
725 break;
726 case eCmd_DeleteBranch:
727 case eCmd_DeleteRemoteBranch:
729 if(ConfirmDeleteRef(selectedLeafs))
730 DoDeleteRefs(selectedLeafs, true);
731 Refresh();
733 break;
734 case eCmd_DeleteTag:
736 if(ConfirmDeleteRef(selectedLeafs))
737 DoDeleteRefs(selectedLeafs, true);
738 Refresh();
740 break;
741 case eCmd_ShowReflog:
743 CRefLogDlg refLogDlg(this);
744 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
745 refLogDlg.DoModal();
747 break;
748 case eCmd_Fetch:
750 CString cmd;
751 cmd.Format(_T("git.exe fetch %s"), remoteName);
752 CProgressDlg progress;
753 progress.m_GitCmd=cmd;
754 progress.DoModal();
755 Refresh();
757 break;
758 case eCmd_Switch:
760 CAppUtils::Switch(NULL, selectedLeafs[0]->GetRefName());
762 break;
763 case eCmd_AddRemote:
765 CAddRemoteDlg(this).DoModal();
766 Refresh();
768 break;
769 case eCmd_ManageRemotes:
771 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();
772 // CSettingGitRemote W_Remotes(m_cmdPath);
773 // W_Remotes.DoModal();
774 Refresh();
776 break;
777 case eCmd_CreateBranch:
779 CAppUtils::CreateBranchTag(false);
780 Refresh();
782 break;
783 case eCmd_CreateTag:
785 CAppUtils::CreateBranchTag(true);
786 Refresh();
788 break;
789 case eCmd_Diff:
791 CFileDiffDlg dlg;
792 dlg.SetDiff(
793 NULL,
794 selectedLeafs[0]->m_csRefHash,
795 selectedLeafs[1]->m_csRefHash);
796 dlg.DoModal();
798 break;
802 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
804 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
805 if(!(*i)->IsFrom(from))
806 return false;
807 return true;
810 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
812 if (pMsg->message == WM_KEYDOWN)
814 switch (pMsg->wParam)
816 /* case VK_RETURN:
818 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
820 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
822 PostMessage(WM_COMMAND, IDOK);
824 return TRUE;
827 break;
828 */ case VK_F5:
830 Refresh();
832 break;
837 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
840 class CRefLeafListCompareFunc
842 public:
843 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
845 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
847 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
850 int Compare(LPARAM lParam1, LPARAM lParam2)
852 return Compare(
853 (CShadowTree*)m_pList->GetItemData(lParam1),
854 (CShadowTree*)m_pList->GetItemData(lParam2));
857 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
859 int result=CompareNoDesc(pLeft,pRight);
860 if(m_desc)
861 return -result;
862 return result;
865 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
867 switch(m_col)
869 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
870 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
871 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
872 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
874 return 0;
877 int m_col;
878 bool m_desc;
879 CListCtrl* m_pList;
885 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
887 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
888 *pResult = 0;
890 if(m_currSortCol == pNMLV->iSubItem)
891 m_currSortDesc = !m_currSortDesc;
892 else
894 m_currSortCol = pNMLV->iSubItem;
895 m_currSortDesc = false;
898 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
899 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
901 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
904 void CBrowseRefsDlg::OnDestroy()
906 m_pickedRef = GetSelectedRef(true, false);
908 CResizableStandAloneDialog::OnDestroy();
911 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
913 UNREFERENCED_PARAMETER(pNMHDR);
914 *pResult = 0;
916 EndDialog(IDOK);
919 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
921 CBrowseRefsDlg dlg(CString(),NULL);
923 if(initialRef.IsEmpty())
924 initialRef = L"HEAD";
925 dlg.m_initialRef = initialRef;
926 dlg.m_pickRef_Kind = pickRef_Kind;
928 if(dlg.DoModal() != IDOK)
929 return CString();
931 return dlg.m_pickedRef;
934 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
936 CString origRef;
937 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
938 CString resultRef = PickRef(false,origRef,pickRef_Kind);
939 if(resultRef.IsEmpty())
940 return false;
941 if(wcsncmp(resultRef,L"refs/",5)==0)
942 resultRef = resultRef.Mid(5);
943 // if(wcsncmp(resultRef,L"heads/",6)==0)
944 // resultRef = resultRef.Mid(6);
946 //Find closest match of choice in combobox
947 int ixFound = -1;
948 int matchLength = 0;
949 CString comboRefName;
950 for(int i = 0; i < pComboBox->GetCount(); ++i)
952 pComboBox->GetLBText(i, comboRefName);
953 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
954 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
955 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
957 matchLength = comboRefName.GetLength();
958 ixFound = i;
961 if(ixFound >= 0)
962 pComboBox->SetCurSel(ixFound);
963 else
964 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)
966 return true;