OSDN Git Service

Pull/Fetch Dlg: Used git config to determine default remote and remote branch. Also...
[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(CString completeRefName)\r
390 {\r
391         CString csMessage;\r
392         CString csTitle;\r
393 \r
394         UINT mbIcon=MB_ICONQUESTION;\r
395         csMessage = L"Are you sure you want to delete the ";\r
396 \r
397         bool bIsRemoteBranch = false;\r
398         bool bIsBranch = false;\r
399         if              (wcsncmp(completeRefName, L"refs/remotes",12)==0)       {bIsBranch = true; bIsRemoteBranch = true;}\r
400         else if (wcsncmp(completeRefName, L"refs/heads",10)==0)         {bIsBranch = true;}\r
401 \r
402         if(bIsBranch)\r
403         {\r
404                 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);\r
405                 csTitle.Format(L"Confirm deletion of %sbranch %s", \r
406                         bIsRemoteBranch? L"remote ": L"", \r
407                         branchToDelete);\r
408                 if(bIsRemoteBranch)\r
409                         csMessage += L"<ct=0x0000FF><i>remote</i></ct> "; \r
410                 csMessage += L"branch:\r\n\r\n<b>";\r
411                 csMessage += branchToDelete;\r
412                 csMessage += L"</b>";\r
413 \r
414                 //Check if branch is fully merged in HEAD\r
415                 CString branchHash = g_Git.GetHash(completeRefName);\r
416                 CString commonAncestor;\r
417                 CString cmd;\r
418                 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
419                 g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
420 \r
421                 branchHash=branchHash.Left(40);\r
422                 commonAncestor=commonAncestor.Left(40);\r
423                 \r
424                 if(commonAncestor != branchHash)\r
425                 {\r
426                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
427                         mbIcon = MB_ICONWARNING;\r
428                 }\r
429                 if(bIsRemoteBranch)\r
430                 {\r
431                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branch on the remote.</b>";\r
432                         mbIcon = MB_ICONWARNING;\r
433                 }\r
434         }\r
435         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
436         {\r
437                 CString tagToDelete = completeRefName.Mid(10);\r
438                 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
439                 csMessage += "tag:\r\n\r\n<b>";\r
440                 csMessage += tagToDelete;\r
441                 csMessage += "</b>";\r
442         }\r
443 \r
444         return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
445 \r
446 }\r
447 \r
448 \r
449 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
450 {\r
451         bool bIsRemoteBranch = false;\r
452         bool bIsBranch = false;\r
453         if              (wcsncmp(completeRefName, L"refs/remotes",12)==0)       {bIsBranch = true; bIsRemoteBranch = true;}\r
454         else if (wcsncmp(completeRefName, L"refs/heads",10)==0)         {bIsBranch = true;}\r
455 \r
456         if(bIsBranch)\r
457         {\r
458                 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);\r
459                 CString cmd;\r
460                 if(bIsRemoteBranch)\r
461                 {\r
462                         int slash = branchToDelete.Find(L'/');\r
463                         if(slash < 0)\r
464                                 return false;\r
465                         CString remoteName = branchToDelete.Left(slash);\r
466                         CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);\r
467                         cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);\r
468                 }\r
469                 else\r
470                         cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
471                 CString resultDummy;\r
472                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
473                 {\r
474                         CString errorMsg;\r
475                         errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
476                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
477                         return false;\r
478                 }\r
479         }\r
480         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
481         {\r
482                 CString tagToDelete = completeRefName.Mid(10);\r
483                 CString cmd;\r
484                 cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
485                 CString resultDummy;\r
486                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
487                 {\r
488                         CString errorMsg;\r
489                         errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
490                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
491                         return false;\r
492                 }\r
493         }\r
494         return true;\r
495 }\r
496 \r
497 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)\r
498 {\r
499         CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);\r
500         if(pLeaf == NULL)\r
501                 return CString();\r
502         return pLeaf->GetRefName();\r
503 }\r
504 \r
505 \r
506 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
507 {\r
508         if(pWndFrom==&m_RefTreeCtrl)       OnContextMenu_RefTreeCtrl(point);\r
509         else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
510 }\r
511 \r
512 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
513 {\r
514         CPoint clientPoint=point;\r
515         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
516 \r
517         HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
518         if(hTreeItem!=NULL)\r
519                 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
520 \r
521         ShowContextMenu(point,hTreeItem,VectorPShadowTree());\r
522 }\r
523 \r
524 \r
525 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
526 {\r
527         std::vector<CShadowTree*> selectedLeafs;\r
528         selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());\r
529         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
530         while(pos)\r
531         {\r
532                 selectedLeafs.push_back(\r
533                         (CShadowTree*)m_ListRefLeafs.GetItemData(\r
534                                 m_ListRefLeafs.GetNextSelectedItem(pos)));\r
535         }\r
536 \r
537         ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);\r
538 }\r
539 \r
540 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)\r
541 {\r
542         CIconMenu popupMenu;\r
543         popupMenu.CreatePopupMenu();\r
544 \r
545         bool bAddSeparator = false;\r
546         if(selectedLeafs.size()==1)\r
547         {\r
548                 bAddSeparator = true;\r
549 \r
550                 bool bShowReflogOption = false;\r
551                 bool bShowDeleteBranchOption = false;\r
552                 bool bShowDeleteTagOption = false;\r
553                 bool bShowDeleteRemoteBranchOption = false;\r
554 \r
555                 if(selectedLeafs[0]->IsFrom(L"refs/heads"))\r
556                 {\r
557                         bShowReflogOption = true;\r
558                         bShowDeleteBranchOption = true;\r
559                 }\r
560                 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))\r
561                 {\r
562                         bShowReflogOption = true;\r
563                         bShowDeleteRemoteBranchOption = true;\r
564                 }\r
565                 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))\r
566                 {\r
567                         bShowDeleteTagOption = true;\r
568                 }\r
569 \r
570                                                                                         popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);\r
571                 if(bShowReflogOption)                           popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);\r
572                 if(bShowDeleteTagOption)                        popupMenu.AppendMenuIcon(eCmd_DeleteTag, L"Delete Tag", IDI_DELETE);\r
573                 if(bShowDeleteBranchOption)                     popupMenu.AppendMenuIcon(eCmd_DeleteBranch, L"Delete Branch", IDI_DELETE);\r
574                 if(bShowDeleteRemoteBranchOption)       popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, L"Delete Remote Branch", IDI_DELETE);\r
575 \r
576 \r
577 \r
578 //              CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
579 //              if(pTree==NULL)\r
580 //                      return;\r
581         }\r
582         else if(selectedLeafs.size() == 2)\r
583         {\r
584                 bAddSeparator = true;\r
585                 \r
586                 popupMenu.AppendMenuIcon(eCmd_Diff, L"Diff These Commits", IDI_DIFF);\r
587         }\r
588 \r
589         if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);\r
590 \r
591         if(hTreePos!=NULL)\r
592         {\r
593                 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);\r
594                 if(pTree->IsFrom(L"refs/remotes"))\r
595                 {\r
596 //                      popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
597                         popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);\r
598                 }\r
599                 else if(pTree->IsFrom(L"refs/heads"))\r
600                         popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);\r
601                 else if(pTree->IsFrom(L"refs/tags"))\r
602                         popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);\r
603         }\r
604 \r
605 \r
606         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
607         switch(cmd)\r
608         {\r
609         case eCmd_ViewLog:\r
610                 {\r
611                         CLogDlg dlg;\r
612                         dlg.SetStartRef(selectedLeafs[0]->GetRefName());\r
613                         dlg.DoModal();\r
614                 }\r
615                 break;\r
616         case eCmd_DeleteBranch:\r
617         case eCmd_DeleteRemoteBranch:\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_DeleteTag:\r
625                 {\r
626                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
627                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
628                         Refresh();\r
629                 }\r
630                 break;\r
631         case eCmd_ShowReflog:\r
632                 {\r
633                         CRefLogDlg refLogDlg(this);\r
634                         refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();\r
635                         refLogDlg.DoModal();\r
636                 }\r
637                 break;\r
638         case eCmd_AddRemote:\r
639                 {\r
640                         CAddRemoteDlg(this).DoModal();\r
641                         Refresh();\r
642                 }\r
643                 break;\r
644         case eCmd_ManageRemotes:\r
645                 {\r
646                         CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();\r
647 //                      CSettingGitRemote W_Remotes(m_cmdPath);\r
648 //                      W_Remotes.DoModal();\r
649                         Refresh();\r
650                 }\r
651                 break;\r
652         case eCmd_CreateBranch:\r
653                 {\r
654                         CAppUtils::CreateBranchTag(false);\r
655                         Refresh();\r
656                 }\r
657                 break;\r
658         case eCmd_CreateTag:\r
659                 {\r
660                         CAppUtils::CreateBranchTag(true);\r
661                         Refresh();\r
662                 }\r
663                 break;\r
664         case eCmd_Diff:\r
665                 {\r
666                         CFileDiffDlg dlg;\r
667                         dlg.SetDiff(\r
668                                 NULL, \r
669                                 selectedLeafs[0]->m_csRefHash, \r
670                                 selectedLeafs[1]->m_csRefHash);\r
671                         dlg.DoModal();\r
672                 }\r
673                 break;\r
674         }\r
675 }\r
676 \r
677 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
678 {\r
679         if (pMsg->message == WM_KEYDOWN)\r
680         {\r
681                 switch (pMsg->wParam)\r
682                 {\r
683 /*              case VK_RETURN:\r
684                         {\r
685                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
686                                 {\r
687                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
688                                         {\r
689                                                 PostMessage(WM_COMMAND, IDOK);\r
690                                         }\r
691                                         return TRUE;\r
692                                 }\r
693                         }\r
694                         break;\r
695 */              case VK_F5:\r
696                         {\r
697                                 Refresh();\r
698                         }\r
699                         break;\r
700                 }\r
701         }\r
702 \r
703 \r
704         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
705 }\r
706 \r
707 class CRefLeafListCompareFunc\r
708 {\r
709 public:\r
710         CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}\r
711 \r
712         static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)\r
713         {\r
714                 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);\r
715         }\r
716 \r
717         int Compare(LPARAM lParam1, LPARAM lParam2)\r
718         {\r
719                 return Compare(\r
720                         (CShadowTree*)m_pList->GetItemData(lParam1), \r
721                         (CShadowTree*)m_pList->GetItemData(lParam2));\r
722         }\r
723 \r
724         int Compare(CShadowTree* pLeft, CShadowTree* pRight)\r
725         {\r
726                 int result=CompareNoDesc(pLeft,pRight);\r
727                 if(m_desc)\r
728                         return -result;\r
729                 return result;\r
730         }\r
731 \r
732         int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)\r
733         {\r
734                 switch(m_col)\r
735                 {\r
736                 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());\r
737                 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);\r
738                 case CBrowseRefsDlg::eCol_Msg:  return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);\r
739                 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);\r
740                 }\r
741                 return 0;\r
742         }\r
743 \r
744         int m_col;\r
745         bool m_desc;\r
746         CListCtrl* m_pList;\r
747 \r
748 \r
749 };\r
750 \r
751 \r
752 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
753 {\r
754         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
755         *pResult = 0;\r
756 \r
757         if(m_currSortCol == pNMLV->iSubItem)\r
758                 m_currSortDesc = !m_currSortDesc;\r
759         else\r
760         {\r
761                 m_currSortCol  = pNMLV->iSubItem;\r
762                 m_currSortDesc = false;\r
763         }\r
764 \r
765         CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);\r
766         m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);\r
767 \r
768         SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);\r
769 }\r
770 \r
771 void CBrowseRefsDlg::OnDestroy()\r
772 {\r
773         m_pickedRef = GetSelectedRef(true, false);\r
774 \r
775         CResizableStandAloneDialog::OnDestroy();\r
776 }\r
777 \r
778 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
779 {\r
780         LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);\r
781         *pResult = 0;\r
782 \r
783         EndDialog(IDOK);\r
784 }\r
785 \r
786 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)\r
787 {\r
788         CBrowseRefsDlg dlg(CString(),NULL);\r
789         \r
790         if(initialRef.IsEmpty())\r
791                 initialRef = L"HEAD";\r
792         dlg.m_initialRef = initialRef;\r
793         dlg.m_pickRef_Kind = pickRef_Kind;\r
794 \r
795         if(dlg.DoModal() != IDOK)\r
796                 return CString();\r
797 \r
798         return dlg.m_pickedRef;\r
799 }\r
800 \r
801 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)\r
802 {\r
803         CString origRef;\r
804         pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);\r
805         CString resultRef = PickRef(false,origRef,pickRef_Kind);\r
806         if(resultRef.IsEmpty())\r
807                 return false;\r
808         if(wcsncmp(resultRef,L"refs/",5)==0)\r
809                 resultRef = resultRef.Mid(5);\r
810 //      if(wcsncmp(resultRef,L"heads/",6)==0)\r
811 //              resultRef = resultRef.Mid(6);\r
812 \r
813         //Find closest match of choice in combobox\r
814         int ixFound = -1;\r
815         int matchLength = 0;\r
816         CString comboRefName;\r
817         for(int i = 0; i < pComboBox->GetCount(); ++i)\r
818         {\r
819                 pComboBox->GetLBText(i, comboRefName);\r
820                 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())\r
821                         comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'\r
822                 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)\r
823                 {\r
824                         matchLength = comboRefName.GetLength();\r
825                         ixFound = i;\r
826                 }\r
827         }\r
828         if(ixFound >= 0)\r
829                 pComboBox->SetCurSel(ixFound);\r
830         else\r
831                 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
832 \r
833         return true;\r
834 }\r