dropped unused TSVN settings
[TortoiseGit.git] / src / TortoiseProc / GitLogListBase.cpp
blobf08a970da51eed8850f1d7186b826981deff6863
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 "GITProgressDlg.h"
33 #include "ProgressDlg.h"
34 //#include "RepositoryBrowser.h"
35 //#include "CopyDlg.h"
36 //#include "StatGraphDlg.h"
37 #include "Logdlg.h"
38 #include "MessageBox.h"
39 #include "Registry.h"
40 #include "AppUtils.h"
41 #include "PathUtils.h"
42 #include "StringUtils.h"
43 #include "UnicodeUtils.h"
44 #include "TempFile.h"
45 //#include "GitInfo.h"
46 //#include "GitDiff.h"
47 #include "IconMenu.h"
48 //#include "RevisionRangeDlg.h"
49 //#include "BrowseFolder.h"
50 //#include "BlameDlg.h"
51 //#include "Blame.h"
52 //#include "GitHelpers.h"
53 #include "GitStatus.h"
54 //#include "LogDlgHelper.h"
55 //#include "CachedLogInfo.h"
56 //#include "RepositoryInfo.h"
57 //#include "EditPropertiesDlg.h"
58 #include "FileDiffDlg.h"
59 #include "..\\TortoiseShell\\Resource.h"
60 #include "FindDlg.h"
62 const UINT CGitLogListBase::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
64 IMPLEMENT_DYNAMIC(CGitLogListBase, CHintListCtrl)
66 CGitLogListBase::CGitLogListBase():CHintListCtrl()
67 ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)
68 ,m_nSearchIndex(0)
69 ,m_bNoDispUpdates(FALSE)
70 , m_bThreadRunning(FALSE)
71 , m_bStrictStopped(false)
72 , m_pStoreSelection(NULL)
73 , m_nSelectedFilter(LOGFILTER_ALL)
74 , m_bVista(false)
75 , m_bShowWC(false)
76 , m_logEntries(&m_LogCache)
77 , m_pFindDialog(NULL)
78 , m_ColumnManager(this)
79 , m_dwDefaultColumns(0)
80 , m_arShownList(&m_critSec)
82 // use the default GUI font, create a copy of it and
83 // change the copy to BOLD (leave the rest of the font
84 // the same)
85 HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
86 LOGFONT lf = {0};
87 GetObject(hFont, sizeof(LOGFONT), &lf);
88 lf.lfWeight = FW_BOLD;
89 m_boldFont = CreateFontIndirect(&lf);
91 m_bShowBugtraqColumn=false;
93 m_IsIDReplaceAction=FALSE;
95 this->m_critSec.Init();
96 m_wcRev.m_CommitHash.Empty();
97 m_wcRev.GetSubject()=_T("Working dir changes");
98 m_wcRev.m_ParentHash.clear();
99 m_wcRev.m_Mark=_T('-');
100 m_wcRev.m_IsUpdateing=FALSE;
101 m_wcRev.m_IsFull = TRUE;
103 m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
104 m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
105 m_hAddedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
106 m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
107 m_hFetchIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONFETCHING), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
109 m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
111 g_Git.GetMapHashToFriendName(m_HashMap);
112 m_CurrentBranch=g_Git.GetCurrentBranch();
113 this->m_HeadHash=g_Git.GetHash(_T("HEAD"));
115 m_From=-1;;
116 m_To=-1;
118 m_ShowMask = 0;
119 m_LoadingThread = NULL;
121 InterlockedExchange(&m_bExitThread,FALSE);
122 m_IsOldFirst = FALSE;
123 m_IsRebaseReplaceGraph = FALSE;
126 for(int i=0;i<Lanes::COLORS_NUM;i++)
128 m_LineColors[i] = m_Colors.GetColor((CColors::Colors)(CColors::BranchLine1+i));
130 // get short/long datetime setting from registry
131 DWORD RegUseShortDateFormat = CRegDWORD(_T("Software\\TortoiseGit\\LogDateFormat"), TRUE);
132 if ( RegUseShortDateFormat )
134 m_DateFormat = DATE_SHORTDATE;
136 else
138 m_DateFormat = DATE_LONGDATE;
140 // get relative time display setting from registry
141 DWORD regRelativeTimes = CRegDWORD(_T("Software\\TortoiseGit\\RelativeTimes"), FALSE);
142 m_bRelativeTimes = (regRelativeTimes != 0);
143 m_ContextMenuMask = 0xFFFFFFFFFFFFFFFF;
145 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_PICK);
146 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SQUASH);
147 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_EDIT);
148 m_ContextMenuMask &= ~GetContextMenuBit(ID_REBASE_SKIP);
149 m_ContextMenuMask &= ~GetContextMenuBit(ID_LOG);
150 m_ContextMenuMask &= ~GetContextMenuBit(ID_BLAME);
152 OSVERSIONINFOEX inf;
153 SecureZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
154 inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
155 GetVersionEx((OSVERSIONINFO *)&inf);
156 WORD fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);
157 m_bVista = (fullver >= 0x0600);
159 m_ColumnRegKey=_T("log");
161 m_AsyncThreadExit = FALSE;
162 m_AsyncDiffEvent = ::CreateEvent(NULL,FALSE,TRUE,NULL);
163 m_AsynDiffListLock.Init();
165 m_DiffingThread = AfxBeginThread(AsyncThread, this, THREAD_PRIORITY_BELOW_NORMAL);
166 if (m_DiffingThread ==NULL)
168 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
169 return;
174 int CGitLogListBase::AsyncDiffThread()
176 m_AsyncThreadExited = false;
177 while(!m_AsyncThreadExit)
179 ::WaitForSingleObject(m_AsyncDiffEvent, INFINITE);
181 GitRev *pRev = NULL;
182 while(!m_AsyncThreadExit && m_AsynDiffList.size() > 0)
184 m_AsynDiffListLock.Lock();
185 pRev = m_AsynDiffList.back();
186 m_AsynDiffList.pop_back();
187 m_AsynDiffListLock.Unlock();
189 if( pRev->m_CommitHash.IsEmpty() )
191 if(pRev->m_IsDiffFiles)
192 continue;
194 pRev->GetFiles(this).Clear();
195 pRev->m_ParentHash.clear();
196 pRev->m_ParentHash.push_back(m_HeadHash);
197 if(g_Git.IsInitRepos())
199 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)
259 m_ContextMenuMask &= hideMask;
261 else
263 m_ContextMenuMask &= ~hideMask;
267 CGitLogListBase::~CGitLogListBase()
269 InterlockedExchange(&m_bNoDispUpdates, TRUE);
270 this->m_arShownList.SafeRemoveAll();
272 DestroyIcon(m_hModifiedIcon);
273 DestroyIcon(m_hReplacedIcon);
274 DestroyIcon(m_hAddedIcon);
275 DestroyIcon(m_hDeletedIcon);
276 m_logEntries.ClearAll();
278 if (m_boldFont)
279 DeleteObject(m_boldFont);
281 if ( m_pStoreSelection )
283 delete m_pStoreSelection;
284 m_pStoreSelection = NULL;
287 SafeTerminateThread();
288 SafeTerminateAsyncDiffThread();
290 if(m_AsyncDiffEvent)
291 CloseHandle(m_AsyncDiffEvent);
295 BEGIN_MESSAGE_MAP(CGitLogListBase, CHintListCtrl)
296 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
297 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)
298 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)
299 ON_WM_CONTEXTMENU()
300 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)
301 ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)
302 ON_WM_CREATE()
303 ON_WM_DESTROY()
304 ON_MESSAGE(MSG_LOADED,OnLoad)
305 ON_WM_MEASUREITEM()
306 ON_WM_MEASUREITEM_REFLECT()
307 ON_NOTIFY(HDN_BEGINTRACKA, 0, OnHdnBegintrack)
308 ON_NOTIFY(HDN_BEGINTRACKW, 0, OnHdnBegintrack)
309 ON_NOTIFY(HDN_ITEMCHANGINGA, 0, OnHdnItemchanging)
310 ON_NOTIFY(HDN_ITEMCHANGINGW, 0, OnHdnItemchanging)
311 ON_NOTIFY(HDN_ENDTRACK, 0, OnColumnResized)
312 ON_NOTIFY(HDN_ENDDRAG, 0, OnColumnMoved)
313 END_MESSAGE_MAP()
315 void CGitLogListBase::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
317 //if (m_nRowHeight>0)
319 lpMeasureItemStruct->itemHeight = 50;
323 int CGitLogListBase:: OnCreate(LPCREATESTRUCT lpCreateStruct)
325 PreSubclassWindow();
326 return CHintListCtrl::OnCreate(lpCreateStruct);
329 void CGitLogListBase::PreSubclassWindow()
331 SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
332 // load the icons for the action columns
333 // m_Theme.Open(m_hWnd, L"ListView");
334 SetWindowTheme(m_hWnd, L"Explorer", NULL);
335 CHintListCtrl::PreSubclassWindow();
338 void CGitLogListBase::InsertGitColumn()
340 CString temp;
342 CRegDWORD regFullRowSelect(_T("Software\\TortoiseGit\\FullRowSelect"), TRUE);
343 DWORD exStyle = LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_SUBITEMIMAGES;
344 if (DWORD(regFullRowSelect))
345 exStyle |= LVS_EX_FULLROWSELECT;
346 SetExtendedStyle(exStyle);
348 UpdateProjectProperties();
350 static UINT normal[] =
352 IDS_LOG_GRAPH,
353 IDS_LOG_REBASE,
354 IDS_LOG_ID,
355 IDS_LOG_HASH,
356 IDS_LOG_ACTIONS,
357 IDS_LOG_MESSAGE,
358 IDS_LOG_AUTHOR,
359 IDS_LOG_DATE,
360 IDS_LOG_EMAIL,
361 IDS_LOG_COMMIT_NAME,
362 IDS_LOG_COMMIT_EMAIL,
363 IDS_LOG_COMMIT_DATE,
364 IDS_LOG_BUGIDS,
367 static int with[] =
369 ICONITEMBORDER+16*4,
370 ICONITEMBORDER+16*4,
371 ICONITEMBORDER+16*4,
372 ICONITEMBORDER+16*4,
373 ICONITEMBORDER+16*4,
374 LOGLIST_MESSAGE_MIN,
375 ICONITEMBORDER+16*4,
376 ICONITEMBORDER+16*4,
377 ICONITEMBORDER+16*4,
378 ICONITEMBORDER+16*4,
379 ICONITEMBORDER+16*4,
380 ICONITEMBORDER+16*4,
381 ICONITEMBORDER+16*4,
383 m_dwDefaultColumns = GIT_LOG_GRAPH|GIT_LOG_ACTIONS|GIT_LOG_MESSAGE|GIT_LOG_AUTHOR|GIT_LOG_DATE;
385 DWORD hideColumns = 0;
386 if(this->m_IsRebaseReplaceGraph)
388 hideColumns |= GIT_LOG_GRAPH;
389 m_dwDefaultColumns |= GIT_LOG_REBASE;
391 else
393 hideColumns |= GIT_LOG_REBASE;
396 if(this->m_IsIDReplaceAction)
398 hideColumns |= GIT_LOG_ACTIONS;
399 m_dwDefaultColumns |= GIT_LOG_ID;
400 m_dwDefaultColumns |= GIT_LOG_HASH;
402 else
404 hideColumns |= GIT_LOG_ID;
406 if(this->m_bShowBugtraqColumn)
408 m_dwDefaultColumns |= GIT_LOGLIST_BUG;
410 else
412 hideColumns |= GIT_LOGLIST_BUG;
414 SetRedraw(false);
416 m_ColumnManager.SetNames(normal, _countof(normal));
417 m_ColumnManager.ReadSettings(m_dwDefaultColumns, hideColumns, m_ColumnRegKey+_T("loglist"), _countof(normal), with);
419 SetRedraw(true);
424 * Resizes all columns in a list control to values in registry.
426 void CGitLogListBase::ResizeAllListCtrlCols()
428 // column max and min widths to allow
429 static const int nMinimumWidth = 10;
430 static const int nMaximumWidth = 1000;
431 CHeaderCtrl* pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
432 if (pHdrCtrl)
434 int numcols = pHdrCtrl->GetItemCount();
435 for (int col = 0; col < numcols; col++)
437 // get width for this col last time from registry
438 CString regentry;
439 regentry.Format( _T("Software\\TortoiseGit\\%s\\ColWidth%d"),m_ColumnRegKey, col);
440 CRegDWORD regwidth(regentry, 0);
441 int cx = regwidth;
442 if ( cx == 0 )
444 // no saved value, setup sensible defaults
445 if (col == this->LOGLIST_MESSAGE)
447 cx = LOGLIST_MESSAGE_MIN;
449 else
451 cx = ICONITEMBORDER+16*4;
454 if (cx < nMinimumWidth)
456 cx = nMinimumWidth;
458 else if (cx > nMaximumWidth)
460 cx = nMaximumWidth;
463 SetColumnWidth(col, cx);
470 BOOL CGitLogListBase::GetShortName(CString ref, CString &shortname,CString prefix)
472 //TRACE(_T("%s %s\r\n"),ref,prefix);
473 if(ref.Left(prefix.GetLength()) == prefix)
475 shortname = ref.Right(ref.GetLength()-prefix.GetLength());
476 if(shortname.Right(3)==_T("^{}"))
477 shortname=shortname.Left(shortname.GetLength()-3);
478 return TRUE;
480 return FALSE;
483 void CGitLogListBase::FillBackGround(HDC hdc, int Index,CRect &rect)
485 // HBRUSH brush;
486 LVITEM rItem;
487 SecureZeroMemory(&rItem, sizeof(LVITEM));
488 rItem.mask = LVIF_STATE;
489 rItem.iItem = Index;
490 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
491 GetItem(&rItem);
493 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(Index);
494 HBRUSH brush = NULL;
496 if (IsAppThemed() && m_bVista)
498 HTHEME hTheme = OpenThemeData(m_hWnd, L"Explorer::ListView;ListView");
499 int state = LISS_NORMAL;
500 if (rItem.state & LVIS_SELECTED)
502 if (::GetFocus() == m_hWnd)
503 state |= LISS_SELECTED;
504 else
505 state |= LISS_SELECTEDNOTFOCUS;
507 else
509 if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
510 brush = ::CreateSolidBrush(RGB(156,156,156));
511 else if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
512 brush = ::CreateSolidBrush(RGB(200,200,128));
515 if (brush != NULL)
517 ::FillRect(hdc, &rect, brush);
518 ::DeleteObject(brush);
520 else
522 if (IsThemeBackgroundPartiallyTransparent(hTheme, LVP_LISTITEM, state))
523 DrawThemeParentBackground(m_hWnd, hdc, &rect);
525 CRect rectDraw = rect;
526 if(rItem.state & LVIS_SELECTED)
527 rectDraw.InflateRect(1,0);
528 else
529 rectDraw.InflateRect(1,1);
531 DrawThemeBackground(hTheme, hdc, LVP_LISTITEM, state, rectDraw, &rect);
533 CloseThemeData(hTheme);
535 else
537 if (rItem.state & LVIS_SELECTED)
539 if (::GetFocus() == m_hWnd)
540 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
541 else
542 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
544 else
546 //if (pLogEntry->bCopiedSelf)
547 // brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
548 //else
549 if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
550 brush = ::CreateSolidBrush(RGB(156,156,156));
551 else if(pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
552 brush = ::CreateSolidBrush(RGB(200,200,128));
553 else
554 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
556 if (brush == NULL)
557 return;
559 ::FillRect(hdc, &rect, brush);
560 ::DeleteObject(brush);
564 void CGitLogListBase::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)
566 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(index);
567 CRect rt=rect;
568 LVITEM rItem;
569 SecureZeroMemory(&rItem, sizeof(LVITEM));
570 rItem.mask = LVIF_STATE;
571 rItem.iItem = index;
572 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
573 GetItem(&rItem);
575 CDC W_Dc;
576 W_Dc.Attach(hdc);
578 HTHEME hTheme = NULL;
579 if (IsAppThemed() && m_bVista)
580 hTheme = OpenThemeData(m_hWnd, L"Explorer::ListView;ListView");
582 for(unsigned int i=0;i<m_HashMap[data->m_CommitHash].size();i++)
584 CString str;
585 str=m_HashMap[data->m_CommitHash][i];
587 CString shortname;
588 HBRUSH brush = 0;
589 shortname = _T("");
590 COLORREF colRef = 0;
592 //Determine label color
593 if(GetShortName(str,shortname,_T("refs/heads/")))
595 if( shortname == m_CurrentBranch )
596 colRef = m_Colors.GetColor(CColors::CurrentBranch);
597 else
598 colRef = m_Colors.GetColor(CColors::LocalBranch);
601 else if(GetShortName(str,shortname,_T("refs/remotes/")))
603 colRef = m_Colors.GetColor(CColors::RemoteBranch);
605 else if(GetShortName(str,shortname,_T("refs/tags/")))
607 colRef = m_Colors.GetColor(CColors::Tag);
609 else if(GetShortName(str,shortname,_T("refs/stash")))
611 colRef = m_Colors.GetColor(CColors::Stash);
612 shortname=_T("stash");
614 else if(GetShortName(str,shortname,_T("refs/bisect/")))
616 if(shortname.Find(_T("good")) == 0)
618 colRef = m_Colors.GetColor(CColors::BisectGood);
619 shortname = _T("good");
622 if(shortname.Find(_T("bad")) == 0)
624 colRef = m_Colors.GetColor(CColors::BisectBad);
625 shortname = _T("bad");
629 //When row selected, ajust label color
630 if (!(IsAppThemed() && m_bVista))
631 if (rItem.state & LVIS_SELECTED)
632 colRef = CColors::MixColors(colRef, ::GetSysColor(COLOR_HIGHLIGHT), 150);
634 brush = ::CreateSolidBrush(colRef);
636 if(!shortname.IsEmpty() && (rt.left<rect.right) )
638 SIZE size;
639 memset(&size,0,sizeof(SIZE));
640 GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);
642 rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);
643 rt.right+=8;
645 int textpos = DT_CENTER;
647 if(rt.right > rect.right)
649 rt.right = rect.right;
650 textpos =0;
653 //Fill interior of ref label
654 ::FillRect(hdc, &rt, brush);
656 //Draw edge of label
658 CRect rectEdge = rt;
660 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,100), m_Colors.Darken(colRef,100));
661 rectEdge.DeflateRect(1,1);
662 W_Dc.Draw3dRect(rectEdge, m_Colors.Lighten(colRef,50), m_Colors.Darken(colRef,50));
664 //Draw text inside label
665 if (IsAppThemed() && m_bVista)
667 int txtState = LISS_NORMAL;
668 if (rItem.state & LVIS_SELECTED)
669 txtState = LISS_SELECTED;
671 DrawThemeText(hTheme,hdc, LVP_LISTITEM, txtState, shortname, -1, textpos | DT_SINGLELINE | DT_VCENTER, 0, &rt);
673 else
675 W_Dc.SetBkMode(TRANSPARENT);
676 if (rItem.state & LVIS_SELECTED)
678 COLORREF clrNew = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
679 COLORREF clrOld = ::SetTextColor(hdc,clrNew);
680 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,textpos | DT_SINGLELINE | DT_VCENTER);
681 ::SetTextColor(hdc,clrOld);
683 else
685 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,textpos | DT_SINGLELINE | DT_VCENTER);
689 //::MoveToEx(hdc,rt.left,rt.top,NULL);
690 //::LineTo(hdc,rt.right,rt.top);
691 //::LineTo(hdc,rt.right,rt.bottom);
692 //::LineTo(hdc,rt.left,rt.bottom);
693 //::LineTo(hdc,rt.left,rt.top);
695 rt.left=rt.right+1;
697 if(brush)
698 ::DeleteObject(brush);
700 rt.right=rect.right;
702 if (IsAppThemed() && m_bVista)
704 int txtState = LISS_NORMAL;
705 if (rItem.state & LVIS_SELECTED)
706 txtState = LISS_SELECTED;
708 DrawThemeText(hTheme,hdc, LVP_LISTITEM, txtState, data->GetSubject(), -1, DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER, 0, &rt);
710 else
712 if (rItem.state & LVIS_SELECTED)
714 COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
715 ::DrawText(hdc,data->GetSubject(),data->GetSubject().GetLength(),&rt,DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER);
716 ::SetTextColor(hdc,clrOld);
718 else
720 ::DrawText(hdc,data->GetSubject(),data->GetSubject().GetLength(),&rt,DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_VCENTER);
724 if (hTheme)
725 CloseThemeData(hTheme);
727 W_Dc.Detach();
730 static COLORREF blend(const COLORREF& col1, const COLORREF& col2, int amount = 128) {
732 // Returns ((256 - amount)*col1 + amount*col2) / 256;
733 return RGB(((256 - amount)*GetRValue(col1) + amount*GetRValue(col2) ) / 256,
734 ((256 - amount)*GetGValue(col1) + amount*GetGValue(col2) ) / 256,
735 ((256 - amount)*GetBValue(col1) + amount*GetBValue(col2) ) / 256);
738 Gdiplus::Color GetGdiColor(COLORREF col)
740 return Gdiplus::Color(GetRValue(col),GetGValue(col),GetBValue(col));
742 void CGitLogListBase::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,
743 const COLORREF& col,const COLORREF& activeColor, int top
746 int h = laneHeight / 2;
747 int m = (x1 + x2) / 2;
748 int r = (x2 - x1) / 3;
749 int d = 2 * r;
751 #define P_CENTER m , h+top
752 #define P_0 x2, h+top
753 #define P_90 m , 0+top-1
754 #define P_180 x1, h+top
755 #define P_270 m , 2 * h+top +1
756 #define R_CENTER m - r, h - r+top, d, d
759 #define DELTA_UR_B 2*(x1 - m), 2*h +top
760 #define DELTA_UR_E 0*16, 90*16 +top // -,
762 #define DELTA_DR_B 2*(x1 - m), 2*-h +top
763 #define DELTA_DR_E 270*16, 90*16 +top // -'
765 #define DELTA_UL_B 2*(x2 - m), 2*h +top
766 #define DELTA_UL_E 90*16, 90*16 +top // ,-
768 #define DELTA_DL_B 2*(x2 - m),2*-h +top
769 #define DELTA_DL_E 180*16, 90*16 // '-
771 #define CENTER_UR x1, 2*h, 225
772 #define CENTER_DR x1, 0 , 135
773 #define CENTER_UL x2, 2*h, 315
774 #define CENTER_DL x2, 0 , 45
777 Gdiplus::Graphics graphics( hdc );
779 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
781 // arc
782 switch (type) {
783 case Lanes::JOIN:
784 case Lanes::JOIN_R:
785 case Lanes::HEAD:
786 case Lanes::HEAD_R:
788 Gdiplus::LinearGradientBrush gradient(
789 Gdiplus::Point(x1-2, h+top-2),
790 Gdiplus::Point(P_270),
791 GetGdiColor(activeColor),GetGdiColor(col));
794 Gdiplus::Pen mypen(&gradient,2);
795 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
797 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);
798 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top+h-1, x2-x1,laneHeight,270,90);
799 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);
801 break;
803 case Lanes::JOIN_L:
806 Gdiplus::LinearGradientBrush gradient(
807 Gdiplus::Point(P_270),
808 Gdiplus::Point(x2+1, h+top-1),
809 GetGdiColor(col),GetGdiColor(activeColor));
812 Gdiplus::Pen mypen(&gradient,2);
813 //Gdiplus::Pen mypen(Gdiplus::Color(0,0,0),2);
815 //graphics.DrawRectangle(&mypen,x1-(x2-x1)/2,top+h, x2-x1,laneHeight);
816 graphics.DrawArc(&mypen,x1+(x2-x1)/2,top+h-1, x2-x1,laneHeight,180,90);
817 //graphics.DrawLine(&mypen,x1-1,h+top,P_270);
820 break;
822 case Lanes::TAIL:
823 case Lanes::TAIL_R:
826 Gdiplus::LinearGradientBrush gradient(
827 Gdiplus::Point(x1-2, h+top-2),
828 Gdiplus::Point(P_90),
829 GetGdiColor(activeColor),GetGdiColor(col));
831 Gdiplus::Pen mypen(&gradient,2);
833 graphics.DrawArc(&mypen,x1-(x2-x1)/2-1,top-h-1, x2-x1,laneHeight,0,90);
835 #if 0
836 QConicalGradient gradient(CENTER_DR);
837 gradient.setColorAt(0.375, activeCol);
838 gradient.setColorAt(0.625, col);
839 myPen.setBrush(gradient);
840 p->setPen(myPen);
841 p->drawArc(P_CENTER, DELTA_DR);
842 #endif
843 break;
845 default:
846 break;
850 //static QPen myPen(Qt::black, 2); // fast path here
851 CPen pen;
852 pen.CreatePen(PS_SOLID,2,col);
853 //myPen.setColor(col);
854 HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);
856 Gdiplus::Pen myPen(GetGdiColor(col),2);
858 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
860 //p->setPen(myPen);
862 // vertical line
863 switch (type) {
864 case Lanes::ACTIVE:
865 case Lanes::NOT_ACTIVE:
866 case Lanes::MERGE_FORK:
867 case Lanes::MERGE_FORK_R:
868 case Lanes::MERGE_FORK_L:
869 case Lanes::JOIN:
870 case Lanes::JOIN_R:
871 case Lanes::JOIN_L:
872 case Lanes::CROSS:
873 //DrawLine(hdc,P_90,P_270);
874 graphics.DrawLine(&myPen,P_90,P_270);
875 //p->drawLine(P_90, P_270);
876 break;
877 case Lanes::HEAD_L:
878 case Lanes::BRANCH:
879 //DrawLine(hdc,P_CENTER,P_270);
880 graphics.DrawLine(&myPen,P_CENTER,P_270);
881 //p->drawLine(P_CENTER, P_270);
882 break;
883 case Lanes::TAIL_L:
884 case Lanes::INITIAL:
885 case Lanes::BOUNDARY:
886 case Lanes::BOUNDARY_C:
887 case Lanes::BOUNDARY_R:
888 case Lanes::BOUNDARY_L:
889 //DrawLine(hdc,P_90, P_CENTER);
890 graphics.DrawLine(&myPen,P_90,P_CENTER);
891 //p->drawLine(P_90, P_CENTER);
892 break;
893 default:
894 break;
897 myPen.SetColor(GetGdiColor(activeColor));
899 // horizontal line
900 switch (type) {
901 case Lanes::MERGE_FORK:
902 case Lanes::JOIN:
903 case Lanes::HEAD:
904 case Lanes::TAIL:
905 case Lanes::CROSS:
906 case Lanes::CROSS_EMPTY:
907 case Lanes::BOUNDARY_C:
908 //DrawLine(hdc,P_180,P_0);
909 graphics.DrawLine(&myPen,P_180,P_0);
910 //p->drawLine(P_180, P_0);
911 break;
912 case Lanes::MERGE_FORK_R:
913 case Lanes::BOUNDARY_R:
914 //DrawLine(hdc,P_180,P_CENTER);
915 graphics.DrawLine(&myPen,P_180,P_CENTER);
916 //p->drawLine(P_180, P_CENTER);
917 break;
918 case Lanes::MERGE_FORK_L:
919 case Lanes::HEAD_L:
920 case Lanes::TAIL_L:
921 case Lanes::BOUNDARY_L:
922 //DrawLine(hdc,P_CENTER,P_0);
923 graphics.DrawLine(&myPen,P_CENTER,P_0);
924 //p->drawLine(P_CENTER, P_0);
925 break;
926 default:
927 break;
930 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
932 CBrush brush;
933 brush.CreateSolidBrush(col);
934 HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);
936 Gdiplus::SolidBrush myBrush(GetGdiColor(col));
937 // center symbol, e.g. rect or ellipse
938 switch (type) {
939 case Lanes::ACTIVE:
940 case Lanes::INITIAL:
941 case Lanes::BRANCH:
943 //p->setPen(Qt::NoPen);
944 //p->setBrush(col);
945 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
946 graphics.FillEllipse(&myBrush, R_CENTER);
947 //p->drawEllipse(R_CENTER);
948 break;
949 case Lanes::MERGE_FORK:
950 case Lanes::MERGE_FORK_R:
951 case Lanes::MERGE_FORK_L:
952 //p->setPen(Qt::NoPen);
953 //p->setBrush(col);
954 //p->drawRect(R_CENTER);
955 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
956 graphics.FillRectangle(&myBrush, R_CENTER);
957 break;
958 case Lanes::UNAPPLIED:
959 // Red minus sign
960 //p->setPen(Qt::NoPen);
961 //p->setBrush(Qt::red);
962 //p->drawRect(m - r, h - 1, d, 2);
963 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
964 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
965 break;
966 case Lanes::APPLIED:
967 // Green plus sign
968 //p->setPen(Qt::NoPen);
969 //p->setBrush(DARK_GREEN);
970 //p->drawRect(m - r, h - 1, d, 2);
971 //p->drawRect(m - 1, h - r, 2, d);
972 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
973 graphics.FillRectangle(&myBrush,m-r,h-1,d,2);
974 graphics.FillRectangle(&myBrush,m-1,h-r,2,d);
975 break;
976 case Lanes::BOUNDARY:
977 //p->setBrush(back);
978 //p->drawEllipse(R_CENTER);
979 graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
980 graphics.DrawEllipse(&myPen, R_CENTER);
981 break;
982 case Lanes::BOUNDARY_C:
983 case Lanes::BOUNDARY_R:
984 case Lanes::BOUNDARY_L:
985 //p->setBrush(back);
986 //p->drawRect(R_CENTER);
987 graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
988 graphics.FillRectangle(&myBrush,R_CENTER);
989 break;
990 default:
991 break;
994 ::SelectObject(hdc,oldpen);
995 ::SelectObject(hdc,oldbrush);
996 #undef P_CENTER
997 #undef P_0
998 #undef P_90
999 #undef P_180
1000 #undef P_270
1001 #undef R_CENTER
1004 void CGitLogListBase::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)
1006 // TODO: unfinished
1007 // return;
1008 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(index);
1009 if(data->m_CommitHash.IsEmpty())
1010 return;
1012 CRect rt=rect;
1013 LVITEM rItem;
1014 SecureZeroMemory(&rItem, sizeof(LVITEM));
1015 rItem.mask = LVIF_STATE;
1016 rItem.iItem = index;
1017 rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
1018 GetItem(&rItem);
1020 // p->translate(QPoint(opt.rect.left(), opt.rect.top()));
1022 if (data->m_Lanes.size() == 0)
1023 m_logEntries.setLane(data->m_CommitHash);
1025 std::vector<int>& lanes=data->m_Lanes;
1026 UINT laneNum = lanes.size();
1027 UINT activeLane = 0;
1028 for (UINT i = 0; i < laneNum; i++)
1029 if (Lanes::isMerge(lanes[i])) {
1030 activeLane = i;
1031 break;
1034 int x1 = 0, x2 = 0;
1035 int maxWidth = rect.Width();
1036 int lw = 3 * rect.Height() / 4; //laneWidth()
1038 COLORREF activeColor = m_LineColors[activeLane % Lanes::COLORS_NUM];
1039 //if (opt.state & QStyle::State_Selected)
1040 // activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);
1042 for (unsigned int i = 0; i < laneNum && x2 < maxWidth; i++)
1045 x1 = x2;
1046 x2 += lw;
1048 int ln = lanes[i];
1049 if (ln == Lanes::EMPTY)
1050 continue;
1052 COLORREF color = i == activeLane ? activeColor : m_LineColors[i % Lanes::COLORS_NUM];
1053 paintGraphLane(hdc, rect.Height(),ln, x1+rect.left, x2+rect.left, color,activeColor, rect.top);
1056 #if 0
1057 for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {
1059 x1 = x2;
1060 x2 += lw;
1062 int ln = lanes[i];
1063 if (ln == Lanes::EMPTY)
1064 continue;
1066 UINT col = ( Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)
1067 || ln ==Lanes:: CROSS_EMPTY) ? activeLane : i;
1069 if (ln == Lanes::CROSS)
1071 paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
1072 paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, m_LineColors[activeLane % Lanes::COLORS_NUM],rect.top);
1074 else
1075 paintGraphLane(hdc, rect.Height(),ln, x1, x2, m_LineColors[col % Lanes::COLORS_NUM],rect.top);
1077 #endif
1081 void CGitLogListBase::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1084 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
1085 // Take the default processing unless we set this to something else below.
1086 *pResult = CDRF_DODEFAULT;
1088 if (m_bNoDispUpdates)
1089 return;
1093 switch (pLVCD->nmcd.dwDrawStage)
1095 case CDDS_PREPAINT:
1097 *pResult = CDRF_NOTIFYITEMDRAW;
1098 return;
1100 break;
1101 case CDDS_ITEMPREPAINT:
1103 // This is the prepaint stage for an item. Here's where we set the
1104 // item's text color.
1106 // Tell Windows to send draw notifications for each subitem.
1107 *pResult = CDRF_NOTIFYSUBITEMDRAW;
1109 COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
1111 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
1113 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1114 if (data)
1116 #if 0
1117 if (data->bCopiedSelf)
1119 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)
1120 if (!IsAppThemed()) || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))
1121 pLVCD->clrTextBk = GetSysColor(COLOR_MENU);
1124 if (data->bCopies)
1125 crText = m_Colors.GetColor(CColors::Modified);
1126 #endif
1127 if (data->GetAction(this)& (CTGitPath::LOGACTIONS_REBASE_DONE| CTGitPath::LOGACTIONS_REBASE_SKIP) )
1128 crText = RGB(128,128,128);
1130 if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_SQUASH)
1131 pLVCD->clrTextBk = RGB(156,156,156);
1132 else if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_EDIT)
1133 pLVCD->clrTextBk = RGB(200,200,128);
1134 else
1135 pLVCD->clrTextBk = ::GetSysColor(COLOR_WINDOW);
1137 if(data->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_CURRENT)
1139 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1140 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1143 if(data->m_CommitHash.ToString() == m_HeadHash)
1145 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1146 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1149 // if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))
1150 // crText = GetSysColor(COLOR_GRAYTEXT);
1152 if (data->m_CommitHash.IsEmpty())
1154 //crText = GetSysColor(RGB(200,200,0));
1155 //SelectObject(pLVCD->nmcd.hdc, m_boldFont);
1156 // We changed the font, so we're returning CDRF_NEWFONT. This
1157 // tells the control to recalculate the extent of the text.
1158 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
1162 if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)
1164 if (m_bStrictStopped)
1165 crText = GetSysColor(COLOR_GRAYTEXT);
1167 // Store the color back in the NMLVCUSTOMDRAW struct.
1168 pLVCD->clrText = crText;
1169 return;
1171 break;
1172 case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:
1174 if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))
1176 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);
1179 if (pLVCD->iSubItem == LOGLIST_GRAPH)
1181 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec && (!this->m_IsRebaseReplaceGraph) )
1183 CRect rect;
1184 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_LABEL, rect);
1186 //TRACE(_T("A Graphic left %d right %d\r\n"),rect.left,rect.right);
1187 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1189 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1190 if( !data ->m_CommitHash.IsEmpty())
1191 DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1193 *pResult = CDRF_SKIPDEFAULT;
1194 return;
1198 if (pLVCD->iSubItem == LOGLIST_MESSAGE)
1200 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
1202 GitRev* data = (GitRev*)m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec);
1203 //if(!data->m_IsFull)
1205 //if(data->SafeFetchFullInfo(&g_Git))
1206 // this->Invalidate();
1207 //TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);
1210 if(m_HashMap[data->m_CommitHash].size()!=0)
1212 CRect rect;
1214 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1216 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1217 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
1219 *pResult = CDRF_SKIPDEFAULT;
1220 return;
1227 if (pLVCD->iSubItem == LOGLIST_ACTION)
1229 if(this->m_IsIDReplaceAction)
1231 *pResult = CDRF_DODEFAULT;
1232 return;
1234 *pResult = CDRF_DODEFAULT;
1236 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)
1237 return;
1239 int nIcons = 0;
1240 int iconwidth = ::GetSystemMetrics(SM_CXSMICON);
1241 int iconheight = ::GetSystemMetrics(SM_CYSMICON);
1243 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.SafeGetAt(pLVCD->nmcd.dwItemSpec));
1244 CRect rect;
1245 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
1246 //TRACE(_T("Action left %d right %d\r\n"),rect.left,rect.right);
1247 // Get the selected state of the
1248 // item being drawn.
1250 // Fill the background
1251 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
1253 // Draw the icon(s) into the compatible DC
1254 pLogEntry->GetAction(this);
1256 if (!pLogEntry->m_IsDiffFiles)
1257 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hFetchIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1259 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_MODIFIED)
1260 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1261 nIcons++;
1263 if (pLogEntry->GetAction(this) & (CTGitPath::LOGACTIONS_ADDED|CTGitPath::LOGACTIONS_COPY) )
1264 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1265 nIcons++;
1267 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_DELETED)
1268 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1269 nIcons++;
1271 if (pLogEntry->GetAction(this) & CTGitPath::LOGACTIONS_REPLACED)
1272 ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
1273 nIcons++;
1274 *pResult = CDRF_SKIPDEFAULT;
1275 return;
1278 break;
1280 *pResult = CDRF_DODEFAULT;
1283 // CGitLogListBase message handlers
1285 void CGitLogListBase::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1287 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1289 // Create a pointer to the item
1290 LV_ITEM* pItem = &(pDispInfo)->item;
1292 // Do the list need text information?
1293 if (!(pItem->mask & LVIF_TEXT))
1294 return;
1296 // By default, clear text buffer.
1297 lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);
1299 bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();
1301 *pResult = 0;
1302 if (m_bNoDispUpdates || bOutOfRange)
1303 return;
1305 // Which item number?
1306 int itemid = pItem->iItem;
1307 GitRev * pLogEntry = NULL;
1308 if (itemid < m_arShownList.GetCount())
1309 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(pItem->iItem));
1311 CString temp;
1312 if(m_IsOldFirst)
1314 temp.Format(_T("%d"),pItem->iItem+1);
1317 else
1319 temp.Format(_T("%d"),m_arShownList.GetCount()-pItem->iItem);
1322 // Which column?
1323 switch (pItem->iSubItem)
1325 case this->LOGLIST_GRAPH: //Graphic
1326 break;
1327 case this->LOGLIST_REBASE:
1329 if(this->m_IsRebaseReplaceGraph)
1331 CTGitPath path;
1332 path.m_Action=pLogEntry->GetAction(this)&CTGitPath::LOGACTIONS_REBASE_MODE_MASK;
1333 lstrcpyn(pItem->pszText,path.GetActionName(), pItem->cchTextMax);
1336 break;
1337 case this->LOGLIST_ACTION: //action -- no text in the column
1338 break;
1339 case this->LOGLIST_HASH:
1340 if(pLogEntry)
1341 lstrcpyn(pItem->pszText, pLogEntry->m_CommitHash.ToString(), pItem->cchTextMax);
1342 break;
1343 case this->LOGLIST_ID:
1344 if(this->m_IsIDReplaceAction)
1345 lstrcpyn(pItem->pszText, temp, pItem->cchTextMax);
1346 break;
1347 case this->LOGLIST_MESSAGE: //Message
1348 if (pLogEntry)
1349 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetSubject(), pItem->cchTextMax);
1350 break;
1351 case this->LOGLIST_AUTHOR: //Author
1352 if (pLogEntry)
1353 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetAuthorName(), pItem->cchTextMax);
1354 break;
1355 case this->LOGLIST_DATE: //Date
1356 if ( pLogEntry && (!pLogEntry->m_CommitHash.IsEmpty()) )
1357 lstrcpyn(pItem->pszText,
1358 CAppUtils::FormatDateAndTime( pLogEntry->GetAuthorDate(), m_DateFormat, true, m_bRelativeTimes ),
1359 pItem->cchTextMax);
1360 break;
1362 case this->LOGLIST_EMAIL:
1363 if (pLogEntry)
1364 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetAuthorEmail(), pItem->cchTextMax);
1365 break;
1367 case this->LOGLIST_COMMIT_NAME: //Commit
1368 if (pLogEntry)
1369 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetCommitterName(), pItem->cchTextMax);
1370 break;
1372 case this->LOGLIST_COMMIT_EMAIL: //Commit Email
1373 if (pLogEntry)
1374 lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->GetCommitterEmail(), pItem->cchTextMax);
1375 break;
1377 case this->LOGLIST_COMMIT_DATE: //Commit Date
1378 if (pLogEntry)
1379 lstrcpyn(pItem->pszText,
1380 CAppUtils::FormatDateAndTime( pLogEntry->GetCommitterDate(), m_DateFormat, true, m_bRelativeTimes ),
1381 pItem->cchTextMax);
1382 break;
1383 case this->LOGLIST_BUG: //Bug ID
1384 if(pLogEntry)
1385 lstrcpyn(pItem->pszText, (LPCTSTR)this->m_ProjectProperties.FindBugID(pLogEntry->GetSubject()), pItem->cchTextMax);
1386 break;
1388 default:
1389 ASSERT(false);
1393 void CGitLogListBase::OnContextMenu(CWnd* pWnd, CPoint point)
1396 if (pWnd == GetHeaderCtrl())
1398 return m_ColumnManager.OnContextMenuHeader(pWnd,point,!!IsGroupViewEnabled());
1401 int selIndex = GetSelectionMark();
1402 if (selIndex < 0)
1403 return; // nothing selected, nothing to do with a context menu
1405 // if the user selected the info text telling about not all revisions shown due to
1406 // the "stop on copy/rename" option, we also don't show the context menu
1407 if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))
1408 return;
1410 // if the context menu is invoked through the keyboard, we have to use
1411 // a calculated position on where to anchor the menu on
1412 if ((point.x == -1) && (point.y == -1))
1414 CRect rect;
1415 GetItemRect(selIndex, &rect, LVIR_LABEL);
1416 ClientToScreen(&rect);
1417 point = rect.CenterPoint();
1419 m_nSearchIndex = selIndex;
1420 m_bCancelled = FALSE;
1422 // calculate some information the context menu commands can use
1423 // CString pathURL = GetURLFromPath(m_path);
1425 POSITION pos = GetFirstSelectedItemPosition();
1426 int indexNext = GetNextSelectedItem(pos);
1427 if (indexNext < 0)
1428 return;
1430 GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(indexNext));
1431 #if 0
1432 GitRev revSelected = pSelLogEntry->Rev;
1433 GitRev revPrevious = git_revnum_t(revSelected)-1;
1434 if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))
1436 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)
1438 LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->SafeGetAt(i);
1439 if (changedpath->lCopyFromRev)
1440 revPrevious = changedpath->lCopyFromRev;
1443 GitRev revSelected2;
1444 if (pos)
1446 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1447 revSelected2 = pLogEntry->Rev;
1449 bool bAllFromTheSameAuthor = true;
1450 CString firstAuthor;
1451 CLogDataVector selEntries;
1452 GitRev revLowest, revHighest;
1453 GitRevRangeArray revisionRanges;
1455 POSITION pos = GetFirstSelectedItemPosition();
1456 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1457 revisionRanges.AddRevision(pLogEntry->Rev);
1458 selEntries.push_back(pLogEntry);
1459 firstAuthor = pLogEntry->sAuthor;
1460 revLowest = pLogEntry->Rev;
1461 revHighest = pLogEntry->Rev;
1462 while (pos)
1464 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1465 revisionRanges.AddRevision(pLogEntry->Rev);
1466 selEntries.push_back(pLogEntry);
1467 if (firstAuthor.Compare(pLogEntry->sAuthor))
1468 bAllFromTheSameAuthor = false;
1469 revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);
1470 revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);
1474 #endif
1476 int FirstSelect=-1, LastSelect=-1;
1477 pos = GetFirstSelectedItemPosition();
1478 FirstSelect = GetNextSelectedItem(pos);
1479 while(pos)
1481 LastSelect = GetNextSelectedItem(pos);
1483 //entry is selected, now show the popup menu
1484 CIconMenu popup;
1485 CIconMenu subbranchmenu, submenu, gnudiffmenu,diffmenu;
1487 if (popup.CreatePopupMenu())
1489 bool isHeadCommit = (pSelLogEntry->m_CommitHash == m_HeadHash);
1491 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_PICK))
1492 popup.AppendMenuIcon(ID_REBASE_PICK, IDS_REBASE_PICK, IDI_PICK);
1494 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SQUASH))
1495 popup.AppendMenuIcon(ID_REBASE_SQUASH, IDS_REBASE_SQUASH, IDI_SQUASH);
1497 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_EDIT))
1498 popup.AppendMenuIcon(ID_REBASE_EDIT, IDS_REBASE_EDIT, IDI_EDIT);
1500 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_SKIP))
1501 popup.AppendMenuIcon(ID_REBASE_SKIP, IDS_REBASE_SKIP, IDI_SKIP);
1503 if(m_ContextMenuMask&(GetContextMenuBit(ID_REBASE_SKIP)|GetContextMenuBit(ID_REBASE_EDIT)|
1504 GetContextMenuBit(ID_REBASE_SQUASH)|GetContextMenuBit(ID_REBASE_PICK)))
1505 popup.AppendMenu(MF_SEPARATOR, NULL);
1507 if (GetSelectedCount() == 1)
1511 if( !pSelLogEntry->m_CommitHash.IsEmpty())
1513 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARE))
1514 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1515 // TODO:
1516 // TortoiseMerge could be improved to take a /blame switch
1517 // and then not 'cat' the files from a unified diff but
1518 // blame then.
1519 // But until that's implemented, the context menu entry for
1520 // this feature is commented out.
1521 //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
1523 else
1525 if(m_ContextMenuMask&GetContextMenuBit(ID_COMMIT))
1526 popup.AppendMenuIcon(ID_COMMIT, IDS_LOG_POPUP_COMMIT, IDI_COMMIT);
1528 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF1))
1530 GitRev *pRev=pSelLogEntry;
1531 if(pSelLogEntry->m_ParentHash.size()==0)
1533 pRev->GetParentFromHash(pRev->m_CommitHash);
1535 if(pRev->m_ParentHash.size()<=1)
1537 popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
1540 else
1542 gnudiffmenu.CreatePopupMenu();
1543 popup.AppendMenuIcon(ID_GNUDIFF1,IDS_LOG_POPUP_GNUDIFF_PARENT, IDI_DIFF, gnudiffmenu.m_hMenu);
1545 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+(0xFFFF<<16),_T("All Parents"));
1546 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+(0xFFFE<<16),_T("Only Merged Files"));
1548 for(int i=0;i<pRev->m_ParentHash.size();i++)
1550 CString str;
1551 str.Format(_T("Parent %d"), i+1);
1552 gnudiffmenu.AppendMenuIcon(ID_GNUDIFF1+((i+1)<<16),str);
1557 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPAREWITHPREVIOUS))
1560 GitRev *pRev=pSelLogEntry;
1561 if(pSelLogEntry->m_ParentHash.size()==0)
1563 pRev->GetParentFromHash(pRev->m_CommitHash);
1565 if(pRev->m_ParentHash.size()<=1)
1567 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
1570 else
1572 diffmenu.CreatePopupMenu();
1573 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF, diffmenu.m_hMenu);
1574 for(int i=0;i<pRev->m_ParentHash.size();i++)
1576 CString str;
1577 str.Format(_T("Parent %d"), i+1);
1578 diffmenu.AppendMenuIcon(ID_COMPAREWITHPREVIOUS +((i+1)<<16),str);
1583 if(m_ContextMenuMask&GetContextMenuBit(ID_BLAME))
1584 popup.AppendMenuIcon(ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);
1586 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);
1587 popup.AppendMenu(MF_SEPARATOR, NULL);
1590 // if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
1591 // {
1592 // popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);
1593 // }
1594 // if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
1595 // {
1596 // popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
1597 // }
1598 // if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
1599 // (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
1600 // {
1601 // popup.AppendMenu(MF_SEPARATOR, NULL);
1602 // }
1604 CString str,format;
1605 //if (m_hasWC)
1606 // popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
1608 if(!pSelLogEntry->m_CommitHash.IsEmpty())
1610 if((m_ContextMenuMask&GetContextMenuBit(ID_LOG)) &&
1611 GetSelectedCount() == 1)
1612 popup.AppendMenuIcon(ID_LOG, IDS_LOG_POPUP_LOG, IDI_LOG);
1614 format.LoadString(IDS_LOG_POPUP_MERGEREV);
1615 str.Format(format,g_Git.GetCurrentBranch());
1617 bool thisbranch = false;
1618 CString currentBranch = _T("refs/heads/")+g_Git.GetCurrentBranch();
1619 for(int i=0; i < m_HashMap[pSelLogEntry->m_CommitHash].size(); i++)
1621 if (m_HashMap[pSelLogEntry->m_CommitHash][i] == currentBranch)
1622 thisbranch = true;
1623 break;
1626 if (m_ContextMenuMask&GetContextMenuBit(ID_MERGEREV) && !isHeadCommit)
1627 popup.AppendMenuIcon(ID_MERGEREV, str, IDI_MERGE);
1629 format.LoadString(IDS_RESET_TO_THIS_FORMAT);
1630 str.Format(format,g_Git.GetCurrentBranch());
1632 if(m_ContextMenuMask&GetContextMenuBit(ID_RESET))
1633 popup.AppendMenuIcon(ID_RESET,str,IDI_REVERT);
1636 // Add Switch Branch express Menu
1637 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end()
1638 && (m_ContextMenuMask&GetContextMenuBit(ID_SWITCHBRANCH))
1641 std::vector<CString *> branchs;
1642 CString ref;
1644 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)
1646 ref = m_HashMap[pSelLogEntry->m_CommitHash][i];
1647 if(ref.Find(_T("refs/heads/")) == 0 && ref != currentBranch)
1649 branchs.push_back(&m_HashMap[pSelLogEntry->m_CommitHash][i]);
1653 CString str;
1654 str.LoadString(IDS_SWITCH_BRANCH);
1656 if(branchs.size() == 1)
1658 str+=_T(" ");
1659 str+= _T('"') + branchs[0]->Mid(11) + _T('"');
1660 popup.AppendMenuIcon(ID_SWITCHBRANCH,str,IDI_SWITCH);
1662 popup.SetMenuItemData(ID_SWITCHBRANCH,(ULONG_PTR)branchs[0]);
1665 else if(branchs.size() > 1)
1667 subbranchmenu.CreatePopupMenu();
1668 for(int i=0;i<branchs.size();i++)
1670 if (*branchs[i] != currentBranch)
1672 subbranchmenu.AppendMenuIcon(ID_SWITCHBRANCH+(i<<16), branchs[i]->Mid(11));
1673 subbranchmenu.SetMenuItemData(ID_SWITCHBRANCH+(i<<16), (ULONG_PTR) branchs[i]);
1677 popup.AppendMenuIcon(ID_SWITCHBRANCH, str, IDI_SWITCH, subbranchmenu.m_hMenu);
1681 if(m_ContextMenuMask&GetContextMenuBit(ID_SWITCHTOREV) && !isHeadCommit)
1682 popup.AppendMenuIcon(ID_SWITCHTOREV, IDS_SWITCH_TO_THIS , IDI_SWITCH);
1684 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_BRANCH))
1685 popup.AppendMenuIcon(ID_CREATE_BRANCH, IDS_CREATE_BRANCH_AT_THIS , IDI_COPY);
1687 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_TAG))
1688 popup.AppendMenuIcon(ID_CREATE_TAG,IDS_CREATE_TAG_AT_THIS , IDI_TAG);
1690 format.LoadString(IDS_REBASE_THIS_FORMAT);
1691 str.Format(format,g_Git.GetCurrentBranch());
1693 if(pSelLogEntry->m_CommitHash != m_HeadHash)
1694 if(m_ContextMenuMask&GetContextMenuBit(ID_REBASE_TO_VERSION))
1695 popup.AppendMenuIcon(ID_REBASE_TO_VERSION, str , IDI_REBASE);
1697 if(m_ContextMenuMask&GetContextMenuBit(ID_EXPORT))
1698 popup.AppendMenuIcon(ID_EXPORT,IDS_EXPORT_TO_THIS, IDI_EXPORT);
1700 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1701 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
1703 if (m_ContextMenuMask&GetContextMenuBit(ID_EDITNOTE))
1704 popup.AppendMenuIcon(ID_EDITNOTE, IDS_EDIT_NOTES, IDI_EDIT);
1706 popup.AppendMenu(MF_SEPARATOR, NULL);
1711 if(!pSelLogEntry->m_Ref.IsEmpty() && GetSelectedCount() == 1)
1713 popup.AppendMenuIcon(ID_REFLOG_DEL, IDS_REFLOG_DEL, IDI_DELETE);
1714 popup.AppendMenuIcon(ID_STASH_APPLY, IDS_MENUSTASHAPPLY, IDI_RELOCATE);
1715 popup.AppendMenu(MF_SEPARATOR, NULL);
1718 if (GetSelectedCount() >= 2)
1720 bool bAddSeparator = false;
1721 if (IsSelectionContinuous() || (GetSelectedCount() == 2))
1723 if(m_ContextMenuMask&GetContextMenuBit(ID_COMPARETWO))
1724 popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
1727 if (GetSelectedCount() == 2)
1729 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);
1730 if(m_ContextMenuMask&GetContextMenuBit(ID_GNUDIFF2))
1731 popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1732 bAddSeparator = true;
1735 if (m_hasWC)
1737 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1738 // if (m_hasWC)
1739 // popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);
1740 bAddSeparator = true;
1743 if (m_ContextMenuMask&GetContextMenuBit(ID_REVERTREV))
1744 popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1746 if (bAddSeparator)
1747 popup.AppendMenu(MF_SEPARATOR, NULL);
1750 if ( GetSelectedCount() >0 && (!pSelLogEntry->m_CommitHash.IsEmpty()))
1752 bool bAddSeparator = false;
1753 if ( IsSelectionContinuous() && GetSelectedCount() >= 2 )
1755 if(m_ContextMenuMask&GetContextMenuBit(ID_COMBINE_COMMIT))
1757 CString head;
1758 int headindex;
1759 headindex = this->GetHeadIndex();
1760 if(headindex>=0)
1762 head.Format(_T("HEAD~%d"),LastSelect-headindex);
1763 CGitHash hash=g_Git.GetHash(head);
1764 GitRev* pLastEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(LastSelect));
1765 if(pLastEntry->m_CommitHash == hash) {
1766 popup.AppendMenuIcon(ID_COMBINE_COMMIT,IDS_COMBINE_TO_ONE,IDI_COMBINE);
1767 bAddSeparator = true;
1772 if(m_ContextMenuMask&GetContextMenuBit(ID_CHERRY_PICK) && !isHeadCommit) {
1773 if (GetSelectedCount() >= 2)
1774 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSIONS, IDI_EXPORT);
1775 else
1776 popup.AppendMenuIcon(ID_CHERRY_PICK, IDS_CHERRY_PICK_VERSION, IDI_EXPORT);
1777 bAddSeparator = true;
1780 if(GetSelectedCount()<=2 || (IsSelectionContinuous() && GetSelectedCount() > 0))
1781 if(m_ContextMenuMask&GetContextMenuBit(ID_CREATE_PATCH)) {
1782 popup.AppendMenuIcon(ID_CREATE_PATCH, IDS_CREATE_PATCH, IDI_PATCH);
1783 bAddSeparator = true;
1786 if (bAddSeparator)
1787 popup.AppendMenu(MF_SEPARATOR, NULL);
1790 #if 0
1791 // if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))
1792 // {
1793 // popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);
1794 // }
1795 // if (GetSelectedCount() == 1)
1796 // {
1797 // popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);
1798 // popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
1799 // popup.AppendMenu(MF_SEPARATOR, NULL);
1800 // }
1801 #endif
1803 if (GetSelectedCount() == 1)
1805 bool bAddSeparator = false;
1806 if(m_ContextMenuMask&GetContextMenuBit(ID_PUSH) && m_HashMap[pSelLogEntry->m_CommitHash].size() >= 1)
1808 // show the push-option only if the log entry has an associated local branch
1809 bool isLocal = false;
1810 for(int i=0; isLocal == false && i < m_HashMap[pSelLogEntry->m_CommitHash].size(); i++)
1812 if (m_HashMap[pSelLogEntry->m_CommitHash][i].Find(_T("refs/heads/")) == 0)
1813 isLocal = true;
1815 if (isLocal)
1817 popup.AppendMenuIcon(ID_PUSH, IDS_LOG_PUSH, IDI_PUSH);
1818 bAddSeparator = true;
1822 if(m_ContextMenuMask &GetContextMenuBit(ID_DELETE))
1824 if( this->m_HashMap.find(pSelLogEntry->m_CommitHash) != m_HashMap.end() )
1826 CString str;
1827 if( m_HashMap[pSelLogEntry->m_CommitHash].size() == 1 )
1829 str.LoadString(IDS_DELETE_BRANCHTAG_SHORT);
1830 str+=_T(" ");
1831 str+=m_HashMap[pSelLogEntry->m_CommitHash].at(0);
1832 popup.AppendMenuIcon(ID_DELETE, str, IDI_DELETE);
1833 bAddSeparator = true;
1835 else if( m_HashMap[pSelLogEntry->m_CommitHash].size() > 1 )
1837 str.LoadString(IDS_DELETE_BRANCHTAG);
1838 submenu.CreatePopupMenu();
1839 for(int i=0;i<m_HashMap[pSelLogEntry->m_CommitHash].size();i++)
1841 submenu.AppendMenuIcon(ID_DELETE+(i<<16),m_HashMap[pSelLogEntry->m_CommitHash][i]);
1844 popup.AppendMenuIcon(ID_DELETE,str, IDI_DELETE, submenu.m_hMenu);
1845 bAddSeparator = true;
1848 } // m_ContextMenuMask &GetContextMenuBit(ID_DELETE)
1849 if (bAddSeparator)
1850 popup.AppendMenu(MF_SEPARATOR, NULL);
1851 } // GetSelectedCount() == 1
1853 if (GetSelectedCount() == 1)
1855 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYHASH))
1856 popup.AppendMenuIcon(ID_COPYHASH, IDS_COPY_COMMIT_HASH, IDI_COPYCLIP);
1858 if (GetSelectedCount() != 0)
1860 if(m_ContextMenuMask&GetContextMenuBit(ID_COPYCLIPBOARD))
1861 popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD, IDI_COPYCLIP);
1864 if(m_ContextMenuMask&GetContextMenuBit(ID_FINDENTRY))
1865 popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND, IDI_FILTEREDIT);
1867 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1868 // DialogEnableWindow(IDOK, FALSE);
1869 // SetPromptApp(&theApp);
1871 this->ContextMenuAction(cmd, FirstSelect, LastSelect, &popup);
1873 // EnableOKButton();
1874 } // if (popup.CreatePopupMenu())
1878 bool CGitLogListBase::IsSelectionContinuous()
1880 if ( GetSelectedCount()==1 )
1882 // if only one revision is selected, the selection is of course
1883 // continuous
1884 return true;
1887 POSITION pos = GetFirstSelectedItemPosition();
1888 bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());
1889 if (bContinuous)
1891 int itemindex = GetNextSelectedItem(pos);
1892 while (pos)
1894 int nextindex = GetNextSelectedItem(pos);
1895 if (nextindex - itemindex > 1)
1897 bContinuous = false;
1898 break;
1900 itemindex = nextindex;
1903 return bContinuous;
1906 void CGitLogListBase::CopySelectionToClipBoard(bool HashOnly)
1909 CString sClipdata;
1910 POSITION pos = GetFirstSelectedItemPosition();
1911 if (pos != NULL)
1913 CString sRev;
1914 sRev.LoadString(IDS_LOG_REVISION);
1915 CString sAuthor;
1916 sAuthor.LoadString(IDS_LOG_AUTHOR);
1917 CString sDate;
1918 sDate.LoadString(IDS_LOG_DATE);
1919 CString sMessage;
1920 sMessage.LoadString(IDS_LOG_MESSAGE);
1921 while (pos)
1923 CString sLogCopyText;
1924 CString sPaths;
1925 GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.SafeGetAt(GetNextSelectedItem(pos)));
1927 if(!HashOnly)
1929 //pLogEntry->GetFiles(this)
1930 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
1932 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->GetFiles(this).GetCount(); ++cpPathIndex)
1934 sPaths += ((CTGitPath&)pLogEntry->GetFiles(this)[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->GetFiles(this)[cpPathIndex].GetGitPathString();
1935 sPaths += _T("\r\n");
1937 sPaths.Trim();
1938 CString body = pLogEntry->GetBody();
1939 body.Replace(_T("\n"), _T("\r\n"));
1940 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"),
1941 (LPCTSTR)sRev, pLogEntry->m_CommitHash.ToString(),
1942 (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->GetAuthorName(),
1943 (LPCTSTR)sDate,
1944 (LPCTSTR)CAppUtils::FormatDateAndTime( pLogEntry->GetAuthorDate(), m_DateFormat, true, m_bRelativeTimes ),
1945 (LPCTSTR)sMessage, pLogEntry->GetSubject().Trim() + _T("\r\n\r\n") + body.Trim(),
1946 (LPCTSTR)sPaths);
1947 sClipdata += sLogCopyText;
1949 else
1951 sClipdata += pLogEntry->m_CommitHash;
1952 break;
1956 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
1961 void CGitLogListBase::DiffSelectedRevWithPrevious()
1963 if (m_bThreadRunning)
1964 return;
1966 int FirstSelect=-1, LastSelect=-1;
1967 POSITION pos = GetFirstSelectedItemPosition();
1968 FirstSelect = GetNextSelectedItem(pos);
1969 while(pos)
1971 LastSelect = GetNextSelectedItem(pos);
1974 ContextMenuAction(ID_COMPAREWITHPREVIOUS,FirstSelect,LastSelect, NULL);
1976 #if 0
1977 UpdateLogInfoLabel();
1978 int selIndex = m_LogList.GetSelectionMark();
1979 if (selIndex < 0)
1980 return;
1981 int selCount = m_LogList.GetSelectedCount();
1982 if (selCount != 1)
1983 return;
1985 // Find selected entry in the log list
1986 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1987 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
1988 long rev1 = pLogEntry->Rev;
1989 long rev2 = rev1-1;
1990 CTGitPath path = m_path;
1992 // See how many files under the relative root were changed in selected revision
1993 int nChanged = 0;
1994 LogChangedPath * changed = NULL;
1995 for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)
1997 LogChangedPath * cpath = pLogEntry->pArChangedPaths->SafeGetAt(c);
1998 if (cpath && cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
2000 ++nChanged;
2001 changed = cpath;
2005 if (m_path.IsDirectory() && nChanged == 1)
2007 // We're looking at the log for a directory and only one file under dir was changed in the revision
2008 // Do diff on that file instead of whole directory
2009 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));
2012 m_bCancelled = FALSE;
2013 DialogEnableWindow(IDOK, FALSE);
2014 SetPromptApp(&theApp);
2015 theApp.DoWaitCursor(1);
2017 if (PromptShown())
2019 GitDiff diff(this, m_hWnd, true);
2020 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
2021 diff.SetHEADPeg(m_LogRevision);
2022 diff.ShowCompare(path, rev2, path, rev1);
2024 else
2026 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
2029 theApp.DoWaitCursor(-1);
2030 EnableOKButton();
2031 #endif
2034 void CGitLogListBase::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)
2036 LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
2037 *pResult = -1;
2039 if (pFindInfo->lvfi.flags & LVFI_PARAM)
2040 return;
2041 if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))
2042 return;
2043 if (pFindInfo->lvfi.psz == 0)
2044 return;
2045 #if 0
2046 CString sCmp = pFindInfo->lvfi.psz;
2047 CString sRev;
2048 for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)
2050 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.SafeGetAt(i));
2051 sRev.Format(_T("%ld"), pLogEntry->Rev);
2052 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
2054 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
2056 *pResult = i;
2057 return;
2060 else
2062 if (sCmp.Compare(sRev)==0)
2064 *pResult = i;
2065 return;
2069 if (pFindInfo->lvfi.flags & LVFI_WRAP)
2071 for (int i=0; i<pFindInfo->iStart; ++i)
2073 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(i));
2074 sRev.Format(_T("%ld"), pLogEntry->Rev);
2075 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
2077 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
2079 *pResult = i;
2080 return;
2083 else
2085 if (sCmp.Compare(sRev)==0)
2087 *pResult = i;
2088 return;
2093 #endif
2094 *pResult = -1;
2097 int CGitLogListBase::FillGitLog(CTGitPath *path,int info,CString *from,CString *to)
2099 ClearText();
2102 this->m_arShownList.SafeRemoveAll();
2104 this->m_logEntries.ClearAll();
2105 this->m_logEntries.ParserFromLog(path,-1,info,from,to);
2107 //this->m_logEntries.ParserFromLog();
2108 SetItemCountEx(this->m_logEntries.size());
2110 for(unsigned int i=0;i<m_logEntries.size();i++)
2112 if(m_IsOldFirst)
2114 m_logEntries.GetGitRevAt(m_logEntries.size()-i-1).m_IsFull=TRUE;
2115 this->m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
2118 else
2120 m_logEntries.GetGitRevAt(i).m_IsFull=TRUE;
2121 this->m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(i));
2125 if(path)
2126 m_Path=*path;
2127 return 0;
2131 int CGitLogListBase::BeginFetchLog()
2133 ClearText();
2135 this->m_arShownList.SafeRemoveAll();
2137 this->m_logEntries.ClearAll();
2138 git_init();
2140 this->m_LogCache.ClearAllParent();
2142 m_LogCache.FetchCacheIndex(g_Git.m_CurrentDir);
2144 CTGitPath *path;
2145 if(this->m_Path.IsEmpty())
2146 path=NULL;
2147 else
2148 path=&this->m_Path;
2150 CString hash;
2151 int mask;
2152 mask = CGit::LOG_INFO_ONLY_HASH | CGit::LOG_INFO_BOUNDARY;
2153 // if(this->m_bAllBranch)
2154 mask |= m_ShowMask ;
2156 if(m_bShowWC)
2158 this->m_logEntries.insert(m_logEntries.begin(),this->m_wcRev.m_CommitHash);
2159 ResetWcRev();
2160 this->m_LogCache.m_HashMap[m_wcRev.m_CommitHash]=m_wcRev;
2163 CString *pFrom, *pTo;
2164 pFrom = pTo = NULL;
2165 CString head(_T("HEAD"));
2166 if(!this->m_startrev.IsEmpty())
2168 pFrom = &this->m_startrev;
2169 if(!this->m_endrev.IsEmpty())
2170 pTo = &this->m_endrev;
2171 else
2172 pTo = &head;
2175 CFilterData data;
2176 data.m_From = m_From;
2177 data.m_To =m_To;
2179 #if 0 /* use tortoiegit filter */
2180 if(this->m_nSelectedFilter == LOGFILTER_ALL || m_nSelectedFilter == LOGFILTER_AUTHORS)
2181 data.m_Author = this->m_sFilterText;
2183 if(this->m_nSelectedFilter == LOGFILTER_ALL || m_nSelectedFilter == LOGFILTER_MESSAGES)
2184 data.m_MessageFilter = this->m_sFilterText;
2186 data.m_IsRegex = m_bFilterWithRegex;
2187 #endif
2189 CString cmd=g_Git.GetLogCmd(m_StartRef,path,-1,mask,pFrom,pTo,true,&data);
2191 //this->m_logEntries.ParserFromLog();
2192 if(IsInWorkingThread())
2194 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL);
2196 else
2198 SetItemCountEx(this->m_logEntries.size());
2201 git_init();
2203 if(g_Git.IsInitRepos())
2204 return 0;
2206 if(git_open_log(&m_DllGitLog,CUnicodeUtils::GetMulti(cmd,CP_ACP).GetBuffer()))
2208 return -1;
2211 return 0;
2214 BOOL CGitLogListBase::PreTranslateMessage(MSG* pMsg)
2216 if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
2218 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))
2220 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
2222 DiffSelectedRevWithPrevious();
2223 return TRUE;
2226 #if 0
2227 if (GetFocus()==GetDlgItem(IDC_LOGMSG))
2229 DiffSelectedFile();
2230 return TRUE;
2232 #endif
2234 else if (pMsg->message == WM_KEYDOWN && pMsg->wParam == 'A' && GetAsyncKeyState(VK_CONTROL)&0x8000)
2236 // select all entries
2237 for (int i=0; i<GetItemCount(); ++i)
2239 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2241 return TRUE;
2244 #if 0
2245 if (m_hAccel && !bSkipAccelerator)
2247 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
2248 if (ret)
2249 return TRUE;
2252 #endif
2253 //m_tooltips.RelayEvent(pMsg);
2254 return __super::PreTranslateMessage(pMsg);
2257 void CGitLogListBase::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2259 // a double click on an entry in the revision list has happened
2260 *pResult = 0;
2262 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
2263 DiffSelectedRevWithPrevious();
2266 int CGitLogListBase::FetchLogAsync(void * data)
2268 m_ProcData=data;
2269 m_bExitThread=FALSE;
2270 InterlockedExchange(&m_bThreadRunning, TRUE);
2271 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2272 m_LoadingThread = AfxBeginThread(LogThreadEntry, this, THREAD_PRIORITY_LOWEST);
2273 if (m_LoadingThread ==NULL)
2275 InterlockedExchange(&m_bThreadRunning, FALSE);
2276 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2277 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
2278 return -1;
2280 return 0;
2283 //this is the thread function which calls the subversion function
2284 UINT CGitLogListBase::LogThreadEntry(LPVOID pVoid)
2286 return ((CGitLogListBase*)pVoid)->LogThread();
2289 void CGitLogListBase::GetTimeRange(CTime &oldest, CTime &latest)
2291 //CTime time;
2292 oldest=CTime::GetCurrentTime();
2293 latest=CTime(1971,1,2,0,0,0);
2294 for(unsigned int i=0;i<m_logEntries.size();i++)
2296 if(m_logEntries[i].IsEmpty())
2297 continue;
2299 if(m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime() < oldest.GetTime())
2300 oldest = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2302 if(m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime() > latest.GetTime())
2303 latest = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2307 if(latest<oldest)
2308 latest=oldest;
2311 UINT CGitLogListBase::LogThread()
2313 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_START,0);
2315 InterlockedExchange(&m_bThreadRunning, TRUE);
2316 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2318 ULONGLONG t1,t2;
2320 if(BeginFetchLog())
2322 InterlockedExchange(&m_bThreadRunning, FALSE);
2323 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2325 return -1;
2328 tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);
2329 bool bRegex = false;
2330 if (m_bFilterWithRegex)
2331 bRegex = ValidateRegexp(m_sFilterText, pat, false);
2333 TRACE(_T("\n===Begin===\n"));
2334 //Update work copy item;
2336 if( m_logEntries.size() > 0)
2338 GitRev *pRev = &m_logEntries.GetGitRevAt(0);
2340 m_arShownList.SafeAdd(pRev);
2344 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2346 // store commit number of the last selected commit/line before the refresh or -1
2347 int lastSelectedHashNItem = -1;
2349 if(!g_Git.IsInitRepos())
2351 g_Git.m_critGitDllSec.Lock();
2352 git_get_log_firstcommit(m_DllGitLog);
2353 int total = git_get_log_estimate_commit_count(m_DllGitLog);
2354 g_Git.m_critGitDllSec.Unlock();
2356 GIT_COMMIT commit;
2357 t2=t1=GetTickCount();
2358 int oldprecentage = 0;
2359 int oldsize=m_logEntries.size();
2360 int ret=0;
2361 while( ret== 0)
2363 g_Git.m_critGitDllSec.Lock();
2364 ret = git_get_log_nextcommit(this->m_DllGitLog, &commit, 0);
2365 g_Git.m_critGitDllSec.Unlock();
2367 if(ret)
2368 break;
2370 //printf("%s\r\n",commit.GetSubject());
2371 if(m_bExitThread)
2372 break;
2374 CGitHash hash = (char*)commit.m_hash ;
2376 GitRev *pRev = m_LogCache.GetCacheData(hash);
2377 pRev->m_GitCommit = commit;
2378 InterlockedExchange(&pRev->m_IsCommitParsed, FALSE);
2380 char *note=NULL;
2381 g_Git.m_critGitDllSec.Lock();
2382 git_get_notes(commit.m_hash,&note);
2383 g_Git.m_critGitDllSec.Unlock();
2385 if(note)
2387 pRev->m_Notes.Empty();
2388 g_Git.StringAppend(&pRev->m_Notes,(BYTE*)note);
2391 if(!pRev->m_IsDiffFiles)
2393 pRev->m_CallDiffAsync = DiffAsync;
2396 pRev->ParserParentFromCommit(&commit);
2398 #ifdef DEBUG
2399 pRev->DbgPrint();
2400 TRACE(_T("\n"));
2401 #endif
2403 if(!m_sFilterText.IsEmpty())
2405 if(!IsMatchFilter(bRegex,pRev,pat))
2406 continue;
2408 this->m_critSec.Lock();
2409 m_logEntries.push_back(hash);
2410 m_arShownList.SafeAdd(pRev);
2411 this->m_critSec.Unlock();
2413 if (lastSelectedHashNItem == -1 && hash == m_lastSelectedHash)
2414 lastSelectedHashNItem = m_arShownList.GetCount() - 1;
2416 t2=GetTickCount();
2418 if(t2-t1>500 || (m_logEntries.size()-oldsize >100))
2420 //update UI
2421 int percent=m_logEntries.size()*100/total + GITLOG_START+1;
2422 if(percent > 99)
2423 percent =99;
2424 if(percent < GITLOG_START)
2425 percent = GITLOG_START +1;
2427 oldsize = m_logEntries.size();
2428 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2430 //if( percent > oldprecentage )
2432 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) percent,0);
2433 oldprecentage = percent;
2435 t1 = t2;
2438 g_Git.m_critGitDllSec.Lock();
2439 git_close_log(m_DllGitLog);
2440 g_Git.m_critGitDllSec.Unlock();
2444 // restore last selected item
2445 if (lastSelectedHashNItem >= 0)
2447 SetItemState(lastSelectedHashNItem, LVIS_SELECTED, LVIS_SELECTED);
2448 EnsureVisible(lastSelectedHashNItem, FALSE);
2451 //Update UI;
2452 PostMessage(LVM_SETITEMCOUNT, (WPARAM) this->m_logEntries.size(),(LPARAM) LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
2453 ::PostMessage(this->GetParent()->m_hWnd,MSG_LOAD_PERCENTAGE,(WPARAM) GITLOG_END,0);
2455 InterlockedExchange(&m_bThreadRunning, FALSE);
2457 return 0;
2460 void CGitLogListBase::Refresh(BOOL IsCleanFilter)
2462 SafeTerminateThread();
2464 this->SetItemCountEx(0);
2465 this->Clear();
2467 ResetWcRev();
2469 //Update branch and Tag info
2470 ReloadHashMap();
2471 //Assume Thread have exited
2472 //if(!m_bThreadRunning)
2474 m_logEntries.clear();
2476 if(IsCleanFilter)
2478 m_sFilterText.Empty();
2479 m_From=-1;
2480 m_To=-1;
2483 InterlockedExchange(&m_bExitThread,FALSE);
2485 InterlockedExchange(&m_bThreadRunning, TRUE);
2486 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2487 if ( (m_LoadingThread=AfxBeginThread(LogThreadEntry, this)) ==NULL)
2489 InterlockedExchange(&m_bThreadRunning, FALSE);
2490 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2491 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
2495 bool CGitLogListBase::ValidateRegexp(LPCTSTR regexp_str, tr1::wregex& pat, bool bMatchCase /* = false */)
2499 tr1::regex_constants::syntax_option_type type = tr1::regex_constants::ECMAScript;
2500 if (!bMatchCase)
2501 type |= tr1::regex_constants::icase;
2502 pat = tr1::wregex(regexp_str, type);
2503 return true;
2505 catch (exception) {}
2506 return false;
2508 BOOL CGitLogListBase::IsMatchFilter(bool bRegex, GitRev *pRev, tr1::wregex &pat)
2511 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;
2512 CString sRev;
2514 if ((bRegex)&&(m_bFilterWithRegex))
2516 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2518 if(this->m_bShowBugtraqColumn)
2520 CString sBugIds = m_ProjectProperties.FindBugID(pRev->GetSubject());
2522 ATLTRACE(_T("bugID = \"%s\"\n"), sBugIds);
2523 if (regex_search(wstring(sBugIds), pat, flags))
2525 return TRUE;
2530 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2532 ATLTRACE(_T("messge = \"%s\"\n"), pRev->GetSubject());
2533 if (regex_search(wstring((LPCTSTR)pRev->GetSubject()), pat, flags))
2535 return TRUE;
2538 ATLTRACE(_T("messge = \"%s\"\n"),pRev->GetBody());
2539 if (regex_search(wstring((LPCTSTR)pRev->GetBody()), pat, flags))
2541 return TRUE;
2545 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2547 if (regex_search(wstring(pRev->GetAuthorName()), pat, flags))
2549 return TRUE;
2552 if (regex_search(wstring(pRev->GetCommitterName()), pat, flags))
2554 return TRUE;
2558 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2560 sRev.Format(_T("%s"), pRev->m_CommitHash.ToString());
2561 if (regex_search(wstring((LPCTSTR)sRev), pat, flags))
2563 return TRUE;
2567 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2569 CTGitPathList *pathList=NULL;
2570 if( pRev->m_IsDiffFiles)
2571 pathList = &pRev->GetFiles(this);
2572 else
2574 if(!pRev->m_IsSimpleListReady)
2575 pRev->SafeGetSimpleList(&g_Git);
2578 if(pathList)
2579 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList->GetCount(); ++cpPathIndex)
2581 if (regex_search(wstring((LPCTSTR)pathList->m_paths.at(cpPathIndex).GetGitOldPathString()), pat, flags))
2583 return true;
2585 if (regex_search(wstring((LPCTSTR)pathList->m_paths.at(cpPathIndex).GetGitPathString()), pat, flags))
2587 return true;
2591 for(INT_PTR i=0;i<pRev->m_SimpleFileList.size();i++)
2593 if (regex_search(wstring((LPCTSTR)pRev->m_SimpleFileList[i]), pat, flags))
2595 return true;
2600 else
2602 CString find = m_sFilterText;
2603 find.MakeLower();
2605 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2607 if(this->m_bShowBugtraqColumn)
2609 CString sBugIds = m_ProjectProperties.FindBugID(pRev->GetSubject());
2611 sBugIds.MakeLower();
2612 if ((sBugIds.Find(find) >= 0))
2614 return TRUE;
2619 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2621 CString msg = pRev->GetSubject();
2623 msg = msg.MakeLower();
2624 if ((msg.Find(find) >= 0))
2626 return TRUE;
2629 msg = pRev->GetBody();
2631 msg = msg.MakeLower();
2632 if ((msg.Find(find) >= 0))
2634 return TRUE;
2638 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2640 CString msg = pRev->GetAuthorName();
2641 msg = msg.MakeLower();
2642 if ((msg.Find(find) >= 0))
2644 return TRUE;
2648 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2650 sRev.Format(_T("%s"), pRev->m_CommitHash.ToString());
2651 if ((sRev.Find(find) >= 0))
2653 return TRUE;
2657 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2659 CTGitPathList *pathList=NULL;
2660 if( pRev->m_IsDiffFiles)
2661 pathList = &pRev->GetFiles(this);
2662 else
2664 if(!pRev->m_IsSimpleListReady)
2665 pRev->SafeGetSimpleList(&g_Git);
2667 if(pathList)
2668 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList->GetCount() ; ++cpPathIndex)
2670 CTGitPath *cpath = &pathList->m_paths.at(cpPathIndex);
2671 CString path = cpath->GetGitOldPathString();
2672 path.MakeLower();
2673 if ((path.Find(find)>=0))
2675 return true;
2677 path = cpath->GetGitPathString();
2678 path.MakeLower();
2679 if ((path.Find(find)>=0))
2681 return true;
2685 for (INT_PTR i=0;i<pRev->m_SimpleFileList.size();i++)
2687 CString path = pRev->m_SimpleFileList[i];
2688 path.MakeLower();
2689 if ((path.Find(find)>=0))
2691 return true;
2695 } // else (from if (bRegex))
2696 return FALSE;
2700 void CGitLogListBase::RecalculateShownList(CThreadSafePtrArray * pShownlist)
2703 pShownlist->SafeRemoveAll();
2705 tr1::wregex pat;//(_T("Remove"), tr1::regex_constants::icase);
2706 bool bRegex = false;
2707 if (m_bFilterWithRegex)
2708 bRegex = ValidateRegexp(m_sFilterText, pat, false);
2710 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_any;
2711 CString sRev;
2712 for (DWORD i=0; i<m_logEntries.size(); ++i)
2714 if ((bRegex)&&(m_bFilterWithRegex))
2716 #if 0
2717 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2719 ATLTRACE(_T("bugID = \"%s\"\n"), (LPCTSTR)m_logEntries[i]->sBugIDs);
2720 if (regex_search(wstring((LPCTSTR)m_logEntries[i]->sBugIDs), pat, flags)&&IsEntryInDateRange(i))
2722 pShownlist->SafeAdd(m_logEntries[i]);
2723 continue;
2726 #endif
2727 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2729 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).GetSubject());
2730 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetSubject()), pat, flags)&&IsEntryInDateRange(i))
2732 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2733 continue;
2736 ATLTRACE(_T("messge = \"%s\"\n"),m_logEntries.GetGitRevAt(i).GetBody());
2737 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetBody()), pat, flags)&&IsEntryInDateRange(i))
2739 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2740 continue;
2743 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2745 CTGitPathList pathList = m_logEntries.GetGitRevAt(i).GetFiles(this);
2747 bool bGoing = true;
2748 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList.GetCount() && bGoing; ++cpPathIndex)
2750 CTGitPath cpath = pathList[cpPathIndex];
2751 if (regex_search(wstring((LPCTSTR)cpath.GetGitOldPathString()), pat, flags)&&IsEntryInDateRange(i))
2753 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2754 bGoing = false;
2755 continue;
2757 if (regex_search(wstring((LPCTSTR)cpath.GetGitPathString()), pat, flags)&&IsEntryInDateRange(i))
2759 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2760 bGoing = false;
2761 continue;
2763 if (regex_search(wstring((LPCTSTR)cpath.GetActionName()), pat, flags)&&IsEntryInDateRange(i))
2765 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2766 bGoing = false;
2767 continue;
2771 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2773 if (regex_search(wstring((LPCTSTR)m_logEntries.GetGitRevAt(i).GetAuthorName()), pat, flags)&&IsEntryInDateRange(i))
2775 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2776 continue;
2779 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2781 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2782 if (regex_search(wstring((LPCTSTR)sRev), pat, flags)&&IsEntryInDateRange(i))
2784 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2785 continue;
2788 } // if (bRegex)
2789 else
2791 CString find = m_sFilterText;
2792 find.MakeLower();
2793 #if 0
2794 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_BUGID))
2796 CString sBugIDs = m_logEntries[i]->sBugIDs;
2798 sBugIDs = sBugIDs.MakeLower();
2799 if ((sBugIDs.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2801 pShownlist->SafeAdd(m_logEntries[i]);
2802 continue;
2805 #endif
2806 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_MESSAGES))
2808 CString msg = m_logEntries.GetGitRevAt(i).GetSubject();
2810 msg = msg.MakeLower();
2811 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2813 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2814 continue;
2816 msg = m_logEntries.GetGitRevAt(i).GetBody();
2818 msg = msg.MakeLower();
2819 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2821 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2822 continue;
2825 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_PATHS))
2827 CTGitPathList pathList = m_logEntries.GetGitRevAt(i).GetFiles(this);
2829 bool bGoing = true;
2830 for (INT_PTR cpPathIndex = 0; cpPathIndex < pathList.GetCount() && bGoing; ++cpPathIndex)
2832 CTGitPath cpath = pathList[cpPathIndex];
2833 CString path = cpath.GetGitOldPathString();
2834 path.MakeLower();
2835 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2837 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2838 bGoing = false;
2839 continue;
2841 path = cpath.GetGitPathString();
2842 path.MakeLower();
2843 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2845 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2846 bGoing = false;
2847 continue;
2849 path = cpath.GetActionName();
2850 path.MakeLower();
2851 if ((path.Find(find)>=0)&&(IsEntryInDateRange(i)))
2853 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2854 bGoing = false;
2855 continue;
2859 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_AUTHORS))
2861 CString msg = m_logEntries.GetGitRevAt(i).GetAuthorName();
2862 msg = msg.MakeLower();
2863 if ((msg.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2865 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2866 continue;
2869 if ((m_nSelectedFilter == LOGFILTER_ALL)||(m_nSelectedFilter == LOGFILTER_REVS))
2871 sRev.Format(_T("%s"), m_logEntries.GetGitRevAt(i).m_CommitHash.ToString());
2872 if ((sRev.Find(find) >= 0)&&(IsEntryInDateRange(i)))
2874 pShownlist->SafeAdd(&m_logEntries.GetGitRevAt(i));
2875 continue;
2878 } // else (from if (bRegex))
2879 } // for (DWORD i=0; i<m_logEntries.size(); ++i)
2883 BOOL CGitLogListBase::IsEntryInDateRange(int /*i*/)
2886 __time64_t time = m_logEntries.GetGitRevAt(i).GetAuthorDate().GetTime();
2888 if(m_From == -1)
2889 if(m_To == -1)
2890 return true;
2891 else
2892 return time <= m_To;
2893 else
2894 if(m_To == -1)
2895 return time >= m_From;
2896 else
2897 return ((time >= m_From)&&(time <= m_To));
2899 return TRUE; /* git dll will filter time range */
2901 // return TRUE;
2903 void CGitLogListBase::StartFilter()
2905 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2906 RecalculateShownList(&m_arShownList);
2907 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2910 DeleteAllItems();
2911 SetItemCountEx(ShownCountWithStopped());
2912 RedrawItems(0, ShownCountWithStopped());
2913 Invalidate();
2916 void CGitLogListBase::RemoveFilter()
2919 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2921 m_arShownList.SafeRemoveAll();
2923 // reset the time filter too
2924 #if 0
2925 m_timFrom = (__time64_t(m_tFrom));
2926 m_timTo = (__time64_t(m_tTo));
2927 m_DateFrom.SetTime(&m_timFrom);
2928 m_DateTo.SetTime(&m_timTo);
2929 m_DateFrom.SetRange(&m_timFrom, &m_timTo);
2930 m_DateTo.SetRange(&m_timFrom, &m_timTo);
2931 #endif
2933 for (DWORD i=0; i<m_logEntries.size(); ++i)
2935 if(this->m_IsOldFirst)
2937 m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(m_logEntries.size()-i-1));
2939 else
2941 m_arShownList.SafeAdd(&m_logEntries.GetGitRevAt(i));
2944 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
2945 DeleteAllItems();
2946 SetItemCountEx(ShownCountWithStopped());
2947 RedrawItems(0, ShownCountWithStopped());
2949 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2952 void CGitLogListBase::Clear()
2954 m_arShownList.SafeRemoveAll();
2955 DeleteAllItems();
2957 m_logEntries.ClearAll();
2961 void CGitLogListBase::OnDestroy()
2963 // save the column widths to the registry
2964 SaveColumnWidths();
2966 SafeTerminateThread();
2967 SafeTerminateAsyncDiffThread();
2969 int retry = 0;
2970 while(m_LogCache.SaveCache())
2972 if(retry > 5)
2973 break;
2974 Sleep(1000);
2976 retry++;
2978 //if(CMessageBox::Show(NULL,_T("Cannot Save Log Cache to Disk. To retry click yes. To give up click no."),_T("TortoiseGit"),
2979 // MB_YESNO) == IDNO)
2980 // break;
2983 CHintListCtrl::OnDestroy();
2986 LRESULT CGitLogListBase::OnLoad(WPARAM wParam,LPARAM lParam)
2988 UNREFERENCED_PARAMETER(lParam);
2989 CRect rect;
2990 int i=(int)wParam;
2991 this->GetItemRect(i,&rect,LVIR_BOUNDS);
2992 this->InvalidateRect(rect);
2994 return 0;
2998 * Save column widths to the registry
3000 void CGitLogListBase::SaveColumnWidths()
3002 int maxcol = m_ColumnManager.GetColumnCount();
3004 for (int col = 0; col < maxcol; col++)
3005 if (m_ColumnManager.IsVisible (col))
3006 m_ColumnManager.ColumnResized (col);
3008 m_ColumnManager.WriteSettings();
3011 int CGitLogListBase::GetHeadIndex()
3013 if(m_HeadHash.IsEmpty())
3014 return -1;
3016 for(int i=0;i<m_arShownList.GetCount();i++)
3018 GitRev *pRev = (GitRev*)m_arShownList[i];
3019 if(pRev)
3021 if(pRev->m_CommitHash.ToString() == m_HeadHash )
3022 return i;
3025 return -1;
3027 void CGitLogListBase::OnFind()
3029 if (!m_pFindDialog)
3031 m_pFindDialog = new CFindDlg(this);
3032 m_pFindDialog->Create(this);
3035 void CGitLogListBase::OnHdnBegintrack(NMHDR *pNMHDR, LRESULT *pResult)
3037 m_ColumnManager.OnHdnBegintrack(pNMHDR, pResult);
3039 void CGitLogListBase::OnHdnItemchanging(NMHDR *pNMHDR, LRESULT *pResult)
3041 if(!m_ColumnManager.OnHdnItemchanging(pNMHDR, pResult))
3042 Default();
3044 LRESULT CGitLogListBase::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
3047 ASSERT(m_pFindDialog != NULL);
3048 bool bFound = false;
3049 int i=0;
3051 if (m_pFindDialog->IsTerminating())
3053 // invalidate the handle identifying the dialog box.
3054 m_pFindDialog = NULL;
3055 return 0;
3058 if(m_pFindDialog->IsRef())
3060 CString str;
3061 str=m_pFindDialog->GetFindString();
3063 CGitHash hash;
3065 if(!str.IsEmpty())
3066 hash = g_Git.GetHash(str);
3068 if(!hash.IsEmpty())
3070 for (i = 0; i<m_arShownList.GetCount(); i++)
3072 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(i);
3073 if(pLogEntry && pLogEntry->m_CommitHash == hash)
3075 bFound = true;
3076 break;
3083 if(m_pFindDialog->FindNext())
3085 //read data from dialog
3086 CString FindText = m_pFindDialog->GetFindString();
3087 bool bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
3089 tr1::wregex pat;
3090 bool bRegex = ValidateRegexp(FindText, pat, bMatchCase);
3092 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_not_null;
3095 for (i = this->m_nSearchIndex; i<m_arShownList.GetCount()&&!bFound; i++)
3097 GitRev* pLogEntry = (GitRev*)m_arShownList.SafeGetAt(i);
3099 CString str;
3100 str+=pLogEntry->m_CommitHash.ToString();
3101 str+=_T("\n");
3103 for(int j=0;j<this->m_HashMap[pLogEntry->m_CommitHash].size();j++)
3105 str+=m_HashMap[pLogEntry->m_CommitHash][j];
3106 str+=_T("\n");
3109 str+=pLogEntry->GetAuthorEmail();
3110 str+=_T("\n");
3111 str+=pLogEntry->GetAuthorName();
3112 str+=_T("\n");
3113 str+=pLogEntry->GetBody();
3114 str+=_T("\n");
3115 str+=pLogEntry->GetCommitterEmail();
3116 str+=_T("\n");
3117 str+=pLogEntry->GetCommitterName();
3118 str+=_T("\n");
3119 str+=pLogEntry->GetSubject();
3120 str+=_T("\n");
3123 /*Because changed files list is loaded on demand when gui show,
3124 files will empty when files have not fetched.
3126 we can add it back by using one-way diff(with outnumber changed and rename detect.
3127 here just need changed filename list. one-way is much quicker.
3129 if(pLogEntry->m_IsFull)
3131 for(int i=0;i<pLogEntry->GetFiles(this).GetCount();i++)
3133 str+=pLogEntry->GetFiles(this)[i].GetWinPath();
3134 str+=_T("\n");
3135 str+=pLogEntry->GetFiles(this)[i].GetGitOldPathString();
3136 str+=_T("\n");
3139 else
3141 if(!pLogEntry->m_IsSimpleListReady)
3142 pLogEntry->SafeGetSimpleList(&g_Git);
3144 for(int i=0;i<pLogEntry->m_SimpleFileList.size();i++)
3146 str+=pLogEntry->m_SimpleFileList[i];
3147 str+=_T("\n");
3153 if (bRegex)
3155 if (regex_search(wstring(str), pat, flags))
3157 bFound = true;
3158 break;
3161 else
3163 if (bMatchCase)
3165 if (str.Find(FindText) >= 0)
3167 bFound = true;
3168 break;
3172 else
3174 CString msg = str;
3175 msg = msg.MakeLower();
3176 CString find = FindText.MakeLower();
3177 if (msg.Find(find) >= 0)
3179 bFound = TRUE;
3180 break;
3184 } // for (i = this->m_nSearchIndex; i<m_arShownList.GetItemCount()&&!bFound; i++)
3186 } // if(m_pFindDialog->FindNext())
3187 //UpdateLogInfoLabel();
3189 if (bFound)
3191 this->m_nSearchIndex = i;
3192 EnsureVisible(i, FALSE);
3193 SetItemState(GetSelectionMark(), 0, LVIS_SELECTED);
3194 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
3195 SetSelectionMark(i);
3196 //FillLogMessageCtrl();
3197 UpdateData(FALSE);
3198 m_nSearchIndex++;
3199 if (m_nSearchIndex >= m_arShownList.GetCount())
3200 m_nSearchIndex = (int)m_arShownList.GetCount()-1;
3203 return 0;
3206 void CGitLogListBase::OnColumnResized(NMHDR *pNMHDR, LRESULT *pResult)
3208 m_ColumnManager.OnColumnResized(pNMHDR,pResult);
3210 *pResult = FALSE;
3213 void CGitLogListBase::OnColumnMoved(NMHDR *pNMHDR, LRESULT *pResult)
3215 m_ColumnManager.OnColumnMoved(pNMHDR, pResult);
3217 Invalidate(FALSE);