BrowseRefs: Removed unused confirm dialog
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob915b5cfec56b561df94780f7ff2876d82081fb70
1 // BrowseRefsDlg.cpp : implementation file
2 //
4 #include "stdafx.h"
5 #include "TortoiseProc.h"
6 #include "BrowseRefsDlg.h"
7 #include "LogDlg.h"
8 #include "AddRemoteDlg.h"
9 #include "CreateBranchTagDlg.h"
10 #include "Settings\SettingGitRemote.h"
11 #include "SinglePropSheetDlg.h"
12 #include "MessageBox.h"
14 // CBrowseRefsDlg dialog
16 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
18 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
19 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
20 m_cmdPath(cmdPath)
25 CBrowseRefsDlg::~CBrowseRefsDlg()
29 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
31 CDialog::DoDataExchange(pDX);
32 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
33 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
37 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
38 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
39 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
40 ON_WM_CONTEXTMENU()
41 END_MESSAGE_MAP()
44 // CBrowseRefsDlg message handlers
46 void CBrowseRefsDlg::OnBnClickedOk()
48 OnOK();
51 BOOL CBrowseRefsDlg::OnInitDialog()
53 CResizableStandAloneDialog::OnInitDialog();
55 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
56 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
58 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
59 m_ListRefLeafs.InsertColumn(0,L"Name",0,150);
60 m_ListRefLeafs.InsertColumn(1,L"Date Last Commit",0,100);
61 m_ListRefLeafs.InsertColumn(2,L"Last Commit",0,300);
62 m_ListRefLeafs.InsertColumn(3,L"Hash",0,80);
64 AddAnchor(IDOK,BOTTOM_RIGHT);
65 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
67 Refresh(true);
70 return TRUE;
73 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
75 int posSlash=nameLeft.Find('/');
76 CString nameSub;
77 if(posSlash<0)
79 nameSub=nameLeft;
80 nameLeft.Empty();//Nothing left
82 else
84 nameSub=nameLeft.Left(posSlash);
85 nameLeft=nameLeft.Mid(posSlash+1);
87 if(nameSub.IsEmpty())
88 return NULL;
90 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
91 return NULL;
93 CShadowTree& nextNode=m_ShadowTree[nameSub];
94 nextNode.m_csRefName=nameSub;
95 nextNode.m_pParent=this;
96 return &nextNode;
99 typedef std::map<CString,CString> MAP_STRING_STRING;
101 void CBrowseRefsDlg::Refresh(bool bSelectCurHead)
103 // m_RefMap.clear();
104 // g_Git.GetMapHashToFriendName(m_RefMap);
106 CString selectRef;
107 if(bSelectCurHead)
109 g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);
110 selectRef.Trim(L"\r\n\t ");
112 else
114 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
115 //List ctrl selection?
116 if(pos)
118 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
119 m_ListRefLeafs.GetNextSelectedItem(pos));
120 selectRef=pTree->GetRefName();
122 else
124 //Tree ctrl selection?
125 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
126 if(hTree!=NULL)
128 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
129 selectRef=pTree->GetRefName();
134 m_RefTreeCtrl.DeleteAllItems();
135 m_ListRefLeafs.DeleteAllItems();
136 m_TreeRoot.m_ShadowTree.clear();
137 m_TreeRoot.m_csRefName="refs";
138 // m_TreeRoot.m_csShowName="Refs";
139 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
140 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
142 CString allRefs;
143 g_Git.Run(L"git for-each-ref --format="
144 L"%(refname)%04"
145 L"%(objectname)%04"
146 L"%(authordate:relative)%04"
147 L"%(subject)%04"
148 L"%(authorname)",
149 &allRefs,CP_UTF8);
151 int linePos=0;
152 CString singleRef;
154 MAP_STRING_STRING refMap;
156 //First sort on ref name
157 while(!(singleRef=allRefs.Tokenize(L"\r\n",linePos)).IsEmpty())
159 int valuePos=0;
160 CString refName=singleRef.Tokenize(L"\04",valuePos);
161 CString refRest=singleRef.Mid(valuePos);
162 refMap[refName]=refRest;
167 // for(MAP_HASH_NAME::iterator iterRef=m_RefMap.begin();iterRef!=m_RefMap.end();++iterRef)
168 // for(STRING_VECTOR::iterator iterRefName=iterRef->second.begin();iterRefName!=iterRef->second.end();++iterRefName)
169 // refName[*iterRefName]=iterRef->first;
171 //Populate ref tree
172 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
174 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
175 CString values=iterRefMap->second;
177 int valuePos=0;
178 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);
179 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);
180 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);
181 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);
185 if(selectRef.IsEmpty() || !SelectRef(selectRef))
186 //Probably not on a branch. Select root node.
187 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
191 bool CBrowseRefsDlg::SelectRef(CString refName)
193 if(wcsnicmp(refName,L"refs/",5)!=0)
194 return false; // Not a ref name
196 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
197 if(treeLeafHead.m_hTree != NULL)
199 //Not a leaf. Select tree node and return
200 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
201 return true;
204 if(treeLeafHead.m_pParent==NULL)
205 return false; //Weird... should not occur.
207 //This is the current head.
208 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
210 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
212 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
213 if(pCurrShadowTree == &treeLeafHead)
215 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
216 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
220 return true;
223 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
225 if(pTreePos==NULL)
227 if(wcsnicmp(refName,L"refs/",5)==0)
228 refName=refName.Mid(5);
229 pTreePos=&m_TreeRoot;
231 if(refName.IsEmpty())
232 return *pTreePos;//Found leaf
234 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
235 if(pNextTree==NULL)
237 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
238 ASSERT(!bCreateIfNotExist);
239 return *pTreePos;
242 if(!refName.IsEmpty())
244 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
245 //Leafs are for the list control.
246 if(pNextTree->m_hTree==NULL)
248 //New tree. Create node in control.
249 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
250 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
254 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
258 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
260 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
261 *pResult = 0;
263 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
266 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
268 m_ListRefLeafs.DeleteAllItems();
270 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
271 if(pTree==NULL)
273 ASSERT(FALSE);
274 return;
276 FillListCtrlForShadowTree(pTree,L"",true);
279 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
281 if(pTree->IsLeaf())
283 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");
285 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
286 m_ListRefLeafs.SetItemText(indexItem,0,refNamePrefix+pTree->m_csRefName);
287 m_ListRefLeafs.SetItemText(indexItem,1,pTree->m_csDate);
288 m_ListRefLeafs.SetItemText(indexItem,2,pTree->m_csSubject);
289 m_ListRefLeafs.SetItemText(indexItem,3,pTree->m_csRefHash);
291 else
294 CString csThisName;
295 if(!isFirstLevel)
296 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
297 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
299 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
304 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
307 CPoint clientPoint=point;
308 m_RefTreeCtrl.ScreenToClient(&clientPoint);
311 std::vector<CShadowTree*> selectedTrees;
312 selectedTrees.reserve(m_ListRefLeafs.GetSelectedCount());
313 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
314 while(pos)
316 selectedTrees.push_back(
317 (CShadowTree*)m_ListRefLeafs.GetItemData(
318 m_ListRefLeafs.GetNextSelectedItem(pos)));
321 CMenu popupMenu;
322 popupMenu.CreatePopupMenu();
324 if(selectedTrees.size()==1)
326 popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");
327 if(selectedTrees[0]->IsFrom(L"refs/heads"))
328 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");
329 else if(selectedTrees[0]->IsFrom(L"refs/tags"))
330 popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");
332 // CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);
333 // if(pTree==NULL)
334 // return;
338 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
339 switch(cmd)
341 case eCmd_ViewLog:
343 CLogDlg dlg;
344 dlg.SetStartRef(selectedTrees[0]->m_csRefHash);
345 dlg.DoModal();
347 break;
348 case eCmd_DeleteBranch:
350 if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))
351 DoDeleteRef(selectedTrees[0]->GetRefName(), true);
352 Refresh();
354 break;
355 case eCmd_DeleteTag:
357 if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))
358 DoDeleteRef(selectedTrees[0]->GetRefName(), true);
359 Refresh();
361 break;
365 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)
367 CString csMessage;
368 CString csTitle;
370 UINT mbIcon=MB_ICONQUESTION;
371 csMessage=L"Are you sure you want to delete the ";
372 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
374 CString branchToDelete = completeRefName.Mid(11);
375 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);
376 csMessage += "branch:\r\n\r\n<b>";
377 csMessage += branchToDelete;
378 csMessage += "</b>";
380 //Check if branch is fully merged in HEAD
381 CString branchHash = g_Git.GetHash(completeRefName);
382 CString commonAncestor;
383 CString cmd;
384 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);
385 g_Git.Run(cmd,&commonAncestor,CP_UTF8);
387 branchHash=branchHash.Left(40);
388 commonAncestor=commonAncestor.Left(40);
390 if(commonAncestor != branchHash)
392 csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";
393 mbIcon=MB_ICONWARNING;
396 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
398 CString tagToDelete = completeRefName.Mid(10);
399 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);
400 csMessage += "tag:\r\n\r\n<b>";
401 csMessage += tagToDelete;
402 csMessage += "</b>";
405 return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;
410 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
412 if(wcsncmp(completeRefName,L"refs/heads",10)==0)
414 CString branchToDelete = completeRefName.Mid(11);
415 CString cmd;
416 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);
417 CString resultDummy;
418 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
420 CString errorMsg;
421 errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);
422 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);
423 return false;
426 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
428 CString tagToDelete = completeRefName.Mid(10);
429 CString cmd;
430 cmd.Format(L"git.exe tag -d %s",tagToDelete);
431 CString resultDummy;
432 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)
434 CString errorMsg;
435 errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);
436 CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);
437 return false;
440 return true;
443 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
445 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
446 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
449 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
451 CMenu popupMenu;
452 popupMenu.CreatePopupMenu();
454 CPoint clientPoint=point;
455 m_RefTreeCtrl.ScreenToClient(&clientPoint);
457 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
458 if(hTreeItem!=NULL)
460 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
461 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreeItem);
462 if(pTree->IsFrom(L"refs/remotes"))
464 // popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");
465 popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");
467 else if(pTree->IsFrom(L"refs/heads"))
468 popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");
469 else if(pTree->IsFrom(L"refs/tags"))
470 popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");
473 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
474 switch(cmd)
476 case eCmd_AddRemote:
478 CAddRemoteDlg(this).DoModal();
479 Refresh();
481 break;
482 case eCmd_ManageRemotes:
484 CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();
485 // CSettingGitRemote W_Remotes(m_cmdPath);
486 // W_Remotes.DoModal();
487 Refresh();
489 break;
490 case eCmd_CreateBranch:
492 CCreateBranchTagDlg dlg(this);
493 dlg.m_bIsTag=false;
494 dlg.DoModal();
495 Refresh();
497 break;
498 case eCmd_CreateTag:
500 CCreateBranchTagDlg dlg(this);
501 dlg.m_bIsTag=true;
502 dlg.DoModal();
503 Refresh();
505 break;
509 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
511 if (pMsg->message == WM_KEYDOWN)
513 switch (pMsg->wParam)
515 /* case VK_RETURN:
517 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
519 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
521 PostMessage(WM_COMMAND, IDOK);
523 return TRUE;
526 break;
527 */ case VK_F5:
529 Refresh();
531 break;
536 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);