OSDN Git Service

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