BrowseRefs: Save / Restore window size
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob04f85242921164638ee251aa1a03d6484472baf5
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"
15 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
17 if (control == NULL)
18 return;
19 // set the sort arrow
20 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
21 HDITEM HeaderItem = {0};
22 HeaderItem.mask = HDI_FORMAT;
23 for (int i=0; i<pHeader->GetItemCount(); ++i)
25 pHeader->GetItem(i, &HeaderItem);
26 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
27 pHeader->SetItem(i, &HeaderItem);
29 if (nColumn >= 0)
31 pHeader->GetItem(nColumn, &HeaderItem);
32 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
33 pHeader->SetItem(nColumn, &HeaderItem);
37 // CBrowseRefsDlg dialog
39 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
41 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
42 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
43 m_cmdPath(cmdPath),
44 m_currSortCol(-1),
45 m_currSortDesc(false),
46 m_initialRef(L"HEAD"),
47 m_pickRef_Kind(gPickRef_All)
52 CBrowseRefsDlg::~CBrowseRefsDlg()
56 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
58 CDialog::DoDataExchange(pDX);
59 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
60 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
64 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
65 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
66 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
67 ON_WM_CONTEXTMENU()
68 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
69 ON_WM_DESTROY()
70 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
71 END_MESSAGE_MAP()
74 // CBrowseRefsDlg message handlers
76 void CBrowseRefsDlg::OnBnClickedOk()
78 OnOK();
81 BOOL CBrowseRefsDlg::OnInitDialog()
83 CResizableStandAloneDialog::OnInitDialog();
85 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
86 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
88 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
89 m_ListRefLeafs.InsertColumn(eCol_Name, L"Name",0,150);
90 m_ListRefLeafs.InsertColumn(eCol_Date, L"Date Last Commit",0,100);
91 m_ListRefLeafs.InsertColumn(eCol_Msg, L"Last Commit",0,300);
92 m_ListRefLeafs.InsertColumn(eCol_Hash, L"Hash",0,80);
94 AddAnchor(IDOK,BOTTOM_RIGHT);
95 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
97 Refresh(m_initialRef);
99 EnableSaveRestore(L"BrowseRefs");
102 m_ListRefLeafs.SetFocus();
103 return FALSE;
106 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
108 int posSlash=nameLeft.Find('/');
109 CString nameSub;
110 if(posSlash<0)
112 nameSub=nameLeft;
113 nameLeft.Empty();//Nothing left
115 else
117 nameSub=nameLeft.Left(posSlash);
118 nameLeft=nameLeft.Mid(posSlash+1);
120 if(nameSub.IsEmpty())
121 return NULL;
123 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
124 return NULL;
126 CShadowTree& nextNode=m_ShadowTree[nameSub];
127 nextNode.m_csRefName=nameSub;
128 nextNode.m_pParent=this;
129 return &nextNode;
132 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
134 if(IsLeaf())
136 if(m_csRefName.GetLength() > partialRefName.GetLength())
137 return NULL;
138 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
140 //Match of leaf name. Try match on total name.
141 CString totalRefName = GetRefName();
142 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
143 return this; //Also match. Found.
146 else
148 //Not a leaf. Search all nodes.
149 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
151 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
152 if(pSubtree != NULL)
153 return pSubtree; //Found
156 return NULL;//Not found
160 typedef std::map<CString,CString> MAP_STRING_STRING;
162 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf)
164 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
165 //List ctrl selection?
166 if(pos)
168 //A leaf is selected
169 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
170 m_ListRefLeafs.GetNextSelectedItem(pos));
171 return pTree->GetRefName();
173 else if(!onlyIfLeaf)
175 //Tree ctrl selection?
176 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
177 if(hTree!=NULL)
179 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
180 return pTree->GetRefName();
183 return CString();//None
186 void CBrowseRefsDlg::Refresh(CString selectRef)
188 // m_RefMap.clear();
189 // g_Git.GetMapHashToFriendName(m_RefMap);
191 if(!selectRef.IsEmpty())
193 if(selectRef == "HEAD")
195 selectRef.Empty();
196 g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);
197 selectRef.Trim(L"\r\n\t ");
200 else
202 selectRef = GetSelectedRef(false);
205 m_RefTreeCtrl.DeleteAllItems();
206 m_ListRefLeafs.DeleteAllItems();
207 m_TreeRoot.m_ShadowTree.clear();
208 m_TreeRoot.m_csRefName="refs";
209 // m_TreeRoot.m_csShowName="Refs";
210 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
211 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
213 CString allRefs;
214 g_Git.Run(L"git for-each-ref --format="
215 L"%(refname)%04"
216 L"%(objectname)%04"
217 L"%(authordate:relative)%04"
218 L"%(subject)%04"
219 L"%(authorname)%04"
220 L"%(authordate:iso8601)",
221 &allRefs,CP_UTF8);
223 int linePos=0;
224 CString singleRef;
226 MAP_STRING_STRING refMap;
228 //First sort on ref name
229 while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())
231 int valuePos=0;
232 CString refName=singleRef.Tokenize(L"\04",valuePos);
233 CString refRest=singleRef.Mid(valuePos);
235 //Use ref based on m_pickRef_Kind
236 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
237 continue; //Skip
238 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
239 continue; //Skip
240 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
241 continue; //Skip
243 refMap[refName] = refRest; //Use
248 //Populate ref tree
249 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
251 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
252 CString values=iterRefMap->second;
254 int valuePos=0;
255 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
256 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
257 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
258 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
259 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
263 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
264 //Probably not on a branch. Select root node.
265 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
269 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
271 if(!bExactMatch)
272 refName = GetFullRefName(refName);
273 if(wcsnicmp(refName,L"refs/",5)!=0)
274 return false; // Not a ref name
276 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
277 if(treeLeafHead.m_hTree != NULL)
279 //Not a leaf. Select tree node and return
280 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
281 return true;
284 if(treeLeafHead.m_pParent==NULL)
285 return false; //Weird... should not occur.
287 //This is the current head.
288 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
290 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
292 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
293 if(pCurrShadowTree == &treeLeafHead)
295 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
296 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
300 return true;
303 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
305 if(pTreePos==NULL)
307 if(wcsnicmp(refName,L"refs/",5)==0)
308 refName=refName.Mid(5);
309 pTreePos=&m_TreeRoot;
311 if(refName.IsEmpty())
312 return *pTreePos;//Found leaf
314 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
315 if(pNextTree==NULL)
317 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
318 ASSERT(!bCreateIfNotExist);
319 return *pTreePos;
322 if(!refName.IsEmpty())
324 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
325 //Leafs are for the list control.
326 if(pNextTree->m_hTree==NULL)
328 //New tree. Create node in control.
329 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
330 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
334 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
338 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
340 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
341 *pResult = 0;
343 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
346 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
348 m_ListRefLeafs.DeleteAllItems();
349 m_currSortCol = -1;
350 m_currSortDesc = false;
351 SetSortArrow(&m_ListRefLeafs,-1,false);
353 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
354 if(pTree==NULL)
356 ASSERT(FALSE);
357 return;
359 FillListCtrlForShadowTree(pTree,L"",true);
362 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
364 if(pTree->IsLeaf())
366 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
368 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
369 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);
370 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
371 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
372 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
374 else
377 CString csThisName;
378 if(!isFirstLevel)
379 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
380 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
382 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
387 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)
389 CString csMessage;
390 CString csTitle;
392 UINT mbIcon=MB_ICONQUESTION;
393 csMessage=L"Are you sure you want to delete the ";
394 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
396 CString branchToDelete = completeRefName.Mid(11);
397 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);
398 csMessage += "branch:\r\n\r\n<b>";
399 csMessage += branchToDelete;
400 csMessage += "</b>";
402 //Check if branch is fully merged in HEAD
403 CString branchHash = g_Git.GetHash(completeRefName);
404 CString commonAncestor;
405 CString cmd;
406 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);
407 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
409 branchHash=branchHash.Left(40);
410 commonAncestor=commonAncestor.Left(40);
412 if(commonAncestor != branchHash)
414 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
415 mbIcon=MB_ICONWARNING;
418 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
420 CString tagToDelete = completeRefName.Mid(10);
421 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
422 csMessage += "tag:\r\n\r\n<b>";
423 csMessage += tagToDelete;
424 csMessage += "</b>";
427 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
432 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
434 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
436 CString branchToDelete = completeRefName.Mid(11);
437 CString cmd;
438 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
439 CString resultDummy;
440 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
442 CString errorMsg;
443 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
444 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
445 return false;
448 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
450 CString tagToDelete = completeRefName.Mid(10);
451 CString cmd;
452 cmd.Format(L"git.exe tag -d %s",tagToDelete);
453 CString resultDummy;
454 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
456 CString errorMsg;
457 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
458 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
459 return false;
462 return true;
465 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
467 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
468 if(pLeaf == NULL)
469 return CString();
470 return pLeaf->GetRefName();
474 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
476 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
477 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
480 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
482 CPoint clientPoint=point;
483 m_RefTreeCtrl.ScreenToClient(&clientPoint);
485 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
486 if(hTreeItem!=NULL)
487 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
489 ShowContextMenu(point,hTreeItem,VectorPShadowTree());
493 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
495 std::vector<CShadowTree*> selectedLeafs;
496 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
497 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
498 while(pos)
500 selectedLeafs.push_back(
501 (CShadowTree*)m_ListRefLeafs.GetItemData(
502 m_ListRefLeafs.GetNextSelectedItem(pos)));
505 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
508 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
510 CMenu popupMenu;
511 popupMenu.CreatePopupMenu();
513 if(selectedLeafs.size()==1)
515 bool bShowReflogOption = false;
516 popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");
517 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
519 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");
520 bShowReflogOption = true;
522 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
524 bShowReflogOption = true;
526 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
528 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");
531 if(bShowReflogOption)
532 popupMenu.AppendMenu(MF_STRING, eCmd_ShowReflog, L"Show Reflog");
537 // CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);
538 // if(pTree==NULL)
539 // return;
542 if(hTreePos!=NULL)
544 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
545 if(pTree->IsFrom(L"refs/remotes"))
547 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
548 if(!m_cmdPath.IsEmpty())
549 popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");
551 else if(pTree->IsFrom(L"refs/heads"))
552 popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");
553 else if(pTree->IsFrom(L"refs/tags"))
554 popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");
558 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
559 switch(cmd)
561 case eCmd_ViewLog:
563 CLogDlg dlg;
564 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
565 dlg.DoModal();
567 break;
568 case eCmd_DeleteBranch:
570 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
571 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
572 Refresh();
574 break;
575 case eCmd_DeleteTag:
577 if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))
578 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);
579 Refresh();
581 break;
582 case eCmd_ShowReflog:
584 CRefLogDlg refLogDlg(this);
585 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
586 refLogDlg.DoModal();
588 break;
589 case eCmd_AddRemote:
591 CAddRemoteDlg(this).DoModal();
592 Refresh();
594 break;
595 case eCmd_ManageRemotes:
597 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();
598 // CSettingGitRemote W_Remotes(m_cmdPath);
599 // W_Remotes.DoModal();
600 Refresh();
602 break;
603 case eCmd_CreateBranch:
605 CAppUtils::CreateBranchTag(false);
606 Refresh();
608 break;
609 case eCmd_CreateTag:
611 CAppUtils::CreateBranchTag(true);
612 Refresh();
614 break;
618 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
620 if (pMsg->message == WM_KEYDOWN)
622 switch (pMsg->wParam)
624 /* case VK_RETURN:
626 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
628 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
630 PostMessage(WM_COMMAND, IDOK);
632 return TRUE;
635 break;
636 */ case VK_F5:
638 Refresh();
640 break;
645 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
648 class CRefLeafListCompareFunc
650 public:
651 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
653 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
655 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
658 int Compare(LPARAM lParam1, LPARAM lParam2)
660 return Compare(
661 (CShadowTree*)m_pList->GetItemData(lParam1),
662 (CShadowTree*)m_pList->GetItemData(lParam2));
665 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
667 int result=CompareNoDesc(pLeft,pRight);
668 if(m_desc)
669 return -result;
670 return result;
673 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
675 switch(m_col)
677 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
678 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
679 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
680 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
682 return 0;
685 int m_col;
686 bool m_desc;
687 CListCtrl* m_pList;
693 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
695 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
696 *pResult = 0;
698 if(m_currSortCol == pNMLV->iSubItem)
699 m_currSortDesc = !m_currSortDesc;
700 else
702 m_currSortCol = pNMLV->iSubItem;
703 m_currSortDesc = false;
706 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
707 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
709 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
712 void CBrowseRefsDlg::OnDestroy()
714 m_pickedRef = GetSelectedRef(true);
716 CResizableStandAloneDialog::OnDestroy();
719 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
721 LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
722 *pResult = 0;
724 EndDialog(IDOK);
727 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)
729 CBrowseRefsDlg dlg(CString(),NULL);
731 dlg.m_initialRef = initialRef;
732 dlg.m_pickRef_Kind = pickRef_Kind;
734 if(dlg.DoModal() != IDOK)
735 return CString();
737 return dlg.m_pickedRef;
740 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
742 CString origRef;
743 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
744 CString resultRef = PickRef(false,origRef,pickRef_Kind);
745 if(resultRef.IsEmpty())
746 return false;
747 if(wcsncmp(resultRef,L"refs/",5)==0)
748 resultRef = resultRef.Mid(5);
749 // if(wcsncmp(resultRef,L"heads/",6)==0)
750 // resultRef = resultRef.Mid(6);
752 //Find closest match of choice in combobox
753 int ixFound = -1;
754 int matchLength = 0;
755 CString comboRefName;
756 for(int i = 0; i < pComboBox->GetCount(); ++i)
758 pComboBox->GetLBText(i, comboRefName);
759 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
761 matchLength = comboRefName.GetLength();
762 ixFound = i;
765 if(ixFound >= 0)
766 pComboBox->SetCurSel(ixFound);
767 else
768 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)
770 return true;