OSDN Git Service

Pick Ref: Implemented ref picking for push dialog
[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
714 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)\r
715 {\r
716         CString origRef;\r
717         pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);\r
718         CString resultRef = PickRef(false,origRef,pickRef_Kind);\r
719         if(resultRef.IsEmpty())\r
720                 return false;\r
721         if(wcsncmp(resultRef,L"refs/",5)==0)\r
722                 resultRef = resultRef.Mid(5);\r
723 //      if(wcsncmp(resultRef,L"heads/",6)==0)\r
724 //              resultRef = resultRef.Mid(6);\r
725 \r
726         //Find closest match of choice in combobox\r
727         int ixFound = -1;\r
728         int matchLength = 0;\r
729         CString comboRefName;\r
730         for(int i = 0; i < pComboBox->GetCount(); ++i)\r
731         {\r
732                 pComboBox->GetLBText(i, comboRefName);\r
733                 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)\r
734                 {\r
735                         matchLength = comboRefName.GetLength();\r
736                         ixFound = i;\r
737                 }\r
738         }\r
739         if(ixFound >= 0)\r
740                 pComboBox->SetCurSel(ixFound);\r
741         else\r
742                 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)\r
743 \r
744         return true;\r
745 }\r