Pick Ref: Prepare ref picker to be able to leave out some ref types.
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob0f6dac1a3fd11540ce0f412d6524b9e4364317cb
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"
14 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
16 if (control == NULL)
17 return;
18 // set the sort arrow
19 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
20 HDITEM HeaderItem = {0};
21 HeaderItem.mask = HDI_FORMAT;
22 for (int i=0; i<pHeader->GetItemCount(); ++i)
24 pHeader->GetItem(i, &HeaderItem);
25 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
26 pHeader->SetItem(i, &HeaderItem);
28 if (nColumn >= 0)
30 pHeader->GetItem(nColumn, &HeaderItem);
31 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
32 pHeader->SetItem(nColumn, &HeaderItem);
36 // CBrowseRefsDlg dialog
38 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
40 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
41 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
42 m_cmdPath(cmdPath),
43 m_currSortCol(-1),
44 m_currSortDesc(false),
45 m_initialRef(L"HEAD"),
46 m_pickRef_Kind(gPickRef_All)
51 CBrowseRefsDlg::~CBrowseRefsDlg()
55 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
57 CDialog::DoDataExchange(pDX);
58 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
59 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
63 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
64 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
65 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
66 ON_WM_CONTEXTMENU()
67 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
68 ON_WM_DESTROY()
69 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
70 END_MESSAGE_MAP()
73 // CBrowseRefsDlg message handlers
75 void CBrowseRefsDlg::OnBnClickedOk()
77 OnOK();
80 BOOL CBrowseRefsDlg::OnInitDialog()
82 CResizableStandAloneDialog::OnInitDialog();
84 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
85 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
87 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
88 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
89 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
90 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
91 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
93 AddAnchor(IDOK,BOTTOM_RIGHT);
94 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
96 Refresh(m_initialRef);
99 m_ListRefLeafs.SetFocus();
100 return FALSE;
103 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
105 int posSlash=nameLeft.Find('/');
106 CString nameSub;
107 if(posSlash<0)
109 nameSub=nameLeft;
110 nameLeft.Empty();//Nothing left
112 else
114 nameSub=nameLeft.Left(posSlash);
115 nameLeft=nameLeft.Mid(posSlash+1);
117 if(nameSub.IsEmpty())
118 return NULL;
120 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
121 return NULL;
123 CShadowTree& nextNode=m_ShadowTree[nameSub];
124 nextNode.m_csRefName=nameSub;
125 nextNode.m_pParent=this;
126 return &nextNode;
129 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
131 if(IsLeaf())
133 if(m_csRefName.GetLength() > partialRefName.GetLength())
134 return NULL;
135 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
137 //Match of leaf name. Try match on total name.
138 CString totalRefName = GetRefName();
139 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
140 return this; //Also match. Found.
143 else
145 //Not a leaf. Search all nodes.
146 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
148 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
149 if(pSubtree != NULL)
150 return pSubtree; //Found
153 return NULL;//Not found
157 typedef std::map<CString,CString> MAP_STRING_STRING;
159 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf)
161 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
162 //List ctrl selection?
163 if(pos)
165 //A leaf is selected
166 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
167 m_ListRefLeafs.GetNextSelectedItem(pos));
168 return pTree->GetRefName();
170 else if(!onlyIfLeaf)
172 //Tree ctrl selection?
173 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
174 if(hTree!=NULL)
176 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
177 return pTree->GetRefName();
180 return CString();//None
183 void CBrowseRefsDlg::Refresh(CString selectRef)
185 // m_RefMap.clear();
186 // g_Git.GetMapHashToFriendName(m_RefMap);
188 if(!selectRef.IsEmpty())
190 if(selectRef == "HEAD")
192 selectRef.Empty();
193 g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);
194 selectRef.Trim(L"\r\n\t ");
197 else
199 selectRef = GetSelectedRef(false);
202 m_RefTreeCtrl.DeleteAllItems();
203 m_ListRefLeafs.DeleteAllItems();
204 m_TreeRoot.m_ShadowTree.clear();
205 m_TreeRoot.m_csRefName="refs";
206 // m_TreeRoot.m_csShowName="Refs";
207 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
208 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
210 CString allRefs;
211 g_Git.Run(L"git for-each-ref --format="
212 L"%(refname)%04"
213 L"%(objectname)%04"
214 L"%(authordate:relative)%04"
215 L"%(subject)%04"
216 L"%(authorname)%04"
217 L"%(authordate:iso8601)",
218 &allRefs,CP_UTF8);
220 int linePos=0;
221 CString singleRef;
223 MAP_STRING_STRING refMap;
225 //First sort on ref name
226 while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())
228 int valuePos=0;
229 CString refName=singleRef.Tokenize(L"\04",valuePos);
230 CString refRest=singleRef.Mid(valuePos);
232 //Use ref based on m_pickRef_Kind
233 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
234 continue; //Skip
235 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
236 continue; //Skip
237 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
238 continue; //Skip
240 refMap[refName] = refRest; //Use
245 //Populate ref tree
246 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
248 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
249 CString values=iterRefMap->second;
251 int valuePos=0;
252 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
253 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
254 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
255 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
256 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
260 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
261 //Probably not on a branch. Select root node.
262 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
266 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
268 if(!bExactMatch)
269 refName = GetFullRefName(refName);
270 if(wcsnicmp(refName,L"refs/",5)!=0)
271 return false; // Not a ref name
273 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
274 if(treeLeafHead.m_hTree != NULL)
276 //Not a leaf. Select tree node and return
277 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
278 return true;
281 if(treeLeafHead.m_pParent==NULL)
282 return false; //Weird... should not occur.
284 //This is the current head.
285 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
287 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
289 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
290 if(pCurrShadowTree == &treeLeafHead)
292 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
293 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
297 return true;
300 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
302 if(pTreePos==NULL)
304 if(wcsnicmp(refName,L"refs/",5)==0)
305 refName=refName.Mid(5);
306 pTreePos=&m_TreeRoot;
308 if(refName.IsEmpty())
309 return *pTreePos;//Found leaf
311 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
312 if(pNextTree==NULL)
314 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
315 ASSERT(!bCreateIfNotExist);
316 return *pTreePos;
319 if(!refName.IsEmpty())
321 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
322 //Leafs are for the list control.
323 if(pNextTree->m_hTree==NULL)
325 //New tree. Create node in control.
326 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
327 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
331 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
335 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
337 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
338 *pResult = 0;
340 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
343 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
345 m_ListRefLeafs.DeleteAllItems();
346 m_currSortCol = -1;
347 m_currSortDesc = false;
348 SetSortArrow(&m_ListRefLeafs,-1,false);
350 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
351 if(pTree==NULL)
353 ASSERT(FALSE);
354 return;
356 FillListCtrlForShadowTree(pTree,L"",true);
359 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
361 if(pTree->IsLeaf())
363 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
365 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
366 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
367 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
368 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
369 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
371 else
374 CString csThisName;
375 if(!isFirstLevel)
376 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
377 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
379 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
384 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)
386 CString csMessage;
387 CString csTitle;
389 UINT mbIcon=MB_ICONQUESTION;
390 csMessage=L"Are you sure you want to delete the ";
391 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
393 CString branchToDelete = completeRefName.Mid(11);
394 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);
395 csMessage += "branch:\r\n\r\n<b>";
396 csMessage += branchToDelete;
397 csMessage += "</b>";
399 //Check if branch is fully merged in HEAD
400 CString branchHash = g_Git.GetHash(completeRefName);
401 CString commonAncestor;
402 CString cmd;
403 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);
404 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
406 branchHash=branchHash.Left(40);
407 commonAncestor=commonAncestor.Left(40);
409 if(commonAncestor != branchHash)
411 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
412 mbIcon=MB_ICONWARNING;
415 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
417 CString tagToDelete = completeRefName.Mid(10);
418 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
419 csMessage += "tag:\r\n\r\n<b>";
420 csMessage += tagToDelete;
421 csMessage += "</b>";
424 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
429 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
431 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
433 CString branchToDelete = completeRefName.Mid(11);
434 CString cmd;
435 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
436 CString resultDummy;
437 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
439 CString errorMsg;
440 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
441 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
442 return false;
445 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
447 CString tagToDelete = completeRefName.Mid(10);
448 CString cmd;
449 cmd.Format(L"git.exe tag -d %s",tagToDelete);
450 CString resultDummy;
451 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
453 CString errorMsg;
454 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
455 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
456 return false;
459 return true;
462 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
464 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
465 if(pLeaf == NULL)
466 return CString();
467 return pLeaf->GetRefName();
471 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
473 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
474 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
477 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
479 CPoint clientPoint=point;
480 m_RefTreeCtrl.ScreenToClient(&clientPoint);
482 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
483 if(hTreeItem!=NULL)
484 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
486 ShowContextMenu(point,hTreeItem,VectorPShadowTree());
490 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
492 std::vector<CShadowTree*> selectedLeafs;
493 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
494 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
495 while(pos)
497 selectedLeafs.push_back(
498 (CShadowTree*)m_ListRefLeafs.GetItemData(
499 m_ListRefLeafs.GetNextSelectedItem(pos)));
502 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
505 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
507 CMenu popupMenu;
508 popupMenu.CreatePopupMenu();
510 if(selectedLeafs.size()==1)
512 popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");
513 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
514 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");
515 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
516 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");
518 // CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);
519 // if(pTree==NULL)
520 // return;
523 if(hTreePos!=NULL)
525 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
526 if(pTree->IsFrom(L"refs/remotes"))
528 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
529 if(!m_cmdPath.IsEmpty())
530 popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");
532 else if(pTree->IsFrom(L"refs/heads"))
533 popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");
534 else if(pTree->IsFrom(L"refs/tags"))
535 popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");
539 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
540 switch(cmd)
542 case eCmd_ViewLog:
544 CLogDlg dlg;
545 dlg.SetStartRef(selectedLeafs[0]->m_csRefHash);
546 dlg.DoModal();
548 break;
549 case eCmd_DeleteBranch:
551 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
552 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
553 Refresh();
555 break;
556 case eCmd_DeleteTag:
558 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
559 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
560 Refresh();
562 break;
563 case eCmd_AddRemote:
565 CAddRemoteDlg(this).DoModal();
566 Refresh();
568 break;
569 case eCmd_ManageRemotes:
571 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();
572 // CSettingGitRemote W_Remotes(m_cmdPath);
573 // W_Remotes.DoModal();
574 Refresh();
576 break;
577 case eCmd_CreateBranch:
579 CAppUtils::CreateBranchTag(false);
580 Refresh();
582 break;
583 case eCmd_CreateTag:
585 CAppUtils::CreateBranchTag(true);
586 Refresh();
588 break;
592 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
594 if (pMsg->message == WM_KEYDOWN)
596 switch (pMsg->wParam)
598 /* case VK_RETURN:
600 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
602 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
604 PostMessage(WM_COMMAND, IDOK);
606 return TRUE;
609 break;
610 */ case VK_F5:
612 Refresh();
614 break;
619 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
622 class CRefLeafListCompareFunc
624 public:
625 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
627 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
629 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
632 int Compare(LPARAM lParam1, LPARAM lParam2)
634 return Compare(
635 (CShadowTree*)m_pList->GetItemData(lParam1),
636 (CShadowTree*)m_pList->GetItemData(lParam2));
639 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
641 int result=CompareNoDesc(pLeft,pRight);
642 if(m_desc)
643 return -result;
644 return result;
647 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
649 switch(m_col)
651 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
652 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
653 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
654 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
656 return 0;
659 int m_col;
660 bool m_desc;
661 CListCtrl* m_pList;
667 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
669 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
670 *pResult = 0;
672 if(m_currSortCol == pNMLV->iSubItem)
673 m_currSortDesc = !m_currSortDesc;
674 else
676 m_currSortCol = pNMLV->iSubItem;
677 m_currSortDesc = false;
680 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
681 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
683 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
686 void CBrowseRefsDlg::OnDestroy()
688 m_pickedRef = GetSelectedRef(true);
690 CResizableStandAloneDialog::OnDestroy();
693 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
695 LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
696 *pResult = 0;
698 EndDialog(IDOK);
701 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)
703 CBrowseRefsDlg dlg(CString(),NULL);
705 dlg.m_initialRef = initialRef;
706 dlg.m_pickRef_Kind = pickRef_Kind;
708 if(dlg.DoModal() != IDOK)
709 return CString();
711 return dlg.m_pickedRef;