OSDN Git Service

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