OSDN Git Service

BrowseRefs: Show context menu icons
[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 \r
16 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)\r
17 {\r
18         if (control == NULL)\r
19                 return;\r
20         // set the sort arrow\r
21         CHeaderCtrl * pHeader = control->GetHeaderCtrl();\r
22         HDITEM HeaderItem = {0};\r
23         HeaderItem.mask = HDI_FORMAT;\r
24         for (int i=0; i<pHeader->GetItemCount(); ++i)\r
25         {\r
26                 pHeader->GetItem(i, &HeaderItem);\r
27                 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);\r
28                 pHeader->SetItem(i, &HeaderItem);\r
29         }\r
30         if (nColumn >= 0)\r
31         {\r
32                 pHeader->GetItem(nColumn, &HeaderItem);\r
33                 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);\r
34                 pHeader->SetItem(nColumn, &HeaderItem);\r
35         }\r
36 }\r
37 \r
38 // CBrowseRefsDlg dialog\r
39 \r
40 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)\r
41 \r
42 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)\r
43 :       CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),\r
44         m_cmdPath(cmdPath),\r
45         m_currSortCol(-1),\r
46         m_currSortDesc(false),\r
47         m_initialRef(L"HEAD"),\r
48         m_pickRef_Kind(gPickRef_All)\r
49 {\r
50 \r
51 }\r
52 \r
53 CBrowseRefsDlg::~CBrowseRefsDlg()\r
54 {\r
55 }\r
56 \r
57 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)\r
58 {\r
59         CDialog::DoDataExchange(pDX);\r
60         DDX_Control(pDX, IDC_TREE_REF,                  m_RefTreeCtrl);\r
61         DDX_Control(pDX, IDC_LIST_REF_LEAFS,    m_ListRefLeafs);\r
62 }\r
63 \r
64 \r
65 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)\r
66         ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)\r
67         ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)\r
68         ON_WM_CONTEXTMENU()\r
69         ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)\r
70         ON_WM_DESTROY()\r
71         ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)\r
72 END_MESSAGE_MAP()\r
73 \r
74 \r
75 // CBrowseRefsDlg message handlers\r
76 \r
77 void CBrowseRefsDlg::OnBnClickedOk()\r
78 {\r
79         OnOK();\r
80 }\r
81 \r
82 BOOL CBrowseRefsDlg::OnInitDialog()\r
83 {\r
84         CResizableStandAloneDialog::OnInitDialog();\r
85 \r
86         AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);\r
87         AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);\r
88 \r
89         m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);\r
90         m_ListRefLeafs.InsertColumn(eCol_Name,  L"Name",0,150);\r
91         m_ListRefLeafs.InsertColumn(eCol_Date,  L"Date Last Commit",0,100);\r
92         m_ListRefLeafs.InsertColumn(eCol_Msg,   L"Last Commit",0,300);\r
93         m_ListRefLeafs.InsertColumn(eCol_Hash,  L"Hash",0,80);\r
94 \r
95         AddAnchor(IDOK,BOTTOM_RIGHT);\r
96         AddAnchor(IDCANCEL,BOTTOM_RIGHT);\r
97 \r
98         Refresh(m_initialRef);\r
99 \r
100         EnableSaveRestore(L"BrowseRefs");\r
101 \r
102 \r
103         m_ListRefLeafs.SetFocus();\r
104         return FALSE;\r
105 }\r
106 \r
107 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)\r
108 {\r
109         int posSlash=nameLeft.Find('/');\r
110         CString nameSub;\r
111         if(posSlash<0)\r
112         {\r
113                 nameSub=nameLeft;\r
114                 nameLeft.Empty();//Nothing left\r
115         }\r
116         else\r
117         {\r
118                 nameSub=nameLeft.Left(posSlash);\r
119                 nameLeft=nameLeft.Mid(posSlash+1);\r
120         }\r
121         if(nameSub.IsEmpty())\r
122                 return NULL;\r
123 \r
124         if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())\r
125                 return NULL;\r
126 \r
127         CShadowTree& nextNode=m_ShadowTree[nameSub];\r
128         nextNode.m_csRefName=nameSub;\r
129         nextNode.m_pParent=this;\r
130         return &nextNode;\r
131 }\r
132 \r
133 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)\r
134 {\r
135         if(IsLeaf())\r
136         {\r
137                 if(m_csRefName.GetLength() > partialRefName.GetLength())\r
138                         return NULL;\r
139                 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)\r
140                 {\r
141                         //Match of leaf name. Try match on total name.\r
142                         CString totalRefName = GetRefName();\r
143                         if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)\r
144                                 return this; //Also match. Found.\r
145                 }\r
146         }\r
147         else\r
148         {\r
149                 //Not a leaf. Search all nodes.\r
150                 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)\r
151                 {\r
152                         CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);\r
153                         if(pSubtree != NULL)\r
154                                 return pSubtree; //Found\r
155                 }\r
156         }\r
157         return NULL;//Not found\r
158 }\r
159 \r
160 \r
161 typedef std::map<CString,CString> MAP_STRING_STRING;\r
162 \r
163 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf)\r
164 {\r
165         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
166         //List ctrl selection?\r
167         if(pos)\r
168         {\r
169                 //A leaf is selected\r
170                 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(\r
171                                 m_ListRefLeafs.GetNextSelectedItem(pos));\r
172                 return pTree->GetRefName();\r
173         }\r
174         else if(!onlyIfLeaf)\r
175         {\r
176                 //Tree ctrl selection?\r
177                 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();\r
178                 if(hTree!=NULL)\r
179                 {\r
180                         CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);\r
181                         return pTree->GetRefName();\r
182                 }\r
183         }\r
184         return CString();//None\r
185 }\r
186 \r
187 void CBrowseRefsDlg::Refresh(CString selectRef)\r
188 {\r
189 //      m_RefMap.clear();\r
190 //      g_Git.GetMapHashToFriendName(m_RefMap);\r
191                 \r
192         if(!selectRef.IsEmpty())\r
193         {\r
194                 if(selectRef == "HEAD")\r
195                 {\r
196                         selectRef.Empty();\r
197                         g_Git.Run(L"git symbolic-ref HEAD",&selectRef,CP_UTF8);\r
198                         selectRef.Trim(L"\r\n\t ");\r
199                 }\r
200         }\r
201         else\r
202         {\r
203                 selectRef = GetSelectedRef(false);\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 \r
255                 int valuePos=0;\r
256                 treeLeaf.m_csRefHash=           values.Tokenize(L"\04",valuePos);\r
257                 treeLeaf.m_csDate=                      values.Tokenize(L"\04",valuePos);\r
258                 treeLeaf.m_csSubject=           values.Tokenize(L"\04",valuePos);\r
259                 treeLeaf.m_csAuthor=            values.Tokenize(L"\04",valuePos);\r
260                 treeLeaf.m_csDate_Iso8601=      values.Tokenize(L"\04",valuePos);\r
261         }\r
262 \r
263 \r
264         if(selectRef.IsEmpty() || !SelectRef(selectRef, false))\r
265                 //Probably not on a branch. Select root node.\r
266                 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);\r
267 \r
268 }\r
269 \r
270 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)\r
271 {\r
272         if(!bExactMatch)\r
273                 refName = GetFullRefName(refName);\r
274         if(wcsnicmp(refName,L"refs/",5)!=0)\r
275                 return false; // Not a ref name\r
276 \r
277         CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);\r
278         if(treeLeafHead.m_hTree != NULL)\r
279         {\r
280                 //Not a leaf. Select tree node and return\r
281                 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);\r
282                 return true;\r
283         }\r
284 \r
285         if(treeLeafHead.m_pParent==NULL)\r
286                 return false; //Weird... should not occur.\r
287 \r
288         //This is the current head.\r
289         m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);\r
290 \r
291         for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)\r
292         {\r
293                 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);\r
294                 if(pCurrShadowTree == &treeLeafHead)\r
295                 {\r
296                         m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);\r
297                         m_ListRefLeafs.EnsureVisible(indexPos,FALSE);\r
298                 }\r
299         }\r
300 \r
301         return true;\r
302 }\r
303 \r
304 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)\r
305 {\r
306         if(pTreePos==NULL)\r
307         {\r
308                 if(wcsnicmp(refName,L"refs/",5)==0)\r
309                         refName=refName.Mid(5);\r
310                 pTreePos=&m_TreeRoot;\r
311         }\r
312         if(refName.IsEmpty())\r
313                 return *pTreePos;//Found leaf\r
314 \r
315         CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);\r
316         if(pNextTree==NULL)\r
317         {\r
318                 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.\r
319                 ASSERT(!bCreateIfNotExist);\r
320                 return *pTreePos;\r
321         }\r
322 \r
323         if(!refName.IsEmpty())\r
324         {\r
325                 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.\r
326                 //Leafs are for the list control.\r
327                 if(pNextTree->m_hTree==NULL)\r
328                 {\r
329                         //New tree. Create node in control.\r
330                         pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);\r
331                         m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);\r
332                 }\r
333         }\r
334 \r
335         return GetTreeNode(refName, pNextTree, bCreateIfNotExist);\r
336 }\r
337 \r
338 \r
339 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)\r
340 {\r
341         LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);\r
342         *pResult = 0;\r
343 \r
344         FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);\r
345 }\r
346 \r
347 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)\r
348 {\r
349         m_ListRefLeafs.DeleteAllItems();\r
350         m_currSortCol = -1;\r
351         m_currSortDesc = false;\r
352         SetSortArrow(&m_ListRefLeafs,-1,false);\r
353 \r
354         CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));\r
355         if(pTree==NULL)\r
356         {\r
357                 ASSERT(FALSE);\r
358                 return;\r
359         }\r
360         FillListCtrlForShadowTree(pTree,L"",true);\r
361 }\r
362 \r
363 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)\r
364 {\r
365         if(pTree->IsLeaf())\r
366         {\r
367                 int indexItem=m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(),L"");\r
368 \r
369                 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);\r
370                 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, refNamePrefix+pTree->m_csRefName);\r
371                 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);\r
372                 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg,  pTree->m_csSubject);\r
373                 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);\r
374         }\r
375         else\r
376         {\r
377 \r
378                 CString csThisName;\r
379                 if(!isFirstLevel)\r
380                         csThisName=refNamePrefix+pTree->m_csRefName+L"/";\r
381                 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)\r
382                 {\r
383                         FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);\r
384                 }\r
385         }\r
386 }\r
387 \r
388 bool CBrowseRefsDlg::ConfirmDeleteRef(CString completeRefName)\r
389 {\r
390         CString csMessage;\r
391         CString csTitle;\r
392 \r
393         UINT mbIcon=MB_ICONQUESTION;\r
394         csMessage=L"Are you sure you want to delete the ";\r
395         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
396         {\r
397                 CString branchToDelete = completeRefName.Mid(11);\r
398                 csTitle.Format(L"Confirm deletion of branch %s", branchToDelete);\r
399                 csMessage += "branch:\r\n\r\n<b>";\r
400                 csMessage += branchToDelete;\r
401                 csMessage += "</b>";\r
402 \r
403                 //Check if branch is fully merged in HEAD\r
404                 CString branchHash = g_Git.GetHash(completeRefName);\r
405                 CString commonAncestor;\r
406                 CString cmd;\r
407                 cmd.Format(L"git.exe merge-base HEAD %s",completeRefName);\r
408                 g_Git.Run(cmd,&commonAncestor,CP_UTF8);\r
409 \r
410                 branchHash=branchHash.Left(40);\r
411                 commonAncestor=commonAncestor.Left(40);\r
412                 \r
413                 if(commonAncestor != branchHash)\r
414                 {\r
415                         csMessage += L"\r\n\r\n<b>Warning:\r\nThis branch is not fully merged into HEAD.</b>";\r
416                         mbIcon=MB_ICONWARNING;\r
417                 }\r
418         }\r
419         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
420         {\r
421                 CString tagToDelete = completeRefName.Mid(10);\r
422                 csTitle.Format(L"Confirm deletion of tag %s", tagToDelete);\r
423                 csMessage += "tag:\r\n\r\n<b>";\r
424                 csMessage += tagToDelete;\r
425                 csMessage += "</b>";\r
426         }\r
427 \r
428         return CMessageBox::Show(m_hWnd,csMessage,csTitle,MB_YESNO|mbIcon)==IDYES;\r
429 \r
430 }\r
431 \r
432 \r
433 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)\r
434 {\r
435         if(wcsncmp(completeRefName,L"refs/heads",10)==0)\r
436         {\r
437                 CString branchToDelete = completeRefName.Mid(11);\r
438                 CString cmd;\r
439                 cmd.Format(L"git.exe branch -%c %s",bForce?L'D':L'd',branchToDelete);\r
440                 CString resultDummy;\r
441                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
442                 {\r
443                         CString errorMsg;\r
444                         errorMsg.Format(L"Could not delete branch %s. Message from git:\r\n\r\n%s",branchToDelete,resultDummy);\r
445                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting branch",MB_OK|MB_ICONERROR);\r
446                         return false;\r
447                 }\r
448         }\r
449         else if(wcsncmp(completeRefName,L"refs/tags",9)==0)\r
450         {\r
451                 CString tagToDelete = completeRefName.Mid(10);\r
452                 CString cmd;\r
453                 cmd.Format(L"git.exe tag -d %s",tagToDelete);\r
454                 CString resultDummy;\r
455                 if(g_Git.Run(cmd,&resultDummy,CP_UTF8)!=0)\r
456                 {\r
457                         CString errorMsg;\r
458                         errorMsg.Format(L"Could not delete tag %s. Message from git:\r\n\r\n%s",tagToDelete,resultDummy);\r
459                         CMessageBox::Show(m_hWnd,errorMsg,L"Error deleting tag",MB_OK|MB_ICONERROR);\r
460                         return false;\r
461                 }\r
462         }\r
463         return true;\r
464 }\r
465 \r
466 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)\r
467 {\r
468         CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);\r
469         if(pLeaf == NULL)\r
470                 return CString();\r
471         return pLeaf->GetRefName();\r
472 }\r
473 \r
474 \r
475 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)\r
476 {\r
477         if(pWndFrom==&m_RefTreeCtrl)       OnContextMenu_RefTreeCtrl(point);\r
478         else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);\r
479 }\r
480 \r
481 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)\r
482 {\r
483         CPoint clientPoint=point;\r
484         m_RefTreeCtrl.ScreenToClient(&clientPoint);\r
485 \r
486         HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);\r
487         if(hTreeItem!=NULL)\r
488                 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);\r
489 \r
490         ShowContextMenu(point,hTreeItem,VectorPShadowTree());\r
491 }\r
492 \r
493 \r
494 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)\r
495 {\r
496         std::vector<CShadowTree*> selectedLeafs;\r
497         selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());\r
498         POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();\r
499         while(pos)\r
500         {\r
501                 selectedLeafs.push_back(\r
502                         (CShadowTree*)m_ListRefLeafs.GetItemData(\r
503                                 m_ListRefLeafs.GetNextSelectedItem(pos)));\r
504         }\r
505 \r
506         ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);\r
507 }\r
508 \r
509 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)\r
510 {\r
511         CIconMenu popupMenu;\r
512         popupMenu.CreatePopupMenu();\r
513 \r
514         bool bAddSeparator = false;\r
515         if(selectedLeafs.size()==1)\r
516         {\r
517                 bAddSeparator = true;\r
518 \r
519                 bool bShowReflogOption = false;\r
520                 bool bShowDeleteBranchOption = false;\r
521                 bool bShowDeleteTagOption = false;\r
522 \r
523                 if(selectedLeafs[0]->IsFrom(L"refs/heads"))\r
524                 {\r
525                         bShowReflogOption = true;\r
526                         bShowDeleteBranchOption = true;\r
527                 }\r
528                 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))\r
529                 {\r
530                         bShowReflogOption = true;\r
531                 }\r
532                 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))\r
533                 {\r
534                         bShowDeleteTagOption = true;\r
535                 }\r
536 \r
537                                                                         popupMenu.AppendMenuIcon(eCmd_ViewLog, L"Show Log", IDI_LOG);\r
538                 if(bShowReflogOption)           popupMenu.AppendMenuIcon(eCmd_ShowReflog, L"Show Reflog", IDI_LOG);\r
539                 if(bShowDeleteTagOption)        popupMenu.AppendMenuIcon(eCmd_DeleteTag, L"Delete Tag", IDI_DELETE);\r
540                 if(bShowDeleteBranchOption) popupMenu.AppendMenuIcon(eCmd_DeleteBranch, L"Delete Branch", IDI_DELETE);\r
541 \r
542 \r
543 \r
544 //              CShadowTree* pTree = (CShadowTree*)m_ListRefLeafs.GetItemData(pNMHDR->idFrom);\r
545 //              if(pTree==NULL)\r
546 //                      return;\r
547         }\r
548 \r
549         if(bAddSeparator) popupMenu.AppendMenu(MF_SEPARATOR);\r
550 \r
551         if(hTreePos!=NULL)\r
552         {\r
553                 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);\r
554                 if(pTree->IsFrom(L"refs/remotes"))\r
555                 {\r
556 //                      popupMenu.AppendMenu(MF_STRING,eCmd_AddRemote,L"Add Remote");\r
557                         popupMenu.AppendMenuIcon(eCmd_ManageRemotes, L"Manage Remotes", IDI_SETTINGS);\r
558                 }\r
559                 else if(pTree->IsFrom(L"refs/heads"))\r
560                         popupMenu.AppendMenuIcon(eCmd_CreateBranch, L"Create Branch", IDI_COPY);\r
561                 else if(pTree->IsFrom(L"refs/tags"))\r
562                         popupMenu.AppendMenuIcon(eCmd_CreateTag, L"Create Tag", IDI_TAG);\r
563         }\r
564 \r
565 \r
566         eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);\r
567         switch(cmd)\r
568         {\r
569         case eCmd_ViewLog:\r
570                 {\r
571                         CLogDlg dlg;\r
572                         dlg.SetStartRef(selectedLeafs[0]->GetRefName());\r
573                         dlg.DoModal();\r
574                 }\r
575                 break;\r
576         case eCmd_DeleteBranch:\r
577                 {\r
578                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
579                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
580                         Refresh();\r
581                 }\r
582                 break;\r
583         case eCmd_DeleteTag:\r
584                 {\r
585                         if(ConfirmDeleteRef(selectedLeafs[0]->GetRefName()))\r
586                                 DoDeleteRef(selectedLeafs[0]->GetRefName(), true);\r
587                         Refresh();\r
588                 }\r
589                 break;\r
590         case eCmd_ShowReflog:\r
591                 {\r
592                         CRefLogDlg refLogDlg(this);\r
593                         refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();\r
594                         refLogDlg.DoModal();\r
595                 }\r
596                 break;\r
597         case eCmd_AddRemote:\r
598                 {\r
599                         CAddRemoteDlg(this).DoModal();\r
600                         Refresh();\r
601                 }\r
602                 break;\r
603         case eCmd_ManageRemotes:\r
604                 {\r
605                         CSinglePropSheetDlg(L"Git Remote Settings",new CSettingGitRemote(g_Git.m_CurrentDir),this).DoModal();\r
606 //                      CSettingGitRemote W_Remotes(m_cmdPath);\r
607 //                      W_Remotes.DoModal();\r
608                         Refresh();\r
609                 }\r
610                 break;\r
611         case eCmd_CreateBranch:\r
612                 {\r
613                         CAppUtils::CreateBranchTag(false);\r
614                         Refresh();\r
615                 }\r
616                 break;\r
617         case eCmd_CreateTag:\r
618                 {\r
619                         CAppUtils::CreateBranchTag(true);\r
620                         Refresh();\r
621                 }\r
622                 break;\r
623         }\r
624 }\r
625 \r
626 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)\r
627 {\r
628         if (pMsg->message == WM_KEYDOWN)\r
629         {\r
630                 switch (pMsg->wParam)\r
631                 {\r
632 /*              case VK_RETURN:\r
633                         {\r
634                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
635                                 {\r
636                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
637                                         {\r
638                                                 PostMessage(WM_COMMAND, IDOK);\r
639                                         }\r
640                                         return TRUE;\r
641                                 }\r
642                         }\r
643                         break;\r
644 */              case VK_F5:\r
645                         {\r
646                                 Refresh();\r
647                         }\r
648                         break;\r
649                 }\r
650         }\r
651 \r
652 \r
653         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
654 }\r
655 \r
656 class CRefLeafListCompareFunc\r
657 {\r
658 public:\r
659         CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}\r
660 \r
661         static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)\r
662         {\r
663                 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);\r
664         }\r
665 \r
666         int Compare(LPARAM lParam1, LPARAM lParam2)\r
667         {\r
668                 return Compare(\r
669                         (CShadowTree*)m_pList->GetItemData(lParam1), \r
670                         (CShadowTree*)m_pList->GetItemData(lParam2));\r
671         }\r
672 \r
673         int Compare(CShadowTree* pLeft, CShadowTree* pRight)\r
674         {\r
675                 int result=CompareNoDesc(pLeft,pRight);\r
676                 if(m_desc)\r
677                         return -result;\r
678                 return result;\r
679         }\r
680 \r
681         int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)\r
682         {\r
683                 switch(m_col)\r
684                 {\r
685                 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());\r
686                 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);\r
687                 case CBrowseRefsDlg::eCol_Msg:  return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);\r
688                 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);\r
689                 }\r
690                 return 0;\r
691         }\r
692 \r
693         int m_col;\r
694         bool m_desc;\r
695         CListCtrl* m_pList;\r
696 \r
697 \r
698 };\r
699 \r
700 \r
701 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
702 {\r
703         LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);\r
704         *pResult = 0;\r
705 \r
706         if(m_currSortCol == pNMLV->iSubItem)\r
707                 m_currSortDesc = !m_currSortDesc;\r
708         else\r
709         {\r
710                 m_currSortCol  = pNMLV->iSubItem;\r
711                 m_currSortDesc = false;\r
712         }\r
713 \r
714         CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);\r
715         m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);\r
716 \r
717         SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);\r
718 }\r
719 \r
720 void CBrowseRefsDlg::OnDestroy()\r
721 {\r
722         m_pickedRef = GetSelectedRef(true);\r
723 \r
724         CResizableStandAloneDialog::OnDestroy();\r
725 }\r
726 \r
727 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)\r
728 {\r
729         LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);\r
730         *pResult = 0;\r
731 \r
732         EndDialog(IDOK);\r
733 }\r
734 \r
735 CString CBrowseRefsDlg::PickRef(bool returnAsHash, CString initialRef, int pickRef_Kind)\r
736 {\r
737         CBrowseRefsDlg dlg(CString(),NULL);\r
738         \r
739         dlg.m_initialRef = initialRef;\r
740         dlg.m_pickRef_Kind = pickRef_Kind;\r
741 \r
742         if(dlg.DoModal() != IDOK)\r
743                 return CString();\r
744 \r
745         return dlg.m_pickedRef;\r
746 }\r
747 \r
748 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)\r
749 {\r
750         CString origRef;\r
751         pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);\r
752         CString resultRef = PickRef(false,origRef,pickRef_Kind);\r
753         if(resultRef.IsEmpty())\r
754                 return false;\r
755         if(wcsncmp(resultRef,L"refs/",5)==0)\r
756                 resultRef = resultRef.Mid(5);\r
757 //      if(wcsncmp(resultRef,L"heads/",6)==0)\r
758 //              resultRef = resultRef.Mid(6);\r
759 \r
760         //Find closest match of choice in combobox\r
761         int ixFound = -1;\r
762         int matchLength = 0;\r
763         CString comboRefName;\r
764         for(int i = 0; i < pComboBox->GetCount(); ++i)\r
765         {\r
766                 pComboBox->GetLBText(i, comboRefName);\r
767                 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)\r
768                 {\r
769                         matchLength = comboRefName.GetLength();\r
770                         ixFound = i;\r
771                 }\r
772         }\r
773         if(ixFound >= 0)\r
774                 pComboBox->SetCurSel(ixFound);\r
775         else\r
776                 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
777 \r
778         return true;\r
779 }\r