OSDN Git Service

09c3fc118dc7d1aad86e3253a5e9365b4d0c0246
[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 "CreateBranchTagDlg.h"\r
10 #include "Settings\SettingGitRemote.h"\r
11 #include "SinglePropSheetDlg.h"\r
12 #include "ConfirmDelRefDlg.h"\r
13 #include "MessageBox.h"\r
14 \r
15 // CBrowseRefsDlg dialog\r
16 \r
17 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)\r
18 \r
19 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)\r
20 :       CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),\r
21         m_cmdPath(cmdPath)\r
22 {\r
23 \r
24 }\r
25 \r
26 CBrowseRefsDlg::~CBrowseRefsDlg()\r
27 {\r
28 }\r
29 \r
30 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)\r
31 {\r
32         CDialog::DoDataExchange(pDX);\r
33         DDX_Control(pDX, IDC_TREE_REF,                  m_RefTreeCtrl);\r
34         DDX_Control(pDX, IDC_LIST_REF_LEAFS,    m_ListRefLeafs);\r
35 }\r
36 \r
37 \r
38 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)\r
39         ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)\r
40         ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)\r
41         ON_WM_CONTEXTMENU()\r
42 END_MESSAGE_MAP()\r
43 \r
44 \r
45 // CBrowseRefsDlg message handlers\r
46 \r
47 void CBrowseRefsDlg::OnBnClickedOk()\r
48 {\r
49         OnOK();\r
50 }\r
51 \r
52 BOOL CBrowseRefsDlg::OnInitDialog()\r
53 {\r
54         CResizableStandAloneDialog::OnInitDialog();\r
55 \r
56         AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);\r
57         AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);\r
58 \r
59         m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);\r
60         m_ListRefLeafs.InsertColumn(0,L"Name",0,150);\r
61         m_ListRefLeafs.InsertColumn(1,L"Date Last Commit",0,100);\r
62         m_ListRefLeafs.InsertColumn(2,L"Last Commit",0,300);\r
63         m_ListRefLeafs.InsertColumn(3,L"Hash",0,80);\r
64 \r
65         AddAnchor(IDOK,BOTTOM_RIGHT);\r
66         AddAnchor(IDCANCEL,BOTTOM_RIGHT);\r
67 \r
68         Refresh();\r
69 \r
70 \r
71         return TRUE;\r
72 }\r
73 \r
74 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft)\r
75 {\r
76         int posSlash=nameLeft.Find('/');\r
77         CString nameSub;\r
78         if(posSlash<0)\r
79         {\r
80                 nameSub=nameLeft;\r
81                 nameLeft.Empty();//Nothing left\r
82         }\r
83         else\r
84         {\r
85                 nameSub=nameLeft.Left(posSlash);\r
86                 nameLeft=nameLeft.Mid(posSlash+1);\r
87         }\r
88         if(nameSub.IsEmpty())\r
89                 return NULL;\r
90 \r
91         CShadowTree& nextNode=m_ShadowTree[nameSub];\r
92         nextNode.m_csRefName=nameSub;\r
93         nextNode.m_pParent=this;\r
94         return &nextNode;\r
95 }\r
96 \r
97 typedef std::map<CString,CString> MAP_STRING_STRING;\r
98 \r
99 void CBrowseRefsDlg::Refresh()\r
100 {\r
101 //      m_RefMap.clear();\r
102 //      g_Git.GetMapHashToFriendName(m_RefMap);\r
103                 \r
104 \r
105         m_RefTreeCtrl.DeleteAllItems();\r
106         m_TreeRoot.m_ShadowTree.clear();\r
107         m_TreeRoot.m_csRefName="refs";\r
108 //      m_TreeRoot.m_csShowName="Refs";\r
109         m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);\r
110         m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);\r
111 \r
112         CString allRefs;\r
113         g_Git.Run(L"git for-each-ref --format="\r
114                           L"%(refname)%04"\r
115                           L"%(objectname)%04"\r
116                           L"%(authordate:relative)%04"\r
117                           L"%(subject)%04"\r
118                           L"%(authorname)",\r
119                           &allRefs,CP_UTF8);\r
120 \r
121         int linePos=0;\r
122         CString singleRef;\r
123 \r
124         MAP_STRING_STRING refMap;\r
125 \r
126         //First sort on ref name\r
127         while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())\r
128         {\r
129                 int valuePos=0;\r
130                 CString refName=singleRef.Tokenize(L"\04",valuePos);\r
131                 CString refRest=singleRef.Mid(valuePos);\r
132                 refMap[refName]=refRest;\r
133         }\r
134 \r
135 \r
136 \r
137 //      for(MAP_HASH_NAME::iterator iterRef=m_RefMap.begin();iterRef!=m_RefMap.end();++iterRef)\r
138 //              for(STRING_VECTOR::iterator iterRefName=iterRef->second.begin();iterRefName!=iterRef->second.end();++iterRefName)\r
139 //                      refName[*iterRefName]=iterRef->first;\r
140 \r
141         //Populate ref tree\r
142         for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)\r
143         {\r
144                 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first);\r
145                 CString values=iterRefMap->second;\r
146 \r
147                 int valuePos=0;\r
148                 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);\r
149                 treeLeaf.m_csDate=    values.Tokenize(L"\04",valuePos);\r
150                 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);\r
151                 treeLeaf.m_csAuthor=  values.Tokenize(L"\04",valuePos);\r
152         }\r
153 \r
154         CString currHead;\r
155         g_Git.Run(L"git symbolic-ref HEAD",&currHead,CP_UTF8);\r
156 \r
157         currHead.Trim(L"\r\n\t ");\r
158 \r
159         if(!SelectRef(currHead))\r
160                 //Probably not on a branch. Select root node.\r
161                 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);\r
162 \r
163 }\r
164 \r
165 bool CBrowseRefsDlg::SelectRef(CString refName)\r
166 {\r
167         if(wcsnicmp(refName,L"refs/",5)!=0)\r
168                 return false; // Not a ref name\r
169 \r
170         CShadowTree& treeLeafHead=GetTreeNode(refName);\r
171         if(treeLeafHead.m_pParent==NULL)\r
172                 return false; //Weird... should not occur.\r
173 \r
174         //This is the current head.\r
175         m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);\r
176 \r
177         for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)\r
178         {\r
179                 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);\r
180                 if(pCurrShadowTree == &treeLeafHead)\r
181                 {\r
182                         m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);\r
183                 }\r
184         }\r
185 \r
186         return true;\r
187 }\r
188 \r
189 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos)\r
190 {\r
191         if(pTreePos==NULL)\r
192         {\r
193                 if(wcsnicmp(refName,L"refs/",5)==0)\r
194                         refName=refName.Mid(5);\r
195                 pTreePos=&m_TreeRoot;\r
196         }\r
197         if(refName.IsEmpty())\r
198                 return *pTreePos;//Found leaf\r
199 \r
200         CShadowTree* pNextTree=pTreePos->GetNextSub(refName);\r
201         if(pNextTree==NULL)\r
202         {\r
203                 //Should not occur when all ref-names are valid.\r
204                 ASSERT(FALSE);\r
205                 return *pTreePos;\r
206         }\r
207 \r
208         if(!refName.IsEmpty())\r
209         {\r
210                 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.\r
211                 //Leafs are for the list control.\r
212                 if(pNextTree->m_hTree==NULL)\r
213                 {\r
214                         //New tree. Create node in control.\r
215                         pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);\r
216                         m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);\r
217                 }\r
218         }\r
219 \r
220         return GetTreeNode(refName,pNextTree);\r
221 }\r
222 \r
223 \r
224 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)\r
225 {\r
226         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
227         *pResult = 0;\r
228 \r
229         FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);\r
230 }\r
231 \r
232 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)\r
233 {\r
234         m_ListRefLeafs.DeleteAllItems();\r
235 \r
236         CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));\r
237         if(pTree==NULL)\r
238         {\r
239                 ASSERT(FALSE);\r
240                 return;\r
241         }\r
242         FillListCtrlForShadowTree(pTree,L"",true);\r
243 }\r
244 \r
245 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)\r
246 {\r
247         if(pTree->IsLeaf())\r
248         {\r
249                 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");\r
250 \r
251                 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);\r
252                 m_ListRefLeafs.SetItemText(indexItem,0,refNamePrefix+pTree->m_csRefName);\r
253                 m_ListRefLeafs.SetItemText(indexItem,1,pTree->m_csDate);\r
254                 m_ListRefLeafs.SetItemText(indexItem,2,pTree->m_csSubject);\r
255                 m_ListRefLeafs.SetItemText(indexItem,3,pTree->m_csRefHash);\r
256         }\r
257         else\r
258         {\r
259 \r
260                 CString csThisName;\r
261                 if(!isFirstLevel)\r
262                         csThisName=refNamePrefix+pTree->m_csRefName+L"/";\r
263                 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)\r
264                 {\r
265                         FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);\r
266                 }\r
267         }\r
268 }\r
269 \r
270 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
271 {\r
272 \r
273         CPoint clientPoint=point;\r
274         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
275 \r
276 \r
277         std::vector<CShadowTree*> selectedTrees;\r
278         selectedTrees.reserve(m_ListRefLeafs.GetSelectedCount());\r
279         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
280         while(pos)\r
281         {\r
282                 selectedTrees.push_back(\r
283                         (CShadowTree*)m_ListRefLeafs.GetItemData(\r
284                                 m_ListRefLeafs.GetNextSelectedItem(pos)));\r
285         }\r
286 \r
287         CMenu popupMenu;\r
288         popupMenu.CreatePopupMenu();\r
289 \r
290         if(selectedTrees.size()==1)\r
291         {\r
292                 popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");\r
293                 if(selectedTrees[0]->IsFrom(L"refs/heads"))\r
294                         popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");\r
295                 else if(selectedTrees[0]->IsFrom(L"refs/tags"))\r
296                         popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");\r
297 \r
298 //              CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
299 //              if(pTree==NULL)\r
300 //                      return;\r
301         }\r
302 \r
303 \r
304         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
305         switch(cmd)\r
306         {\r
307         case eCmd_ViewLog:\r
308                 {\r
309                         CLogDlg dlg;\r
310                         dlg.SetStartRef(selectedTrees[0]->m_csRefHash);\r
311                         dlg.DoModal();\r
312                 }\r
313                 break;\r
314         case eCmd_DeleteBranch:\r
315                 {\r
316                         if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))\r
317                                 DoDeleteRef(selectedTrees[0]->GetRefName(), true);\r
318                         Refresh();\r
319                 }\r
320                 break;\r
321         case eCmd_DeleteTag:\r
322                 {\r
323                         if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))\r
324                                 DoDeleteRef(selectedTrees[0]->GetRefName(), true);\r
325                         Refresh();\r
326                 }\r
327                 break;\r
328         }\r
329 }\r
330 \r
331 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)\r
332 {\r
333         CString csMessage;\r
334         CString csTitle;\r
335 \r
336         UINT mbIcon=MB_ICONQUESTION;\r
337         csMessage=L"Are you sure you want to delete the ";\r
338         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
339         {\r
340                 CString branchToDelete = completeRefName.Mid(11);\r
341                 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);\r
342                 csMessage += "branch:\r\n\r\n<b>";\r
343                 csMessage += branchToDelete;\r
344                 csMessage += "</b>";\r
345 \r
346                 //Check if branch is fully merged in HEAD\r
347                 CString branchHash = g_Git.GetHash(completeRefName);\r
348                 CString commonAncestor;\r
349                 CString cmd;\r
350                 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
351                 g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
352 \r
353                 branchHash=branchHash.Left(40);\r
354                 commonAncestor=commonAncestor.Left(40);\r
355                 \r
356                 if(commonAncestor != branchHash)\r
357                 {\r
358                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
359                         mbIcon=MB_ICONWARNING;\r
360                 }\r
361         }\r
362         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
363         {\r
364                 CString tagToDelete = completeRefName.Mid(10);\r
365                 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
366                 csMessage += "tag:\r\n\r\n<b>";\r
367                 csMessage += tagToDelete;\r
368                 csMessage += "</b>";\r
369         }\r
370 \r
371         return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
372 \r
373 }\r
374 \r
375 \r
376 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
377 {\r
378         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
379         {\r
380                 CString branchToDelete = completeRefName.Mid(11);\r
381                 CString cmd;\r
382                 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
383                 CString resultDummy;\r
384                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
385                 {\r
386                         CString errorMsg;\r
387                         errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
388                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
389                         return false;\r
390                 }\r
391         }\r
392         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
393         {\r
394                 CString tagToDelete = completeRefName.Mid(10);\r
395                 CString cmd;\r
396                 cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
397                 CString resultDummy;\r
398                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
399                 {\r
400                         CString errorMsg;\r
401                         errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
402                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
403                         return false;\r
404                 }\r
405         }\r
406         return true;\r
407 }\r
408 \r
409 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
410 {\r
411         if(pWndFrom==&m_RefTreeCtrl)       OnContextMenu_RefTreeCtrl(point);\r
412         else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
413 }\r
414 \r
415 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
416 {\r
417         CMenu popupMenu;\r
418         popupMenu.CreatePopupMenu();\r
419 \r
420         CPoint clientPoint=point;\r
421         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
422 \r
423         HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
424         if(hTreeItem!=NULL)\r
425         {\r
426                 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
427                 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreeItem);\r
428                 if(pTree->IsFrom(L"refs/remotes"))\r
429                 {\r
430 //                      popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
431                         popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");\r
432                 }\r
433                 else if(pTree->IsFrom(L"refs/heads"))\r
434                         popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");\r
435                 else if(pTree->IsFrom(L"refs/tags"))\r
436                         popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");\r
437         }\r
438 \r
439         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
440         switch(cmd)\r
441         {\r
442         case eCmd_AddRemote:\r
443                 {\r
444                         CAddRemoteDlg(this).DoModal();\r
445                         Refresh();\r
446                 }\r
447                 break;\r
448         case eCmd_ManageRemotes:\r
449                 {\r
450                         CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();\r
451 //                      CSettingGitRemote W_Remotes(m_cmdPath);\r
452 //                      W_Remotes.DoModal();\r
453                         Refresh();\r
454                 }\r
455                 break;\r
456         case eCmd_CreateBranch:\r
457                 {\r
458                         CCreateBranchTagDlg dlg(this);\r
459                         dlg.m_bIsTag=false;\r
460                         dlg.DoModal();\r
461                         Refresh();\r
462                 }\r
463                 break;\r
464         case eCmd_CreateTag:\r
465                 {\r
466                         CCreateBranchTagDlg dlg(this);\r
467                         dlg.m_bIsTag=true;\r
468                         dlg.DoModal();\r
469                         Refresh();\r
470                 }\r
471                 break;\r
472         }\r
473 }\r
474 \r
475 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
476 {\r
477         if (pMsg->message == WM_KEYDOWN)\r
478         {\r
479                 switch (pMsg->wParam)\r
480                 {\r
481 /*              case VK_RETURN:\r
482                         {\r
483                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
484                                 {\r
485                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
486                                         {\r
487                                                 PostMessage(WM_COMMAND, IDOK);\r
488                                         }\r
489                                         return TRUE;\r
490                                 }\r
491                         }\r
492                         break;\r
493 */              case VK_F5:\r
494                         {\r
495                                 Refresh();\r
496                         }\r
497                         break;\r
498                 }\r
499         }\r
500 \r
501 \r
502         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
503 }\r