#include "stdafx.h"\r
#include "TortoiseProc.h"\r
#include "BrowseRefsDlg.h"\r
-\r
+#include "LogDlg.h"\r
+#include "AddRemoteDlg.h"\r
+#include "CreateBranchTagDlg.h"\r
+#include "Settings\SettingGitRemote.h"\r
+#include "SinglePropSheetDlg.h"\r
+#include "MessageBox.h"\r
\r
// CBrowseRefsDlg dialog\r
\r
IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)\r
\r
-CBrowseRefsDlg::CBrowseRefsDlg(CWnd* pParent /*=NULL*/)\r
- : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent)\r
+CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)\r
+: CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),\r
+ m_cmdPath(cmdPath)\r
{\r
\r
}\r
BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)\r
ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)\r
ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)\r
+ ON_WM_CONTEXTMENU()\r
END_MESSAGE_MAP()\r
\r
\r
AddAnchor(IDOK,BOTTOM_RIGHT);\r
AddAnchor(IDCANCEL,BOTTOM_RIGHT);\r
\r
- Refresh();\r
+ Refresh(true);\r
\r
\r
return TRUE;\r
}\r
\r
-CShadowTree* CShadowTree::GetNextSub(CString& nameLeft)\r
+CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)\r
{\r
int posSlash=nameLeft.Find('/');\r
CString nameSub;\r
if(nameSub.IsEmpty())\r
return NULL;\r
\r
+ if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())\r
+ return NULL;\r
+\r
CShadowTree& nextNode=m_ShadowTree[nameSub];\r
- nextNode.m_csName=nameSub;\r
+ nextNode.m_csRefName=nameSub;\r
nextNode.m_pParent=this;\r
return &nextNode;\r
}\r
\r
typedef std::map<CString,CString> MAP_STRING_STRING;\r
\r
-void CBrowseRefsDlg::Refresh()\r
+void CBrowseRefsDlg::Refresh(bool bSelectCurHead)\r
{\r
// m_RefMap.clear();\r
// g_Git.GetMapHashToFriendName(m_RefMap);\r
\r
+ CString selectRef;\r
+ if(bSelectCurHead)\r
+ {\r
+ g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);\r
+ selectRef.Trim(L"\r\n\t ");\r
+ }\r
+ else\r
+ {\r
+ POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
+ //List ctrl selection?\r
+ if(pos)\r
+ {\r
+ CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(\r
+ m_ListRefLeafs.GetNextSelectedItem(pos));\r
+ selectRef=pTree->GetRefName();\r
+ }\r
+ else\r
+ {\r
+ //Tree ctrl selection?\r
+ HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();\r
+ if(hTree!=NULL)\r
+ {\r
+ CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);\r
+ selectRef=pTree->GetRefName();\r
+ }\r
+ }\r
+ }\r
\r
m_RefTreeCtrl.DeleteAllItems();\r
+ m_ListRefLeafs.DeleteAllItems();\r
m_TreeRoot.m_ShadowTree.clear();\r
- m_TreeRoot.m_csName="Refs";\r
+ m_TreeRoot.m_csRefName="refs";\r
+// m_TreeRoot.m_csShowName="Refs";\r
m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);\r
m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);\r
\r
//Populate ref tree\r
for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)\r
{\r
- CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first);\r
+ CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);\r
CString values=iterRefMap->second;\r
\r
int valuePos=0;\r
- treeLeaf.m_csRef= values.Tokenize(L"\04",valuePos);\r
+ treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos);\r
treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos);\r
treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos);\r
treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos);\r
}\r
\r
- CString currHead;\r
- g_Git.Run(L"git symbolic-ref HEAD",&currHead,CP_UTF8);\r
\r
- currHead.Trim(L"\r\n\t ");\r
-\r
- if(!SelectRef(currHead))\r
+ if(selectRef.IsEmpty() || !SelectRef(selectRef))\r
//Probably not on a branch. Select root node.\r
m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);\r
\r
if(wcsnicmp(refName,L"refs/",5)!=0)\r
return false; // Not a ref name\r
\r
- CShadowTree& treeLeafHead=GetTreeNode(refName);\r
+ CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);\r
+ if(treeLeafHead.m_hTree != NULL)\r
+ {\r
+ //Not a leaf. Select tree node and return\r
+ m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);\r
+ return true;\r
+ }\r
+\r
if(treeLeafHead.m_pParent==NULL)\r
return false; //Weird... should not occur.\r
\r
if(pCurrShadowTree == &treeLeafHead)\r
{\r
m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);\r
+ m_ListRefLeafs.EnsureVisible(indexPos,FALSE);\r
}\r
}\r
\r
return true;\r
}\r
\r
-CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos)\r
+CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)\r
{\r
if(pTreePos==NULL)\r
{\r
if(refName.IsEmpty())\r
return *pTreePos;//Found leaf\r
\r
- CShadowTree* pNextTree=pTreePos->GetNextSub(refName);\r
+ CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);\r
if(pNextTree==NULL)\r
{\r
- //Should not occur when all ref-names are valid.\r
- ASSERT(FALSE);\r
+ //Should not occur when all ref-names are valid and bCreateIfNotExist is true.\r
+ ASSERT(!bCreateIfNotExist);\r
return *pTreePos;\r
}\r
\r
if(pNextTree->m_hTree==NULL)\r
{\r
//New tree. Create node in control.\r
- pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csName,pTreePos->m_hTree,NULL);\r
+ pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);\r
m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);\r
}\r
}\r
\r
- return GetTreeNode(refName,pNextTree);\r
+ return GetTreeNode(refName, pNextTree, bCreateIfNotExist);\r
}\r
\r
\r
int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");\r
\r
m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);\r
- m_ListRefLeafs.SetItemText(indexItem,0,refNamePrefix+pTree->m_csName);\r
- m_ListRefLeafs.SetItemText(indexItem,1,refNamePrefix+pTree->m_csDate);\r
- m_ListRefLeafs.SetItemText(indexItem,2,refNamePrefix+pTree->m_csSubject);\r
- m_ListRefLeafs.SetItemText(indexItem,3,pTree->m_csRef);\r
+ m_ListRefLeafs.SetItemText(indexItem,0,refNamePrefix+pTree->m_csRefName);\r
+ m_ListRefLeafs.SetItemText(indexItem,1,pTree->m_csDate);\r
+ m_ListRefLeafs.SetItemText(indexItem,2,pTree->m_csSubject);\r
+ m_ListRefLeafs.SetItemText(indexItem,3,pTree->m_csRefHash);\r
}\r
else\r
{\r
\r
CString csThisName;\r
if(!isFirstLevel)\r
- csThisName=refNamePrefix+pTree->m_csName+L"/";\r
+ csThisName=refNamePrefix+pTree->m_csRefName+L"/";\r
for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)\r
{\r
FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);\r
}\r
}\r
}\r
+\r
+void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
+{\r
+\r
+ CPoint clientPoint=point;\r
+ m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
+\r
+\r
+ std::vector<CShadowTree*> selectedTrees;\r
+ selectedTrees.reserve(m_ListRefLeafs.GetSelectedCount());\r
+ POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
+ while(pos)\r
+ {\r
+ selectedTrees.push_back(\r
+ (CShadowTree*)m_ListRefLeafs.GetItemData(\r
+ m_ListRefLeafs.GetNextSelectedItem(pos)));\r
+ }\r
+\r
+ CMenu popupMenu;\r
+ popupMenu.CreatePopupMenu();\r
+\r
+ if(selectedTrees.size()==1)\r
+ {\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_ViewLog,L"View log");\r
+ if(selectedTrees[0]->IsFrom(L"refs/heads"))\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_DeleteBranch,L"Delete Branch");\r
+ else if(selectedTrees[0]->IsFrom(L"refs/tags"))\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_DeleteTag,L"Delete Tag");\r
+\r
+// CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
+// if(pTree==NULL)\r
+// return;\r
+ }\r
+\r
+\r
+ eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
+ switch(cmd)\r
+ {\r
+ case eCmd_ViewLog:\r
+ {\r
+ CLogDlg dlg;\r
+ dlg.SetStartRef(selectedTrees[0]->m_csRefHash);\r
+ dlg.DoModal();\r
+ }\r
+ break;\r
+ case eCmd_DeleteBranch:\r
+ {\r
+ if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))\r
+ DoDeleteRef(selectedTrees[0]->GetRefName(), true);\r
+ Refresh();\r
+ }\r
+ break;\r
+ case eCmd_DeleteTag:\r
+ {\r
+ if(ConfirmDeleteRef(selectedTrees[0]->GetRefName()))\r
+ DoDeleteRef(selectedTrees[0]->GetRefName(), true);\r
+ Refresh();\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)\r
+{\r
+ CString csMessage;\r
+ CString csTitle;\r
+\r
+ UINT mbIcon=MB_ICONQUESTION;\r
+ csMessage=L"Are you sure you want to delete the ";\r
+ if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
+ {\r
+ CString branchToDelete = completeRefName.Mid(11);\r
+ csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);\r
+ csMessage += "branch:\r\n\r\n<b>";\r
+ csMessage += branchToDelete;\r
+ csMessage += "</b>";\r
+\r
+ //Check if branch is fully merged in HEAD\r
+ CString branchHash = g_Git.GetHash(completeRefName);\r
+ CString commonAncestor;\r
+ CString cmd;\r
+ cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
+ g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
+\r
+ branchHash=branchHash.Left(40);\r
+ commonAncestor=commonAncestor.Left(40);\r
+ \r
+ if(commonAncestor != branchHash)\r
+ {\r
+ csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
+ mbIcon=MB_ICONWARNING;\r
+ }\r
+ }\r
+ else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
+ {\r
+ CString tagToDelete = completeRefName.Mid(10);\r
+ csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
+ csMessage += "tag:\r\n\r\n<b>";\r
+ csMessage += tagToDelete;\r
+ csMessage += "</b>";\r
+ }\r
+\r
+ return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
+\r
+}\r
+\r
+\r
+bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
+{\r
+ if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
+ {\r
+ CString branchToDelete = completeRefName.Mid(11);\r
+ CString cmd;\r
+ cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
+ CString resultDummy;\r
+ if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
+ {\r
+ CString errorMsg;\r
+ errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
+ CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
+ return false;\r
+ }\r
+ }\r
+ else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
+ {\r
+ CString tagToDelete = completeRefName.Mid(10);\r
+ CString cmd;\r
+ cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
+ CString resultDummy;\r
+ if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
+ {\r
+ CString errorMsg;\r
+ errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
+ CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+}\r
+\r
+void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
+{\r
+ if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);\r
+ else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
+}\r
+\r
+void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
+{\r
+ CMenu popupMenu;\r
+ popupMenu.CreatePopupMenu();\r
+\r
+ CPoint clientPoint=point;\r
+ m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
+\r
+ HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
+ if(hTreeItem!=NULL)\r
+ {\r
+ m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
+ CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreeItem);\r
+ if(pTree->IsFrom(L"refs/remotes"))\r
+ {\r
+// popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_ManageRemotes,L"Manage Remotes");\r
+ }\r
+ else if(pTree->IsFrom(L"refs/heads"))\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_CreateBranch,L"Create Branch");\r
+ else if(pTree->IsFrom(L"refs/tags"))\r
+ popupMenu.AppendMenu(MF_STRING,eCmd_CreateTag,L"Create Tag");\r
+ }\r
+\r
+ eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
+ switch(cmd)\r
+ {\r
+ case eCmd_AddRemote:\r
+ {\r
+ CAddRemoteDlg(this).DoModal();\r
+ Refresh();\r
+ }\r
+ break;\r
+ case eCmd_ManageRemotes:\r
+ {\r
+ CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(m_cmdPath),this).DoModal();\r
+// CSettingGitRemote W_Remotes(m_cmdPath);\r
+// W_Remotes.DoModal();\r
+ Refresh();\r
+ }\r
+ break;\r
+ case eCmd_CreateBranch:\r
+ {\r
+ CCreateBranchTagDlg dlg(this);\r
+ dlg.m_bIsTag=false;\r
+ dlg.DoModal();\r
+ Refresh();\r
+ }\r
+ break;\r
+ case eCmd_CreateTag:\r
+ {\r
+ CCreateBranchTagDlg dlg(this);\r
+ dlg.m_bIsTag=true;\r
+ dlg.DoModal();\r
+ Refresh();\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
+{\r
+ if (pMsg->message == WM_KEYDOWN)\r
+ {\r
+ switch (pMsg->wParam)\r
+ {\r
+/* case VK_RETURN:\r
+ {\r
+ if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
+ {\r
+ if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
+ {\r
+ PostMessage(WM_COMMAND, IDOK);\r
+ }\r
+ return TRUE;\r
+ }\r
+ }\r
+ break;\r
+*/ case VK_F5:\r
+ {\r
+ Refresh();\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
+}\r