OSDN Git Service

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