BrowseRefs: Show context menu icons
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob0236410d614fde0602c3525e4b575337e2c568c9
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"
16 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
18 if (control == NULL)
19 return;
20 // set the sort arrow
21 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
22 HDITEM HeaderItem = {0};
23 HeaderItem.mask = HDI_FORMAT;
24 for (int i=0; i<pHeader->GetItemCount(); ++i)
26 pHeader->GetItem(i, &HeaderItem);
27 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
28 pHeader->SetItem(i, &HeaderItem);
30 if (nColumn >= 0)
32 pHeader->GetItem(nColumn, &HeaderItem);
33 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
34 pHeader->SetItem(nColumn, &HeaderItem);
38 // CBrowseRefsDlg dialog
40 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
42 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
43 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
44 m_cmdPath(cmdPath),
45 m_currSortCol(-1),
46 m_currSortDesc(false),
47 m_initialRef(L"HEAD"),
48 m_pickRef_Kind(gPickRef_All)
53 CBrowseRefsDlg::~CBrowseRefsDlg()
57 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
59 CDialog::DoDataExchange(pDX);
60 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
61 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
65 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
66 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
67 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
68 ON_WM_CONTEXTMENU()
69 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
70 ON_WM_DESTROY()
71 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
72 END_MESSAGE_MAP()
75 // CBrowseRefsDlg message handlers
77 void CBrowseRefsDlg::OnBnClickedOk()
79 OnOK();
82 BOOL CBrowseRefsDlg::OnInitDialog()
84 CResizableStandAloneDialog::OnInitDialog();
86 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
87 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
89 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
90 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
91 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
92 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
93 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
95 AddAnchor(IDOK,BOTTOM_RIGHT);
96 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
98 Refresh(m_initialRef);
100 EnableSaveRestore(L"BrowseRefs");
103 m_ListRefLeafs.SetFocus();
104 return FALSE;
107 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
109 int posSlash=nameLeft.Find('/');
110 CString nameSub;
111 if(posSlash<0)
113 nameSub=nameLeft;
114 nameLeft.Empty();//Nothing left
116 else
118 nameSub=nameLeft.Left(posSlash);
119 nameLeft=nameLeft.Mid(posSlash+1);
121 if(nameSub.IsEmpty())
122 return NULL;
124 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
125 return NULL;
127 CShadowTree& nextNode=m_ShadowTree[nameSub];
128 nextNode.m_csRefName=nameSub;
129 nextNode.m_pParent=this;
130 return &nextNode;
133 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
135 if(IsLeaf())
137 if(m_csRefName.GetLength() > partialRefName.GetLength())
138 return NULL;
139 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
141 //Match of leaf name. Try match on total name.
142 CString totalRefName = GetRefName();
143 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
144 return this; //Also match. Found.
147 else
149 //Not a leaf. Search all nodes.
150 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
152 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
153 if(pSubtree != NULL)
154 return pSubtree; //Found
157 return NULL;//Not found
161 typedef std::map<CString,CString> MAP_STRING_STRING;
163 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf)
165 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
166 //List ctrl selection?
167 if(pos)
169 //A leaf is selected
170 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
171 m_ListRefLeafs.GetNextSelectedItem(pos));
172 return pTree->GetRefName();
174 else if(!onlyIfLeaf)
176 //Tree ctrl selection?
177 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
178 if(hTree!=NULL)
180 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
181 return pTree->GetRefName();
184 return CString();//None
187 void CBrowseRefsDlg::Refresh(CString selectRef)
189 // m_RefMap.clear();
190 // g_Git.GetMapHashToFriendName(m_RefMap);
192 if(!selectRef.IsEmpty())
194 if(selectRef == "HEAD")
196 selectRef.Empty();
197 g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);
198 selectRef.Trim(L"\r\n\t ");
201 else
203 selectRef = GetSelectedRef(false);
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)",
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"\r\n",linePos)).IsEmpty())
232 int valuePos=0;
233 CString refName=singleRef.Tokenize(L"\04",valuePos);
234 CString refRest=singleRef.Mid(valuePos);
236 //Use ref based on m_pickRef_Kind
237 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
238 continue; //Skip
239 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
240 continue; //Skip
241 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
242 continue; //Skip
244 refMap[refName] = refRest; //Use
249 //Populate ref tree
250 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
252 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
253 CString values=iterRefMap->second;
255 int valuePos=0;
256 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
257 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
258 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
259 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
260 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
264 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
265 //Probably not on a branch. Select root node.
266 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
270 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
272 if(!bExactMatch)
273 refName = GetFullRefName(refName);
274 if(wcsnicmp(refName,L"refs/",5)!=0)
275 return false; // Not a ref name
277 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
278 if(treeLeafHead.m_hTree != NULL)
280 //Not a leaf. Select tree node and return
281 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
282 return true;
285 if(treeLeafHead.m_pParent==NULL)
286 return false; //Weird... should not occur.
288 //This is the current head.
289 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
291 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
293 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
294 if(pCurrShadowTree == &treeLeafHead)
296 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
297 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
301 return true;
304 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
306 if(pTreePos==NULL)
308 if(wcsnicmp(refName,L"refs/",5)==0)
309 refName=refName.Mid(5);
310 pTreePos=&m_TreeRoot;
312 if(refName.IsEmpty())
313 return *pTreePos;//Found leaf
315 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
316 if(pNextTree==NULL)
318 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
319 ASSERT(!bCreateIfNotExist);
320 return *pTreePos;
323 if(!refName.IsEmpty())
325 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
326 //Leafs are for the list control.
327 if(pNextTree->m_hTree==NULL)
329 //New tree. Create node in control.
330 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
331 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
335 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
339 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
341 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
342 *pResult = 0;
344 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
347 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
349 m_ListRefLeafs.DeleteAllItems();
350 m_currSortCol = -1;
351 m_currSortDesc = false;
352 SetSortArrow(&m_ListRefLeafs,-1,false);
354 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
355 if(pTree==NULL)
357 ASSERT(FALSE);
358 return;
360 FillListCtrlForShadowTree(pTree,L"",true);
363 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
365 if(pTree->IsLeaf())
367 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
369 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
370 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
371 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
372 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
373 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
375 else
378 CString csThisName;
379 if(!isFirstLevel)
380 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
381 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
383 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
388 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)
390 CString csMessage;
391 CString csTitle;
393 UINT mbIcon=MB_ICONQUESTION;
394 csMessage=L"Are you sure you want to delete the ";
395 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
397 CString branchToDelete = completeRefName.Mid(11);
398 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);
399 csMessage += "branch:\r\n\r\n<b>";
400 csMessage += branchToDelete;
401 csMessage += "</b>";
403 //Check if branch is fully merged in HEAD
404 CString branchHash = g_Git.GetHash(completeRefName);
405 CString commonAncestor;
406 CString cmd;
407 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);
408 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
410 branchHash=branchHash.Left(40);
411 commonAncestor=commonAncestor.Left(40);
413 if(commonAncestor != branchHash)
415 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
416 mbIcon=MB_ICONWARNING;
419 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
421 CString tagToDelete = completeRefName.Mid(10);
422 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
423 csMessage += "tag:\r\n\r\n<b>";
424 csMessage += tagToDelete;
425 csMessage += "</b>";
428 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
433 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
435 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
437 CString branchToDelete = completeRefName.Mid(11);
438 CString cmd;
439 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
440 CString resultDummy;
441 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
443 CString errorMsg;
444 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
445 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
446 return false;
449 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
451 CString tagToDelete = completeRefName.Mid(10);
452 CString cmd;
453 cmd.Format(L"git.exe tag -d %s",tagToDelete);
454 CString resultDummy;
455 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
457 CString errorMsg;
458 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
459 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
460 return false;
463 return true;
466 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
468 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
469 if(pLeaf == NULL)
470 return CString();
471 return pLeaf->GetRefName();
475 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
477 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
478 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
481 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
483 CPoint clientPoint=point;
484 m_RefTreeCtrl.ScreenToClient(&clientPoint);
486 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
487 if(hTreeItem!=NULL)
488 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
490 ShowContextMenu(point,hTreeItem,VectorPShadowTree());
494 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
496 std::vector<CShadowTree*> selectedLeafs;
497 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
498 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
499 while(pos)
501 selectedLeafs.push_back(
502 (CShadowTree*)m_ListRefLeafs.GetItemData(
503 m_ListRefLeafs.GetNextSelectedItem(pos)));
506 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
509 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
511 CIconMenu popupMenu;
512 popupMenu.CreatePopupMenu();
514 bool bAddSeparator = false;
515 if(selectedLeafs.size()==1)
517 bAddSeparator = true;
519 bool bShowReflogOption = false;
520 bool bShowDeleteBranchOption = false;
521 bool bShowDeleteTagOption = false;
523 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
525 bShowReflogOption = true;
526 bShowDeleteBranchOption = true;
528 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
530 bShowReflogOption = true;
532 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
534 bShowDeleteTagOption = true;
537 popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);
538 if(bShowReflogOption) popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);
539 if(bShowDeleteTagOption) popupMenu.AppendMenuIcon(eCmd_DeleteTag, L"Delete Tag", IDI_DELETE);
540 if(bShowDeleteBranchOption) popupMenu.AppendMenuIcon(eCmd_DeleteBranch, L"Delete Branch", IDI_DELETE);
544 // CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);
545 // if(pTree==NULL)
546 // return;
549 if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);
551 if(hTreePos!=NULL)
553 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
554 if(pTree->IsFrom(L"refs/remotes"))
556 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
557 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);
559 else if(pTree->IsFrom(L"refs/heads"))
560 popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);
561 else if(pTree->IsFrom(L"refs/tags"))
562 popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);
566 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
567 switch(cmd)
569 case eCmd_ViewLog:
571 CLogDlg dlg;
572 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
573 dlg.DoModal();
575 break;
576 case eCmd_DeleteBranch:
578 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
579 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
580 Refresh();
582 break;
583 case eCmd_DeleteTag:
585 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
586 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
587 Refresh();
589 break;
590 case eCmd_ShowReflog:
592 CRefLogDlg refLogDlg(this);
593 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
594 refLogDlg.DoModal();
596 break;
597 case eCmd_AddRemote:
599 CAddRemoteDlg(this).DoModal();
600 Refresh();
602 break;
603 case eCmd_ManageRemotes:
605 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();
606 // CSettingGitRemote W_Remotes(m_cmdPath);
607 // W_Remotes.DoModal();
608 Refresh();
610 break;
611 case eCmd_CreateBranch:
613 CAppUtils::CreateBranchTag(false);
614 Refresh();
616 break;
617 case eCmd_CreateTag:
619 CAppUtils::CreateBranchTag(true);
620 Refresh();
622 break;
626 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
628 if (pMsg->message == WM_KEYDOWN)
630 switch (pMsg->wParam)
632 /* case VK_RETURN:
634 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
636 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
638 PostMessage(WM_COMMAND, IDOK);
640 return TRUE;
643 break;
644 */ case VK_F5:
646 Refresh();
648 break;
653 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
656 class CRefLeafListCompareFunc
658 public:
659 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
661 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
663 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
666 int Compare(LPARAM lParam1, LPARAM lParam2)
668 return Compare(
669 (CShadowTree*)m_pList->GetItemData(lParam1),
670 (CShadowTree*)m_pList->GetItemData(lParam2));
673 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
675 int result=CompareNoDesc(pLeft,pRight);
676 if(m_desc)
677 return -result;
678 return result;
681 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
683 switch(m_col)
685 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
686 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
687 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
688 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
690 return 0;
693 int m_col;
694 bool m_desc;
695 CListCtrl* m_pList;
701 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
703 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
704 *pResult = 0;
706 if(m_currSortCol == pNMLV->iSubItem)
707 m_currSortDesc = !m_currSortDesc;
708 else
710 m_currSortCol = pNMLV->iSubItem;
711 m_currSortDesc = false;
714 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
715 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
717 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
720 void CBrowseRefsDlg::OnDestroy()
722 m_pickedRef = GetSelectedRef(true);
724 CResizableStandAloneDialog::OnDestroy();
727 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
729 LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
730 *pResult = 0;
732 EndDialog(IDOK);
735 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)
737 CBrowseRefsDlg dlg(CString(),NULL);
739 dlg.m_initialRef = initialRef;
740 dlg.m_pickRef_Kind = pickRef_Kind;
742 if(dlg.DoModal() != IDOK)
743 return CString();
745 return dlg.m_pickedRef;
748 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
750 CString origRef;
751 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
752 CString resultRef = PickRef(false,origRef,pickRef_Kind);
753 if(resultRef.IsEmpty())
754 return false;
755 if(wcsncmp(resultRef,L"refs/",5)==0)
756 resultRef = resultRef.Mid(5);
757 // if(wcsncmp(resultRef,L"heads/",6)==0)
758 // resultRef = resultRef.Mid(6);
760 //Find closest match of choice in combobox
761 int ixFound = -1;
762 int matchLength = 0;
763 CString comboRefName;
764 for(int i = 0; i < pComboBox->GetCount(); ++i)
766 pComboBox->GetLBText(i, comboRefName);
767 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
769 matchLength = comboRefName.GetLength();
770 ixFound = i;
773 if(ixFound >= 0)
774 pComboBox->SetCurSel(ixFound);
775 else
776 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)
778 return true;