RefBrowser: improved filter
[TortoiseGit.git] / src / TortoiseProc / BrowseRefsDlg.cpp
blob82261e08f80df342fe9868f0525f08b3051be66a
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // BrowseRefsDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "BrowseRefsDlg.h"
25 #include "LogDlg.h"
26 #include "AddRemoteDlg.h"
27 #include "AppUtils.h"
28 #include "Settings\SettingGitRemote.h"
29 #include "SinglePropSheetDlg.h"
30 #include "MessageBox.h"
31 #include "RefLogDlg.h"
32 #include "IconMenu.h"
33 #include "FileDiffDlg.h"
35 void SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
37 if (control == NULL)
38 return;
39 // set the sort arrow
40 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
41 HDITEM HeaderItem = {0};
42 HeaderItem.mask = HDI_FORMAT;
43 for (int i=0; i<pHeader->GetItemCount(); ++i)
45 pHeader->GetItem(i, &HeaderItem);
46 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
47 pHeader->SetItem(i, &HeaderItem);
49 if (nColumn >= 0)
51 pHeader->GetItem(nColumn, &HeaderItem);
52 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
53 pHeader->SetItem(nColumn, &HeaderItem);
57 // CBrowseRefsDlg dialog
59 IMPLEMENT_DYNAMIC(CBrowseRefsDlg, CResizableStandAloneDialog)
61 CBrowseRefsDlg::CBrowseRefsDlg(CString cmdPath, CWnd* pParent /*=NULL*/)
62 : CResizableStandAloneDialog(CBrowseRefsDlg::IDD, pParent),
63 m_cmdPath(cmdPath),
64 m_currSortCol(-1),
65 m_currSortDesc(false),
66 m_initialRef(L"HEAD"),
67 m_pickRef_Kind(gPickRef_All),
68 m_pListCtrlRoot(NULL),
69 m_bHasWC(true),
70 m_SelectedFilters(LOGFILTER_ALL)
75 CBrowseRefsDlg::~CBrowseRefsDlg()
79 void CBrowseRefsDlg::DoDataExchange(CDataExchange* pDX)
81 CDialog::DoDataExchange(pDX);
82 DDX_Control(pDX, IDC_TREE_REF, m_RefTreeCtrl);
83 DDX_Control(pDX, IDC_LIST_REF_LEAFS, m_ListRefLeafs);
84 DDX_Control(pDX, IDC_BROWSEREFS_EDIT_FILTER, m_ctrlFilter);
88 BEGIN_MESSAGE_MAP(CBrowseRefsDlg, CResizableStandAloneDialog)
89 ON_BN_CLICKED(IDOK, &CBrowseRefsDlg::OnBnClickedOk)
90 ON_NOTIFY(TVN_SELCHANGED, IDC_TREE_REF, &CBrowseRefsDlg::OnTvnSelchangedTreeRef)
91 ON_WM_CONTEXTMENU()
92 ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnColumnclickListRefLeafs)
93 ON_WM_DESTROY()
94 ON_NOTIFY(NM_DBLCLK, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnNMDblclkListRefLeafs)
95 ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs)
96 ON_NOTIFY(LVN_BEGINLABELEDIT, IDC_LIST_REF_LEAFS, &CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs)
97 ON_EN_CHANGE(IDC_BROWSEREFS_EDIT_FILTER, &CBrowseRefsDlg::OnEnChangeEditFilter)
98 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
99 ON_WM_TIMER()
100 END_MESSAGE_MAP()
103 // CBrowseRefsDlg message handlers
105 void CBrowseRefsDlg::OnBnClickedOk()
107 OnOK();
110 BOOL CBrowseRefsDlg::OnInitDialog()
112 CResizableStandAloneDialog::OnInitDialog();
113 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
115 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
116 m_ctrlFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
117 m_ctrlFilter.SetInfoIcon(IDI_FILTEREDIT);
118 SetFilterCueText();
120 AddAnchor(IDC_TREE_REF, TOP_LEFT, BOTTOM_LEFT);
121 AddAnchor(IDC_LIST_REF_LEAFS, TOP_LEFT, BOTTOM_RIGHT);
122 AddAnchor(IDC_BROWSEREFS_STATIC_FILTER, BOTTOM_LEFT);
123 AddAnchor(IDC_BROWSEREFS_EDIT_FILTER, BOTTOM_LEFT, BOTTOM_RIGHT);
124 AddAnchor(IDHELP, BOTTOM_RIGHT);
126 m_ListRefLeafs.SetExtendedStyle(m_ListRefLeafs.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
127 CString temp;
128 temp.LoadString(IDS_BRANCHNAME);
129 m_ListRefLeafs.InsertColumn(eCol_Name, temp, 0, 150);
130 temp.LoadString(IDS_DATELASTCOMMIT);
131 m_ListRefLeafs.InsertColumn(eCol_Date, temp, 0, 100);
132 temp.LoadString(IDS_LASTCOMMIT);
133 m_ListRefLeafs.InsertColumn(eCol_Msg, temp, 0, 300);
134 temp.LoadString(IDS_HASH);
135 m_ListRefLeafs.InsertColumn(eCol_Hash, temp, 0, 80);
137 AddAnchor(IDOK,BOTTOM_RIGHT);
138 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
140 Refresh(m_initialRef);
142 EnableSaveRestore(L"BrowseRefs");
144 CString sWindowTitle;
145 GetWindowText(sWindowTitle);
146 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
148 m_bHasWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
150 m_ListRefLeafs.SetFocus();
151 return FALSE;
154 CShadowTree* CShadowTree::GetNextSub(CString& nameLeft, bool bCreateIfNotExist)
156 int posSlash=nameLeft.Find('/');
157 CString nameSub;
158 if(posSlash<0)
160 nameSub=nameLeft;
161 nameLeft.Empty();//Nothing left
163 else
165 nameSub=nameLeft.Left(posSlash);
166 nameLeft=nameLeft.Mid(posSlash+1);
168 if(nameSub.IsEmpty())
169 return NULL;
171 if(!bCreateIfNotExist && m_ShadowTree.find(nameSub)==m_ShadowTree.end())
172 return NULL;
174 CShadowTree& nextNode=m_ShadowTree[nameSub];
175 nextNode.m_csRefName=nameSub;
176 nextNode.m_pParent=this;
177 return &nextNode;
180 CShadowTree* CShadowTree::FindLeaf(CString partialRefName)
182 if(IsLeaf())
184 if(m_csRefName.GetLength() > partialRefName.GetLength())
185 return NULL;
186 if(partialRefName.Right(m_csRefName.GetLength()) == m_csRefName)
188 //Match of leaf name. Try match on total name.
189 CString totalRefName = GetRefName();
190 if(totalRefName.Right(partialRefName.GetLength()) == partialRefName)
191 return this; //Also match. Found.
194 else
196 //Not a leaf. Search all nodes.
197 for(TShadowTreeMap::iterator itShadowTree = m_ShadowTree.begin(); itShadowTree != m_ShadowTree.end(); ++itShadowTree)
199 CShadowTree* pSubtree = itShadowTree->second.FindLeaf(partialRefName);
200 if(pSubtree != NULL)
201 return pSubtree; //Found
204 return NULL;//Not found
208 typedef std::map<CString,CString> MAP_STRING_STRING;
210 CString CBrowseRefsDlg::GetSelectedRef(bool onlyIfLeaf, bool pickFirstSelIfMultiSel)
212 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
213 //List ctrl selection?
214 if(pos && (pickFirstSelIfMultiSel || m_ListRefLeafs.GetSelectedCount() == 1))
216 //A leaf is selected
217 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(
218 m_ListRefLeafs.GetNextSelectedItem(pos));
219 return pTree->GetRefName();
221 else if(!onlyIfLeaf)
223 //Tree ctrl selection?
224 HTREEITEM hTree=m_RefTreeCtrl.GetSelectedItem();
225 if(hTree!=NULL)
227 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTree);
228 return pTree->GetRefName();
231 return CString();//None
234 void CBrowseRefsDlg::Refresh(CString selectRef)
236 // m_RefMap.clear();
237 // g_Git.GetMapHashToFriendName(m_RefMap);
239 if(!selectRef.IsEmpty())
241 if(selectRef == "HEAD")
243 selectRef = g_Git.GetSymbolicRef(selectRef, false);
246 else
248 selectRef = GetSelectedRef(false, true);
251 m_RefTreeCtrl.DeleteAllItems();
252 m_ListRefLeafs.DeleteAllItems();
253 m_TreeRoot.m_ShadowTree.clear();
254 m_TreeRoot.m_csRefName = "refs";
255 m_TreeRoot.m_hTree=m_RefTreeCtrl.InsertItem(L"Refs",NULL,NULL);
256 m_RefTreeCtrl.SetItemData(m_TreeRoot.m_hTree,(DWORD_PTR)&m_TreeRoot);
258 CString allRefs;
259 g_Git.Run(L"git for-each-ref --format="
260 L"%(refname)%04"
261 L"%(objectname)%04"
262 L"%(authordate:relative)%04"
263 L"%(subject)%04"
264 L"%(authorname)%04"
265 L"%(authordate:iso8601)%03",
266 &allRefs, NULL, CP_UTF8);
268 int linePos=0;
269 CString singleRef;
271 MAP_STRING_STRING refMap;
273 //First sort on ref name
274 while(!(singleRef=allRefs.Tokenize(L"\03",linePos)).IsEmpty())
276 singleRef.TrimLeft(L"\r\n");
277 int valuePos=0;
278 CString refName=singleRef.Tokenize(L"\04",valuePos);
279 if(refName.IsEmpty())
280 continue;
281 CString refRest=singleRef.Mid(valuePos);
284 //Use ref based on m_pickRef_Kind
285 if(wcsncmp(refName,L"refs/heads",10)==0 && !(m_pickRef_Kind & gPickRef_Head) )
286 continue; //Skip
287 if(wcsncmp(refName,L"refs/tags",9)==0 && !(m_pickRef_Kind & gPickRef_Tag) )
288 continue; //Skip
289 if(wcsncmp(refName,L"refs/remotes",12)==0 && !(m_pickRef_Kind & gPickRef_Remote) )
290 continue; //Skip
292 refMap[refName] = refRest; //Use
297 //Populate ref tree
298 for(MAP_STRING_STRING::iterator iterRefMap=refMap.begin();iterRefMap!=refMap.end();++iterRefMap)
300 CShadowTree& treeLeaf=GetTreeNode(iterRefMap->first,NULL,true);
301 CString values=iterRefMap->second;
302 values.Replace(L"\04" L"\04",L"\04 \04");//Workaround Tokenize problem (treating 2 tokens as one)
304 int valuePos=0;
305 treeLeaf.m_csRefHash= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
306 treeLeaf.m_csDate= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
307 treeLeaf.m_csSubject= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
308 treeLeaf.m_csAuthor= values.Tokenize(L"\04",valuePos); if(valuePos < 0) continue;
309 treeLeaf.m_csDate_Iso8601= values.Tokenize(L"\04",valuePos);
313 if(selectRef.IsEmpty() || !SelectRef(selectRef, false))
314 //Probably not on a branch. Select root node.
315 m_RefTreeCtrl.Expand(m_TreeRoot.m_hTree,TVE_EXPAND);
319 bool CBrowseRefsDlg::SelectRef(CString refName, bool bExactMatch)
321 if(!bExactMatch)
323 CString newRefName = GetFullRefName(refName);
324 if(!newRefName.IsEmpty())
325 refName = newRefName;
326 //else refName is not a valid ref. Try to select as good as possible.
328 if(_wcsnicmp(refName, L"refs/", 5) != 0)
329 return false; // Not a ref name
331 CShadowTree& treeLeafHead=GetTreeNode(refName,NULL,false);
332 if(treeLeafHead.m_hTree != NULL)
334 //Not a leaf. Select tree node and return
335 m_RefTreeCtrl.Select(treeLeafHead.m_hTree,TVGN_CARET);
336 return true;
339 if(treeLeafHead.m_pParent==NULL)
340 return false; //Weird... should not occur.
342 //This is the current head.
343 m_RefTreeCtrl.Select(treeLeafHead.m_pParent->m_hTree,TVGN_CARET);
345 for(int indexPos = 0; indexPos < m_ListRefLeafs.GetItemCount(); ++indexPos)
347 CShadowTree* pCurrShadowTree = (CShadowTree*)m_ListRefLeafs.GetItemData(indexPos);
348 if(pCurrShadowTree == &treeLeafHead)
350 m_ListRefLeafs.SetItemState(indexPos,LVIS_SELECTED,LVIS_SELECTED);
351 m_ListRefLeafs.EnsureVisible(indexPos,FALSE);
355 return true;
358 CShadowTree& CBrowseRefsDlg::GetTreeNode(CString refName, CShadowTree* pTreePos, bool bCreateIfNotExist)
360 if(pTreePos==NULL)
362 if(_wcsnicmp(refName, L"refs/", 5) == 0)
363 refName=refName.Mid(5);
364 pTreePos=&m_TreeRoot;
366 if(refName.IsEmpty())
367 return *pTreePos;//Found leaf
369 CShadowTree* pNextTree=pTreePos->GetNextSub(refName,bCreateIfNotExist);
370 if(pNextTree==NULL)
372 //Should not occur when all ref-names are valid and bCreateIfNotExist is true.
373 ASSERT(!bCreateIfNotExist);
374 return *pTreePos;
377 if(!refName.IsEmpty())
379 //When the refName is not empty, this node is not a leaf, so lets add it to the tree control.
380 //Leafs are for the list control.
381 if(pNextTree->m_hTree==NULL)
383 //New tree. Create node in control.
384 pNextTree->m_hTree=m_RefTreeCtrl.InsertItem(pNextTree->m_csRefName,pTreePos->m_hTree,NULL);
385 m_RefTreeCtrl.SetItemData(pNextTree->m_hTree,(DWORD_PTR)pNextTree);
389 return GetTreeNode(refName, pNextTree, bCreateIfNotExist);
393 void CBrowseRefsDlg::OnTvnSelchangedTreeRef(NMHDR *pNMHDR, LRESULT *pResult)
395 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
396 *pResult = 0;
398 FillListCtrlForTreeNode(pNMTreeView->itemNew.hItem);
401 void CBrowseRefsDlg::FillListCtrlForTreeNode(HTREEITEM treeNode)
403 m_ListRefLeafs.DeleteAllItems();
404 m_currSortCol = -1;
405 m_currSortDesc = false;
406 SetSortArrow(&m_ListRefLeafs,-1,false);
408 CShadowTree* pTree=(CShadowTree*)(m_RefTreeCtrl.GetItemData(treeNode));
409 if(pTree==NULL)
411 ASSERT(FALSE);
412 return;
414 FillListCtrlForShadowTree(pTree,L"",true);
417 void CBrowseRefsDlg::FillListCtrlForShadowTree(CShadowTree* pTree, CString refNamePrefix, bool isFirstLevel)
419 if(pTree->IsLeaf())
421 CString filter;
422 m_ctrlFilter.GetWindowText(filter);
423 filter.MakeLower();
424 CString ref = refNamePrefix + pTree->m_csRefName;
425 if (!(pTree->m_csRefName.IsEmpty() || pTree->m_csRefName == "refs" && pTree->m_pParent == NULL) && IsMatchFilter(pTree, ref, filter))
427 int indexItem = m_ListRefLeafs.InsertItem(m_ListRefLeafs.GetItemCount(), L"");
429 m_ListRefLeafs.SetItemData(indexItem,(DWORD_PTR)pTree);
430 m_ListRefLeafs.SetItemText(indexItem,eCol_Name, ref);
431 m_ListRefLeafs.SetItemText(indexItem,eCol_Date, pTree->m_csDate);
432 m_ListRefLeafs.SetItemText(indexItem,eCol_Msg, pTree->m_csSubject);
433 m_ListRefLeafs.SetItemText(indexItem,eCol_Hash, pTree->m_csRefHash);
436 else
439 CString csThisName;
440 if(!isFirstLevel)
441 csThisName=refNamePrefix+pTree->m_csRefName+L"/";
442 else
443 m_pListCtrlRoot = pTree;
444 for(CShadowTree::TShadowTreeMap::iterator itSubTree=pTree->m_ShadowTree.begin(); itSubTree!=pTree->m_ShadowTree.end(); ++itSubTree)
446 FillListCtrlForShadowTree(&itSubTree->second,csThisName,false);
451 bool CBrowseRefsDlg::IsMatchFilter(const CShadowTree* pTree, const CString &ref, const CString &filter)
453 if (m_SelectedFilters & LOGFILTER_REFNAME)
455 CString msg = ref;
456 msg = msg.MakeLower();
458 if (msg.Find(filter) >= 0)
459 return true;
462 if (m_SelectedFilters & LOGFILTER_SUBJECT)
464 CString msg = pTree->m_csSubject;
465 msg = msg.MakeLower();
467 if (msg.Find(filter) >= 0)
468 return true;
471 if (m_SelectedFilters & LOGFILTER_AUTHORS)
473 CString msg = pTree->m_csAuthor;
474 msg = msg.MakeLower();
476 if (msg.Find(filter) >= 0)
477 return true;
480 if (m_SelectedFilters & LOGFILTER_REVS)
482 CString msg = pTree->m_csRefHash;
483 msg = msg.MakeLower();
485 if (msg.Find(filter) >= 0)
486 return true;
488 return false;
491 bool CBrowseRefsDlg::ConfirmDeleteRef(VectorPShadowTree& leafs)
493 ASSERT(!leafs.empty());
495 CString csMessage;
496 UINT mbIcon=MB_ICONQUESTION;
498 bool bIsRemoteBranch = false;
499 bool bIsBranch = false;
500 if (leafs[0]->IsFrom(L"refs/remotes")) {bIsBranch = true; bIsRemoteBranch = true;}
501 else if (leafs[0]->IsFrom(L"refs/heads")) {bIsBranch = true;}
503 if(bIsBranch)
505 if(leafs.size() == 1)
507 CString branchToDelete = leafs[0]->GetRefName().Mid(bIsRemoteBranch ? 13 : 11);
508 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, branchToDelete);
510 //Check if branch is fully merged in HEAD
511 CGitHash branchHash = g_Git.GetHash(leafs[0]->GetRefName());
512 CGitHash commonAncestor;
513 CString commonAncestorstr;
514 CString cmd;
515 cmd.Format(L"git.exe merge-base HEAD %s", leafs[0]->GetRefName());
516 g_Git.Run(cmd, &commonAncestorstr, NULL, CP_UTF8);
518 commonAncestor=commonAncestorstr;
520 if(commonAncestor != branchHash)
522 csMessage += L"\r\n\r\n";
523 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGUNMERGED));
524 mbIcon = MB_ICONWARNING;
527 if(bIsRemoteBranch)
529 csMessage += L"\r\n\r\n";
530 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
531 mbIcon = MB_ICONWARNING;
534 else
536 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
538 csMessage += L"\r\n\r\n";
539 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGNOMERGECHECK));
540 mbIcon = MB_ICONWARNING;
542 if(bIsRemoteBranch)
544 csMessage += L"\r\n\r\n";
545 csMessage += CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_WARNINGDELETEREMOTEBRANCHES));
546 mbIcon = MB_ICONWARNING;
551 else if(leafs[0]->IsFrom(L"refs/tags"))
553 if(leafs.size() == 1)
555 CString tagToDelete = leafs[0]->GetRefName().Mid(10);
556 csMessage.Format(IDS_PROC_DELETEBRANCHTAG, tagToDelete);
558 else
560 csMessage.Format(IDS_PROC_DELETENREFS, leafs.size());
564 return CMessageBox::Show(m_hWnd, csMessage, _T("TortoiseGit"), MB_YESNO | mbIcon) == IDYES;
568 bool CBrowseRefsDlg::DoDeleteRefs(VectorPShadowTree& leafs, bool bForce)
570 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
571 if(!DoDeleteRef((*i)->GetRefName(), bForce))
572 return false;
573 return true;
576 bool CBrowseRefsDlg::DoDeleteRef(CString completeRefName, bool bForce)
578 bool bIsRemoteBranch = false;
579 bool bIsBranch = false;
580 if (wcsncmp(completeRefName, L"refs/remotes",12)==0) {bIsBranch = true; bIsRemoteBranch = true;}
581 else if (wcsncmp(completeRefName, L"refs/heads",10)==0) {bIsBranch = true;}
583 if(bIsBranch)
585 CString branchToDelete = completeRefName.Mid(bIsRemoteBranch ? 13 : 11);
586 CString cmd;
587 if(bIsRemoteBranch)
589 int slash = branchToDelete.Find(L'/');
590 if(slash < 0)
591 return false;
592 CString remoteName = branchToDelete.Left(slash);
593 CString remoteBranchToDelete = branchToDelete.Mid(slash + 1);
595 if(CAppUtils::IsSSHPutty())
597 CAppUtils::LaunchPAgent(NULL, &remoteName);
600 cmd.Format(L"git.exe push \"%s\" :%s", remoteName, remoteBranchToDelete);
602 else
603 cmd.Format(L"git.exe branch -%c -- %s",bForce?L'D':L'd',branchToDelete);
604 CString errorMsg;
605 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
607 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
608 return false;
611 else if(wcsncmp(completeRefName,L"refs/tags",9)==0)
613 CString tagToDelete = completeRefName.Mid(10);
614 CString cmd;
615 cmd.Format(L"git.exe tag -d -- %s",tagToDelete);
616 CString errorMsg;
617 if(g_Git.Run(cmd,&errorMsg,CP_UTF8)!=0)
619 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
620 return false;
623 return true;
626 CString CBrowseRefsDlg::GetFullRefName(CString partialRefName)
628 CShadowTree* pLeaf = m_TreeRoot.FindLeaf(partialRefName);
629 if(pLeaf == NULL)
630 return CString();
631 return pLeaf->GetRefName();
635 void CBrowseRefsDlg::OnContextMenu(CWnd* pWndFrom, CPoint point)
637 if(pWndFrom==&m_RefTreeCtrl) OnContextMenu_RefTreeCtrl(point);
638 else if(pWndFrom==&m_ListRefLeafs) OnContextMenu_ListRefLeafs(point);
641 void CBrowseRefsDlg::OnContextMenu_RefTreeCtrl(CPoint point)
643 CPoint clientPoint=point;
644 m_RefTreeCtrl.ScreenToClient(&clientPoint);
646 HTREEITEM hTreeItem=m_RefTreeCtrl.HitTest(clientPoint);
647 if(hTreeItem!=NULL)
648 m_RefTreeCtrl.Select(hTreeItem,TVGN_CARET);
650 VectorPShadowTree tree;
651 ShowContextMenu(point,hTreeItem,tree);
655 void CBrowseRefsDlg::OnContextMenu_ListRefLeafs(CPoint point)
657 std::vector<CShadowTree*> selectedLeafs;
658 selectedLeafs.reserve(m_ListRefLeafs.GetSelectedCount());
659 POSITION pos=m_ListRefLeafs.GetFirstSelectedItemPosition();
660 while(pos)
662 selectedLeafs.push_back(
663 (CShadowTree*)m_ListRefLeafs.GetItemData(
664 m_ListRefLeafs.GetNextSelectedItem(pos)));
667 ShowContextMenu(point,m_RefTreeCtrl.GetSelectedItem(),selectedLeafs);
670 void CBrowseRefsDlg::ShowContextMenu(CPoint point, HTREEITEM hTreePos, VectorPShadowTree& selectedLeafs)
672 CIconMenu popupMenu;
673 popupMenu.CreatePopupMenu();
675 bool bAddSeparator = false;
676 CString remoteName;
678 if(selectedLeafs.size()==1)
680 bAddSeparator = true;
682 bool bShowReflogOption = false;
683 bool bShowFetchOption = false;
684 bool bShowSwitchOption = false;
685 bool bShowRenameOption = false;
686 bool bShowCreateBranchOption = false;
688 CString fetchFromCmd;
690 if(selectedLeafs[0]->IsFrom(L"refs/heads"))
692 bShowReflogOption = true;
693 bShowSwitchOption = true;
694 bShowRenameOption = true;
696 else if(selectedLeafs[0]->IsFrom(L"refs/remotes"))
698 bShowReflogOption = true;
699 bShowFetchOption = true;
700 bShowCreateBranchOption = true;
702 int dummy = 0;//Needed for tokenize
703 remoteName = selectedLeafs[0]->GetRefName();
704 remoteName = remoteName.Mid(13);
705 remoteName = remoteName.Tokenize(L"/", dummy);
706 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
708 else if(selectedLeafs[0]->IsFrom(L"refs/tags"))
712 CString temp;
713 temp.LoadString(IDS_MENULOG);
714 popupMenu.AppendMenuIcon(eCmd_ViewLog, temp, IDI_LOG);
715 popupMenu.AppendMenuIcon(eCmd_RepoBrowser, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
716 if(bShowReflogOption)
718 temp.LoadString(IDS_MENUREFLOG);
719 popupMenu.AppendMenuIcon(eCmd_ShowReflog, temp, IDI_LOG);
722 popupMenu.AppendMenu(MF_SEPARATOR);
723 bAddSeparator = false;
725 if(bShowFetchOption)
727 bAddSeparator = true;
728 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
731 if(bAddSeparator)
732 popupMenu.AppendMenu(MF_SEPARATOR);
734 bAddSeparator = false;
735 if (m_bHasWC)
737 popupMenu.AppendMenuIcon(eCmd_Switch, CString(MAKEINTRESOURCE(IDS_SWITCH_TO_THIS)), IDI_SWITCH);
738 popupMenu.AppendMenu(MF_SEPARATOR);
741 if(bShowCreateBranchOption)
743 bAddSeparator = true;
744 temp.LoadString(IDS_MENUBRANCH);
745 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
748 if(bShowRenameOption)
750 bAddSeparator = true;
751 popupMenu.AppendMenuIcon(eCmd_Rename, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_RENAME)), IDI_RENAME);
754 else if(selectedLeafs.size() == 2)
756 bAddSeparator = true;
757 popupMenu.AppendMenuIcon(eCmd_Diff, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_COMPAREREFS)), IDI_DIFF);
760 if(!selectedLeafs.empty())
762 if(AreAllFrom(selectedLeafs, L"refs/remotes/"))
764 if(bAddSeparator)
765 popupMenu.AppendMenu(MF_SEPARATOR);
766 CString menuItemName;
767 if(selectedLeafs.size() == 1)
768 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCH);
769 else
770 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEREMOTEBRANCHES, selectedLeafs.size());
772 popupMenu.AppendMenuIcon(eCmd_DeleteRemoteBranch, menuItemName, IDI_DELETE);
773 bAddSeparator = true;
775 else if(AreAllFrom(selectedLeafs, L"refs/heads/"))
777 if(bAddSeparator)
778 popupMenu.AppendMenu(MF_SEPARATOR);
779 CString menuItemName;
780 if(selectedLeafs.size() == 1)
781 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETEBRANCH);
782 else
783 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETEBRANCHES, selectedLeafs.size());
785 popupMenu.AppendMenuIcon(eCmd_DeleteBranch, menuItemName, IDI_DELETE);
786 bAddSeparator = true;
788 else if(AreAllFrom(selectedLeafs, L"refs/tags/"))
790 if(bAddSeparator)
791 popupMenu.AppendMenu(MF_SEPARATOR);
792 CString menuItemName;
793 if(selectedLeafs.size() == 1)
794 menuItemName.LoadString(IDS_PROC_BROWSEREFS_DELETETAG);
795 else
796 menuItemName.Format(IDS_PROC_BROWSEREFS_DELETETAGS, selectedLeafs.size());
798 popupMenu.AppendMenuIcon(eCmd_DeleteTag, menuItemName, IDI_DELETE);
799 bAddSeparator = true;
804 if(hTreePos!=NULL && selectedLeafs.empty())
806 CShadowTree* pTree=(CShadowTree*)m_RefTreeCtrl.GetItemData(hTreePos);
807 if(pTree->IsFrom(L"refs/remotes"))
809 if(bAddSeparator)
810 popupMenu.AppendMenu(MF_SEPARATOR);
811 popupMenu.AppendMenuIcon(eCmd_ManageRemotes, CString(MAKEINTRESOURCE(IDS_PROC_BROWSEREFS_MANAGEREMOTES)), IDI_SETTINGS);
812 bAddSeparator = true;
813 if(selectedLeafs.empty())
815 int dummy = 0;//Needed for tokenize
816 remoteName = pTree->GetRefName();
817 remoteName = remoteName.Mid(13);
818 remoteName = remoteName.Tokenize(L"/", dummy);
819 if(!remoteName.IsEmpty())
821 CString fetchFromCmd;
822 fetchFromCmd.Format(IDS_PROC_BROWSEREFS_FETCHFROM, remoteName);
823 popupMenu.AppendMenuIcon(eCmd_Fetch, fetchFromCmd, IDI_PULL);
827 if(pTree->IsFrom(L"refs/heads"))
829 if(bAddSeparator)
830 popupMenu.AppendMenu(MF_SEPARATOR);
831 CString temp;
832 temp.LoadString(IDS_MENUBRANCH);
833 popupMenu.AppendMenuIcon(eCmd_CreateBranch, temp, IDI_COPY);
835 if(pTree->IsFrom(L"refs/tags"))
837 if(bAddSeparator)
838 popupMenu.AppendMenu(MF_SEPARATOR);
839 CString temp;
840 temp.LoadString(IDS_MENUTAG);
841 popupMenu.AppendMenuIcon(eCmd_CreateTag, temp, IDI_TAG);
846 eCmd cmd=(eCmd)popupMenu.TrackPopupMenuEx(TPM_LEFTALIGN|TPM_RETURNCMD, point.x, point.y, this, 0);
847 switch(cmd)
849 case eCmd_ViewLog:
851 CLogDlg dlg;
852 dlg.SetStartRef(selectedLeafs[0]->GetRefName());
853 dlg.DoModal();
855 break;
856 case eCmd_RepoBrowser:
857 CAppUtils::RunTortoiseProc(_T("/command:repobrowser /path:\"") + g_Git.m_CurrentDir + _T("\" /rev:") + selectedLeafs[0]->GetRefName());
858 break;
859 case eCmd_DeleteBranch:
860 case eCmd_DeleteRemoteBranch:
862 if(ConfirmDeleteRef(selectedLeafs))
863 DoDeleteRefs(selectedLeafs, true);
864 Refresh();
866 break;
867 case eCmd_DeleteTag:
869 if(ConfirmDeleteRef(selectedLeafs))
870 DoDeleteRefs(selectedLeafs, true);
871 Refresh();
873 break;
874 case eCmd_ShowReflog:
876 CRefLogDlg refLogDlg(this);
877 refLogDlg.m_CurrentBranch = selectedLeafs[0]->GetRefName();
878 refLogDlg.DoModal();
880 break;
881 case eCmd_Fetch:
883 CAppUtils::Fetch(remoteName);
884 Refresh();
886 break;
887 case eCmd_Switch:
889 CAppUtils::Switch(NULL, selectedLeafs[0]->GetRefName());
891 break;
892 case eCmd_Rename:
894 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
895 if(pos != NULL)
896 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
898 break;
899 case eCmd_AddRemote:
901 CAddRemoteDlg(this).DoModal();
902 Refresh();
904 break;
905 case eCmd_ManageRemotes:
907 CSinglePropSheetDlg(CString(MAKEINTRESOURCE(IDS_PROCS_TITLE_GITREMOTESETTINGS)), new CSettingGitRemote(g_Git.m_CurrentDir), this).DoModal();
908 // CSettingGitRemote W_Remotes(m_cmdPath);
909 // W_Remotes.DoModal();
910 Refresh();
912 break;
913 case eCmd_CreateBranch:
915 CString *commitHash = NULL;
916 if (selectedLeafs.size() == 1)
917 commitHash = &(selectedLeafs[0]->m_csRefHash);
918 CAppUtils::CreateBranchTag(false, commitHash);
919 Refresh();
921 break;
922 case eCmd_CreateTag:
924 CAppUtils::CreateBranchTag(true);
925 Refresh();
927 break;
928 case eCmd_Diff:
930 CFileDiffDlg dlg;
931 dlg.SetDiff(
932 NULL,
933 selectedLeafs[0]->m_csRefHash,
934 selectedLeafs[1]->m_csRefHash);
935 dlg.DoModal();
937 break;
941 bool CBrowseRefsDlg::AreAllFrom(VectorPShadowTree& leafs, const wchar_t* from)
943 for(VectorPShadowTree::iterator i = leafs.begin(); i != leafs.end(); ++i)
944 if(!(*i)->IsFrom(from))
945 return false;
946 return true;
949 BOOL CBrowseRefsDlg::PreTranslateMessage(MSG* pMsg)
951 if (pMsg->message == WM_KEYDOWN)
953 switch (pMsg->wParam)
955 /* case VK_RETURN:
957 if (GetAsyncKeyState(VK_CONTROL)&0x8000)
959 if ( GetDlgItem(IDOK)->IsWindowEnabled() )
961 PostMessage(WM_COMMAND, IDOK);
963 return TRUE;
966 break;
967 */ case VK_F2:
969 if(pMsg->hwnd == m_ListRefLeafs.m_hWnd)
971 POSITION pos = m_ListRefLeafs.GetFirstSelectedItemPosition();
972 if(pos != NULL)
973 m_ListRefLeafs.EditLabel(m_ListRefLeafs.GetNextSelectedItem(pos));
976 break;
978 case VK_F5:
980 Refresh();
982 break;
987 return CResizableStandAloneDialog::PreTranslateMessage(pMsg);
990 class CRefLeafListCompareFunc
992 public:
993 CRefLeafListCompareFunc(CListCtrl* pList, int col, bool desc):m_col(col),m_desc(desc),m_pList(pList){}
995 static int CALLBACK StaticCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
997 return ((CRefLeafListCompareFunc*)lParamSort)->Compare(lParam1,lParam2);
1000 int Compare(LPARAM lParam1, LPARAM lParam2)
1002 return Compare(
1003 (CShadowTree*)m_pList->GetItemData(lParam1),
1004 (CShadowTree*)m_pList->GetItemData(lParam2));
1007 int Compare(CShadowTree* pLeft, CShadowTree* pRight)
1009 int result=CompareNoDesc(pLeft,pRight);
1010 if(m_desc)
1011 return -result;
1012 return result;
1015 int CompareNoDesc(CShadowTree* pLeft, CShadowTree* pRight)
1017 switch(m_col)
1019 case CBrowseRefsDlg::eCol_Name: return pLeft->GetRefName().CompareNoCase(pRight->GetRefName());
1020 case CBrowseRefsDlg::eCol_Date: return pLeft->m_csDate_Iso8601.CompareNoCase(pRight->m_csDate_Iso8601);
1021 case CBrowseRefsDlg::eCol_Msg: return pLeft->m_csSubject.CompareNoCase(pRight->m_csSubject);
1022 case CBrowseRefsDlg::eCol_Hash: return pLeft->m_csRefHash.CompareNoCase(pRight->m_csRefHash);
1024 return 0;
1027 int m_col;
1028 bool m_desc;
1029 CListCtrl* m_pList;
1035 void CBrowseRefsDlg::OnLvnColumnclickListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1037 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1038 *pResult = 0;
1040 if(m_currSortCol == pNMLV->iSubItem)
1041 m_currSortDesc = !m_currSortDesc;
1042 else
1044 m_currSortCol = pNMLV->iSubItem;
1045 m_currSortDesc = false;
1048 CRefLeafListCompareFunc compareFunc(&m_ListRefLeafs, m_currSortCol, m_currSortDesc);
1049 m_ListRefLeafs.SortItemsEx(&CRefLeafListCompareFunc::StaticCompare, (DWORD_PTR)&compareFunc);
1051 SetSortArrow(&m_ListRefLeafs,m_currSortCol,!m_currSortDesc);
1054 void CBrowseRefsDlg::OnDestroy()
1056 m_pickedRef = GetSelectedRef(true, false);
1058 CResizableStandAloneDialog::OnDestroy();
1061 void CBrowseRefsDlg::OnNMDblclkListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1063 UNREFERENCED_PARAMETER(pNMHDR);
1064 *pResult = 0;
1066 EndDialog(IDOK);
1069 CString CBrowseRefsDlg::PickRef(bool /*returnAsHash*/, CString initialRef, int pickRef_Kind)
1071 CBrowseRefsDlg dlg(CString(),NULL);
1073 if(initialRef.IsEmpty())
1074 initialRef = L"HEAD";
1075 dlg.m_initialRef = initialRef;
1076 dlg.m_pickRef_Kind = pickRef_Kind;
1078 if(dlg.DoModal() != IDOK)
1079 return CString();
1081 return dlg.m_pickedRef;
1084 bool CBrowseRefsDlg::PickRefForCombo(CComboBoxEx* pComboBox, int pickRef_Kind)
1086 CString origRef;
1087 pComboBox->GetLBText(pComboBox->GetCurSel(), origRef);
1088 CString resultRef = PickRef(false,origRef,pickRef_Kind);
1089 if(resultRef.IsEmpty())
1090 return false;
1091 if(wcsncmp(resultRef,L"refs/",5)==0)
1092 resultRef = resultRef.Mid(5);
1093 // if(wcsncmp(resultRef,L"heads/",6)==0)
1094 // resultRef = resultRef.Mid(6);
1096 //Find closest match of choice in combobox
1097 int ixFound = -1;
1098 int matchLength = 0;
1099 CString comboRefName;
1100 for(int i = 0; i < pComboBox->GetCount(); ++i)
1102 pComboBox->GetLBText(i, comboRefName);
1103 if(comboRefName.Find(L'/') < 0 && !comboRefName.IsEmpty())
1104 comboRefName.Insert(0,L"heads/"); // If combo contains single level ref name, it is usualy from 'heads/'
1105 if(matchLength < comboRefName.GetLength() && resultRef.Right(comboRefName.GetLength()) == comboRefName)
1107 matchLength = comboRefName.GetLength();
1108 ixFound = i;
1111 if(ixFound >= 0)
1112 pComboBox->SetCurSel(ixFound);
1113 else
1114 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)
1116 return true;
1119 void CBrowseRefsDlg::OnLvnEndlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1121 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1122 *pResult = FALSE;
1124 if(pDispInfo->item.pszText == NULL)
1125 return; //User canceled changing
1127 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1129 if(!pTree->IsFrom(L"refs/heads"))
1131 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_RENAMEONLYBRANCHES, IDS_APPNAME, MB_OK | MB_ICONERROR);
1132 return;
1135 CString origName = pTree->GetRefName().Mid(11);
1137 CString newName;
1138 if(m_pListCtrlRoot != NULL)
1139 newName = m_pListCtrlRoot->GetRefName() + L'/';
1140 newName += pDispInfo->item.pszText;
1142 if(wcsncmp(newName,L"refs/heads/",11)!=0)
1144 CMessageBox::Show(m_hWnd, IDS_PROC_BROWSEREFS_NOCHANGEOFTYPE, IDS_APPNAME, MB_OK | MB_ICONERROR);
1145 return;
1148 CString newNameTrunced = newName.Mid(11);
1150 CString errorMsg;
1151 if(g_Git.Run(L"git branch -m \"" + origName + L"\" \"" + newNameTrunced + L"\"", &errorMsg, CP_UTF8) != 0)
1153 CMessageBox::Show(m_hWnd, errorMsg, _T("TortoiseGit"), MB_OK | MB_ICONERROR);
1154 return;
1156 //Do as if it failed to rename. Let Refresh() do the job.
1157 //*pResult = TRUE;
1159 Refresh(newName);
1161 // CString W_csPopup;W_csPopup.Format8(L"Ref: %s. New name: %s. With path: %s", pTree->GetRefName(), pDispInfo->item.pszText, newName);
1163 // AfxMessageBox(W_csPopup);
1167 void CBrowseRefsDlg::OnLvnBeginlabeleditListRefLeafs(NMHDR *pNMHDR, LRESULT *pResult)
1169 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1170 *pResult = FALSE;
1172 CShadowTree* pTree=(CShadowTree*)m_ListRefLeafs.GetItemData(pDispInfo->item.iItem);
1174 if(!pTree->IsFrom(L"refs/heads"))
1176 *pResult = TRUE; //Dont allow renaming any other things then branches at the moment.
1177 return;
1181 void CBrowseRefsDlg::OnEnChangeEditFilter()
1183 SetTimer(IDT_FILTER, 1000, NULL);
1186 void CBrowseRefsDlg::OnTimer(UINT_PTR nIDEvent)
1188 if (nIDEvent == IDT_FILTER)
1190 KillTimer(IDT_FILTER);
1191 FillListCtrlForTreeNode(m_RefTreeCtrl.GetSelectedItem());
1194 CResizableStandAloneDialog::OnTimer(nIDEvent);
1197 LRESULT CBrowseRefsDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1199 // FIXME: x64 version would get this function called with unexpected parameters.
1200 if (!lParam)
1201 return 0;
1203 RECT * rect = (LPRECT)lParam;
1204 CPoint point;
1205 CString temp;
1206 point = CPoint(rect->left, rect->bottom);
1207 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1208 CMenu popup;
1209 if (popup.CreatePopupMenu())
1211 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1212 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1214 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1215 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1217 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1218 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1220 temp.LoadString(IDS_LOG_FILTER_REVS);
1221 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1223 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1224 if (selection != 0)
1226 m_SelectedFilters ^= selection;
1227 SetFilterCueText();
1228 SetTimer(IDT_FILTER, 1000, NULL);
1231 return 0L;
1234 void CBrowseRefsDlg::SetFilterCueText()
1236 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1237 temp += _T(" ");
1239 if (m_SelectedFilters & LOGFILTER_REFNAME)
1240 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1242 if (m_SelectedFilters & LOGFILTER_SUBJECT)
1244 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1245 temp += _T(", ");
1246 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1249 if (m_SelectedFilters & LOGFILTER_AUTHORS)
1251 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1252 temp += _T(", ");
1253 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1256 if (m_SelectedFilters & LOGFILTER_REVS)
1258 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1259 temp += _T(", ");
1260 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1263 // to make the cue banner text appear more to the right of the edit control
1264 temp = _T(" ") + temp;
1265 m_ctrlFilter.SetCueBanner(temp.TrimRight());