Merge branch 'master' of git://github.com/Jopie64/tortoisegit
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
bloba02bf1cad0a45f2eac59450c974b5f5318d34d8d
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);
90 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
91 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
92 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
93 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
94 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
96 AddAnchor(IDOK,BOTTOM_RIGHT);
97 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
99 Refresh(m_initialRef);
101 EnableSaveRestore(L"BrowseRefs");
104 m_ListRefLeafs.SetFocus();
105 return FALSE;
108 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
110 int posSlash=nameLeft.Find('/');
111 CString nameSub;
112 if(posSlash<0)
114 nameSub=nameLeft;
115 nameLeft.Empty();//Nothing left
117 else
119 nameSub=nameLeft.Left(posSlash);
120 nameLeft=nameLeft.Mid(posSlash+1);
122 if(nameSub.IsEmpty())
123 return NULL;
125 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
126 return NULL;
128 CShadowTree& nextNode=m_ShadowTree[nameSub];
129 nextNode.m_csRefName=nameSub;
130 nextNode.m_pParent=this;
131 return &nextNode;
134 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
136 if(IsLeaf())
138 if(m_csRefName.GetLength() > partialRefName.GetLength())
139 return NULL;
140 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
142 //Match of leaf name. Try match on total name.
143 CString totalRefName = GetRefName();
144 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
145 return this; //Also match. Found.
148 else
150 //Not a leaf. Search all nodes.
151 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
153 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
154 if(pSubtree != NULL)
155 return pSubtree; //Found
158 return NULL;//Not found
162 typedef std::map<CString,CString> MAP_STRING_STRING;
164 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
166 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
167 //List ctrl selection?
168 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
170 //A leaf is selected
171 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
172 m_ListRefLeafs.GetNextSelectedItem(pos));
173 return pTree->GetRefName();
175 else if(!onlyIfLeaf)
177 //Tree ctrl selection?
178 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
179 if(hTree!=NULL)
181 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
182 return pTree->GetRefName();
185 return CString();//None
188 void CBrowseRefsDlg::Refresh(CString selectRef)
190 // m_RefMap.clear();
191 // g_Git.GetMapHashToFriendName(m_RefMap);
193 if(!selectRef.IsEmpty())
195 if(selectRef == "HEAD")
197 selectRef.Empty();
198 g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);
199 selectRef.Trim(L"\r\n\t ");
202 else
204 selectRef = GetSelectedRef(false, true);
207 m_RefTreeCtrl.DeleteAllItems();
208 m_ListRefLeafs.DeleteAllItems();
209 m_TreeRoot.m_ShadowTree.clear();
210 m_TreeRoot.m_csRefName="refs";
211 // m_TreeRoot.m_csShowName="Refs";
212 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
213 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
215 CString allRefs;
216 g_Git.Run(L"git for-each-ref --format="
217 L"%(refname)%04"
218 L"%(objectname)%04"
219 L"%(authordate:relative)%04"
220 L"%(subject)%04"
221 L"%(authorname)%04"
222 L"%(authordate:iso8601)",
223 &allRefs,CP_UTF8);
225 int linePos=0;
226 CString singleRef;
228 MAP_STRING_STRING refMap;
230 //First sort on ref name
231 while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())
233 int valuePos=0;
234 CString refName=singleRef.Tokenize(L"\04",valuePos);
235 CString refRest=singleRef.Mid(valuePos);
237 //Use ref based on m_pickRef_Kind
238 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
239 continue; //Skip
240 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
241 continue; //Skip
242 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
243 continue; //Skip
245 refMap[refName] = refRest; //Use
250 //Populate ref tree
251 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
253 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
254 CString values=iterRefMap->second;
256 int valuePos=0;
257 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
258 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
259 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
260 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
261 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
265 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
266 //Probably not on a branch. Select root node.
267 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
271 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
273 if(!bExactMatch)
274 refName = GetFullRefName(refName);
275 if(wcsnicmp(refName,L"refs/",5)!=0)
276 return false; // Not a ref name
278 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
279 if(treeLeafHead.m_hTree != NULL)
281 //Not a leaf. Select tree node and return
282 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
283 return true;
286 if(treeLeafHead.m_pParent==NULL)
287 return false; //Weird... should not occur.
289 //This is the current head.
290 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
292 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
294 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
295 if(pCurrShadowTree == &treeLeafHead)
297 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
298 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
302 return true;
305 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
307 if(pTreePos==NULL)
309 if(wcsnicmp(refName,L"refs/",5)==0)
310 refName=refName.Mid(5);
311 pTreePos=&m_TreeRoot;
313 if(refName.IsEmpty())
314 return *pTreePos;//Found leaf
316 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
317 if(pNextTree==NULL)
319 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
320 ASSERT(!bCreateIfNotExist);
321 return *pTreePos;
324 if(!refName.IsEmpty())
326 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
327 //Leafs are for the list control.
328 if(pNextTree->m_hTree==NULL)
330 //New tree. Create node in control.
331 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
332 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
336 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
340 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
342 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
343 *pResult = 0;
345 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
348 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
350 m_ListRefLeafs.DeleteAllItems();
351 m_currSortCol = -1;
352 m_currSortDesc = false;
353 SetSortArrow(&m_ListRefLeafs,-1,false);
355 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
356 if(pTree==NULL)
358 ASSERT(FALSE);
359 return;
361 FillListCtrlForShadowTree(pTree,L"",true);
364 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
366 if(pTree->IsLeaf())
368 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
370 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
371 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
372 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
373 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
374 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
376 else
379 CString csThisName;
380 if(!isFirstLevel)
381 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
382 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
384 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
389 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)
391 CString csMessage;
392 CString csTitle;
394 UINT mbIcon=MB_ICONQUESTION;
395 csMessage = L"Are you sure you want to delete the ";
397 bool bIsRemoteBranch = false;
398 bool bIsBranch = false;
399 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
400 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
402 if(bIsBranch)
404 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
405 csTitle.Format(L"Confirm deletion of %sbranch %s",
406 bIsRemoteBranch? L"remote ": L"",
407 branchToDelete);
408 if(bIsRemoteBranch)
409 csMessage += L"<ct=0x0000FF><i>remote</i></ct> ";
410 csMessage += L"branch:\r\n\r\n<b>";
411 csMessage += branchToDelete;
412 csMessage += L"</b>";
414 //Check if branch is fully merged in HEAD
415 CString branchHash = g_Git.GetHash(completeRefName);
416 CString commonAncestor;
417 CString cmd;
418 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);
419 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
421 branchHash=branchHash.Left(40);
422 commonAncestor=commonAncestor.Left(40);
424 if(commonAncestor != branchHash)
426 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
427 mbIcon = MB_ICONWARNING;
429 if(bIsRemoteBranch)
431 csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branch on the remote.</b>";
432 mbIcon = MB_ICONWARNING;
435 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
437 CString tagToDelete = completeRefName.Mid(10);
438 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
439 csMessage += "tag:\r\n\r\n<b>";
440 csMessage += tagToDelete;
441 csMessage += "</b>";
444 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
449 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
451 bool bIsRemoteBranch = false;
452 bool bIsBranch = false;
453 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
454 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
456 if(bIsBranch)
458 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
459 CString cmd;
460 if(bIsRemoteBranch)
462 int slash = branchToDelete.Find(L'/');
463 if(slash < 0)
464 return false;
465 CString remoteName = branchToDelete.Left(slash);
466 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
467 cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);
469 else
470 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
471 CString resultDummy;
472 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
474 CString errorMsg;
475 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
476 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
477 return false;
480 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
482 CString tagToDelete = completeRefName.Mid(10);
483 CString cmd;
484 cmd.Format(L"git.exe tag -d %s",tagToDelete);
485 CString resultDummy;
486 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
488 CString errorMsg;
489 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
490 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
491 return false;
494 return true;
497 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
499 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
500 if(pLeaf == NULL)
501 return CString();
502 return pLeaf->GetRefName();
506 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
508 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
509 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
512 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
514 CPoint clientPoint=point;
515 m_RefTreeCtrl.ScreenToClient(&clientPoint);
517 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
518 if(hTreeItem!=NULL)
519 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
521 ShowContextMenu(point,hTreeItem,VectorPShadowTree());
525 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
527 std::vector<CShadowTree*> selectedLeafs;
528 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
529 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
530 while(pos)
532 selectedLeafs.push_back(
533 (CShadowTree*)m_ListRefLeafs.GetItemData(
534 m_ListRefLeafs.GetNextSelectedItem(pos)));
537 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
540 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
542 CIconMenu popupMenu;
543 popupMenu.CreatePopupMenu();
545 bool bAddSeparator = false;
546 if(selectedLeafs.size()==1)
548 bAddSeparator = true;
550 bool bShowReflogOption = false;
551 bool bShowDeleteBranchOption = false;
552 bool bShowDeleteTagOption = false;
553 bool bShowDeleteRemoteBranchOption = false;
555 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
557 bShowReflogOption = true;
558 bShowDeleteBranchOption = true;
560 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
562 bShowReflogOption = true;
563 bShowDeleteRemoteBranchOption = true;
565 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
567 bShowDeleteTagOption = true;
570 popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);
571 if(bShowReflogOption) popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);
572 if(bShowDeleteTagOption) popupMenu.AppendMenuIcon(eCmd_DeleteTag, L"Delete Tag", IDI_DELETE);
573 if(bShowDeleteBranchOption) popupMenu.AppendMenuIcon(eCmd_DeleteBranch, L"Delete Branch", IDI_DELETE);
574 if(bShowDeleteRemoteBranchOption) popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, L"Delete Remote Branch", IDI_DELETE);
578 // CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);
579 // if(pTree==NULL)
580 // return;
582 else if(selectedLeafs.size() == 2)
584 bAddSeparator = true;
586 popupMenu.AppendMenuIcon(eCmd_Diff, L"Diff These Commits", IDI_DIFF);
589 if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);
591 if(hTreePos!=NULL)
593 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
594 if(pTree->IsFrom(L"refs/remotes"))
596 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
597 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);
599 else if(pTree->IsFrom(L"refs/heads"))
600 popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);
601 else if(pTree->IsFrom(L"refs/tags"))
602 popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);
606 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
607 switch(cmd)
609 case eCmd_ViewLog:
611 CLogDlg dlg;
612 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
613 dlg.DoModal();
615 break;
616 case eCmd_DeleteBranch:
617 case eCmd_DeleteRemoteBranch:
619 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
620 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
621 Refresh();
623 break;
624 case eCmd_DeleteTag:
626 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
627 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
628 Refresh();
630 break;
631 case eCmd_ShowReflog:
633 CRefLogDlg refLogDlg(this);
634 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
635 refLogDlg.DoModal();
637 break;
638 case eCmd_AddRemote:
640 CAddRemoteDlg(this).DoModal();
641 Refresh();
643 break;
644 case eCmd_ManageRemotes:
646 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();
647 // CSettingGitRemote W_Remotes(m_cmdPath);
648 // W_Remotes.DoModal();
649 Refresh();
651 break;
652 case eCmd_CreateBranch:
654 CAppUtils::CreateBranchTag(false);
655 Refresh();
657 break;
658 case eCmd_CreateTag:
660 CAppUtils::CreateBranchTag(true);
661 Refresh();
663 break;
664 case eCmd_Diff:
666 CFileDiffDlg dlg;
667 dlg.SetDiff(
668 NULL,
669 selectedLeafs[0]->m_csRefHash,
670 selectedLeafs[1]->m_csRefHash);
671 dlg.DoModal();
673 break;
677 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
679 if (pMsg->message == WM_KEYDOWN)
681 switch (pMsg->wParam)
683 /* case VK_RETURN:
685 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
687 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
689 PostMessage(WM_COMMAND, IDOK);
691 return TRUE;
694 break;
695 */ case VK_F5:
697 Refresh();
699 break;
704 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
707 class CRefLeafListCompareFunc
709 public:
710 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
712 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
714 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
717 int Compare(LPARAM lParam1, LPARAM lParam2)
719 return Compare(
720 (CShadowTree*)m_pList->GetItemData(lParam1),
721 (CShadowTree*)m_pList->GetItemData(lParam2));
724 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
726 int result=CompareNoDesc(pLeft,pRight);
727 if(m_desc)
728 return -result;
729 return result;
732 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
734 switch(m_col)
736 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
737 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
738 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
739 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
741 return 0;
744 int m_col;
745 bool m_desc;
746 CListCtrl* m_pList;
752 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
754 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
755 *pResult = 0;
757 if(m_currSortCol == pNMLV->iSubItem)
758 m_currSortDesc = !m_currSortDesc;
759 else
761 m_currSortCol = pNMLV->iSubItem;
762 m_currSortDesc = false;
765 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
766 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
768 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
771 void CBrowseRefsDlg::OnDestroy()
773 m_pickedRef = GetSelectedRef(true, false);
775 CResizableStandAloneDialog::OnDestroy();
778 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
780 LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
781 *pResult = 0;
783 EndDialog(IDOK);
786 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)
788 CBrowseRefsDlg dlg(CString(),NULL);
790 if(initialRef.IsEmpty())
791 initialRef = L"HEAD";
792 dlg.m_initialRef = initialRef;
793 dlg.m_pickRef_Kind = pickRef_Kind;
795 if(dlg.DoModal() != IDOK)
796 return CString();
798 return dlg.m_pickedRef;
801 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
803 CString origRef;
804 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
805 CString resultRef = PickRef(false,origRef,pickRef_Kind);
806 if(resultRef.IsEmpty())
807 return false;
808 if(wcsncmp(resultRef,L"refs/",5)==0)
809 resultRef = resultRef.Mid(5);
810 // if(wcsncmp(resultRef,L"heads/",6)==0)
811 // resultRef = resultRef.Mid(6);
813 //Find closest match of choice in combobox
814 int ixFound = -1;
815 int matchLength = 0;
816 CString comboRefName;
817 for(int i = 0; i < pComboBox->GetCount(); ++i)
819 pComboBox->GetLBText(i, comboRefName);
820 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
821 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
822 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
824 matchLength = comboRefName.GetLength();
825 ixFound = i;
828 if(ixFound >= 0)
829 pComboBox->SetCurSel(ixFound);
830 else
831 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)
833 return true;