OSDN Git Service

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