OSDN Git Service

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