OSDN Git Service

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