OSDN Git Service

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