OSDN Git Service

Add Many Help button at 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.Empty();\r
199                         g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);\r
200                         selectRef.Trim(L"\r\n\t ");\r
201                 }\r
202         }\r
203         else\r
204         {\r
205                 selectRef = GetSelectedRef(false, true);\r
206         }\r
207 \r
208         m_RefTreeCtrl.DeleteAllItems();\r
209         m_ListRefLeafs.DeleteAllItems();\r
210         m_TreeRoot.m_ShadowTree.clear();\r
211         m_TreeRoot.m_csRefName="refs";\r
212 //      m_TreeRoot.m_csShowName="Refs";\r
213         m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);\r
214         m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);\r
215 \r
216         CString allRefs;\r
217         g_Git.Run(L"git for-each-ref --format="\r
218                           L"%(refname)%04"\r
219                           L"%(objectname)%04"\r
220                           L"%(authordate:relative)%04"\r
221                           L"%(subject)%04"\r
222                           L"%(authorname)%04"\r
223                           L"%(authordate:iso8601)",\r
224                           &allRefs,CP_UTF8);\r
225 \r
226         int linePos=0;\r
227         CString singleRef;\r
228 \r
229         MAP_STRING_STRING refMap;\r
230 \r
231         //First sort on ref name\r
232         while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())\r
233         {\r
234                 int valuePos=0;\r
235                 CString refName=singleRef.Tokenize(L"\04",valuePos);\r
236                 CString refRest=singleRef.Mid(valuePos);\r
237 \r
238                 //Use ref based on m_pickRef_Kind\r
239                 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )\r
240                         continue; //Skip\r
241                 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )\r
242                         continue; //Skip\r
243                 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )\r
244                         continue; //Skip\r
245 \r
246                 refMap[refName] = refRest; //Use\r
247         }\r
248 \r
249 \r
250 \r
251         //Populate ref tree\r
252         for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)\r
253         {\r
254                 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);\r
255                 CString values=iterRefMap->second;\r
256 \r
257                 int valuePos=0;\r
258                 treeLeaf.m_csRefHash=           values.Tokenize(L"\04",valuePos);\r
259                 treeLeaf.m_csDate=                      values.Tokenize(L"\04",valuePos);\r
260                 treeLeaf.m_csSubject=           values.Tokenize(L"\04",valuePos);\r
261                 treeLeaf.m_csAuthor=            values.Tokenize(L"\04",valuePos);\r
262                 treeLeaf.m_csDate_Iso8601=      values.Tokenize(L"\04",valuePos);\r
263         }\r
264 \r
265 \r
266         if(selectRef.IsEmpty() || !SelectRef(selectRef, false))\r
267                 //Probably not on a branch. Select root node.\r
268                 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);\r
269 \r
270 }\r
271 \r
272 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)\r
273 {\r
274         if(!bExactMatch)\r
275                 refName = GetFullRefName(refName);\r
276         if(wcsnicmp(refName,L"refs/",5)!=0)\r
277                 return false; // Not a ref name\r
278 \r
279         CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);\r
280         if(treeLeafHead.m_hTree != NULL)\r
281         {\r
282                 //Not a leaf. Select tree node and return\r
283                 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);\r
284                 return true;\r
285         }\r
286 \r
287         if(treeLeafHead.m_pParent==NULL)\r
288                 return false; //Weird... should not occur.\r
289 \r
290         //This is the current head.\r
291         m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);\r
292 \r
293         for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)\r
294         {\r
295                 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);\r
296                 if(pCurrShadowTree == &treeLeafHead)\r
297                 {\r
298                         m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);\r
299                         m_ListRefLeafs.EnsureVisible(indexPos,FALSE);\r
300                 }\r
301         }\r
302 \r
303         return true;\r
304 }\r
305 \r
306 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)\r
307 {\r
308         if(pTreePos==NULL)\r
309         {\r
310                 if(wcsnicmp(refName,L"refs/",5)==0)\r
311                         refName=refName.Mid(5);\r
312                 pTreePos=&m_TreeRoot;\r
313         }\r
314         if(refName.IsEmpty())\r
315                 return *pTreePos;//Found leaf\r
316 \r
317         CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);\r
318         if(pNextTree==NULL)\r
319         {\r
320                 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.\r
321                 ASSERT(!bCreateIfNotExist);\r
322                 return *pTreePos;\r
323         }\r
324 \r
325         if(!refName.IsEmpty())\r
326         {\r
327                 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.\r
328                 //Leafs are for the list control.\r
329                 if(pNextTree->m_hTree==NULL)\r
330                 {\r
331                         //New tree. Create node in control.\r
332                         pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);\r
333                         m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);\r
334                 }\r
335         }\r
336 \r
337         return GetTreeNode(refName, pNextTree, bCreateIfNotExist);\r
338 }\r
339 \r
340 \r
341 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)\r
342 {\r
343         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
344         *pResult = 0;\r
345 \r
346         FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);\r
347 }\r
348 \r
349 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)\r
350 {\r
351         m_ListRefLeafs.DeleteAllItems();\r
352         m_currSortCol = -1;\r
353         m_currSortDesc = false;\r
354         SetSortArrow(&m_ListRefLeafs,-1,false);\r
355 \r
356         CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));\r
357         if(pTree==NULL)\r
358         {\r
359                 ASSERT(FALSE);\r
360                 return;\r
361         }\r
362         FillListCtrlForShadowTree(pTree,L"",true);\r
363 }\r
364 \r
365 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)\r
366 {\r
367         if(pTree->IsLeaf())\r
368         {\r
369                 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");\r
370 \r
371                 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);\r
372                 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);\r
373                 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);\r
374                 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg,  pTree->m_csSubject);\r
375                 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);\r
376         }\r
377         else\r
378         {\r
379 \r
380                 CString csThisName;\r
381                 if(!isFirstLevel)\r
382                         csThisName=refNamePrefix+pTree->m_csRefName+L"/";\r
383                 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)\r
384                 {\r
385                         FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);\r
386                 }\r
387         }\r
388 }\r
389 \r
390 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)\r
391 {\r
392         CString csMessage;\r
393         CString csTitle;\r
394 \r
395         UINT mbIcon=MB_ICONQUESTION;\r
396         csMessage = L"Are you sure you want to delete the ";\r
397 \r
398         bool bIsRemoteBranch = false;\r
399         bool bIsBranch = false;\r
400         if              (wcsncmp(completeRefName, L"refs/remotes",12)==0)       {bIsBranch = true; bIsRemoteBranch = true;}\r
401         else if (wcsncmp(completeRefName, L"refs/heads",10)==0)         {bIsBranch = true;}\r
402 \r
403         if(bIsBranch)\r
404         {\r
405                 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);\r
406                 csTitle.Format(L"Confirm deletion of %sbranch %s", \r
407                         bIsRemoteBranch? L"remote ": L"", \r
408                         branchToDelete);\r
409                 if(bIsRemoteBranch)\r
410                         csMessage += L"<ct=0x0000FF><i>remote</i></ct> "; \r
411                 csMessage += L"branch:\r\n\r\n<b>";\r
412                 csMessage += branchToDelete;\r
413                 csMessage += L"</b>";\r
414 \r
415                 //Check if branch is fully merged in HEAD\r
416                 CString branchHash = g_Git.GetHash(completeRefName);\r
417                 CString commonAncestor;\r
418                 CString cmd;\r
419                 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
420                 g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
421 \r
422                 branchHash=branchHash.Left(40);\r
423                 commonAncestor=commonAncestor.Left(40);\r
424                 \r
425                 if(commonAncestor != branchHash)\r
426                 {\r
427                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
428                         mbIcon = MB_ICONWARNING;\r
429                 }\r
430                 if(bIsRemoteBranch)\r
431                 {\r
432                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis action will remove the branch on the remote.</b>";\r
433                         mbIcon = MB_ICONWARNING;\r
434                 }\r
435         }\r
436         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
437         {\r
438                 CString tagToDelete = completeRefName.Mid(10);\r
439                 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
440                 csMessage += "tag:\r\n\r\n<b>";\r
441                 csMessage += tagToDelete;\r
442                 csMessage += "</b>";\r
443         }\r
444 \r
445         return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
446 \r
447 }\r
448 \r
449 \r
450 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
451 {\r
452         bool bIsRemoteBranch = false;\r
453         bool bIsBranch = false;\r
454         if              (wcsncmp(completeRefName, L"refs/remotes",12)==0)       {bIsBranch = true; bIsRemoteBranch = true;}\r
455         else if (wcsncmp(completeRefName, L"refs/heads",10)==0)         {bIsBranch = true;}\r
456 \r
457         if(bIsBranch)\r
458         {\r
459                 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);\r
460                 CString cmd;\r
461                 if(bIsRemoteBranch)\r
462                 {\r
463                         int slash = branchToDelete.Find(L'/');\r
464                         if(slash < 0)\r
465                                 return false;\r
466                         CString remoteName = branchToDelete.Left(slash);\r
467                         CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);\r
468                         cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);\r
469                 }\r
470                 else\r
471                         cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
472                 CString resultDummy;\r
473                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
474                 {\r
475                         CString errorMsg;\r
476                         errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
477                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
478                         return false;\r
479                 }\r
480         }\r
481         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
482         {\r
483                 CString tagToDelete = completeRefName.Mid(10);\r
484                 CString cmd;\r
485                 cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
486                 CString resultDummy;\r
487                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
488                 {\r
489                         CString errorMsg;\r
490                         errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
491                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
492                         return false;\r
493                 }\r
494         }\r
495         return true;\r
496 }\r
497 \r
498 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)\r
499 {\r
500         CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);\r
501         if(pLeaf == NULL)\r
502                 return CString();\r
503         return pLeaf->GetRefName();\r
504 }\r
505 \r
506 \r
507 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
508 {\r
509         if(pWndFrom==&m_RefTreeCtrl)       OnContextMenu_RefTreeCtrl(point);\r
510         else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
511 }\r
512 \r
513 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
514 {\r
515         CPoint clientPoint=point;\r
516         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
517 \r
518         HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
519         if(hTreeItem!=NULL)\r
520                 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
521 \r
522         ShowContextMenu(point,hTreeItem,VectorPShadowTree());\r
523 }\r
524 \r
525 \r
526 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
527 {\r
528         std::vector<CShadowTree*> selectedLeafs;\r
529         selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());\r
530         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
531         while(pos)\r
532         {\r
533                 selectedLeafs.push_back(\r
534                         (CShadowTree*)m_ListRefLeafs.GetItemData(\r
535                                 m_ListRefLeafs.GetNextSelectedItem(pos)));\r
536         }\r
537 \r
538         ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);\r
539 }\r
540 \r
541 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)\r
542 {\r
543         CIconMenu popupMenu;\r
544         popupMenu.CreatePopupMenu();\r
545 \r
546         bool bAddSeparator = false;\r
547         if(selectedLeafs.size()==1)\r
548         {\r
549                 bAddSeparator = true;\r
550 \r
551                 bool bShowReflogOption = false;\r
552                 bool bShowDeleteBranchOption = false;\r
553                 bool bShowDeleteTagOption = false;\r
554                 bool bShowDeleteRemoteBranchOption = false;\r
555 \r
556                 if(selectedLeafs[0]->IsFrom(L"refs/heads"))\r
557                 {\r
558                         bShowReflogOption = true;\r
559                         bShowDeleteBranchOption = true;\r
560                 }\r
561                 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))\r
562                 {\r
563                         bShowReflogOption = true;\r
564                         bShowDeleteRemoteBranchOption = true;\r
565                 }\r
566                 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))\r
567                 {\r
568                         bShowDeleteTagOption = true;\r
569                 }\r
570 \r
571                                                                                         popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);\r
572                 if(bShowReflogOption)                           popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);\r
573                 if(bShowDeleteTagOption)                        popupMenu.AppendMenuIcon(eCmd_DeleteTag, L"Delete Tag", IDI_DELETE);\r
574                 if(bShowDeleteBranchOption)                     popupMenu.AppendMenuIcon(eCmd_DeleteBranch, L"Delete Branch", IDI_DELETE);\r
575                 if(bShowDeleteRemoteBranchOption)       popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, L"Delete Remote Branch", IDI_DELETE);\r
576 \r
577 \r
578 \r
579 //              CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
580 //              if(pTree==NULL)\r
581 //                      return;\r
582         }\r
583         else if(selectedLeafs.size() == 2)\r
584         {\r
585                 bAddSeparator = true;\r
586                 \r
587                 popupMenu.AppendMenuIcon(eCmd_Diff, L"Diff These Commits", IDI_DIFF);\r
588         }\r
589 \r
590         if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);\r
591 \r
592         if(hTreePos!=NULL)\r
593         {\r
594                 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);\r
595                 if(pTree->IsFrom(L"refs/remotes"))\r
596                 {\r
597 //                      popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
598                         popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);\r
599                 }\r
600                 else if(pTree->IsFrom(L"refs/heads"))\r
601                         popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);\r
602                 else if(pTree->IsFrom(L"refs/tags"))\r
603                         popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);\r
604         }\r
605 \r
606 \r
607         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
608         switch(cmd)\r
609         {\r
610         case eCmd_ViewLog:\r
611                 {\r
612                         CLogDlg dlg;\r
613                         dlg.SetStartRef(selectedLeafs[0]->GetRefName());\r
614                         dlg.DoModal();\r
615                 }\r
616                 break;\r
617         case eCmd_DeleteBranch:\r
618         case eCmd_DeleteRemoteBranch:\r
619                 {\r
620                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
621                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
622                         Refresh();\r
623                 }\r
624                 break;\r
625         case eCmd_DeleteTag:\r
626                 {\r
627                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
628                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
629                         Refresh();\r
630                 }\r
631                 break;\r
632         case eCmd_ShowReflog:\r
633                 {\r
634                         CRefLogDlg refLogDlg(this);\r
635                         refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();\r
636                         refLogDlg.DoModal();\r
637                 }\r
638                 break;\r
639         case eCmd_AddRemote:\r
640                 {\r
641                         CAddRemoteDlg(this).DoModal();\r
642                         Refresh();\r
643                 }\r
644                 break;\r
645         case eCmd_ManageRemotes:\r
646                 {\r
647                         CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();\r
648 //                      CSettingGitRemote W_Remotes(m_cmdPath);\r
649 //                      W_Remotes.DoModal();\r
650                         Refresh();\r
651                 }\r
652                 break;\r
653         case eCmd_CreateBranch:\r
654                 {\r
655                         CAppUtils::CreateBranchTag(false);\r
656                         Refresh();\r
657                 }\r
658                 break;\r
659         case eCmd_CreateTag:\r
660                 {\r
661                         CAppUtils::CreateBranchTag(true);\r
662                         Refresh();\r
663                 }\r
664                 break;\r
665         case eCmd_Diff:\r
666                 {\r
667                         CFileDiffDlg dlg;\r
668                         dlg.SetDiff(\r
669                                 NULL, \r
670                                 selectedLeafs[0]->m_csRefHash, \r
671                                 selectedLeafs[1]->m_csRefHash);\r
672                         dlg.DoModal();\r
673                 }\r
674                 break;\r
675         }\r
676 }\r
677 \r
678 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
679 {\r
680         if (pMsg->message == WM_KEYDOWN)\r
681         {\r
682                 switch (pMsg->wParam)\r
683                 {\r
684 /*              case VK_RETURN:\r
685                         {\r
686                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
687                                 {\r
688                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
689                                         {\r
690                                                 PostMessage(WM_COMMAND, IDOK);\r
691                                         }\r
692                                         return TRUE;\r
693                                 }\r
694                         }\r
695                         break;\r
696 */              case VK_F5:\r
697                         {\r
698                                 Refresh();\r
699                         }\r
700                         break;\r
701                 }\r
702         }\r
703 \r
704 \r
705         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
706 }\r
707 \r
708 class CRefLeafListCompareFunc\r
709 {\r
710 public:\r
711         CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}\r
712 \r
713         static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)\r
714         {\r
715                 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);\r
716         }\r
717 \r
718         int Compare(LPARAM lParam1, LPARAM lParam2)\r
719         {\r
720                 return Compare(\r
721                         (CShadowTree*)m_pList->GetItemData(lParam1), \r
722                         (CShadowTree*)m_pList->GetItemData(lParam2));\r
723         }\r
724 \r
725         int Compare(CShadowTree* pLeft, CShadowTree* pRight)\r
726         {\r
727                 int result=CompareNoDesc(pLeft,pRight);\r
728                 if(m_desc)\r
729                         return -result;\r
730                 return result;\r
731         }\r
732 \r
733         int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)\r
734         {\r
735                 switch(m_col)\r
736                 {\r
737                 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());\r
738                 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);\r
739                 case CBrowseRefsDlg::eCol_Msg:  return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);\r
740                 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);\r
741                 }\r
742                 return 0;\r
743         }\r
744 \r
745         int m_col;\r
746         bool m_desc;\r
747         CListCtrl* m_pList;\r
748 \r
749 \r
750 };\r
751 \r
752 \r
753 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
754 {\r
755         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
756         *pResult = 0;\r
757 \r
758         if(m_currSortCol == pNMLV->iSubItem)\r
759                 m_currSortDesc = !m_currSortDesc;\r
760         else\r
761         {\r
762                 m_currSortCol  = pNMLV->iSubItem;\r
763                 m_currSortDesc = false;\r
764         }\r
765 \r
766         CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);\r
767         m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);\r
768 \r
769         SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);\r
770 }\r
771 \r
772 void CBrowseRefsDlg::OnDestroy()\r
773 {\r
774         m_pickedRef = GetSelectedRef(true, false);\r
775 \r
776         CResizableStandAloneDialog::OnDestroy();\r
777 }\r
778 \r
779 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
780 {\r
781         LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);\r
782         *pResult = 0;\r
783 \r
784         EndDialog(IDOK);\r
785 }\r
786 \r
787 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)\r
788 {\r
789         CBrowseRefsDlg dlg(CString(),NULL);\r
790         \r
791         if(initialRef.IsEmpty())\r
792                 initialRef = L"HEAD";\r
793         dlg.m_initialRef = initialRef;\r
794         dlg.m_pickRef_Kind = pickRef_Kind;\r
795 \r
796         if(dlg.DoModal() != IDOK)\r
797                 return CString();\r
798 \r
799         return dlg.m_pickedRef;\r
800 }\r
801 \r
802 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)\r
803 {\r
804         CString origRef;\r
805         pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);\r
806         CString resultRef = PickRef(false,origRef,pickRef_Kind);\r
807         if(resultRef.IsEmpty())\r
808                 return false;\r
809         if(wcsncmp(resultRef,L"refs/",5)==0)\r
810                 resultRef = resultRef.Mid(5);\r
811 //      if(wcsncmp(resultRef,L"heads/",6)==0)\r
812 //              resultRef = resultRef.Mid(6);\r
813 \r
814         //Find closest match of choice in combobox\r
815         int ixFound = -1;\r
816         int matchLength = 0;\r
817         CString comboRefName;\r
818         for(int i = 0; i < pComboBox->GetCount(); ++i)\r
819         {\r
820                 pComboBox->GetLBText(i, comboRefName);\r
821                 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())\r
822                         comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'\r
823                 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)\r
824                 {\r
825                         matchLength = comboRefName.GetLength();\r
826                         ixFound = i;\r
827                 }\r
828         }\r
829         if(ixFound >= 0)\r
830                 pComboBox->SetCurSel(ixFound);\r
831         else\r
832                 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
833 \r
834         return true;\r
835 }\r