OSDN Git Service

BrowseRefs: Initial branch selection also possible with partial ref-name
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / BrowseRefsDlg.cpp
1 // BrowseRefsDlg.cpp : implementation file\r
2 //\r
3 \r
4 #include "stdafx.h"\r
5 #include "TortoiseProc.h"\r
6 #include "BrowseRefsDlg.h"\r
7 #include "LogDlg.h"\r
8 #include "AddRemoteDlg.h"\r
9 #include "AppUtils.h"\r
10 #include "Settings\SettingGitRemote.h"\r
11 #include "SinglePropSheetDlg.h"\r
12 #include "MessageBox.h"\r
13 \r
14 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)\r
15 {\r
16         if (control == NULL)\r
17                 return;\r
18         // set the sort arrow\r
19         CHeaderCtrl * pHeader = control->GetHeaderCtrl();\r
20         HDITEM HeaderItem = {0};\r
21         HeaderItem.mask = HDI_FORMAT;\r
22         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
23         {\r
24                 pHeader->GetItem(i, &HeaderItem);\r
25                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
26                 pHeader->SetItem(i, &HeaderItem);\r
27         }\r
28         if (nColumn >= 0)\r
29         {\r
30                 pHeader->GetItem(nColumn, &HeaderItem);\r
31                 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
32                 pHeader->SetItem(nColumn, &HeaderItem);\r
33         }\r
34 }\r
35 \r
36 // CBrowseRefsDlg dialog\r
37 \r
38 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)\r
39 \r
40 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)\r
41 :       CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),\r
42         m_cmdPath(cmdPath),\r
43         m_currSortCol(-1),\r
44         m_currSortDesc(false),\r
45         m_initialRef(L"HEAD")\r
46 {\r
47 \r
48 }\r
49 \r
50 CBrowseRefsDlg::~CBrowseRefsDlg()\r
51 {\r
52 }\r
53 \r
54 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)\r
55 {\r
56         CDialog::DoDataExchange(pDX);\r
57         DDX_Control(pDX, IDC_TREE_REF,                  m_RefTreeCtrl);\r
58         DDX_Control(pDX, IDC_LIST_REF_LEAFS,    m_ListRefLeafs);\r
59 }\r
60 \r
61 \r
62 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)\r
63         ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)\r
64         ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)\r
65         ON_WM_CONTEXTMENU()\r
66         ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)\r
67         ON_WM_DESTROY()\r
68         ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)\r
69 END_MESSAGE_MAP()\r
70 \r
71 \r
72 // CBrowseRefsDlg message handlers\r
73 \r
74 void CBrowseRefsDlg::OnBnClickedOk()\r
75 {\r
76         OnOK();\r
77 }\r
78 \r
79 BOOL CBrowseRefsDlg::OnInitDialog()\r
80 {\r
81         CResizableStandAloneDialog::OnInitDialog();\r
82 \r
83         AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);\r
84         AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);\r
85 \r
86         m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);\r
87         m_ListRefLeafs.InsertColumn(eCol_Name,  L"Name",0,150);\r
88         m_ListRefLeafs.InsertColumn(eCol_Date,  L"Date Last Commit",0,100);\r
89         m_ListRefLeafs.InsertColumn(eCol_Msg,   L"Last Commit",0,300);\r
90         m_ListRefLeafs.InsertColumn(eCol_Hash,  L"Hash",0,80);\r
91 \r
92         AddAnchor(IDOK,BOTTOM_RIGHT);\r
93         AddAnchor(IDCANCEL,BOTTOM_RIGHT);\r
94 \r
95         Refresh(m_initialRef);\r
96 \r
97 \r
98         m_ListRefLeafs.SetFocus();\r
99         return FALSE;\r
100 }\r
101 \r
102 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)\r
103 {\r
104         int posSlash=nameLeft.Find('/');\r
105         CString nameSub;\r
106         if(posSlash<0)\r
107         {\r
108                 nameSub=nameLeft;\r
109                 nameLeft.Empty();//Nothing left\r
110         }\r
111         else\r
112         {\r
113                 nameSub=nameLeft.Left(posSlash);\r
114                 nameLeft=nameLeft.Mid(posSlash+1);\r
115         }\r
116         if(nameSub.IsEmpty())\r
117                 return NULL;\r
118 \r
119         if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())\r
120                 return NULL;\r
121 \r
122         CShadowTree& nextNode=m_ShadowTree[nameSub];\r
123         nextNode.m_csRefName=nameSub;\r
124         nextNode.m_pParent=this;\r
125         return &nextNode;\r
126 }\r
127 \r
128 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)\r
129 {\r
130         if(IsLeaf())\r
131         {\r
132                 if(m_csRefName.GetLength() > partialRefName.GetLength())\r
133                         return NULL;\r
134                 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)\r
135                 {\r
136                         //Match of leaf name. Try match on total name.\r
137                         CString totalRefName = GetRefName();\r
138                         if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)\r
139                                 return this; //Also match. Found.\r
140                 }\r
141         }\r
142         else\r
143         {\r
144                 //Not a leaf. Search all nodes.\r
145                 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)\r
146                 {\r
147                         CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);\r
148                         if(pSubtree != NULL)\r
149                                 return pSubtree; //Found\r
150                 }\r
151         }\r
152         return NULL;//Not found\r
153 }\r
154 \r
155 \r
156 typedef std::map<CString,CString> MAP_STRING_STRING;\r
157 \r
158 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf)\r
159 {\r
160         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
161         //List ctrl selection?\r
162         if(pos)\r
163         {\r
164                 //A leaf is selected\r
165                 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(\r
166                                 m_ListRefLeafs.GetNextSelectedItem(pos));\r
167                 return pTree->GetRefName();\r
168         }\r
169         else if(!onlyIfLeaf)\r
170         {\r
171                 //Tree ctrl selection?\r
172                 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();\r
173                 if(hTree!=NULL)\r
174                 {\r
175                         CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);\r
176                         return pTree->GetRefName();\r
177                 }\r
178         }\r
179         return CString();//None\r
180 }\r
181 \r
182 void CBrowseRefsDlg::Refresh(CString selectRef)\r
183 {\r
184 //      m_RefMap.clear();\r
185 //      g_Git.GetMapHashToFriendName(m_RefMap);\r
186                 \r
187         if(!selectRef.IsEmpty())\r
188         {\r
189                 if(selectRef == "HEAD")\r
190                 {\r
191                         selectRef.Empty();\r
192                         g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);\r
193                         selectRef.Trim(L"\r\n\t ");\r
194                 }\r
195         }\r
196         else\r
197         {\r
198                 selectRef = GetSelectedRef(false);\r
199         }\r
200 \r
201         m_RefTreeCtrl.DeleteAllItems();\r
202         m_ListRefLeafs.DeleteAllItems();\r
203         m_TreeRoot.m_ShadowTree.clear();\r
204         m_TreeRoot.m_csRefName="refs";\r
205 //      m_TreeRoot.m_csShowName="Refs";\r
206         m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);\r
207         m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);\r
208 \r
209         CString allRefs;\r
210         g_Git.Run(L"git for-each-ref --format="\r
211                           L"%(refname)%04"\r
212                           L"%(objectname)%04"\r
213                           L"%(authordate:relative)%04"\r
214                           L"%(subject)%04"\r
215                           L"%(authorname)%04"\r
216                           L"%(authordate:iso8601)",\r
217                           &allRefs,CP_UTF8);\r
218 \r
219         int linePos=0;\r
220         CString singleRef;\r
221 \r
222         MAP_STRING_STRING refMap;\r
223 \r
224         //First sort on ref name\r
225         while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())\r
226         {\r
227                 int valuePos=0;\r
228                 CString refName=singleRef.Tokenize(L"\04",valuePos);\r
229                 CString refRest=singleRef.Mid(valuePos);\r
230                 refMap[refName]=refRest;\r
231         }\r
232 \r
233 \r
234 \r
235 //      for(MAP_HASH_NAME::iterator iterRef=m_RefMap.begin();iterRef!=m_RefMap.end();++iterRef)\r
236 //              for(STRING_VECTOR::iterator iterRefName=iterRef->second.begin();iterRefName!=iterRef->second.end();++iterRefName)\r
237 //                      refName[*iterRefName]=iterRef->first;\r
238 \r
239         //Populate ref tree\r
240         for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)\r
241         {\r
242                 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);\r
243                 CString values=iterRefMap->second;\r
244 \r
245                 int valuePos=0;\r
246                 treeLeaf.m_csRefHash=           values.Tokenize(L"\04",valuePos);\r
247                 treeLeaf.m_csDate=                      values.Tokenize(L"\04",valuePos);\r
248                 treeLeaf.m_csSubject=           values.Tokenize(L"\04",valuePos);\r
249                 treeLeaf.m_csAuthor=            values.Tokenize(L"\04",valuePos);\r
250                 treeLeaf.m_csDate_Iso8601=      values.Tokenize(L"\04",valuePos);\r
251         }\r
252 \r
253 \r
254         if(selectRef.IsEmpty() || !SelectRef(selectRef, false))\r
255                 //Probably not on a branch. Select root node.\r
256                 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);\r
257 \r
258 }\r
259 \r
260 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)\r
261 {\r
262         if(!bExactMatch)\r
263                 refName = GetFullRefName(refName);\r
264         if(wcsnicmp(refName,L"refs/",5)!=0)\r
265                 return false; // Not a ref name\r
266 \r
267         CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);\r
268         if(treeLeafHead.m_hTree != NULL)\r
269         {\r
270                 //Not a leaf. Select tree node and return\r
271                 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);\r
272                 return true;\r
273         }\r
274 \r
275         if(treeLeafHead.m_pParent==NULL)\r
276                 return false; //Weird... should not occur.\r
277 \r
278         //This is the current head.\r
279         m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);\r
280 \r
281         for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)\r
282         {\r
283                 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);\r
284                 if(pCurrShadowTree == &treeLeafHead)\r
285                 {\r
286                         m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);\r
287                         m_ListRefLeafs.EnsureVisible(indexPos,FALSE);\r
288                 }\r
289         }\r
290 \r
291         return true;\r
292 }\r
293 \r
294 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)\r
295 {\r
296         if(pTreePos==NULL)\r
297         {\r
298                 if(wcsnicmp(refName,L"refs/",5)==0)\r
299                         refName=refName.Mid(5);\r
300                 pTreePos=&m_TreeRoot;\r
301         }\r
302         if(refName.IsEmpty())\r
303                 return *pTreePos;//Found leaf\r
304 \r
305         CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);\r
306         if(pNextTree==NULL)\r
307         {\r
308                 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.\r
309                 ASSERT(!bCreateIfNotExist);\r
310                 return *pTreePos;\r
311         }\r
312 \r
313         if(!refName.IsEmpty())\r
314         {\r
315                 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.\r
316                 //Leafs are for the list control.\r
317                 if(pNextTree->m_hTree==NULL)\r
318                 {\r
319                         //New tree. Create node in control.\r
320                         pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);\r
321                         m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);\r
322                 }\r
323         }\r
324 \r
325         return GetTreeNode(refName, pNextTree, bCreateIfNotExist);\r
326 }\r
327 \r
328 \r
329 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)\r
330 {\r
331         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
332         *pResult = 0;\r
333 \r
334         FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);\r
335 }\r
336 \r
337 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)\r
338 {\r
339         m_ListRefLeafs.DeleteAllItems();\r
340         m_currSortCol = -1;\r
341         m_currSortDesc = false;\r
342         SetSortArrow(&m_ListRefLeafs,-1,false);\r
343 \r
344         CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));\r
345         if(pTree==NULL)\r
346         {\r
347                 ASSERT(FALSE);\r
348                 return;\r
349         }\r
350         FillListCtrlForShadowTree(pTree,L"",true);\r
351 }\r
352 \r
353 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)\r
354 {\r
355         if(pTree->IsLeaf())\r
356         {\r
357                 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");\r
358 \r
359                 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);\r
360                 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);\r
361                 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);\r
362                 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg,  pTree->m_csSubject);\r
363                 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);\r
364         }\r
365         else\r
366         {\r
367 \r
368                 CString csThisName;\r
369                 if(!isFirstLevel)\r
370                         csThisName=refNamePrefix+pTree->m_csRefName+L"/";\r
371                 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)\r
372                 {\r
373                         FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);\r
374                 }\r
375         }\r
376 }\r
377 \r
378 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)\r
379 {\r
380         CString csMessage;\r
381         CString csTitle;\r
382 \r
383         UINT mbIcon=MB_ICONQUESTION;\r
384         csMessage=L"Are you sure you want to delete the ";\r
385         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
386         {\r
387                 CString branchToDelete = completeRefName.Mid(11);\r
388                 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);\r
389                 csMessage += "branch:\r\n\r\n<b>";\r
390                 csMessage += branchToDelete;\r
391                 csMessage += "</b>";\r
392 \r
393                 //Check if branch is fully merged in HEAD\r
394                 CString branchHash = g_Git.GetHash(completeRefName);\r
395                 CString commonAncestor;\r
396                 CString cmd;\r
397                 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
398                 g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
399 \r
400                 branchHash=branchHash.Left(40);\r
401                 commonAncestor=commonAncestor.Left(40);\r
402                 \r
403                 if(commonAncestor != branchHash)\r
404                 {\r
405                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
406                         mbIcon=MB_ICONWARNING;\r
407                 }\r
408         }\r
409         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
410         {\r
411                 CString tagToDelete = completeRefName.Mid(10);\r
412                 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
413                 csMessage += "tag:\r\n\r\n<b>";\r
414                 csMessage += tagToDelete;\r
415                 csMessage += "</b>";\r
416         }\r
417 \r
418         return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
419 \r
420 }\r
421 \r
422 \r
423 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
424 {\r
425         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
426         {\r
427                 CString branchToDelete = completeRefName.Mid(11);\r
428                 CString cmd;\r
429                 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
430                 CString resultDummy;\r
431                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
432                 {\r
433                         CString errorMsg;\r
434                         errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
435                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
436                         return false;\r
437                 }\r
438         }\r
439         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
440         {\r
441                 CString tagToDelete = completeRefName.Mid(10);\r
442                 CString cmd;\r
443                 cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
444                 CString resultDummy;\r
445                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
446                 {\r
447                         CString errorMsg;\r
448                         errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
449                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
450                         return false;\r
451                 }\r
452         }\r
453         return true;\r
454 }\r
455 \r
456 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)\r
457 {\r
458         CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);\r
459         if(pLeaf == NULL)\r
460                 return CString();\r
461         return pLeaf->GetRefName();\r
462 }\r
463 \r
464 \r
465 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
466 {\r
467         if(pWndFrom==&m_RefTreeCtrl)       OnContextMenu_RefTreeCtrl(point);\r
468         else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
469 }\r
470 \r
471 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
472 {\r
473         CPoint clientPoint=point;\r
474         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
475 \r
476         HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
477         if(hTreeItem!=NULL)\r
478                 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
479 \r
480         ShowContextMenu(point,hTreeItem,VectorPShadowTree());\r
481 }\r
482 \r
483 \r
484 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
485 {\r
486         std::vector<CShadowTree*> selectedLeafs;\r
487         selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());\r
488         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
489         while(pos)\r
490         {\r
491                 selectedLeafs.push_back(\r
492                         (CShadowTree*)m_ListRefLeafs.GetItemData(\r
493                                 m_ListRefLeafs.GetNextSelectedItem(pos)));\r
494         }\r
495 \r
496         ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);\r
497 }\r
498 \r
499 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)\r
500 {\r
501         CMenu popupMenu;\r
502         popupMenu.CreatePopupMenu();\r
503 \r
504         if(selectedLeafs.size()==1)\r
505         {\r
506                 popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");\r
507                 if(selectedLeafs[0]->IsFrom(L"refs/heads"))\r
508                         popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");\r
509                 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))\r
510                         popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");\r
511 \r
512 //              CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
513 //              if(pTree==NULL)\r
514 //                      return;\r
515         }\r
516 \r
517         if(hTreePos!=NULL)\r
518         {\r
519                 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);\r
520                 if(pTree->IsFrom(L"refs/remotes"))\r
521                 {\r
522 //                      popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
523                         if(!m_cmdPath.IsEmpty())\r
524                                 popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");\r
525                 }\r
526                 else if(pTree->IsFrom(L"refs/heads"))\r
527                         popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");\r
528                 else if(pTree->IsFrom(L"refs/tags"))\r
529                         popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");\r
530         }\r
531 \r
532 \r
533         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
534         switch(cmd)\r
535         {\r
536         case eCmd_ViewLog:\r
537                 {\r
538                         CLogDlg dlg;\r
539                         dlg.SetStartRef(selectedLeafs[0]->m_csRefHash);\r
540                         dlg.DoModal();\r
541                 }\r
542                 break;\r
543         case eCmd_DeleteBranch:\r
544                 {\r
545                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
546                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
547                         Refresh();\r
548                 }\r
549                 break;\r
550         case eCmd_DeleteTag:\r
551                 {\r
552                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
553                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
554                         Refresh();\r
555                 }\r
556                 break;\r
557         case eCmd_AddRemote:\r
558                 {\r
559                         CAddRemoteDlg(this).DoModal();\r
560                         Refresh();\r
561                 }\r
562                 break;\r
563         case eCmd_ManageRemotes:\r
564                 {\r
565                         CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();\r
566 //                      CSettingGitRemote W_Remotes(m_cmdPath);\r
567 //                      W_Remotes.DoModal();\r
568                         Refresh();\r
569                 }\r
570                 break;\r
571         case eCmd_CreateBranch:\r
572                 {\r
573                         CAppUtils::CreateBranchTag(false);\r
574                         Refresh();\r
575                 }\r
576                 break;\r
577         case eCmd_CreateTag:\r
578                 {\r
579                         CAppUtils::CreateBranchTag(true);\r
580                         Refresh();\r
581                 }\r
582                 break;\r
583         }\r
584 }\r
585 \r
586 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
587 {\r
588         if (pMsg->message == WM_KEYDOWN)\r
589         {\r
590                 switch (pMsg->wParam)\r
591                 {\r
592 /*              case VK_RETURN:\r
593                         {\r
594                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
595                                 {\r
596                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
597                                         {\r
598                                                 PostMessage(WM_COMMAND, IDOK);\r
599                                         }\r
600                                         return TRUE;\r
601                                 }\r
602                         }\r
603                         break;\r
604 */              case VK_F5:\r
605                         {\r
606                                 Refresh();\r
607                         }\r
608                         break;\r
609                 }\r
610         }\r
611 \r
612 \r
613         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
614 }\r
615 \r
616 class CRefLeafListCompareFunc\r
617 {\r
618 public:\r
619         CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}\r
620 \r
621         static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)\r
622         {\r
623                 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);\r
624         }\r
625 \r
626         int Compare(LPARAM lParam1, LPARAM lParam2)\r
627         {\r
628                 return Compare(\r
629                         (CShadowTree*)m_pList->GetItemData(lParam1), \r
630                         (CShadowTree*)m_pList->GetItemData(lParam2));\r
631         }\r
632 \r
633         int Compare(CShadowTree* pLeft, CShadowTree* pRight)\r
634         {\r
635                 int result=CompareNoDesc(pLeft,pRight);\r
636                 if(m_desc)\r
637                         return -result;\r
638                 return result;\r
639         }\r
640 \r
641         int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)\r
642         {\r
643                 switch(m_col)\r
644                 {\r
645                 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());\r
646                 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);\r
647                 case CBrowseRefsDlg::eCol_Msg:  return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);\r
648                 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);\r
649                 }\r
650                 return 0;\r
651         }\r
652 \r
653         int m_col;\r
654         bool m_desc;\r
655         CListCtrl* m_pList;\r
656 \r
657 \r
658 };\r
659 \r
660 \r
661 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
662 {\r
663         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
664         *pResult = 0;\r
665 \r
666         if(m_currSortCol == pNMLV->iSubItem)\r
667                 m_currSortDesc = !m_currSortDesc;\r
668         else\r
669         {\r
670                 m_currSortCol  = pNMLV->iSubItem;\r
671                 m_currSortDesc = false;\r
672         }\r
673 \r
674         CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);\r
675         m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);\r
676 \r
677         SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);\r
678 }\r
679 \r
680 void CBrowseRefsDlg::OnDestroy()\r
681 {\r
682         m_pickedRef = GetSelectedRef(true);\r
683 \r
684         CResizableStandAloneDialog::OnDestroy();\r
685 }\r
686 \r
687 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
688 {\r
689         LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);\r
690         *pResult = 0;\r
691 \r
692         EndDialog(IDOK);\r
693 }\r
694 \r
695 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef)\r
696 {\r
697         CBrowseRefsDlg dlg(CString(),NULL);\r
698         \r
699         dlg.m_initialRef = initialRef;\r
700 \r
701         if(dlg.DoModal() != IDOK)\r
702                 return CString();\r
703 \r
704         return dlg.m_pickedRef;\r
705 }\r
706 \r