allow to select all entries in the log using CTRL-A
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.cpp
blob11abc2743af34cd79739f0b4fc0621256fa7a86c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
4 // Copyright (C) 2005-2007 Marco Costalba
5 // Copyright (C) 2011 - Sven Strickroth <email@cs-ware.de>
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 // GitLogList.cpp : implementation file
23 #include "stdafx.h"
24 #include "resource.h"
25 #include "GitLogListBase.h"
26 #include "GitRev.h"
27 //#include "VssStyle.h"
28 #include "IconMenu.h"
29 // CGitLogListBase
30 #include "cursor.h"
31 #include "InputDlg.h"
32 #include "PropDlg.h"
33 #include "SVNProgressDlg.h"
34 #include "ProgressDlg.h"
35 //#include "RepositoryBrowser.h"
36 //#include "CopyDlg.h"
37 //#include "StatGraphDlg.h"
38 #include "Logdlg.h"
39 #include "MessageBox.h"
40 #include "Registry.h"
41 #include "AppUtils.h"
42 #include "PathUtils.h"
43 #include "StringUtils.h"
44 #include "UnicodeUtils.h"
45 #include "TempFile.h"
46 //#include "GitInfo.h"
47 //#include "GitDiff.h"
48 #include "IconMenu.h"
49 //#include "RevisionRangeDlg.h"
50 //#include "BrowseFolder.h"
51 //#include "BlameDlg.h"
52 //#include "Blame.h"
53 //#include "GitHelpers.h"
54 #include "GitStatus.h"
55 //#include "LogDlgHelper.h"
56 //#include "CachedLogInfo.h"
57 //#include "RepositoryInfo.h"
58 //#include "EditPropertiesDlg.h"
59 #include "FileDiffDlg.h"
60 #include "..\\TortoiseShell\\Resource.h"
61 #include "FindDlg.h"
63 const UINT CGitLogListBase::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
65 IMPLEMENT_DYNAMIC(CGitLogListBase, CHintListCtrl)
67 CGitLogListBase::CGitLogListBase():CHintListCtrl()
68 ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)
69 ,m_nSearchIndex(0)
70 ,m_bNoDispUpdates(FALSE)
71 , m_bThreadRunning(FALSE)
72 , m_bStrictStopped(false)
73 , m_pStoreSelection(NULL)
74 , m_nSelectedFilter(LOGFILTER_ALL)
75 , m_bVista(false)
76 , m_bShowWC(false)
77 , m_logEntries(&m_LogCache)
78 , m_pFindDialog(NULL)
79 , m_ColumnManager(this)
80 , m_dwDefaultColumns(0)
81 , m_arShownList(&m_critSec)
83 // use the default GUI font, create a copy of it and
84 // change the copy to BOLD (leave the rest of the font
85 // the same)
86 HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
87 LOGFONT lf = {0};
88 GetObject(hFont, sizeof(LOGFONT), &lf);
89 lf.lfWeight = FW_BOLD;
90 m_boldFont = CreateFontIndirect(&lf);
92 m_bShowBugtraqColumn=false;
94 m_IsIDReplaceAction=FALSE;
96 this->m_critSec.Init();
97 m_wcRev.m_CommitHash.Empty();
98 m_wcRev.GetSubject()=_T("Working dir changes");
99 m_wcRev.m_ParentHash.clear();
100 m_wcRev.m_Mark=_T('-');
101 m_wcRev.m_IsUpdateing=FALSE;
102 m_wcRev.m_IsFull = TRUE;
104 m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
105 m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
106 m_hAddedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
107 m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
108 m_hFetchIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONFETCHING), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
110 m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
112 g_Git.GetMapHashToFriendName(m_HashMap);
113 m_CurrentBranch=g_Git.GetCurrentBranch();
114 this->m_HeadHash=g_Git.GetHash(_T("HEAD"));
116 m_From=-1;;
117 m_To=-1;
119 m_ShowMask = 0;
120 m_LoadingThread = NULL;
122 InterlockedExchange(&m_bExitThread,FALSE);
123 m_IsOldFirst = FALSE;
124 m_IsRebaseReplaceGraph = FALSE;
127 for(int i=0;i<Lanes::COLORS_NUM;i++)
129 m_LineColors[i] = m_Colors.GetColor((CColors::Colors)(CColors::BranchLine1+i));
131 // get short/long datetime setting from registry
132 DWORD RegUseShortDateFormat = CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE);
133 if ( RegUseShortDateFormat )
135 m_DateFormat = DATE_SHORTDATE;
137 else
139 m_DateFormat = DATE_LONGDATE;
141 // get relative time display setting from registry
142 DWORD regRelativeTimes = CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE);
143 m_bRelativeTimes = (regRelativeTimes != 0);
144 m_ContextMenuMask = 0xFFFFFFFFFFFFFFFF;
146 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_PICK);
147 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SQUASH);
148 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_EDIT);
149 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SKIP);
150 m_ContextMenuMask &= ~GetContextMenuBit(ID_LOG);
151 m_ContextMenuMask &= ~GetContextMenuBit(ID_BLAME);
153 OSVERSIONINFOEX inf;
154 SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
155 inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
156 GetVersionEx((OSVERSIONINFO *)&inf);
157 WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);
158 m_bVista = (fullver >= 0x0600);
160 m_ColumnRegKey=_T("log");
162 m_AsyncThreadExit = FALSE;
163 m_AsyncDiffEvent = ::CreateEvent(NULL,FALSE,TRUE,NULL);
164 m_AsynDiffListLock.Init();
166 m_DiffingThread = AfxBeginThread(AsyncThread, this, THREAD_PRIORITY_BELOW_NORMAL);
167 if (m_DiffingThread ==NULL)
169 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
170 return;
175 int CGitLogListBase::AsyncDiffThread()
177 m_AsyncThreadExited = false;
178 while(!m_AsyncThreadExit)
180 ::WaitForSingleObject(m_AsyncDiffEvent, INFINITE);
182 GitRev *pRev = NULL;
183 while(!m_AsyncThreadExit && m_AsynDiffList.size() > 0)
185 m_AsynDiffListLock.Lock();
186 pRev = m_AsynDiffList.back();
187 m_AsynDiffList.pop_back();
188 m_AsynDiffListLock.Unlock();
190 if( pRev->m_CommitHash.IsEmpty() )
192 if(pRev->m_IsDiffFiles)
193 continue;
195 pRev->GetFiles(this).Clear();
196 pRev->m_ParentHash.clear();
197 pRev->m_ParentHash.push_back(m_HeadHash);
198 if(g_Git.IsInitRepos())
200 g_Git.GetInitAddList(pRev->GetFiles(this));
202 }else
204 g_Git.GetCommitDiffList(pRev->m_CommitHash.ToString(),this->m_HeadHash.ToString(), pRev->GetFiles(this));
206 pRev->GetAction(this) = 0;
208 for(int j=0;j< pRev->GetFiles(this).GetCount();j++)
209 pRev->GetAction(this) |= pRev->GetFiles(this)[j].m_Action;
211 InterlockedExchange(&pRev->m_IsDiffFiles, TRUE);
212 InterlockedExchange(&pRev->m_IsFull, TRUE);
214 pRev->GetBody().Format(_T("%d files changed"),pRev->GetFiles(this).GetCount());
215 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)0,0);
216 this->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
219 if(!pRev->CheckAndDiff())
220 { // fetch change file list
221 for(int i=GetTopIndex(); !m_AsyncThreadExit && i <= GetTopIndex()+GetCountPerPage(); i++)
223 if(i < m_arShownList.GetCount())
225 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(i);
226 if(data->m_CommitHash == pRev->m_CommitHash)
228 ::PostMessage(m_hWnd,MSG_LOADED,(WPARAM)i,0);
229 break;
234 if(!m_AsyncThreadExit && GetSelectedCount() == 1)
236 POSITION pos = GetFirstSelectedItemPosition();
237 int nItem = GetNextSelectedItem(pos);
239 if(nItem>=0)
241 GitRev* data = (GitRev*)m_arShownList[nItem];
242 if(data)
243 if(data->m_CommitHash == pRev->m_CommitHash)
245 this->GetParent()->PostMessage(WM_COMMAND, MSG_FETCHED_DIFF, 0);
252 m_AsyncThreadExited = true;
253 return 0;
255 void CGitLogListBase::hideFromContextMenu(unsigned __int64 hideMask, bool exclusivelyShow)
257 if (exclusivelyShow) {
258 m_ContextMenuMask &= hideMask;
259 } else {
260 m_ContextMenuMask &= ~hideMask;
264 CGitLogListBase::~CGitLogListBase()
266 InterlockedExchange(&m_bNoDispUpdates, TRUE);
267 this->m_arShownList.SafeRemoveAll();
269 DestroyIcon(m_hModifiedIcon);
270 DestroyIcon(m_hReplacedIcon);
271 DestroyIcon(m_hAddedIcon);
272 DestroyIcon(m_hDeletedIcon);
273 m_logEntries.ClearAll();
275 if (m_boldFont)
276 DeleteObject(m_boldFont);
278 if ( m_pStoreSelection )
280 delete m_pStoreSelection;
281 m_pStoreSelection = NULL;
284 SafeTerminateThread();
285 SafeTerminateAsyncDiffThread();
287 if(m_AsyncDiffEvent)
288 CloseHandle(m_AsyncDiffEvent);
292 BEGIN_MESSAGE_MAP(CGitLogListBase, CHintListCtrl)
293 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
294 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)
295 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)
296 ON_WM_CONTEXTMENU()
297 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)
298 ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)
299 ON_WM_CREATE()
300 ON_WM_DESTROY()
301 ON_MESSAGE(MSG_LOADED,OnLoad)
302 ON_WM_MEASUREITEM()
303 ON_WM_MEASUREITEM_REFLECT()
304 ON_NOTIFY(HDN_BEGINTRACKA, 0, OnHdnBegintrack)
305 ON_NOTIFY(HDN_BEGINTRACKW, 0, OnHdnBegintrack)
306 ON_NOTIFY(HDN_ITEMCHANGINGA, 0, OnHdnItemchanging)
307 ON_NOTIFY(HDN_ITEMCHANGINGW, 0, OnHdnItemchanging)
308 ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)
309 ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)
310 END_MESSAGE_MAP()
312 void CGitLogListBase::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
314 //if (m_nRowHeight>0)
316 lpMeasureItemStruct->itemHeight = 50;
320 int CGitLogListBase:: OnCreate(LPCREATESTRUCT lpCreateStruct)
322 PreSubclassWindow();
323 return CHintListCtrl::OnCreate(lpCreateStruct);
326 void CGitLogListBase::PreSubclassWindow()
328 SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
329 // load the icons for the action columns
330 // m_Theme.Open(m_hWnd, L"ListView");
331 m_Theme.Open(m_hWnd, L"Explorer::ListView;ListView");
332 m_Theme.SetWindowTheme(m_hWnd, L"Explorer", NULL);
333 CHintListCtrl::PreSubclassWindow();
336 void CGitLogListBase::InsertGitColumn()
338 CString temp;
340 CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);
341 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;
342 if (DWORD(regFullRowSelect))
343 exStyle |= LVS_EX_FULLROWSELECT;
344 SetExtendedStyle(exStyle);
346 UpdateProjectProperties();
348 static UINT normal[] =
350 IDS_LOG_GRAPH,
351 IDS_LOG_REBASE,
352 IDS_LOG_ID,
353 IDS_LOG_HASH,
354 IDS_LOG_ACTIONS,
355 IDS_LOG_MESSAGE,
356 IDS_LOG_AUTHOR,
357 IDS_LOG_DATE,
358 IDS_LOG_EMAIL,
359 IDS_LOG_COMMIT_NAME,
360 IDS_LOG_COMMIT_EMAIL,
361 IDS_LOG_COMMIT_DATE,
362 IDS_LOG_BUGIDS,
365 static int with[] =
367 ICONITEMBORDER+16*4,
368 ICONITEMBORDER+16*4,
369 ICONITEMBORDER+16*4,
370 ICONITEMBORDER+16*4,
371 ICONITEMBORDER+16*4,
372 LOGLIST_MESSAGE_MIN,
373 ICONITEMBORDER+16*4,
374 ICONITEMBORDER+16*4,
375 ICONITEMBORDER+16*4,
376 ICONITEMBORDER+16*4,
377 ICONITEMBORDER+16*4,
378 ICONITEMBORDER+16*4,
379 ICONITEMBORDER+16*4,
381 m_dwDefaultColumns = GIT_LOG_GRAPH|GIT_LOG_ACTIONS|GIT_LOG_MESSAGE|GIT_LOG_AUTHOR|GIT_LOG_DATE;
383 DWORD hideColumns = 0;
384 if(this->m_IsRebaseReplaceGraph)
386 hideColumns |= GIT_LOG_GRAPH;
387 m_dwDefaultColumns |= GIT_LOG_REBASE;
388 } else {
389 hideColumns |= GIT_LOG_REBASE;
392 if(this->m_IsIDReplaceAction)
394 hideColumns |= GIT_LOG_ACTIONS;
395 m_dwDefaultColumns |= GIT_LOG_ID;
396 m_dwDefaultColumns |= GIT_LOG_HASH;
397 } else {
398 hideColumns |= GIT_LOG_ID;
400 if(this->m_bShowBugtraqColumn)
402 m_dwDefaultColumns |= GIT_LOGLIST_BUG;
403 } else {
404 hideColumns |= GIT_LOGLIST_BUG;
406 SetRedraw(false);
408 m_ColumnManager.SetNames(normal, sizeof(normal)/sizeof(UINT));
409 m_ColumnManager.ReadSettings(m_dwDefaultColumns, hideColumns, m_ColumnRegKey+_T("loglist"), sizeof(normal)/sizeof(UINT), with);
411 SetRedraw(true);
416 * Resizes all columns in a list control to values in registry.
418 void CGitLogListBase::ResizeAllListCtrlCols()
420 // column max and min widths to allow
421 static const int nMinimumWidth = 10;
422 static const int nMaximumWidth = 1000;
423 CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
424 if (pHdrCtrl)
426 int numcols = pHdrCtrl->GetItemCount();
427 for (int col = 0; col < numcols; col++)
429 // get width for this col last time from registry
430 CString regentry;
431 regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);
432 CRegDWORD regwidth(regentry, 0);
433 int cx = regwidth;
434 if ( cx == 0 )
436 // no saved value, setup sensible defaults
437 if (col == this->LOGLIST_MESSAGE)
439 cx = LOGLIST_MESSAGE_MIN;
441 else
443 cx = ICONITEMBORDER+16*4;
446 if (cx < nMinimumWidth)
448 cx = nMinimumWidth;
449 } else if (cx > nMaximumWidth)
451 cx = nMaximumWidth;
454 SetColumnWidth(col, cx);
461 BOOL CGitLogListBase::GetShortName(CString ref, CString &shortname,CString prefix)
463 //TRACE(_T("%s %s\r\n"),ref,prefix);
464 if(ref.Left(prefix.GetLength()) == prefix)
466 shortname = ref.Right(ref.GetLength()-prefix.GetLength());
467 if(shortname.Right(3)==_T("^{}"))
468 shortname=shortname.Left(shortname.GetLength()-3);
469 return TRUE;
471 return FALSE;
474 void CGitLogListBase::FillBackGround(HDC hdc, int Index,CRect &rect)
476 // HBRUSH brush;
477 LVITEM rItem;
478 SecureZeroMemory(&rItem, sizeof(LVITEM));
479 rItem.mask = LVIF_STATE;
480 rItem.iItem = Index;
481 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
482 GetItem(&rItem);
484 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(Index);
485 HBRUSH brush = NULL;
487 if (m_Theme.IsAppThemed() && m_bVista)
489 int state = LISS_NORMAL;
490 if (rItem.state & LVIS_SELECTED)
492 if (::GetFocus() == m_hWnd)
493 state |= LISS_SELECTED;
494 else
495 state |= LISS_SELECTEDNOTFOCUS;
497 else
499 if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
500 brush = ::CreateSolidBrush(RGB(156,156,156));
501 else if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
502 brush = ::CreateSolidBrush(RGB(200,200,128));
505 if (brush != NULL)
507 ::FillRect(hdc, &rect, brush);
508 ::DeleteObject(brush);
510 else
512 if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTITEM, state))
513 m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);
515 CRect rectDraw = rect;
516 if(rItem.state & LVIS_SELECTED)
517 rectDraw.InflateRect(1,0);
518 else
519 rectDraw.InflateRect(1,1);
521 m_Theme.DrawBackground(hdc, LVP_LISTITEM, state, rectDraw, &rect);
524 else
526 if (rItem.state & LVIS_SELECTED)
528 if (::GetFocus() == m_hWnd)
529 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
530 else
531 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
533 else
535 //if (pLogEntry->bCopiedSelf)
536 // brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
537 //else
538 if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
539 brush = ::CreateSolidBrush(RGB(156,156,156));
540 else if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
541 brush = ::CreateSolidBrush(RGB(200,200,128));
542 else
543 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
545 if (brush == NULL)
546 return;
548 ::FillRect(hdc, &rect, brush);
549 ::DeleteObject(brush);
553 void CGitLogListBase::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)
555 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(index);
556 CRect rt=rect;
557 LVITEM rItem;
558 SecureZeroMemory(&rItem, sizeof(LVITEM));
559 rItem.mask = LVIF_STATE;
560 rItem.iItem = index;
561 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
562 GetItem(&rItem);
564 CDC W_Dc;
565 W_Dc.Attach(hdc);
567 for(unsigned int i=0;i<m_HashMap[data->m_CommitHash].size();i++)
569 CString str;
570 str=m_HashMap[data->m_CommitHash][i];
572 CString shortname;
573 HBRUSH brush = 0;
574 shortname = _T("");
575 COLORREF colRef = 0;
577 //Determine label color
578 if(GetShortName(str,shortname,_T("refs/heads/")))
580 if( shortname == m_CurrentBranch )
581 colRef = m_Colors.GetColor(CColors::CurrentBranch);
582 else
583 colRef = m_Colors.GetColor(CColors::LocalBranch);
585 }else if(GetShortName(str,shortname,_T("refs/remotes/")))
587 colRef = m_Colors.GetColor(CColors::RemoteBranch);
589 else if(GetShortName(str,shortname,_T("refs/tags/")))
591 colRef = m_Colors.GetColor(CColors::Tag);
593 else if(GetShortName(str,shortname,_T("refs/stash")))
595 colRef = m_Colors.GetColor(CColors::Stash);
596 shortname=_T("stash");
597 }else if(GetShortName(str,shortname,_T("refs/bisect/")))
599 if(shortname.Find(_T("good")) == 0)
601 colRef = m_Colors.GetColor(CColors::BisectGood);
602 shortname = _T("good");
605 if(shortname.Find(_T("bad")) == 0)
607 colRef = m_Colors.GetColor(CColors::BisectBad);
608 shortname = _T("bad");
612 //When row selected, ajust label color
613 if (!(m_Theme.IsAppThemed() && m_bVista))
614 if (rItem.state & LVIS_SELECTED)
615 colRef = CColors::MixColors(colRef, ::GetSysColor(COLOR_HIGHLIGHT), 150);
617 brush = ::CreateSolidBrush(colRef);
619 if(!shortname.IsEmpty() && (rt.left<rect.right) )
621 SIZE size;
622 memset(&size,0,sizeof(SIZE));
623 GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);
625 rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);
626 rt.right+=8;
628 int textpos = DT_CENTER;
630 if(rt.right > rect.right)
632 rt.right = rect.right;
633 textpos =0;
636 //Fill interior of ref label
637 ::FillRect(hdc, &rt, brush);
639 //Draw edge of label
641 CRect rectEdge = rt;
643 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,100), m_Colors.Darken(colRef,100));
644 rectEdge.DeflateRect(1,1);
645 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,50), m_Colors.Darken(colRef,50));
647 //Draw text inside label
648 if (m_Theme.IsAppThemed() && m_bVista)
650 int txtState = LISS_NORMAL;
651 if (rItem.state & LVIS_SELECTED)
652 txtState = LISS_SELECTED;
654 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, shortname, -1, textpos | DT_SINGLELINE | DT_VCENTER, 0, &rt);
656 else
658 W_Dc.SetBkMode(TRANSPARENT);
659 if (rItem.state & LVIS_SELECTED)
661 COLORREF clrNew = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
662 COLORREF clrOld = ::SetTextColor(hdc,clrNew);
663 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,textpos | DT_SINGLELINE | DT_VCENTER);
664 ::SetTextColor(hdc,clrOld);
665 }else
667 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,textpos | DT_SINGLELINE | DT_VCENTER);
671 //::MoveToEx(hdc,rt.left,rt.top,NULL);
672 //::LineTo(hdc,rt.right,rt.top);
673 //::LineTo(hdc,rt.right,rt.bottom);
674 //::LineTo(hdc,rt.left,rt.bottom);
675 //::LineTo(hdc,rt.left,rt.top);
677 rt.left=rt.right+1;
679 if(brush)
680 ::DeleteObject(brush);
682 rt.right=rect.right;
684 if (m_Theme.IsAppThemed() && m_bVista)
686 int txtState = LISS_NORMAL;
687 if (rItem.state & LVIS_SELECTED)
688 txtState = LISS_SELECTED;
690 m_Theme.DrawText(hdc, LVP_LISTITEM, txtState, data->GetSubject(), -1, DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER, 0, &rt);
692 else
694 if (rItem.state & LVIS_SELECTED)
696 COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
697 ::DrawText(hdc,data->GetSubject(),data->GetSubject().GetLength(),&rt,DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER);
698 ::SetTextColor(hdc,clrOld);
699 }else
701 ::DrawText(hdc,data->GetSubject(),data->GetSubject().GetLength(),&rt,DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER);
705 W_Dc.Detach();
708 static COLORREF blend(const COLORREF& col1, const COLORREF& col2, int amount = 128) {
710 // Returns ((256 - amount)*col1 + amount*col2) / 256;
711 return RGB(((256 - amount)*GetRValue(col1) + amount*GetRValue(col2) ) / 256,
712 ((256 - amount)*GetGValue(col1) + amount*GetGValue(col2) ) / 256,
713 ((256 - amount)*GetBValue(col1) + amount*GetBValue(col2) ) / 256);
716 Gdiplus::Color GetGdiColor(COLORREF col)
718 return Gdiplus::Color(GetRValue(col),GetGValue(col),GetBValue(col));
720 void CGitLogListBase::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,
721 const COLORREF& col,const COLORREF& activeColor, int top
724 int h = laneHeight / 2;
725 int m = (x1 + x2) / 2;
726 int r = (x2 - x1) / 3;
727 int d = 2 * r;
729 #define P_CENTER m , h+top
730 #define P_0 x2, h+top
731 #define P_90 m , 0+top-1
732 #define P_180 x1, h+top
733 #define P_270 m , 2 * h+top +1
734 #define R_CENTER m - r, h - r+top, d, d
737 #define DELTA_UR_B 2*(x1 - m), 2*h +top
738 #define DELTA_UR_E 0*16, 90*16 +top // -,
740 #define DELTA_DR_B 2*(x1 - m), 2*-h +top
741 #define DELTA_DR_E 270*16, 90*16 +top // -'
743 #define DELTA_UL_B 2*(x2 - m), 2*h +top
744 #define DELTA_UL_E 90*16, 90*16 +top // ,-
746 #define DELTA_DL_B 2*(x2 - m),2*-h +top
747 #define DELTA_DL_E 180*16, 90*16 // '-
749 #define CENTER_UR x1, 2*h, 225
750 #define CENTER_DR x1, 0 , 135
751 #define CENTER_UL x2, 2*h, 315
752 #define CENTER_DL x2, 0 , 45
755 Gdiplus::Graphics graphics( hdc );
757 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
759 // arc
760 switch (type) {
761 case Lanes::JOIN:
762 case Lanes::JOIN_R:
763 case Lanes::HEAD:
764 case Lanes::HEAD_R:
766 Gdiplus::LinearGradientBrush gradient(
767 Gdiplus::Point(x1-2, h+top-2),
768 Gdiplus::Point(P_270),
769 GetGdiColor(activeColor),GetGdiColor(col));
772 Gdiplus::Pen mypen(&gradient,2);
773 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
775 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);
776 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top+h-1, x2-x1,laneHeight,270,90);
777 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);
779 break;
781 case Lanes::JOIN_L:
784 Gdiplus::LinearGradientBrush gradient(
785 Gdiplus::Point(P_270),
786 Gdiplus::Point(x2+1, h+top-1),
787 GetGdiColor(col),GetGdiColor(activeColor));
790 Gdiplus::Pen mypen(&gradient,2);
791 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
793 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);
794 graphics.DrawArc(&mypen,x1+(x2-x1)/2,top+h-1, x2-x1,laneHeight,180,90);
795 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);
798 break;
800 case Lanes::TAIL:
801 case Lanes::TAIL_R:
804 Gdiplus::LinearGradientBrush gradient(
805 Gdiplus::Point(x1-2, h+top-2),
806 Gdiplus::Point(P_90),
807 GetGdiColor(activeColor),GetGdiColor(col));
809 Gdiplus::Pen mypen(&gradient,2);
811 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top-h-1, x2-x1,laneHeight,0,90);
813 #if 0
814 QConicalGradient gradient(CENTER_DR);
815 gradient.setColorAt(0.375, activeCol);
816 gradient.setColorAt(0.625, col);
817 myPen.setBrush(gradient);
818 p->setPen(myPen);
819 p->drawArc(P_CENTER, DELTA_DR);
820 #endif
821 break;
823 default:
824 break;
828 //static QPen myPen(Qt::black, 2); // fast path here
829 CPen pen;
830 pen.CreatePen(PS_SOLID,2,col);
831 //myPen.setColor(col);
832 HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);
834 Gdiplus::Pen myPen(GetGdiColor(col),2);
836 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
838 //p->setPen(myPen);
840 // vertical line
841 switch (type) {
842 case Lanes::ACTIVE:
843 case Lanes::NOT_ACTIVE:
844 case Lanes::MERGE_FORK:
845 case Lanes::MERGE_FORK_R:
846 case Lanes::MERGE_FORK_L:
847 case Lanes::JOIN:
848 case Lanes::JOIN_R:
849 case Lanes::JOIN_L:
850 case Lanes::CROSS:
851 //DrawLine(hdc,P_90,P_270);
852 graphics.DrawLine(&myPen,P_90,P_270);
853 //p->drawLine(P_90, P_270);
854 break;
855 case Lanes::HEAD_L:
856 case Lanes::BRANCH:
857 //DrawLine(hdc,P_CENTER,P_270);
858 graphics.DrawLine(&myPen,P_CENTER,P_270);
859 //p->drawLine(P_CENTER, P_270);
860 break;
861 case Lanes::TAIL_L:
862 case Lanes::INITIAL:
863 case Lanes::BOUNDARY:
864 case Lanes::BOUNDARY_C:
865 case Lanes::BOUNDARY_R:
866 case Lanes::BOUNDARY_L:
867 //DrawLine(hdc,P_90, P_CENTER);
868 graphics.DrawLine(&myPen,P_90,P_CENTER);
869 //p->drawLine(P_90, P_CENTER);
870 break;
871 default:
872 break;
875 myPen.SetColor(GetGdiColor(activeColor));
877 // horizontal line
878 switch (type) {
879 case Lanes::MERGE_FORK:
880 case Lanes::JOIN:
881 case Lanes::HEAD:
882 case Lanes::TAIL:
883 case Lanes::CROSS:
884 case Lanes::CROSS_EMPTY:
885 case Lanes::BOUNDARY_C:
886 //DrawLine(hdc,P_180,P_0);
887 graphics.DrawLine(&myPen,P_180,P_0);
888 //p->drawLine(P_180, P_0);
889 break;
890 case Lanes::MERGE_FORK_R:
891 case Lanes::BOUNDARY_R:
892 //DrawLine(hdc,P_180,P_CENTER);
893 graphics.DrawLine(&myPen,P_180,P_CENTER);
894 //p->drawLine(P_180, P_CENTER);
895 break;
896 case Lanes::MERGE_FORK_L:
897 case Lanes::HEAD_L:
898 case Lanes::TAIL_L:
899 case Lanes::BOUNDARY_L:
900 //DrawLine(hdc,P_CENTER,P_0);
901 graphics.DrawLine(&myPen,P_CENTER,P_0);
902 //p->drawLine(P_CENTER, P_0);
903 break;
904 default:
905 break;
908 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
910 CBrush brush;
911 brush.CreateSolidBrush(col);
912 HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);
914 Gdiplus::SolidBrush myBrush(GetGdiColor(col));
915 // center symbol, e.g. rect or ellipse
916 switch (type) {
917 case Lanes::ACTIVE:
918 case Lanes::INITIAL:
919 case Lanes::BRANCH:
921 //p->setPen(Qt::NoPen);
922 //p->setBrush(col);
923 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
924 graphics.FillEllipse(&myBrush, R_CENTER);
925 //p->drawEllipse(R_CENTER);
926 break;
927 case Lanes::MERGE_FORK:
928 case Lanes::MERGE_FORK_R:
929 case Lanes::MERGE_FORK_L:
930 //p->setPen(Qt::NoPen);
931 //p->setBrush(col);
932 //p->drawRect(R_CENTER);
933 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
934 graphics.FillRectangle(&myBrush, R_CENTER);
935 break;
936 case Lanes::UNAPPLIED:
937 // Red minus sign
938 //p->setPen(Qt::NoPen);
939 //p->setBrush(Qt::red);
940 //p->drawRect(m - r, h - 1, d, 2);
941 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
942 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
943 break;
944 case Lanes::APPLIED:
945 // Green plus sign
946 //p->setPen(Qt::NoPen);
947 //p->setBrush(DARK_GREEN);
948 //p->drawRect(m - r, h - 1, d, 2);
949 //p->drawRect(m - 1, h - r, 2, d);
950 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
951 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
952 graphics.FillRectangle(&myBrush,m-1,h-r,2,d);
953 break;
954 case Lanes::BOUNDARY:
955 //p->setBrush(back);
956 //p->drawEllipse(R_CENTER);
957 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
958 graphics.DrawEllipse(&myPen, R_CENTER);
959 break;
960 case Lanes::BOUNDARY_C:
961 case Lanes::BOUNDARY_R:
962 case Lanes::BOUNDARY_L:
963 //p->setBrush(back);
964 //p->drawRect(R_CENTER);
965 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
966 graphics.FillRectangle(&myBrush,R_CENTER);
967 break;
968 default:
969 break;
972 ::SelectObject(hdc,oldpen);
973 ::SelectObject(hdc,oldbrush);
974 #undef P_CENTER
975 #undef P_0
976 #undef P_90
977 #undef P_180
978 #undef P_270
979 #undef R_CENTER
982 void CGitLogListBase::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)
984 // TODO: unfinished
985 // return;
986 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(index);
987 if(data->m_CommitHash.IsEmpty())
988 return;
990 CRect rt=rect;
991 LVITEM rItem;
992 SecureZeroMemory(&rItem, sizeof(LVITEM));
993 rItem.mask = LVIF_STATE;
994 rItem.iItem = index;
995 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
996 GetItem(&rItem);
998 // p->translate(QPoint(opt.rect.left(), opt.rect.top()));
1000 if (data->m_Lanes.size() == 0)
1001 m_logEntries.setLane(data->m_CommitHash);
1003 std::vector<int>& lanes=data->m_Lanes;
1004 UINT laneNum = lanes.size();
1005 UINT activeLane = 0;
1006 for (UINT i = 0; i < laneNum; i++)
1007 if (Lanes::isMerge(lanes[i])) {
1008 activeLane = i;
1009 break;
1012 int x1 = 0, x2 = 0;
1013 int maxWidth = rect.Width();
1014 int lw = 3 * rect.Height() / 4; //laneWidth()
1016 COLORREF activeColor = m_LineColors[activeLane % Lanes::COLORS_NUM];
1017 //if (opt.state & QStyle::State_Selected)
1018 // activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);
1020 for (unsigned int i = 0; i < laneNum && x2 < maxWidth; i++)
1023 x1 = x2;
1024 x2 += lw;
1026 int ln = lanes[i];
1027 if (ln == Lanes::EMPTY)
1028 continue;
1030 COLORREF color = i == activeLane ? activeColor : m_LineColors[i % Lanes::COLORS_NUM];
1031 paintGraphLane(hdc, rect.Height(),ln, x1+rect.left, x2+rect.left, color,activeColor, rect.top);
1034 #if 0
1035 for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {
1037 x1 = x2;
1038 x2 += lw;
1040 int ln = lanes[i];
1041 if (ln == Lanes::EMPTY)
1042 continue;
1044 UINT col = ( Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)
1045 || ln ==Lanes:: CROSS_EMPTY) ? activeLane : i;
1047 if (ln == Lanes::CROSS) {
1048 paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
1049 paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, m_LineColors[activeLane % Lanes::COLORS_NUM],rect.top);
1050 } else
1051 paintGraphLane(hdc, rect.Height(),ln, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
1053 #endif
1057 void CGitLogListBase::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1060 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
1061 // Take the default processing unless we set this to something else below.
1062 *pResult = CDRF_DODEFAULT;
1064 if (m_bNoDispUpdates)
1065 return;
1069 switch (pLVCD->nmcd.dwDrawStage)
1071 case CDDS_PREPAINT:
1073 *pResult = CDRF_NOTIFYITEMDRAW;
1074 return;
1076 break;
1077 case CDDS_ITEMPREPAINT:
1079 // This is the prepaint stage for an item. Here's where we set the
1080 // item's text color.
1082 // Tell Windows to send draw notifications for each subitem.
1083 *pResult = CDRF_NOTIFYSUBITEMDRAW;
1085 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
1087 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
1089 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1090 if (data)
1092 #if 0
1093 if (data->bCopiedSelf)
1095 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)
1096 if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))
1097 pLVCD->clrTextBk = GetSysColor(COLOR_MENU);
1100 if (data->bCopies)
1101 crText = m_Colors.GetColor(CColors::Modified);
1102 #endif
1103 if (data->GetAction(this)& (CTGitPath::LOGACTIONS_REBASE_DONE| CTGitPath::LOGACTIONS_REBASE_SKIP) )
1104 crText = RGB(128,128,128);
1106 if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
1107 pLVCD->clrTextBk = RGB(156,156,156);
1108 else if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
1109 pLVCD->clrTextBk = RGB(200,200,128);
1110 else
1111 pLVCD->clrTextBk = ::GetSysColor(COLOR_WINDOW);
1113 if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_CURRENT)
1115 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1116 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1119 if(data->m_CommitHash.ToString() == m_HeadHash)
1121 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1122 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1125 // if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))
1126 // crText = GetSysColor(COLOR_GRAYTEXT);
1128 if (data->m_CommitHash.IsEmpty())
1130 //crText = GetSysColor(RGB(200,200,0));
1131 //SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1132 // We changed the font, so we're returning CDRF_NEWFONT. This
1133 // tells the control to recalculate the extent of the text.
1134 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1138 if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)
1140 if (m_bStrictStopped)
1141 crText = GetSysColor(COLOR_GRAYTEXT);
1143 // Store the color back in the NMLVCUSTOMDRAW struct.
1144 pLVCD->clrText = crText;
1145 return;
1147 break;
1148 case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:
1150 if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))
1152 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);
1155 if (pLVCD->iSubItem == LOGLIST_GRAPH)
1157 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec && (!this->m_IsRebaseReplaceGraph) )
1159 CRect rect;
1160 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_LABEL, rect);
1162 //TRACE(_T("A Graphic left %d right %d\r\n"),rect.left,rect.right);
1163 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1165 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1166 if( !data ->m_CommitHash.IsEmpty())
1167 DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1169 *pResult = CDRF_SKIPDEFAULT;
1170 return;
1174 if (pLVCD->iSubItem == LOGLIST_MESSAGE)
1176 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
1178 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1179 //if(!data->m_IsFull)
1181 //if(data->SafeFetchFullInfo(&g_Git))
1182 // this->Invalidate();
1183 //TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);
1186 if(m_HashMap[data->m_CommitHash].size()!=0)
1188 CRect rect;
1190 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1192 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1193 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1195 *pResult = CDRF_SKIPDEFAULT;
1196 return;
1203 if (pLVCD->iSubItem == LOGLIST_ACTION)
1205 if(this->m_IsIDReplaceAction)
1207 *pResult = CDRF_DODEFAULT;
1208 return;
1210 *pResult = CDRF_DODEFAULT;
1212 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)
1213 return;
1215 int nIcons = 0;
1216 int iconwidth = ::GetSystemMetrics(SM_CXSMICON);
1217 int iconheight = ::GetSystemMetrics(SM_CYSMICON);
1219 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec));
1220 CRect rect;
1221 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1222 //TRACE(_T("Action left %d right %d\r\n"),rect.left,rect.right);
1223 // Get the selected state of the
1224 // item being drawn.
1226 // Fill the background
1227 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1229 // Draw the icon(s) into the compatible DC
1230 pLogEntry->GetAction(this);
1232 if (!pLogEntry->m_IsDiffFiles)
1233 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hFetchIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1235 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_MODIFIED)
1236 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1237 nIcons++;
1239 if (pLogEntry->GetAction(this) & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY) )
1240 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1241 nIcons++;
1243 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_DELETED)
1244 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1245 nIcons++;
1247 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_REPLACED)
1248 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1249 nIcons++;
1250 *pResult = CDRF_SKIPDEFAULT;
1251 return;
1254 break;
1256 *pResult = CDRF_DODEFAULT;
1259 // CGitLogListBase message handlers
1261 void CGitLogListBase::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1263 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1265 // Create a pointer to the item
1266 LV_ITEM* pItem = &(pDispInfo)->item;
1268 // Do the list need text information?
1269 if (!(pItem->mask & LVIF_TEXT))
1270 return;
1272 // By default, clear text buffer.
1273 lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);
1275 bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();
1277 *pResult = 0;
1278 if (m_bNoDispUpdates || bOutOfRange)
1279 return;
1281 // Which item number?
1282 int itemid = pItem->iItem;
1283 GitRev * pLogEntry = NULL;
1284 if (itemid < m_arShownList.GetCount())
1285 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(pItem->iItem));
1287 CString temp;
1288 if(m_IsOldFirst)
1290 temp.Format(_T("%d"),pItem->iItem+1);
1292 }else
1294 temp.Format(_T("%d"),m_arShownList.GetCount()-pItem->iItem);
1297 // Which column?
1298 switch (pItem->iSubItem)
1300 case this->LOGLIST_GRAPH: //Graphic
1301 break;
1302 case this->LOGLIST_REBASE:
1304 if(this->m_IsRebaseReplaceGraph)
1306 CTGitPath path;
1307 path.m_Action=pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_MODE_MASK;
1308 lstrcpyn(pItem->pszText,path.GetActionName(), pItem->cchTextMax);
1311 break;
1312 case this->LOGLIST_ACTION: //action -- no text in the column
1313 break;
1314 case this->LOGLIST_HASH:
1315 if(pLogEntry)
1316 lstrcpyn(pItem->pszText, pLogEntry->m_CommitHash.ToString(), pItem->cchTextMax);
1317 break;
1318 case this->LOGLIST_ID:
1319 if(this->m_IsIDReplaceAction)
1320 lstrcpyn(pItem->pszText, temp, pItem->cchTextMax);
1321 break;
1322 case this->LOGLIST_MESSAGE: //Message
1323 if (pLogEntry)
1324 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetSubject(), pItem->cchTextMax);
1325 break;
1326 case this->LOGLIST_AUTHOR: //Author
1327 if (pLogEntry)
1328 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetAuthorName(), pItem->cchTextMax);
1329 break;
1330 case this->LOGLIST_DATE: //Date
1331 if ( pLogEntry && (!pLogEntry->m_CommitHash.IsEmpty()) )
1332 lstrcpyn(pItem->pszText,
1333 CAppUtils::FormatDateAndTime( pLogEntry->GetAuthorDate(), m_DateFormat, true, m_bRelativeTimes ),
1334 pItem->cchTextMax);
1335 break;
1337 case this->LOGLIST_EMAIL:
1338 if (pLogEntry)
1339 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetAuthorEmail(), pItem->cchTextMax);
1340 break;
1342 case this->LOGLIST_COMMIT_NAME: //Commit
1343 if (pLogEntry)
1344 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetCommitterName(), pItem->cchTextMax);
1345 break;
1347 case this->LOGLIST_COMMIT_EMAIL: //Commit Email
1348 if (pLogEntry)
1349 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetCommitterEmail(), pItem->cchTextMax);
1350 break;
1352 case this->LOGLIST_COMMIT_DATE: //Commit Date
1353 if (pLogEntry)
1354 lstrcpyn(pItem->pszText,
1355 CAppUtils::FormatDateAndTime( pLogEntry->GetCommitterDate(), m_DateFormat, true, m_bRelativeTimes ),
1356 pItem->cchTextMax);
1357 break;
1358 case this->LOGLIST_BUG: //Bug ID
1359 if(pLogEntry)
1360 lstrcpyn(pItem->pszText, (LPCTSTR)this->m_ProjectProperties.FindBugID(pLogEntry->GetSubject()), pItem->cchTextMax);
1361 break;
1363 default:
1364 ASSERT(false);
1368 void CGitLogListBase::OnContextMenu(CWnd* pWnd, CPoint point)
1371 if (pWnd == GetHeaderCtrl())
1373 return m_ColumnManager.OnContextMenuHeader(pWnd,point,!!IsGroupViewEnabled());
1376 int selIndex = GetSelectionMark();
1377 if (selIndex < 0)
1378 return; // nothing selected, nothing to do with a context menu
1380 // if the user selected the info text telling about not all revisions shown due to
1381 // the "stop on copy/rename" option, we also don't show the context menu
1382 if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))
1383 return;
1385 // if the context menu is invoked through the keyboard, we have to use
1386 // a calculated position on where to anchor the menu on
1387 if ((point.x == -1) && (point.y == -1))
1389 CRect rect;
1390 GetItemRect(selIndex, &rect, LVIR_LABEL);
1391 ClientToScreen(&rect);
1392 point = rect.CenterPoint();
1394 m_nSearchIndex = selIndex;
1395 m_bCancelled = FALSE;
1397 // calculate some information the context menu commands can use
1398 // CString pathURL = GetURLFromPath(m_path);
1400 POSITION pos = GetFirstSelectedItemPosition();
1401 int indexNext = GetNextSelectedItem(pos);
1402 if (indexNext < 0)
1403 return;
1405 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(indexNext));
1406 #if 0
1407 GitRev revSelected = pSelLogEntry->Rev;
1408 GitRev revPrevious = git_revnum_t(revSelected)-1;
1409 if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))
1411 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)
1413 LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->SafeGetAt(i);
1414 if (changedpath->lCopyFromRev)
1415 revPrevious = changedpath->lCopyFromRev;
1418 GitRev revSelected2;
1419 if (pos)
1421 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1422 revSelected2 = pLogEntry->Rev;
1424 bool bAllFromTheSameAuthor = true;
1425 CString firstAuthor;
1426 CLogDataVector selEntries;
1427 GitRev revLowest, revHighest;
1428 GitRevRangeArray revisionRanges;
1430 POSITION pos = GetFirstSelectedItemPosition();
1431 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1432 revisionRanges.AddRevision(pLogEntry->Rev);
1433 selEntries.push_back(pLogEntry);
1434 firstAuthor = pLogEntry->sAuthor;
1435 revLowest = pLogEntry->Rev;
1436 revHighest = pLogEntry->Rev;
1437 while (pos)
1439 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1440 revisionRanges.AddRevision(pLogEntry->Rev);
1441 selEntries.push_back(pLogEntry);
1442 if (firstAuthor.Compare(pLogEntry->sAuthor))
1443 bAllFromTheSameAuthor = false;
1444 revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);
1445 revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);
1449 #endif
1451 int FirstSelect=-1, LastSelect=-1;
1452 pos = GetFirstSelectedItemPosition();
1453 FirstSelect = GetNextSelectedItem(pos);
1454 while(pos)
1456 LastSelect = GetNextSelectedItem(pos);
1458 //entry is selected, now show the popup menu
1459 CIconMenu popup;
1460 CIconMenu subbranchmenu, submenu, gnudiffmenu,diffmenu;
1462 if (popup.CreatePopupMenu())
1464 bool isHeadCommit = (pSelLogEntry->m_CommitHash == m_HeadHash);
1466 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_PICK))
1467 popup.AppendMenuIcon(ID_REBASE_PICK, IDS_REBASE_PICK, IDI_PICK);
1469 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SQUASH))
1470 popup.AppendMenuIcon(ID_REBASE_SQUASH, IDS_REBASE_SQUASH, IDI_SQUASH);
1472 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_EDIT))
1473 popup.AppendMenuIcon(ID_REBASE_EDIT, IDS_REBASE_EDIT, IDI_EDIT);
1475 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SKIP))
1476 popup.AppendMenuIcon(ID_REBASE_SKIP, IDS_REBASE_SKIP, IDI_SKIP);
1478 if(m_ContextMenuMask&(GetContextMenuBit(ID_REBASE_SKIP)|GetContextMenuBit(ID_REBASE_EDIT)|
1479 GetContextMenuBit(ID_REBASE_SQUASH)|GetContextMenuBit(ID_REBASE_PICK)))
1480 popup.AppendMenu(MF_SEPARATOR, NULL);
1482 if (GetSelectedCount() == 1)
1486 if( !pSelLogEntry->m_CommitHash.IsEmpty())
1488 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARE))
1489 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1490 // TODO:
1491 // TortoiseMerge could be improved to take a /blame switch
1492 // and then not 'cat' the files from a unified diff but
1493 // blame then.
1494 // But until that's implemented, the context menu entry for
1495 // this feature is commented out.
1496 //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
1497 }else
1499 if(m_ContextMenuMask&GetContextMenuBit(ID_COMMIT))
1500 popup.AppendMenuIcon(ID_COMMIT, IDS_LOG_POPUP_COMMIT, IDI_COMMIT);
1502 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF1))
1504 GitRev *pRev=pSelLogEntry;
1505 if(pSelLogEntry->m_ParentHash.size()==0)
1507 pRev->GetParentFromHash(pRev->m_CommitHash);
1509 if(pRev->m_ParentHash.size()<=1)
1511 popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
1513 }else
1515 gnudiffmenu.CreatePopupMenu();
1516 popup.AppendMenuIcon(ID_GNUDIFF1,IDS_LOG_POPUP_GNUDIFF_PARENT, IDI_DIFF, gnudiffmenu.m_hMenu);
1518 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+(0xFFFF<<16),_T("All Parents"));
1519 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+(0xFFFE<<16),_T("Only Merged Files"));
1521 for(int i=0;i<pRev->m_ParentHash.size();i++)
1523 CString str;
1524 str.Format(_T("%d parent"), i+1);
1525 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+((i+1)<<16),str);
1530 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPAREWITHPREVIOUS))
1533 GitRev *pRev=pSelLogEntry;
1534 if(pSelLogEntry->m_ParentHash.size()==0)
1536 pRev->GetParentFromHash(pRev->m_CommitHash);
1538 if(pRev->m_ParentHash.size()<=1)
1540 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
1543 else
1545 diffmenu.CreatePopupMenu();
1546 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF, diffmenu.m_hMenu);
1547 for(int i=0;i<pRev->m_ParentHash.size();i++)
1549 CString str;
1550 str.Format(_T("%d Parent"), i+1);
1551 diffmenu.AppendMenuIcon(ID_COMPAREWITHPREVIOUS +((i+1)<<16),str);
1556 if(m_ContextMenuMask&GetContextMenuBit(ID_BLAME))
1557 popup.AppendMenuIcon(ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);
1559 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);
1560 popup.AppendMenu(MF_SEPARATOR, NULL);
1563 // if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
1564 // {
1565 // popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);
1566 // }
1567 // if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
1568 // {
1569 // popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
1570 // }
1571 // if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
1572 // (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
1573 // {
1574 // popup.AppendMenu(MF_SEPARATOR, NULL);
1575 // }
1577 CString str,format;
1578 //if (m_hasWC)
1579 // popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
1581 if(!pSelLogEntry->m_CommitHash.IsEmpty())
1583 if((m_ContextMenuMask&GetContextMenuBit(ID_LOG)) &&
1584 GetSelectedCount() == 1)
1585 popup.AppendMenuIcon(ID_LOG, IDS_LOG_POPUP_LOG, IDI_LOG);
1587 format.LoadString(IDS_LOG_POPUP_MERGEREV);
1588 str.Format(format,g_Git.GetCurrentBranch());
1590 bool thisbranch = false;
1591 CString currentBranch = _T("refs/heads/")+g_Git.GetCurrentBranch();
1592 for(int i=0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); i++)
1594 if (m_HashMap[pSelLogEntry->m_CommitHash][i] == currentBranch)
1595 thisbranch = true;
1596 break;
1599 if (m_ContextMenuMask&GetContextMenuBit(ID_MERGEREV) && !isHeadCommit)
1600 popup.AppendMenuIcon(ID_MERGEREV, str, IDI_MERGE);
1602 format.LoadString(IDS_RESET_TO_THIS_FORMAT);
1603 str.Format(format,g_Git.GetCurrentBranch());
1605 if(m_ContextMenuMask&GetContextMenuBit(ID_RESET))
1606 popup.AppendMenuIcon(ID_RESET,str,IDI_REVERT);
1609 // Add Switch Branch express Menu
1610 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end()
1611 && (m_ContextMenuMask&GetContextMenuBit(ID_SWITCHBRANCH))
1614 std::vector<CString *> branchs;
1615 CString ref;
1617 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)
1619 ref = m_HashMap[pSelLogEntry->m_CommitHash][i];
1620 if(ref.Find(_T("refs/heads/")) == 0)
1622 branchs.push_back(&m_HashMap[pSelLogEntry->m_CommitHash][i]);
1626 CString str;
1627 str.LoadString(IDS_SWITCH_BRANCH);
1629 int branchCount = branchs.size();
1630 for(int i=0; i<branchs.size(); i++)
1632 if (*branchs[i] == currentBranch)
1634 branchCount--;
1638 if(branchCount == 1)
1640 str+=_T(" ");
1641 str+= _T('"') + branchs[0]->Mid(11) + _T('"');
1642 popup.AppendMenuIcon(ID_SWITCHBRANCH,str,IDI_SWITCH);
1644 popup.SetMenuItemData(ID_SWITCHBRANCH,(ULONG_PTR)branchs[0]);
1647 else if(branchCount > 1)
1649 subbranchmenu.CreatePopupMenu();
1650 for(int i=0;i<branchs.size();i++)
1652 if (*branchs[i] != currentBranch)
1654 subbranchmenu.AppendMenuIcon(ID_SWITCHBRANCH+(i<<16), branchs[i]->Mid(11));
1655 subbranchmenu.SetMenuItemData(ID_SWITCHBRANCH+(i<<16), (ULONG_PTR) branchs[i]);
1659 popup.AppendMenuIcon(ID_SWITCHBRANCH, str, IDI_SWITCH, subbranchmenu.m_hMenu);
1663 if(m_ContextMenuMask&GetContextMenuBit(ID_SWITCHTOREV) && !isHeadCommit)
1664 popup.AppendMenuIcon(ID_SWITCHTOREV, IDS_SWITCH_TO_THIS , IDI_SWITCH);
1666 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_BRANCH))
1667 popup.AppendMenuIcon(ID_CREATE_BRANCH, IDS_CREATE_BRANCH_AT_THIS , IDI_COPY);
1669 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_TAG))
1670 popup.AppendMenuIcon(ID_CREATE_TAG,IDS_CREATE_TAG_AT_THIS , IDI_TAG);
1672 format.LoadString(IDS_REBASE_THIS_FORMAT);
1673 str.Format(format,g_Git.GetCurrentBranch());
1675 if(pSelLogEntry->m_CommitHash != m_HeadHash)
1676 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_TO_VERSION))
1677 popup.AppendMenuIcon(ID_REBASE_TO_VERSION, str , IDI_REBASE);
1679 if(m_ContextMenuMask&GetContextMenuBit(ID_EXPORT))
1680 popup.AppendMenuIcon(ID_EXPORT,IDS_EXPORT_TO_THIS, IDI_EXPORT);
1682 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1683 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
1685 if (m_ContextMenuMask&GetContextMenuBit(ID_EDITNOTE))
1686 popup.AppendMenuIcon(ID_EDITNOTE, IDS_EDIT_NOTES, IDI_EDIT);
1688 popup.AppendMenu(MF_SEPARATOR, NULL);
1693 if(!pSelLogEntry->m_Ref.IsEmpty() && GetSelectedCount() == 1)
1695 popup.AppendMenuIcon(ID_REFLOG_DEL, IDS_REFLOG_DEL, IDI_DELETE);
1696 popup.AppendMenuIcon(ID_STASH_APPLY, IDS_MENUSTASHAPPLY, IDI_RELOCATE);
1697 popup.AppendMenu(MF_SEPARATOR, NULL);
1700 if (GetSelectedCount() >= 2)
1702 bool bAddSeparator = false;
1703 if (IsSelectionContinuous() || (GetSelectedCount() == 2))
1705 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARETWO))
1706 popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
1709 if (GetSelectedCount() == 2)
1711 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);
1712 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF2))
1713 popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1714 bAddSeparator = true;
1717 if (m_hasWC)
1719 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1720 // if (m_hasWC)
1721 // popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);
1722 bAddSeparator = true;
1725 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1726 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1728 if (bAddSeparator)
1729 popup.AppendMenu(MF_SEPARATOR, NULL);
1732 if ( GetSelectedCount() >0 && (!pSelLogEntry->m_CommitHash.IsEmpty()))
1734 bool bAddSeparator = false;
1735 if ( IsSelectionContinuous() && GetSelectedCount() >= 2 )
1737 if(m_ContextMenuMask&GetContextMenuBit(ID_COMBINE_COMMIT))
1739 CString head;
1740 int headindex;
1741 headindex = this->GetHeadIndex();
1742 if(headindex>=0)
1744 head.Format(_T("HEAD~%d"),LastSelect-headindex);
1745 CGitHash hash=g_Git.GetHash(head);
1746 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(LastSelect));
1747 if(pLastEntry->m_CommitHash == hash) {
1748 popup.AppendMenuIcon(ID_COMBINE_COMMIT,IDS_COMBINE_TO_ONE,IDI_COMBINE);
1749 bAddSeparator = true;
1754 if(m_ContextMenuMask&GetContextMenuBit(ID_CHERRY_PICK) && !isHeadCommit) {
1755 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSION, IDI_EXPORT);
1756 bAddSeparator = true;
1759 if(GetSelectedCount()<=2 || (IsSelectionContinuous() && GetSelectedCount() > 0))
1760 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_PATCH)) {
1761 popup.AppendMenuIcon(ID_CREATE_PATCH, IDS_CREATE_PATCH, IDI_PATCH);
1762 bAddSeparator = true;
1765 if (bAddSeparator)
1766 popup.AppendMenu(MF_SEPARATOR, NULL);
1769 #if 0
1770 // if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))
1771 // {
1772 // popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);
1773 // }
1774 // if (GetSelectedCount() == 1)
1775 // {
1776 // popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);
1777 // popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
1778 // popup.AppendMenu(MF_SEPARATOR, NULL);
1779 // }
1780 #endif
1782 if (GetSelectedCount() == 1)
1784 bool bAddSeparator = false;
1785 if(m_ContextMenuMask&GetContextMenuBit(ID_PUSH) && m_HashMap[pSelLogEntry->m_CommitHash].size() >= 1)
1787 // show the push-option only if the log entry has an associated local branch
1788 bool isLocal = false;
1789 for(int i=0; isLocal == false && i < m_HashMap[pSelLogEntry->m_CommitHash].size(); i++)
1791 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/heads/")) == 0)
1792 isLocal = true;
1794 if (isLocal)
1796 popup.AppendMenuIcon(ID_PUSH, IDS_LOG_PUSH, IDI_PUSH);
1797 bAddSeparator = true;
1801 if(m_ContextMenuMask &GetContextMenuBit(ID_DELETE))
1803 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end() )
1805 CString str;
1806 if( m_HashMap[pSelLogEntry->m_CommitHash].size() == 1 )
1808 str.LoadString(IDS_DELETE_BRANCHTAG_SHORT);
1809 str+=_T(" ");
1810 str+=m_HashMap[pSelLogEntry->m_CommitHash].at(0);
1811 popup.AppendMenuIcon(ID_DELETE, str, IDI_DELETE);
1812 bAddSeparator = true;
1814 else if( m_HashMap[pSelLogEntry->m_CommitHash].size() > 1 )
1816 str.LoadString(IDS_DELETE_BRANCHTAG);
1817 submenu.CreatePopupMenu();
1818 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)
1820 submenu.AppendMenuIcon(ID_DELETE+(i<<16),m_HashMap[pSelLogEntry->m_CommitHash][i]);
1823 popup.AppendMenuIcon(ID_DELETE,str, IDI_DELETE, submenu.m_hMenu);
1824 bAddSeparator = true;
1827 } // m_ContextMenuMask &GetContextMenuBit(ID_DELETE)
1828 if (bAddSeparator)
1829 popup.AppendMenu(MF_SEPARATOR, NULL);
1830 } // GetSelectedCount() == 1
1832 if (GetSelectedCount() == 1)
1834 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYHASH))
1835 popup.AppendMenuIcon(ID_COPYHASH, IDS_COPY_COMMIT_HASH, IDI_COPYCLIP);
1837 if (GetSelectedCount() != 0)
1839 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYCLIPBOARD))
1840 popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD, IDI_COPYCLIP);
1843 if(m_ContextMenuMask&GetContextMenuBit(ID_FINDENTRY))
1844 popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND, IDI_FILTEREDIT);
1846 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1847 // DialogEnableWindow(IDOK, FALSE);
1848 // SetPromptApp(&theApp);
1850 this->ContextMenuAction(cmd, FirstSelect, LastSelect, &popup);
1852 // EnableOKButton();
1853 } // if (popup.CreatePopupMenu())
1857 bool CGitLogListBase::IsSelectionContinuous()
1859 if ( GetSelectedCount()==1 )
1861 // if only one revision is selected, the selection is of course
1862 // continuous
1863 return true;
1866 POSITION pos = GetFirstSelectedItemPosition();
1867 bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());
1868 if (bContinuous)
1870 int itemindex = GetNextSelectedItem(pos);
1871 while (pos)
1873 int nextindex = GetNextSelectedItem(pos);
1874 if (nextindex - itemindex > 1)
1876 bContinuous = false;
1877 break;
1879 itemindex = nextindex;
1882 return bContinuous;
1885 void CGitLogListBase::CopySelectionToClipBoard(bool HashOnly)
1888 CString sClipdata;
1889 POSITION pos = GetFirstSelectedItemPosition();
1890 if (pos != NULL)
1892 CString sRev;
1893 sRev.LoadString(IDS_LOG_REVISION);
1894 CString sAuthor;
1895 sAuthor.LoadString(IDS_LOG_AUTHOR);
1896 CString sDate;
1897 sDate.LoadString(IDS_LOG_DATE);
1898 CString sMessage;
1899 sMessage.LoadString(IDS_LOG_MESSAGE);
1900 while (pos)
1902 CString sLogCopyText;
1903 CString sPaths;
1904 GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1906 if(!HashOnly)
1908 //pLogEntry->GetFiles(this)
1909 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
1911 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->GetFiles(this).GetCount(); ++cpPathIndex)
1913 sPaths += ((CTGitPath&)pLogEntry->GetFiles(this)[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->GetFiles(this)[cpPathIndex].GetGitPathString();
1914 sPaths += _T("\r\n");
1916 sPaths.Trim();
1917 CString body = pLogEntry->GetBody();
1918 body.Replace(_T("\n"), _T("\r\n"));
1919 sLogCopyText.Format(_T("%s: %s\r\n%s: %s\r\n%s: %s\r\n%s:\r\n%s\r\n----\r\n%s\r\n\r\n"),
1920 (LPCTSTR)sRev, pLogEntry->m_CommitHash.ToString(),
1921 (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->GetAuthorName(),
1922 (LPCTSTR)sDate,
1923 (LPCTSTR)CAppUtils::FormatDateAndTime( pLogEntry->GetAuthorDate(), m_DateFormat, true, m_bRelativeTimes ),
1924 (LPCTSTR)sMessage, pLogEntry->GetSubject().Trim() + _T("\r\n\r\n") + body.Trim(),
1925 (LPCTSTR)sPaths);
1926 sClipdata += sLogCopyText;
1927 }else
1929 sClipdata += pLogEntry->m_CommitHash;
1930 break;
1934 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
1939 void CGitLogListBase::DiffSelectedRevWithPrevious()
1941 if (m_bThreadRunning)
1942 return;
1944 int FirstSelect=-1, LastSelect=-1;
1945 POSITION pos = GetFirstSelectedItemPosition();
1946 FirstSelect = GetNextSelectedItem(pos);
1947 while(pos)
1949 LastSelect = GetNextSelectedItem(pos);
1952 ContextMenuAction(ID_COMPAREWITHPREVIOUS,FirstSelect,LastSelect, NULL);
1954 #if 0
1955 UpdateLogInfoLabel();
1956 int selIndex = m_LogList.GetSelectionMark();
1957 if (selIndex < 0)
1958 return;
1959 int selCount = m_LogList.GetSelectedCount();
1960 if (selCount != 1)
1961 return;
1963 // Find selected entry in the log list
1964 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1965 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
1966 long rev1 = pLogEntry->Rev;
1967 long rev2 = rev1-1;
1968 CTGitPath path = m_path;
1970 // See how many files under the relative root were changed in selected revision
1971 int nChanged = 0;
1972 LogChangedPath * changed = NULL;
1973 for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)
1975 LogChangedPath * cpath = pLogEntry->pArChangedPaths->SafeGetAt(c);
1976 if (cpath && cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
1978 ++nChanged;
1979 changed = cpath;
1983 if (m_path.IsDirectory() && nChanged == 1)
1985 // We're looking at the log for a directory and only one file under dir was changed in the revision
1986 // Do diff on that file instead of whole directory
1987 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));
1990 m_bCancelled = FALSE;
1991 DialogEnableWindow(IDOK, FALSE);
1992 SetPromptApp(&theApp);
1993 theApp.DoWaitCursor(1);
1995 if (PromptShown())
1997 GitDiff diff(this, m_hWnd, true);
1998 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1999 diff.SetHEADPeg(m_LogRevision);
2000 diff.ShowCompare(path, rev2, path, rev1);
2002 else
2004 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
2007 theApp.DoWaitCursor(-1);
2008 EnableOKButton();
2009 #endif
2012 void CGitLogListBase::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)
2014 LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
2015 *pResult = -1;
2017 if (pFindInfo->lvfi.flags & LVFI_PARAM)
2018 return;
2019 if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))
2020 return;
2021 if (pFindInfo->lvfi.psz == 0)
2022 return;
2023 #if 0
2024 CString sCmp = pFindInfo->lvfi.psz;
2025 CString sRev;
2026 for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)
2028 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(i));
2029 sRev.Format(_T("%ld"), pLogEntry->Rev);
2030 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
2032 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
2034 *pResult = i;
2035 return;
2038 else
2040 if (sCmp.Compare(sRev)==0)
2042 *pResult = i;
2043 return;
2047 if (pFindInfo->lvfi.flags & LVFI_WRAP)
2049 for (int i=0; i<pFindInfo->iStart; ++i)
2051 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(i));
2052 sRev.Format(_T("%ld"), pLogEntry->Rev);
2053 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
2055 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
2057 *pResult = i;
2058 return;
2061 else
2063 if (sCmp.Compare(sRev)==0)
2065 *pResult = i;
2066 return;
2071 #endif
2072 *pResult = -1;
2075 int CGitLogListBase::FillGitLog(CTGitPath *path,int info,CString *from,CString *to)
2077 ClearText();
2080 this->m_arShownList.SafeRemoveAll();
2082 this->m_logEntries.ClearAll();
2083 this->m_logEntries.ParserFromLog(path,-1,info,from,to);
2085 //this->m_logEntries.ParserFromLog();
2086 SetItemCountEx(this->m_logEntries.size());
2088 for(unsigned int i=0;i<m_logEntries.size();i++)
2090 if(m_IsOldFirst)
2092 m_logEntries.GetGitRevAt(m_logEntries.size()-i-1).m_IsFull=TRUE;
2093 this->m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
2095 }else
2097 m_logEntries.GetGitRevAt(i).m_IsFull=TRUE;
2098 this->m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(i));
2102 if(path)
2103 m_Path=*path;
2104 return 0;
2108 int CGitLogListBase::BeginFetchLog()
2110 ClearText();
2112 this->m_arShownList.SafeRemoveAll();
2114 this->m_logEntries.ClearAll();
2115 git_init();
2117 this->m_LogCache.ClearAllParent();
2119 m_LogCache.FetchCacheIndex(g_Git.m_CurrentDir);
2121 CTGitPath *path;
2122 if(this->m_Path.IsEmpty())
2123 path=NULL;
2124 else
2125 path=&this->m_Path;
2127 CString hash;
2128 int mask;
2129 mask = CGit::LOG_INFO_ONLY_HASH | CGit::LOG_INFO_BOUNDARY;
2130 // if(this->m_bAllBranch)
2131 mask |= m_ShowMask ;
2133 if(m_bShowWC)
2135 this->m_logEntries.insert(m_logEntries.begin(),this->m_wcRev.m_CommitHash);
2136 ResetWcRev();
2137 this->m_LogCache.m_HashMap[m_wcRev.m_CommitHash]=m_wcRev;
2140 CString *pFrom, *pTo;
2141 pFrom = pTo = NULL;
2142 CString head(_T("HEAD"));
2143 if(!this->m_startrev.IsEmpty())
2145 pFrom = &this->m_startrev;
2146 if(!this->m_endrev.IsEmpty())
2147 pTo = &this->m_endrev;
2148 else
2149 pTo = &head;
2152 CFilterData data;
2153 data.m_From = m_From;
2154 data.m_To =m_To;
2156 #if 0 /* use tortoiegit filter */
2157 if(this->m_nSelectedFilter == LOGFILTER_ALL || m_nSelectedFilter == LOGFILTER_AUTHORS)
2158 data.m_Author = this->m_sFilterText;
2160 if(this->m_nSelectedFilter == LOGFILTER_ALL || m_nSelectedFilter == LOGFILTER_MESSAGES)
2161 data.m_MessageFilter = this->m_sFilterText;
2163 data.m_IsRegex = m_bFilterWithRegex;
2164 #endif
2166 CString cmd=g_Git.GetLogCmd(m_StartRef,path,-1,mask,pFrom,pTo,true,&data);
2168 //this->m_logEntries.ParserFromLog();
2169 if(IsInWorkingThread())
2171 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL);
2173 else
2175 SetItemCountEx(this->m_logEntries.size());
2178 git_init();
2180 if(g_Git.IsInitRepos())
2181 return 0;
2183 if(git_open_log(&m_DllGitLog,CUnicodeUtils::GetMulti(cmd,CP_ACP).GetBuffer()))
2185 return -1;
2188 return 0;
2191 BOOL CGitLogListBase::PreTranslateMessage(MSG* pMsg)
2193 if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
2195 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))
2197 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
2199 DiffSelectedRevWithPrevious();
2200 return TRUE;
2203 #if 0
2204 if (GetFocus()==GetDlgItem(IDC_LOGMSG))
2206 DiffSelectedFile();
2207 return TRUE;
2209 #endif
2211 else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == 'A' && GetAsyncKeyState(VK_CONTROL)&0x8000)
2213 // select all entries
2214 for (int i=0; i<GetItemCount(); ++i)
2216 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2218 return TRUE;
2221 #if 0
2222 if (m_hAccel && !bSkipAccelerator)
2224 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
2225 if (ret)
2226 return TRUE;
2229 #endif
2230 //m_tooltips.RelayEvent(pMsg);
2231 return __super::PreTranslateMessage(pMsg);
2234 void CGitLogListBase::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2236 // a double click on an entry in the revision list has happened
2237 *pResult = 0;
2239 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
2240 DiffSelectedRevWithPrevious();
2243 int CGitLogListBase::FetchLogAsync(void * data)
2245 m_ProcData=data;
2246 m_bExitThread=FALSE;
2247 InterlockedExchange(&m_bThreadRunning, TRUE);
2248 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2249 m_LoadingThread = AfxBeginThread(LogThreadEntry, this, THREAD_PRIORITY_LOWEST);
2250 if (m_LoadingThread ==NULL)
2252 InterlockedExchange(&m_bThreadRunning, FALSE);
2253 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2254 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
2255 return -1;
2257 return 0;
2260 //this is the thread function which calls the subversion function
2261 UINT CGitLogListBase::LogThreadEntry(LPVOID pVoid)
2263 return ((CGitLogListBase*)pVoid)->LogThread();
2266 void CGitLogListBase::GetTimeRange(CTime &oldest, CTime &latest)
2268 //CTime time;
2269 oldest=CTime::GetCurrentTime();
2270 latest=CTime(1971,1,2,0,0,0);
2271 for(unsigned int i=0;i<m_logEntries.size();i++)
2273 if(m_logEntries[i].IsEmpty())
2274 continue;
2276 if(m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime() < oldest.GetTime())
2277 oldest = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2279 if(m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime() > latest.GetTime())
2280 latest = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2284 if(latest<oldest)
2285 latest=oldest;
2288 UINT CGitLogListBase::LogThread()
2290 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);
2292 InterlockedExchange(&m_bThreadRunning, TRUE);
2293 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2295 ULONGLONG t1,t2;
2297 if(BeginFetchLog())
2299 InterlockedExchange(&m_bThreadRunning, FALSE);
2300 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2302 return -1;
2305 tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);
2306 bool bRegex = false;
2307 if (m_bFilterWithRegex)
2308 bRegex = ValidateRegexp(m_sFilterText, pat, false);
2310 TRACE(_T("\n===Begin===\n"));
2311 //Update work copy item;
2313 if( m_logEntries.size() > 0)
2315 GitRev *pRev = &m_logEntries.GetGitRevAt(0);
2317 m_arShownList.SafeAdd(pRev);
2321 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2323 if(!g_Git.IsInitRepos())
2325 g_Git.m_critGitDllSec.Lock();
2326 git_get_log_firstcommit(m_DllGitLog);
2327 int total = git_get_log_estimate_commit_count(m_DllGitLog);
2328 g_Git.m_critGitDllSec.Unlock();
2330 GIT_COMMIT commit;
2331 t2=t1=GetTickCount();
2332 int oldprecentage = 0;
2333 int oldsize=m_logEntries.size();
2334 int ret=0;
2335 while( ret== 0)
2337 g_Git.m_critGitDllSec.Lock();
2338 ret=git_get_log_nextcommit(this->m_DllGitLog,&commit);
2339 g_Git.m_critGitDllSec.Unlock();
2341 if(ret)
2342 break;
2344 //printf("%s\r\n",commit.GetSubject());
2345 if(m_bExitThread)
2346 break;
2348 CGitHash hash = (char*)commit.m_hash ;
2350 GitRev *pRev = m_LogCache.GetCacheData(hash);
2351 pRev->m_GitCommit = commit;
2352 InterlockedExchange(&pRev->m_IsCommitParsed, FALSE);
2354 char *note=NULL;
2355 g_Git.m_critGitDllSec.Lock();
2356 git_get_notes(commit.m_hash,&note);
2357 g_Git.m_critGitDllSec.Unlock();
2359 if(note)
2361 pRev->m_Notes.Empty();
2362 g_Git.StringAppend(&pRev->m_Notes,(BYTE*)note);
2365 if(!pRev->m_IsDiffFiles)
2367 pRev->m_CallDiffAsync = DiffAsync;
2370 pRev->ParserParentFromCommit(&commit);
2372 #ifdef DEBUG
2373 pRev->DbgPrint();
2374 TRACE(_T("\n"));
2375 #endif
2377 if(!m_sFilterText.IsEmpty())
2379 if(!IsMatchFilter(bRegex,pRev,pat))
2380 continue;
2382 this->m_critSec.Lock();
2383 m_logEntries.push_back(hash);
2384 m_arShownList.SafeAdd(pRev);
2385 this->m_critSec.Unlock();
2387 t2=GetTickCount();
2389 if(t2-t1>500 || (m_logEntries.size()-oldsize >100))
2391 //update UI
2392 int percent=m_logEntries.size()*100/total + GITLOG_START+1;
2393 if(percent > 99)
2394 percent =99;
2395 if(percent < GITLOG_START)
2396 percent = GITLOG_START +1;
2398 oldsize = m_logEntries.size();
2399 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2401 //if( percent > oldprecentage )
2403 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);
2404 oldprecentage = percent;
2406 t1 = t2;
2409 g_Git.m_critGitDllSec.Lock();
2410 git_close_log(m_DllGitLog);
2411 g_Git.m_critGitDllSec.Unlock();
2414 //Update UI;
2415 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2416 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);
2418 InterlockedExchange(&m_bThreadRunning, FALSE);
2420 return 0;
2423 void CGitLogListBase::Refresh(BOOL IsCleanFilter)
2425 SafeTerminateThread();
2427 this->SetItemCountEx(0);
2428 this->Clear();
2430 ResetWcRev();
2432 //Update branch and Tag info
2433 ReloadHashMap();
2434 //Assume Thread have exited
2435 //if(!m_bThreadRunning)
2437 m_logEntries.clear();
2439 if(IsCleanFilter)
2441 m_sFilterText.Empty();
2442 m_From=-1;
2443 m_To=-1;
2446 InterlockedExchange(&m_bExitThread,FALSE);
2448 InterlockedExchange(&m_bThreadRunning, TRUE);
2449 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2450 if ( (m_LoadingThread=AfxBeginThread(LogThreadEntry, this)) ==NULL)
2452 InterlockedExchange(&m_bThreadRunning, FALSE);
2453 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2454 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
2458 bool CGitLogListBase::ValidateRegexp(LPCTSTR regexp_str, tr1::wregex& pat, bool bMatchCase /* = false */)
2462 tr1::regex_constants::syntax_option_type type = tr1::regex_constants::ECMAScript;
2463 if (!bMatchCase)
2464 type |= tr1::regex_constants::icase;
2465 pat = tr1::wregex(regexp_str, type);
2466 return true;
2468 catch (exception) {}
2469 return false;
2471 BOOL CGitLogListBase::IsMatchFilter(bool bRegex, GitRev *pRev, tr1::wregex &pat)
2474 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;
2475 CString sRev;
2477 if ((bRegex)&&(m_bFilterWithRegex))
2479 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2481 if(this->m_bShowBugtraqColumn)
2483 CString sBugIds = m_ProjectProperties.FindBugID(pRev->GetSubject());
2485 ATLTRACE(_T("bugID = \"%s\"\n"), sBugIds);
2486 if (regex_search(wstring(sBugIds), pat, flags))
2488 return TRUE;
2493 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2495 ATLTRACE(_T("messge = \"%s\"\n"), pRev->GetSubject());
2496 if (regex_search(wstring((LPCTSTR)pRev->GetSubject()), pat, flags))
2498 return TRUE;
2501 ATLTRACE(_T("messge = \"%s\"\n"),pRev->GetBody());
2502 if (regex_search(wstring((LPCTSTR)pRev->GetBody()), pat, flags))
2504 return TRUE;
2508 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2510 if (regex_search(wstring(pRev->GetAuthorName()), pat, flags))
2512 return TRUE;
2515 if (regex_search(wstring(pRev->GetCommitterName()), pat, flags))
2517 return TRUE;
2521 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2523 sRev.Format(_T("%s"), pRev->m_CommitHash.ToString());
2524 if (regex_search(wstring((LPCTSTR)sRev), pat, flags))
2526 return TRUE;
2530 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2532 CTGitPathList *pathList=NULL;
2533 if( pRev->m_IsDiffFiles)
2534 pathList = &pRev->GetFiles(this);
2535 else
2537 if(!pRev->m_IsSimpleListReady)
2538 pRev->SafeGetSimpleList(&g_Git);
2541 if(pathList)
2542 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList->GetCount(); ++cpPathIndex)
2544 if (regex_search(wstring((LPCTSTR)pathList->m_paths.at(cpPathIndex).GetGitOldPathString()), pat, flags))
2546 return true;
2548 if (regex_search(wstring((LPCTSTR)pathList->m_paths.at(cpPathIndex).GetGitPathString()), pat, flags))
2550 return true;
2554 for(INT_PTR i=0;i<pRev->m_SimpleFileList.size();i++)
2556 if (regex_search(wstring((LPCTSTR)pRev->m_SimpleFileList[i]), pat, flags))
2558 return true;
2563 else
2565 CString find = m_sFilterText;
2566 find.MakeLower();
2568 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2570 if(this->m_bShowBugtraqColumn)
2572 CString sBugIds = m_ProjectProperties.FindBugID(pRev->GetSubject());
2574 sBugIds.MakeLower();
2575 if ((sBugIds.Find(find) >= 0))
2577 return TRUE;
2582 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2584 CString msg = pRev->GetSubject();
2586 msg = msg.MakeLower();
2587 if ((msg.Find(find) >= 0))
2589 return TRUE;
2592 msg = pRev->GetBody();
2594 msg = msg.MakeLower();
2595 if ((msg.Find(find) >= 0))
2597 return TRUE;
2601 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2603 CString msg = pRev->GetAuthorName();
2604 msg = msg.MakeLower();
2605 if ((msg.Find(find) >= 0))
2607 return TRUE;
2611 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2613 sRev.Format(_T("%s"), pRev->m_CommitHash.ToString());
2614 if ((sRev.Find(find) >= 0))
2616 return TRUE;
2620 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2622 CTGitPathList *pathList=NULL;
2623 if( pRev->m_IsDiffFiles)
2624 pathList = &pRev->GetFiles(this);
2625 else
2627 if(!pRev->m_IsSimpleListReady)
2628 pRev->SafeGetSimpleList(&g_Git);
2630 if(pathList)
2631 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList->GetCount() ; ++cpPathIndex)
2633 CTGitPath *cpath = &pathList->m_paths.at(cpPathIndex);
2634 CString path = cpath->GetGitOldPathString();
2635 path.MakeLower();
2636 if ((path.Find(find)>=0))
2638 return true;
2640 path = cpath->GetGitPathString();
2641 path.MakeLower();
2642 if ((path.Find(find)>=0))
2644 return true;
2648 for (INT_PTR i=0;i<pRev->m_SimpleFileList.size();i++)
2650 CString path = pRev->m_SimpleFileList[i];
2651 path.MakeLower();
2652 if ((path.Find(find)>=0))
2654 return true;
2658 } // else (from if (bRegex))
2659 return FALSE;
2663 void CGitLogListBase::RecalculateShownList(CThreadSafePtrArray * pShownlist)
2666 pShownlist->SafeRemoveAll();
2668 tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);
2669 bool bRegex = false;
2670 if (m_bFilterWithRegex)
2671 bRegex = ValidateRegexp(m_sFilterText, pat, false);
2673 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;
2674 CString sRev;
2675 for (DWORD i=0; i<m_logEntries.size(); ++i)
2677 if ((bRegex)&&(m_bFilterWithRegex))
2679 #if 0
2680 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2682 ATLTRACE(_T("bugID = \"%s\"\n"), (LPCTSTR)m_logEntries[i]->sBugIDs);
2683 if (regex_search(wstring((LPCTSTR)m_logEntries[i]->sBugIDs), pat, flags)&&IsEntryInDateRange(i))
2685 pShownlist->SafeAdd(m_logEntries[i]);
2686 continue;
2689 #endif
2690 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2692 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).GetSubject());
2693 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetSubject()), pat, flags)&&IsEntryInDateRange(i))
2695 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2696 continue;
2699 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).GetBody());
2700 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetBody()), pat, flags)&&IsEntryInDateRange(i))
2702 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2703 continue;
2706 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2708 CTGitPathList pathList = m_logEntries.GetGitRevAt(i).GetFiles(this);
2710 bool bGoing = true;
2711 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList.GetCount() && bGoing; ++cpPathIndex)
2713 CTGitPath cpath = pathList[cpPathIndex];
2714 if (regex_search(wstring((LPCTSTR)cpath.GetGitOldPathString()), pat, flags)&&IsEntryInDateRange(i))
2716 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2717 bGoing = false;
2718 continue;
2720 if (regex_search(wstring((LPCTSTR)cpath.GetGitPathString()), pat, flags)&&IsEntryInDateRange(i))
2722 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2723 bGoing = false;
2724 continue;
2726 if (regex_search(wstring((LPCTSTR)cpath.GetActionName()), pat, flags)&&IsEntryInDateRange(i))
2728 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2729 bGoing = false;
2730 continue;
2734 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2736 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetAuthorName()), pat, flags)&&IsEntryInDateRange(i))
2738 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2739 continue;
2742 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2744 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2745 if (regex_search(wstring((LPCTSTR)sRev), pat, flags)&&IsEntryInDateRange(i))
2747 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2748 continue;
2751 } // if (bRegex)
2752 else
2754 CString find = m_sFilterText;
2755 find.MakeLower();
2756 #if 0
2757 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2759 CString sBugIDs = m_logEntries[i]->sBugIDs;
2761 sBugIDs = sBugIDs.MakeLower();
2762 if ((sBugIDs.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2764 pShownlist->SafeAdd(m_logEntries[i]);
2765 continue;
2768 #endif
2769 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2771 CString msg = m_logEntries.GetGitRevAt(i).GetSubject();
2773 msg = msg.MakeLower();
2774 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2776 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2777 continue;
2779 msg = m_logEntries.GetGitRevAt(i).GetBody();
2781 msg = msg.MakeLower();
2782 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2784 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2785 continue;
2788 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2790 CTGitPathList pathList = m_logEntries.GetGitRevAt(i).GetFiles(this);
2792 bool bGoing = true;
2793 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList.GetCount() && bGoing; ++cpPathIndex)
2795 CTGitPath cpath = pathList[cpPathIndex];
2796 CString path = cpath.GetGitOldPathString();
2797 path.MakeLower();
2798 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2800 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2801 bGoing = false;
2802 continue;
2804 path = cpath.GetGitPathString();
2805 path.MakeLower();
2806 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2808 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2809 bGoing = false;
2810 continue;
2812 path = cpath.GetActionName();
2813 path.MakeLower();
2814 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2816 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2817 bGoing = false;
2818 continue;
2822 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2824 CString msg = m_logEntries.GetGitRevAt(i).GetAuthorName();
2825 msg = msg.MakeLower();
2826 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2828 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2829 continue;
2832 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2834 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2835 if ((sRev.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2837 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2838 continue;
2841 } // else (from if (bRegex))
2842 } // for (DWORD i=0; i<m_logEntries.size(); ++i)
2846 BOOL CGitLogListBase::IsEntryInDateRange(int /*i*/)
2849 __time64_t time = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2851 if(m_From == -1)
2852 if(m_To == -1)
2853 return true;
2854 else
2855 return time <= m_To;
2856 else
2857 if(m_To == -1)
2858 return time >= m_From;
2859 else
2860 return ((time >= m_From)&&(time <= m_To));
2862 return TRUE; /* git dll will filter time range */
2864 // return TRUE;
2866 void CGitLogListBase::StartFilter()
2868 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2869 RecalculateShownList(&m_arShownList);
2870 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2873 DeleteAllItems();
2874 SetItemCountEx(ShownCountWithStopped());
2875 RedrawItems(0, ShownCountWithStopped());
2876 Invalidate();
2879 void CGitLogListBase::RemoveFilter()
2882 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2884 m_arShownList.SafeRemoveAll();
2886 // reset the time filter too
2887 #if 0
2888 m_timFrom = (__time64_t(m_tFrom));
2889 m_timTo = (__time64_t(m_tTo));
2890 m_DateFrom.SetTime(&m_timFrom);
2891 m_DateTo.SetTime(&m_timTo);
2892 m_DateFrom.SetRange(&m_timFrom, &m_timTo);
2893 m_DateTo.SetRange(&m_timFrom, &m_timTo);
2894 #endif
2896 for (DWORD i=0; i<m_logEntries.size(); ++i)
2898 if(this->m_IsOldFirst)
2900 m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
2901 }else
2903 m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(i));
2906 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
2907 DeleteAllItems();
2908 SetItemCountEx(ShownCountWithStopped());
2909 RedrawItems(0, ShownCountWithStopped());
2911 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2914 void CGitLogListBase::Clear()
2916 m_arShownList.SafeRemoveAll();
2917 DeleteAllItems();
2919 m_logEntries.ClearAll();
2923 void CGitLogListBase::OnDestroy()
2925 // save the column widths to the registry
2926 SaveColumnWidths();
2928 SafeTerminateThread();
2929 SafeTerminateAsyncDiffThread();
2931 int retry = 0;
2932 while(m_LogCache.SaveCache())
2934 if(retry > 5)
2935 break;
2936 Sleep(1000);
2938 retry++;
2940 //if(CMessageBox::Show(NULL,_T("Cannot Save Log Cache to Disk. To retry click yes. To give up click no."),_T("TortoiseGit"),
2941 // MB_YESNO) == IDNO)
2942 // break;
2945 CHintListCtrl::OnDestroy();
2948 LRESULT CGitLogListBase::OnLoad(WPARAM wParam,LPARAM lParam)
2950 UNREFERENCED_PARAMETER(lParam);
2951 CRect rect;
2952 int i=(int)wParam;
2953 this->GetItemRect(i,&rect,LVIR_BOUNDS);
2954 this->InvalidateRect(rect);
2956 return 0;
2960 * Save column widths to the registry
2962 void CGitLogListBase::SaveColumnWidths()
2964 int maxcol = m_ColumnManager.GetColumnCount();
2966 for (int col = 0; col < maxcol; col++)
2967 if (m_ColumnManager.IsVisible (col))
2968 m_ColumnManager.ColumnResized (col);
2970 m_ColumnManager.WriteSettings();
2973 int CGitLogListBase::GetHeadIndex()
2975 if(m_HeadHash.IsEmpty())
2976 return -1;
2978 for(int i=0;i<m_arShownList.GetCount();i++)
2980 GitRev *pRev = (GitRev*)m_arShownList[i];
2981 if(pRev)
2983 if(pRev->m_CommitHash.ToString() == m_HeadHash )
2984 return i;
2987 return -1;
2989 void CGitLogListBase::OnFind()
2991 if (!m_pFindDialog)
2993 m_pFindDialog = new CFindDlg(this);
2994 m_pFindDialog->Create(this);
2997 void CGitLogListBase::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)
2999 m_ColumnManager.OnHdnBegintrack(pNMHDR, pResult);
3001 void CGitLogListBase::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
3003 if(!m_ColumnManager.OnHdnItemchanging(pNMHDR, pResult))
3004 Default();
3006 LRESULT CGitLogListBase::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
3009 ASSERT(m_pFindDialog != NULL);
3010 bool bFound = false;
3011 int i=0;
3013 if (m_pFindDialog->IsTerminating())
3015 // invalidate the handle identifying the dialog box.
3016 m_pFindDialog = NULL;
3017 return 0;
3020 if(m_pFindDialog->IsRef())
3022 CString str;
3023 str=m_pFindDialog->GetFindString();
3025 CGitHash hash;
3027 if(!str.IsEmpty())
3028 hash = g_Git.GetHash(str);
3030 if(!hash.IsEmpty())
3032 for (i = 0; i<m_arShownList.GetCount(); i++)
3034 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(i);
3035 if(pLogEntry && pLogEntry->m_CommitHash == hash)
3037 bFound = true;
3038 break;
3045 if(m_pFindDialog->FindNext())
3047 //read data from dialog
3048 CString FindText = m_pFindDialog->GetFindString();
3049 bool bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
3051 tr1::wregex pat;
3052 bool bRegex = ValidateRegexp(FindText, pat, bMatchCase);
3054 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_not_null;
3057 for (i = this->m_nSearchIndex; i<m_arShownList.GetCount()&&!bFound; i++)
3059 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(i);
3061 CString str;
3062 str+=pLogEntry->m_CommitHash.ToString();
3063 str+=_T("\n");
3065 for(int j=0;j<this->m_HashMap[pLogEntry->m_CommitHash].size();j++)
3067 str+=m_HashMap[pLogEntry->m_CommitHash][j];
3068 str+=_T("\n");
3071 str+=pLogEntry->GetAuthorEmail();
3072 str+=_T("\n");
3073 str+=pLogEntry->GetAuthorName();
3074 str+=_T("\n");
3075 str+=pLogEntry->GetBody();
3076 str+=_T("\n");
3077 str+=pLogEntry->GetCommitterEmail();
3078 str+=_T("\n");
3079 str+=pLogEntry->GetCommitterName();
3080 str+=_T("\n");
3081 str+=pLogEntry->GetSubject();
3082 str+=_T("\n");
3085 /*Because changed files list is loaded on demand when gui show,
3086 files will empty when files have not fetched.
3088 we can add it back by using one-way diff(with outnumber changed and rename detect.
3089 here just need changed filename list. one-way is much quicker.
3091 if(pLogEntry->m_IsFull)
3093 for(int i=0;i<pLogEntry->GetFiles(this).GetCount();i++)
3095 str+=pLogEntry->GetFiles(this)[i].GetWinPath();
3096 str+=_T("\n");
3097 str+=pLogEntry->GetFiles(this)[i].GetGitOldPathString();
3098 str+=_T("\n");
3100 }else
3102 if(!pLogEntry->m_IsSimpleListReady)
3103 pLogEntry->SafeGetSimpleList(&g_Git);
3105 for(int i=0;i<pLogEntry->m_SimpleFileList.size();i++)
3107 str+=pLogEntry->m_SimpleFileList[i];
3108 str+=_T("\n");
3114 if (bRegex)
3116 if (regex_search(wstring(str), pat, flags))
3118 bFound = true;
3119 break;
3122 else
3124 if (bMatchCase)
3126 if (str.Find(FindText) >= 0)
3128 bFound = true;
3129 break;
3133 else
3135 CString msg = str;
3136 msg = msg.MakeLower();
3137 CString find = FindText.MakeLower();
3138 if (msg.Find(find) >= 0)
3140 bFound = TRUE;
3141 break;
3145 } // for (i = this->m_nSearchIndex; i<m_arShownList.GetItemCount()&&!bFound; i++)
3147 } // if(m_pFindDialog->FindNext())
3148 //UpdateLogInfoLabel();
3150 if (bFound)
3152 this->m_nSearchIndex = i;
3153 EnsureVisible(i, FALSE);
3154 SetItemState(GetSelectionMark(), 0, LVIS_SELECTED);
3155 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
3156 SetSelectionMark(i);
3157 //FillLogMessageCtrl();
3158 UpdateData(FALSE);
3159 m_nSearchIndex++;
3160 if (m_nSearchIndex >= m_arShownList.GetCount())
3161 m_nSearchIndex = (int)m_arShownList.GetCount()-1;
3164 return 0;
3167 void CGitLogListBase::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)
3169 m_ColumnManager.OnColumnResized(pNMHDR,pResult);
3171 *pResult = FALSE;
3174 void CGitLogListBase::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)
3176 m_ColumnManager.OnColumnMoved(pNMHDR, pResult);
3178 Invalidate(FALSE);