Remember recently selected branches
[TortoiseGit.git] / src / TortoiseProc / LogDlg.cpp
blob3c6f7b3c74721e6cf1f76c8cad5173bf5287e256
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2013 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "cursor.h"
23 #include "StatGraphDlg.h"
24 #include "LogDlg.h"
25 #include "MessageBox.h"
26 #include "registry.h"
27 #include "AppUtils.h"
28 #include "PathUtils.h"
29 #include "StringUtils.h"
30 #include "UnicodeUtils.h"
31 #include "TempFile.h"
32 #include "IconMenu.h"
33 #include "BrowseRefsDlg.h"
34 #include "SmartHandle.h"
35 #include "LogOrdering.h"
37 IMPLEMENT_DYNAMIC(CLogDlg, CResizableStandAloneDialog)
38 CLogDlg::CLogDlg(CWnd* pParent /*=NULL*/)
39 : CResizableStandAloneDialog(CLogDlg::IDD, pParent)
40 , m_wParam(0)
41 , m_currentChangedArray(NULL)
42 , m_nSortColumn(0)
43 , m_bFollowRenames(false)
44 , m_bSelect(false)
45 , m_bSelectionMustBeSingle(true)
46 , m_bShowTags(true)
47 , m_bShowLocalBranches(true)
48 , m_bShowRemoteBranches(true)
49 , m_bNoMerges(false)
50 , m_iHidePaths(0)
51 , m_bWalkBehavior(FALSE)
52 , m_bFirstParent(false)
53 , m_bWholeProject(FALSE)
54 , m_iCompressedGraph(0)
56 , m_bSelectionMustBeContinuous(false)
58 , m_sLogInfo(_T(""))
60 , m_bCancelled(FALSE)
61 , m_pNotifyWindow(NULL)
63 , m_bAscending(FALSE)
65 , m_limit(0)
66 , m_hAccel(NULL)
68 m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
70 CString str;
71 str=g_Git.m_CurrentDir;
72 str.Replace(_T(":"),_T("_"));
73 str=CString(_T("Software\\TortoiseGit\\LogDialog\\AllBranch\\"))+str;
75 m_regbAllBranch=CRegDWORD(str,FALSE);
77 m_bAllBranch=m_regbAllBranch;
79 str = g_Git.m_CurrentDir;
80 str.Replace(_T(":"),_T("_"));
81 m_regbShowTags = CRegDWORD(_T("Software\\TortoiseGit\\LogDialog\\ShowTags\\") + str, TRUE);
82 m_bShowTags = !!m_regbShowTags;
83 m_regbShowLocalBranches = CRegDWORD(_T("Software\\TortoiseGit\\LogDialog\\ShowLocalBranches\\") + str, TRUE);
84 m_bShowLocalBranches = !!m_regbShowLocalBranches;
85 m_regbShowRemoteBranches = CRegDWORD(_T("Software\\TortoiseGit\\LogDialog\\ShowRemoteBranches\\") + str, TRUE);
86 m_bShowRemoteBranches = !!m_regbShowRemoteBranches;
88 m_bShowUnversioned = CRegDWORD(_T("Software\\TortoiseGit\\AddBeforeCommit"), TRUE);
90 m_bShowGravatar = !!CRegDWORD(_T("Software\\TortoiseGit\\EnableGravatar"), FALSE);
91 m_regbShowGravatar = CRegDWORD(_T("Software\\TortoiseGit\\LogDialog\\ShowGravatar\\") + str, m_bShowGravatar);
92 m_bShowGravatar = !!m_regbShowGravatar;
95 CLogDlg::~CLogDlg()
98 m_regbAllBranch=m_bAllBranch;
99 m_regbShowTags = m_bShowTags;
100 m_regbShowLocalBranches = m_bShowLocalBranches;
101 m_regbShowRemoteBranches = m_bShowRemoteBranches;
102 m_regbShowGravatar = m_bShowGravatar;
104 m_CurrentFilteredChangedArray.RemoveAll();
108 void CLogDlg::DoDataExchange(CDataExchange* pDX)
110 CResizableStandAloneDialog::DoDataExchange(pDX);
111 DDX_Control(pDX, IDC_LOGLIST, m_LogList);
112 DDX_Control(pDX, IDC_LOGMSG, m_ChangedFileListCtrl);
113 DDX_Control(pDX, IDC_PROGRESS, m_LogProgress);
114 DDX_Control(pDX, IDC_SPLITTERTOP, m_wndSplitter1);
115 DDX_Control(pDX, IDC_SPLITTERBOTTOM, m_wndSplitter2);
116 DDX_Text(pDX, IDC_SEARCHEDIT, m_LogList.m_sFilterText);
117 DDX_Control(pDX, IDC_DATEFROM, m_DateFrom);
118 DDX_Control(pDX, IDC_DATETO, m_DateTo);
119 DDX_Control(pDX, IDC_LOG_JUMPTYPE, m_JumpType);
120 DDX_Control(pDX, IDC_LOG_JUMPUP, m_JumpUp);
121 DDX_Control(pDX, IDC_LOG_JUMPDOWN, m_JumpDown);
122 DDX_Text(pDX, IDC_LOGINFO, m_sLogInfo);
123 DDX_Check(pDX, IDC_LOG_ALLBRANCH,m_bAllBranch);
124 DDX_Check(pDX, IDC_SHOWWHOLEPROJECT,m_bWholeProject);
125 DDX_Check(pDX, IDC_WALKBEHAVIOUR, m_bWalkBehavior);
126 DDX_Control(pDX, IDC_SEARCHEDIT, m_cFilter);
127 DDX_Control(pDX, IDC_STATIC_REF, m_staticRef);
128 DDX_Control(pDX, IDC_PIC_AUTHOR, m_gravatar);
131 BEGIN_MESSAGE_MAP(CLogDlg, CResizableStandAloneDialog)
132 ON_WM_CONTEXTMENU()
133 ON_WM_SETCURSOR()
134 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LOGLIST, OnLvnItemchangedLoglist)
135 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LOGMSG, OnLvnItemchangedLogmsg)
136 ON_NOTIFY(EN_LINK, IDC_MSGVIEW, OnEnLinkMsgview)
137 ON_BN_CLICKED(IDC_STATBUTTON, OnBnClickedStatbutton)
139 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
140 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
142 ON_MESSAGE(MSG_LOAD_PERCENTAGE,OnLogListLoading)
144 ON_EN_CHANGE(IDC_SEARCHEDIT, OnEnChangeSearchedit)
145 ON_WM_TIMER()
146 ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATETO, OnDtnDatetimechangeDateto)
147 ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATEFROM, OnDtnDatetimechangeDatefrom)
148 ON_CBN_SELCHANGE(IDC_LOG_JUMPTYPE, &CLogDlg::OnCbnSelchangeJumpType)
149 ON_COMMAND(IDC_LOG_JUMPUP, &CLogDlg::OnBnClickedJumpUp)
150 ON_COMMAND(IDC_LOG_JUMPDOWN, &CLogDlg::OnBnClickedJumpDown)
151 ON_BN_CLICKED(IDC_WALKBEHAVIOUR, OnBnClickedWalkBehaviour)
152 ON_BN_CLICKED(IDC_VIEW, OnBnClickedView)
153 ON_BN_CLICKED(IDC_SHOWWHOLEPROJECT, OnBnClickShowWholeProject)
154 ON_NOTIFY(LVN_COLUMNCLICK,IDC_LOGLIST, OnLvnColumnclick)
155 ON_COMMAND(MSG_FETCHED_DIFF, OnBnClickedHidepaths)
156 ON_BN_CLICKED(IDC_LOG_ALLBRANCH, OnBnClickedAllBranch)
158 ON_NOTIFY(DTN_DROPDOWN, IDC_DATEFROM, &CLogDlg::OnDtnDropdownDatefrom)
159 ON_NOTIFY(DTN_DROPDOWN, IDC_DATETO, &CLogDlg::OnDtnDropdownDateto)
160 ON_WM_SIZE()
161 ON_BN_CLICKED(IDC_REFRESH, &CLogDlg::OnBnClickedRefresh)
162 ON_STN_CLICKED(IDC_STATIC_REF, &CLogDlg::OnBnClickedBrowseRef)
163 ON_COMMAND(ID_LOGDLG_REFRESH, &CLogDlg::OnBnClickedRefresh)
164 ON_COMMAND(ID_LOGDLG_FIND, &CLogDlg::OnFind)
165 ON_COMMAND(ID_LOGDLG_FOCUSFILTER, &CLogDlg::OnFocusFilter)
166 ON_COMMAND(ID_EDIT_COPY, &CLogDlg::OnEditCopy)
167 ON_MESSAGE(MSG_REFLOG_CHANGED, OnRefLogChanged)
168 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
169 END_MESSAGE_MAP()
171 enum JumpType
173 JumpType_AuthorEmail,
174 JumpType_CommitterEmail,
175 JumpType_MergePoint,
176 JumpType_Parent1,
177 JumpType_Parent2,
178 JumpType_Tag,
179 JumpType_TagFF,
180 JumpType_Branch,
181 JumpType_BranchFF,
184 void CLogDlg::SetParams(const CTGitPath& orgPath, const CTGitPath& path, CString hightlightRevision, CString range, int limit)
186 m_orgPath = orgPath;
187 m_path = path;
188 m_hightlightRevision = hightlightRevision;
190 if (!(range.IsEmpty() || range == _T("HEAD")))
191 m_bAllBranch = false;
193 SetRange(range);
195 m_limit = limit;
196 if (::IsWindow(m_hWnd))
197 UpdateData(FALSE);
200 void CLogDlg::SetFilter(const CString& findstr, LONG findtype, bool findregex)
202 m_LogList.m_sFilterText = findstr;
203 if (findtype)
204 m_LogList.m_SelectedFilters = findtype;
205 m_LogList.m_bFilterWithRegex = m_bFilterWithRegex = findregex;
208 BOOL CLogDlg::OnInitDialog()
210 CString temp;
211 CResizableStandAloneDialog::OnInitDialog();
212 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
214 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
215 // do this, Explorer would be unable to send that message to our window if we
216 // were running elevated. It's OK to make the call all the time, since if we're
217 // not elevated, this is a no-op.
218 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
219 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
220 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
221 if (hUser)
223 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
224 if (pfnChangeWindowMessageFilterEx)
226 pfnChangeWindowMessageFilterEx(m_hWnd, WM_TASKBARBTNCREATED, MSGFLT_ALLOW, &cfs);
229 m_pTaskbarList.Release();
230 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
231 m_pTaskbarList = nullptr;
233 m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_LOGDLG));
235 // use the state of the "stop on copy/rename" option from the last time
236 UpdateData(FALSE);
238 // set the font to use in the log message view, configured in the settings dialog
239 CAppUtils::CreateFontForLogs(m_logFont);
240 GetDlgItem(IDC_MSGVIEW)->SetFont(&m_logFont);
241 // automatically detect URLs in the log message and turn them into links
242 GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_AUTOURLDETECT, TRUE, NULL);
243 // make the log message rich edit control send a message when the mouse pointer is over a link
244 GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK);
246 // "unrelated paths" should be in gray color
247 m_iHidePaths = 2;
249 //SetWindowTheme(m_LogList.GetSafeHwnd(), L"Explorer", NULL);
250 //SetWindowTheme(m_ChangedFileListCtrl.GetSafeHwnd(), L"Explorer", NULL);
252 // set up the columns
253 m_LogList.DeleteAllItems();
255 m_LogList.m_Path=m_path;
256 m_LogList.m_hasWC = m_LogList.m_bShowWC = !g_GitAdminDir.IsBareRepo(g_Git.m_CurrentDir);
257 m_LogList.InsertGitColumn();
259 m_ChangedFileListCtrl.Init(GITSLC_COLEXT | GITSLC_COLSTATUS |GITSLC_COLADD|GITSLC_COLDEL, _T("LogDlg"), (GITSLC_POPALL ^ (GITSLC_POPCOMMIT|GITSLC_POPIGNORE|GITSLC_POPRESTORE)), false, m_LogList.m_hasWC, GITSLC_COLEXT | GITSLC_COLSTATUS | GITSLC_COLADD| GITSLC_COLDEL);
261 GetDlgItem(IDC_LOGLIST)->UpdateData(FALSE);
263 SetDlgTitle();
265 m_tooltips.Create(this);
266 CheckRegexpTooltip();
268 SetSplitterRange();
270 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
271 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
272 m_cFilter.SetInfoIcon(IDI_LOGFILTER);
273 m_cFilter.SetValidator(this);
275 AdjustControlSize(IDC_LOG_ALLBRANCH);
276 AdjustControlSize(IDC_SHOWWHOLEPROJECT);
278 ShowGravatar();
280 GetClientRect(m_DlgOrigRect);
281 m_LogList.GetClientRect(m_LogListOrigRect);
282 GetDlgItem(IDC_MSGVIEW)->GetClientRect(m_MsgViewOrigRect);
283 m_ChangedFileListCtrl.GetClientRect(m_ChgOrigRect);
285 m_DateFrom.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);
286 m_DateTo.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);
288 m_staticRef.SetURL(CString());
290 // resizable stuff
291 AddAnchor(IDC_STATIC_REF, TOP_LEFT);
292 //AddAnchor(IDC_BUTTON_BROWSE_REF, TOP_LEFT);
293 AddAnchor(IDC_FROMLABEL, TOP_LEFT);
294 AddAnchor(IDC_DATEFROM, TOP_LEFT);
295 AddAnchor(IDC_TOLABEL, TOP_LEFT);
296 AddAnchor(IDC_DATETO, TOP_LEFT);
298 SetFilterCueText();
299 AddAnchor(IDC_SEARCHEDIT, TOP_LEFT, TOP_RIGHT);
300 AddAnchor(IDC_LOG_JUMPTYPE, TOP_RIGHT);
301 AddAnchor(IDC_LOG_JUMPUP, TOP_RIGHT);
302 AddAnchor(IDC_LOG_JUMPDOWN, TOP_RIGHT);
304 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
305 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
306 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
307 AddAnchor(IDC_PIC_AUTHOR, TOP_RIGHT);
308 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
309 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
311 AddAnchor(IDC_LOGINFO, BOTTOM_LEFT, BOTTOM_RIGHT);
312 AddAnchor(IDC_WALKBEHAVIOUR, BOTTOM_LEFT);
313 AddAnchor(IDC_VIEW, BOTTOM_LEFT);
314 AddAnchor(IDC_LOG_ALLBRANCH,BOTTOM_LEFT);
315 AddAnchor(IDC_SHOWWHOLEPROJECT, BOTTOM_LEFT);
316 AddAnchor(IDC_REFRESH, BOTTOM_LEFT);
317 AddAnchor(IDC_STATBUTTON, BOTTOM_RIGHT);
318 AddAnchor(IDC_PROGRESS, BOTTOM_LEFT, BOTTOM_RIGHT);
319 AddAnchor(IDOK, BOTTOM_RIGHT);
320 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
321 AddAnchor(IDHELP, BOTTOM_RIGHT);
323 if(this->m_bAllBranch)
324 m_LogList.m_ShowMask|=CGit::LOG_INFO_ALL_BRANCH;
325 else
326 m_LogList.m_ShowMask&=~CGit::LOG_INFO_ALL_BRANCH;
328 HandleShowLabels(m_bShowTags, LOGLIST_SHOWTAGS);
329 HandleShowLabels(m_bShowLocalBranches, LOGLIST_SHOWLOCALBRANCHES);
330 HandleShowLabels(m_bShowRemoteBranches, LOGLIST_SHOWREMOTEBRANCHES);
332 // SetPromptParentWindow(m_hWnd);
333 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_AUTHOREMAIL)));
334 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_COMMITTEREMAIL)));
335 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_MERGEPOINT)));
336 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_PARENT1)));
337 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_PARENT2)));
338 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_TAG)));
339 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_TAG_FF)));
340 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_BRANCH)));
341 m_JumpType.AddString(CString(MAKEINTRESOURCE(IDS_PROC_BRANCH_FF)));
342 m_JumpType.SetCurSel(0);
343 m_JumpUp.SetIcon((HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_JUMPUP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
344 m_JumpDown.SetIcon((HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_JUMPDOWN), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
346 if (hWndExplorer)
347 CenterWindow(CWnd::FromHandle(hWndExplorer));
348 EnableSaveRestore(_T("LogDlg"));
350 DWORD yPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
351 DWORD yPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
352 RECT rcDlg, rcLogList, rcChgMsg;
353 GetClientRect(&rcDlg);
354 m_LogList.GetWindowRect(&rcLogList);
355 ScreenToClient(&rcLogList);
356 m_ChangedFileListCtrl.GetWindowRect(&rcChgMsg);
357 ScreenToClient(&rcChgMsg);
358 if (yPos1)
360 RECT rectSplitter;
361 m_wndSplitter1.GetWindowRect(&rectSplitter);
362 ScreenToClient(&rectSplitter);
363 int delta = yPos1 - rectSplitter.top;
365 if ((rcLogList.bottom + delta > rcLogList.top)&&(rcLogList.bottom + delta < rcChgMsg.bottom - 30))
367 m_wndSplitter1.SetWindowPos(NULL, rectSplitter.left, yPos1, 0, 0, SWP_NOSIZE);
368 DoSizeV1(delta);
371 if (yPos2)
373 RECT rectSplitter;
374 m_wndSplitter2.GetWindowRect(&rectSplitter);
375 ScreenToClient(&rectSplitter);
376 int delta = yPos2 - rectSplitter.top;
378 if ((rcChgMsg.top + delta < rcChgMsg.bottom)&&(rcChgMsg.top + delta > rcLogList.top + 30))
380 m_wndSplitter2.SetWindowPos(NULL, rectSplitter.left, yPos2, 0, 0, SWP_NOSIZE);
381 DoSizeV2(delta);
385 SetSplitterRange();
387 if (m_bSelect)
389 // the dialog is used to select revisions
390 // enable the OK button if appropriate
391 EnableOKButton();
393 else
395 // the dialog is used to just view log messages
396 // hide the OK button and set text on Cancel button to OK
397 GetDlgItemText(IDOK, temp);
398 SetDlgItemText(IDCANCEL, temp);
399 GetDlgItem(IDOK)->ShowWindow(SW_HIDE);
402 // first start a thread to obtain the log messages without
403 // blocking the dialog
404 //m_tTo = 0;
405 //m_tFrom = (DWORD)-1;
407 // scroll to user selected or current revision
408 if (!m_hightlightRevision.IsEmpty() && m_hightlightRevision.GetLength() >= GIT_HASH_SIZE)
409 m_LogList.m_lastSelectedHash = m_hightlightRevision;
410 else
412 if (g_Git.GetHash(m_LogList.m_lastSelectedHash, _T("HEAD")))
413 MessageBox(g_Git.GetGitLastErr(_T("Could not get HEAD hash.")), _T("TortoiseGit"), MB_ICONERROR);
416 m_LogList.FetchLogAsync(this);
417 m_gravatar.Init();
419 GetDlgItem(IDC_LOGLIST)->SetFocus();
421 m_History.SetMaxHistoryItems((LONG)CRegDWORD(_T("Software\\TortoiseGit\\MaxRefHistoryItems"), 5));
422 CString reg;
423 reg.Format(_T("Software\\TortoiseGit\\History\\log-refs\\%s"), g_Git.m_CurrentDir);
424 reg.Replace(_T(':'),_T('_'));
425 m_History.Load(reg, _T("ref"));
427 ShowStartRef();
428 return FALSE;
431 LRESULT CLogDlg::OnLogListLoading(WPARAM wParam, LPARAM /*lParam*/)
433 int cur=(int)wParam;
435 if( cur == GITLOG_START )
437 CString temp;
438 temp.LoadString(IDS_PROGRESSWAIT);
440 this->m_LogList.ShowText(temp, true);
442 // We use a progress bar while getting the logs
443 m_LogProgress.SetRange32(0, 100);
444 m_LogProgress.SetPos(0);
445 if (m_pTaskbarList)
447 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
448 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
451 GetDlgItem(IDC_PROGRESS)->ShowWindow(TRUE);
453 DialogEnableWindow(IDC_WALKBEHAVIOUR, FALSE);
454 DialogEnableWindow(IDC_STATBUTTON, FALSE);
455 //DialogEnableWindow(IDC_REFRESH, FALSE);
456 DialogEnableWindow(IDC_VIEW, FALSE);
459 else if( cur == GITLOG_END)
461 if(this->m_LogList.HasText())
463 this->m_LogList.ClearText();
465 UpdateLogInfoLabel();
467 if (m_pTaskbarList)
468 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
470 DialogEnableWindow(IDC_SHOWWHOLEPROJECT, !m_bFollowRenames);
472 DialogEnableWindow(IDC_STATBUTTON, !(m_LogList.m_arShownList.IsEmpty() || m_LogList.m_arShownList.GetCount() == 1 && m_LogList.m_bShowWC));
473 DialogEnableWindow(IDC_REFRESH, TRUE);
474 DialogEnableWindow(IDC_VIEW, TRUE);
475 DialogEnableWindow(IDC_WALKBEHAVIOUR, TRUE);
476 // PostMessage(WM_TIMER, LOGFILTER_TIMER);
477 GetDlgItem(IDC_PROGRESS)->ShowWindow(FALSE);
478 //CTime time=m_LogList.GetOldestTime();
479 CTime begin,end;
480 m_LogList.GetTimeRange(begin,end);
482 if(m_LogList.m_From == -1)
483 m_DateFrom.SetTime(&begin);
485 if(m_LogList.m_To == -1)
486 m_DateTo.SetTime(&end);
490 else
492 if(this->m_LogList.HasText())
494 this->m_LogList.ClearText();
495 this->m_LogList.Invalidate();
497 UpdateLogInfoLabel();
498 m_LogProgress.SetPos(cur);
499 if (m_pTaskbarList)
501 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
502 m_pTaskbarList->SetProgressValue(m_hWnd, cur, 100);
505 return 0;
507 void CLogDlg::SetDlgTitle()
509 if (m_sTitle.IsEmpty())
510 GetWindowText(m_sTitle);
512 if (m_LogList.m_Path.IsEmpty() || m_orgPath.GetWinPathString().IsEmpty())
514 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, m_sTitle);
516 else
517 CAppUtils::SetWindowTitle(m_hWnd, m_orgPath.GetWinPathString(), m_sTitle);
520 void CLogDlg::CheckRegexpTooltip()
522 CWnd *pWnd = GetDlgItem(IDC_SEARCHEDIT);
523 // Since tooltip describes regexp features, show it only if regexps are enabled.
524 if (m_bFilterWithRegex)
526 m_tooltips.AddTool(pWnd, IDS_LOG_FILTER_REGEX_TT);
528 else
529 m_tooltips.DelTool(pWnd);
532 void CLogDlg::EnableOKButton()
534 if (m_bSelect)
536 // the dialog is used to select revisions
537 if (m_bSelectionMustBeSingle)
539 // enable OK button if only a single revision is selected
540 DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()==1));
542 else if (m_bSelectionMustBeContinuous)
543 DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()!=0)&&(m_LogList.IsSelectionContinuous()));
544 else
545 DialogEnableWindow(IDOK, m_LogList.GetSelectedCount()!=0);
547 else
548 DialogEnableWindow(IDOK, TRUE);
551 CString CLogDlg::GetTagInfo(GitRev* pLogEntry)
553 CString cmd;
554 CString output;
556 if(m_LogList.m_HashMap.find(pLogEntry->m_CommitHash) != m_LogList.m_HashMap.end())
558 STRING_VECTOR &vector = m_LogList.m_HashMap[pLogEntry->m_CommitHash];
559 for (size_t i = 0; i < vector.size(); ++i)
561 if(vector[i].Find(_T("refs/tags/")) == 0 )
563 CString tag= vector[i];
564 int start = vector[i].Find(_T("^{}"));
565 if(start>0)
566 tag=tag.Left(start);
567 else
568 continue;
570 cmd.Format(_T("git.exe cat-file tag %s"), tag);
572 if(g_Git.Run(cmd, &output, NULL, CP_UTF8) == 0 )
573 output+=_T("\n");
578 if(!output.IsEmpty())
580 output = _T("\n*") + CString(MAKEINTRESOURCE(IDS_PROC_LOG_TAGINFO)) + _T("*\n\n") + output;
583 return output;
586 bool LookLikeGitHash(const CString& msg, int &pos)
588 int c = 0;
589 for (; pos < msg.GetLength(); ++pos)
591 if (msg[pos] >= '0' && msg[pos] <= '9' || msg[pos] >= 'a' && msg[pos] <= 'f')
593 c++;
595 else
597 return c >= g_Git.GetShortHASHLength() && c <= GIT_HASH_SIZE * 2;
600 return c >= g_Git.GetShortHASHLength() && c <= GIT_HASH_SIZE * 2;
603 std::vector<CHARRANGE> FindGitHashPositions(const CString& msg, int offset)
605 std::vector<CHARRANGE> result;
606 offset = offset < 0 ? 0 : offset;
607 int old = offset;
608 while (offset < msg.GetLength())
610 old = offset;
611 TCHAR b = offset > 1 ? msg[offset - 2] : '\0';
612 TCHAR c = offset > 0 ? msg[offset - 1] : '\0';
613 // git hash can optionally starts with 'g'
614 if ((c == 'g' && (offset == 0 || !((b >= 'A' && b <= 'Z') || (b >= 'h' && b <= 'z'))))
615 || (!((c >= 'A' && c <= 'Z') || (c >= 'h' && c <= 'z'))))
617 if (b == '\n')
619 if (msg.Mid(offset, 11) == _T("git-svn-id:") || msg.Mid(offset, 14) == _T("Signed-off-by:"))
621 offset += 11;
622 while (offset < msg.GetLength())
624 if (msg[offset++] == '\n')
625 break;
627 continue;
631 if (LookLikeGitHash(msg, offset))
633 TCHAR d = offset < msg.GetLength() ? msg[offset] : '\0';
634 if (!((d >= 'A' && d <= 'Z') || (d >= 'g' && d <= 'z')))
636 CHARRANGE range = { old, offset };
637 result.push_back(range);
641 ++offset;
644 return result;
647 BOOL FindGitHash(const CString& msg, int offset, CWnd *pWnd)
649 std::vector<CHARRANGE> positions = FindGitHashPositions(msg, offset);
650 CAppUtils::SetCharFormat(pWnd, CFM_LINK, CFE_LINK, positions);
652 return positions.empty() ? FALSE : TRUE;
655 void CLogDlg::FillLogMessageCtrl(bool bShow /* = true*/)
657 // we fill here the log message rich edit control,
658 // and also populate the changed files list control
659 // according to the selected revision(s).
661 CRichEditCtrl * pMsgView = (CRichEditCtrl*)GetDlgItem(IDC_MSGVIEW);
662 // empty the log message view
663 pMsgView->SetWindowText(_T(" "));
664 // empty the changed files list
665 m_ChangedFileListCtrl.SetRedraw(FALSE);
666 // InterlockedExchange(&m_bNoDispUpdates, TRUE);
667 m_currentChangedArray = NULL;
668 m_ChangedFileListCtrl.DeleteAllItems();
670 // if we're not here to really show a selected revision, just
671 // get out of here after clearing the views, which is what is intended
672 // if that flag is not set.
673 if (!bShow)
675 // force a redraw
676 m_ChangedFileListCtrl.Invalidate();
677 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
678 m_ChangedFileListCtrl.SetRedraw(TRUE);
679 m_gravatar.LoadGravatar();
680 return;
683 // depending on how many revisions are selected, we have to do different
684 // tasks.
685 int selCount = m_LogList.GetSelectedCount();
686 if (selCount == 0)
688 // if nothing is selected, we have nothing more to do
689 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
690 m_ChangedFileListCtrl.SetRedraw(TRUE);
691 m_gravatar.LoadGravatar();
692 return;
694 else if (selCount == 1)
696 // if one revision is selected, we have to fill the log message view
697 // with the corresponding log message, and also fill the changed files
698 // list fully.
699 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
700 int selIndex = m_LogList.GetNextSelectedItem(pos);
701 if (selIndex >= m_LogList.m_arShownList.GetCount())
703 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
704 m_ChangedFileListCtrl.SetRedraw(TRUE);
705 return;
707 Locker lock(m_LogList.m_critSec_AsyncDiff);
708 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.SafeGetAt(selIndex));
711 // set the log message text
712 pMsgView->SetWindowText(CString(MAKEINTRESOURCE(IDS_HASH)) + _T(": ") + pLogEntry->m_CommitHash.ToString() + _T("\r\n\r\n"));
713 // turn bug ID's into links if the bugtraq: properties have been set
714 // and we can find a match of those in the log message
716 pMsgView->SetSel(-1,-1);
717 CHARFORMAT2 format;
718 SecureZeroMemory(&format, sizeof(CHARFORMAT2));
719 format.cbSize = sizeof(CHARFORMAT2);
720 format.dwMask = CFM_BOLD;
721 format.dwEffects = CFE_BOLD;
722 pMsgView->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
724 CString msg=_T("* ");
725 msg+=pLogEntry->GetSubject();
726 pMsgView->ReplaceSel(msg);
728 pMsgView->SetSel(-1,-1);
729 format.dwEffects = 0;
730 pMsgView->SendMessage(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
732 msg=_T("\n");
733 msg+=pLogEntry->GetBody();
735 if(!pLogEntry->m_Notes.IsEmpty())
737 msg+= _T("\n*") + CString(MAKEINTRESOURCE(IDS_NOTES)) + _T("* ");
738 msg+= pLogEntry->m_Notes;
739 msg+= _T("\n\n");
742 msg+=GetTagInfo(pLogEntry);
744 pMsgView->ReplaceSel(msg);
746 CString text;
747 pMsgView->GetWindowText(text);
748 // the rich edit control doesn't count the CR char!
749 // to be exact: CRLF is treated as one char.
750 text.Remove('\r');
752 FindGitHash(text, text.Find('\n'), pMsgView);
753 m_LogList.m_ProjectProperties.FindBugID(text, pMsgView);
754 if (((DWORD)CRegStdDWORD(_T("Software\\TortoiseGit\\StyleCommitMessages"), TRUE)) == TRUE)
755 CAppUtils::FormatTextInRichEditControl(pMsgView);
757 CString matchpath=this->m_path.GetGitPathString();
759 int count = pLogEntry->GetFiles(&m_LogList).GetCount();
760 for (int i = 0 ; i < count && (!matchpath.IsEmpty()); ++i)
762 if( m_bWholeProject )
763 break;
765 ((CTGitPath&)pLogEntry->GetFiles(&m_LogList)[i]).m_Action &= ~(CTGitPath::LOGACTIONS_HIDE|CTGitPath::LOGACTIONS_GRAY);
767 if(pLogEntry->GetFiles(&m_LogList)[i].GetGitPathString().Left(matchpath.GetLength()) != matchpath && pLogEntry->GetFiles(&m_LogList)[i].GetGitOldPathString().Left(matchpath.GetLength()) != matchpath)
769 if (m_iHidePaths == 1)
770 ((CTGitPath&)pLogEntry->GetFiles(&m_LogList)[i]).m_Action |= CTGitPath::LOGACTIONS_HIDE;
771 else if (m_iHidePaths == 2)
772 ((CTGitPath&)pLogEntry->GetFiles(&m_LogList)[i]).m_Action |= CTGitPath::LOGACTIONS_GRAY;
776 m_ChangedFileListCtrl.UpdateWithGitPathList(pLogEntry->GetFiles(&m_LogList));
777 m_ChangedFileListCtrl.m_CurrentVersion=pLogEntry->m_CommitHash;
778 if (pLogEntry->m_CommitHash.IsEmpty() && m_bShowUnversioned)
780 m_ChangedFileListCtrl.UpdateUnRevFileList(pLogEntry->GetUnRevFiles());
781 m_ChangedFileListCtrl.Show(GITSLC_SHOWVERSIONED | GITSLC_SHOWUNVERSIONED);
783 else
784 m_ChangedFileListCtrl.Show(GITSLC_SHOWVERSIONED);
786 m_ChangedFileListCtrl.SetBusyString(CString(MAKEINTRESOURCE(IDS_PROC_LOG_FETCHINGFILES)));
788 if(!pLogEntry->m_IsDiffFiles)
789 m_ChangedFileListCtrl.SetBusy(TRUE);
790 else
791 m_ChangedFileListCtrl.SetBusy(FALSE);
793 m_ChangedFileListCtrl.SetRedraw(TRUE);
794 m_gravatar.LoadGravatar(pLogEntry->GetAuthorEmail());
795 return;
799 else
801 // more than one revision is selected:
802 // the log message view must be emptied
803 // the changed files list contains all the changed paths from all
804 // selected revisions, with 'doubles' removed
805 m_currentChangedPathList = GetChangedPathsFromSelectedRevisions(true);
808 // redraw the views
809 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
810 #if 0
811 if (m_currentChangedArray)
813 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedArray->GetCount());
814 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedArray->GetCount());
816 else if (m_currentChangedPathList.GetCount())
818 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedPathList.GetCount());
819 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedPathList.GetCount());
821 else
823 m_ChangedFileListCtrl.SetItemCountEx(0);
824 m_ChangedFileListCtrl.Invalidate();
826 #endif
827 // sort according to the settings
828 if (m_nSortColumnPathList > 0)
829 SetSortArrow(&m_ChangedFileListCtrl, m_nSortColumnPathList, m_bAscendingPathList);
830 else
831 SetSortArrow(&m_ChangedFileListCtrl, -1, false);
832 m_ChangedFileListCtrl.SetRedraw(TRUE);
833 m_gravatar.LoadGravatar();
837 void CLogDlg::OnBnClickedRefresh()
839 Refresh (true);
842 void CLogDlg::Refresh (bool clearfilter /*autoGoOnline*/)
844 m_limit = 0;
845 m_LogList.Refresh(clearfilter);
846 EnableOKButton();
847 ShowStartRef();
848 FillLogMessageCtrl(false);
853 BOOL CLogDlg::Cancel()
855 return m_bCancelled;
858 void CLogDlg::SaveSplitterPos()
860 if (!IsIconic())
862 CRegDWORD regPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
863 CRegDWORD regPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
864 RECT rectSplitter;
865 m_wndSplitter1.GetWindowRect(&rectSplitter);
866 ScreenToClient(&rectSplitter);
867 regPos1 = rectSplitter.top;
868 m_wndSplitter2.GetWindowRect(&rectSplitter);
869 ScreenToClient(&rectSplitter);
870 regPos2 = rectSplitter.top;
874 void CLogDlg::OnCancel()
876 this->ShowWindow(SW_HIDE);
878 // canceling means stopping the working thread if it's still running.
879 m_LogList.SafeTerminateAsyncDiffThread();
880 if (this->IsThreadRunning())
882 m_LogList.SafeTerminateThread();
884 m_gravatar.SafeTerminateGravatarThread();
885 UpdateData();
887 SaveSplitterPos();
888 __super::OnCancel();
891 void CLogDlg::CopyChangedSelectionToClipBoard()
894 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
895 if (pos == NULL)
896 return; // nothing is selected, get out of here
898 CString sPaths;
900 // CGitRev* pLogEntry = reinterpret_cast<CGitRev* >(m_LogList.m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
901 // if (pos)
903 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
904 while (pos)
906 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
907 CTGitPath *path = (CTGitPath*)m_ChangedFileListCtrl.GetItemData(nItem);
908 if(path)
909 sPaths += path->GetGitPathString();
910 sPaths += _T("\r\n");
913 #if 0
914 else
916 // only one revision is selected in the log dialog top pane
917 // but multiple items could be selected in the changed items list
918 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
919 while (pos)
921 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
922 LogChangedPath * changedlogpath = pLogEntry->pArChangedPaths->SafeGetAt(nItem);
924 if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)
926 // some items are hidden! So find out which item the user really selected
927 INT_PTR selRealIndex = -1;
928 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)
930 if (pLogEntry->pArChangedPaths->SafeGetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
931 ++selRealIndex;
932 if (selRealIndex == nItem)
934 changedlogpath = pLogEntry->pArChangedPaths->SafeGetAt(hiddenindex);
935 break;
939 if (changedlogpath)
941 sPaths += changedlogpath->sPath;
942 sPaths += _T("\r\n");
946 #endif
947 sPaths.Trim();
948 CStringUtils::WriteAsciiStringToClipboard(sPaths, GetSafeHwnd());
952 void CLogDlg::OnContextMenu(CWnd* pWnd, CPoint point)
954 // we have three separate context menus:
955 // one for the branch label in the upper left
956 // one shown on the log message list control,
957 // one shown in the changed-files list control
958 if (pWnd == GetDlgItem(IDC_STATIC_REF))
960 CIconMenu popup;
961 if (!popup.CreatePopupMenu())
962 return;
964 int cnt = 0;
965 popup.AppendMenuIcon(++cnt, IDS_MENUREFBROWSE);
966 popup.SetDefaultItem(cnt);
967 popup.AppendMenuIcon(++cnt, _T("HEAD"));
968 popup.AppendMenuIcon(++cnt, IDS_ALL);
969 int offset = ++cnt;
970 if (m_History.GetCount() > 0)
972 popup.AppendMenu(MF_SEPARATOR, 0);
973 for (int i = 0; i < m_History.GetCount(); ++i)
974 popup.AppendMenuIcon(cnt++, m_History.GetEntry(i));
977 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
978 if (cmd == 0)
979 return;
980 else if (cmd == 1)
981 OnBnClickedBrowseRef();
982 else if (cmd == 2)
984 SetRange(g_Git.GetCurrentBranch(true));
985 ((CButton*)GetDlgItem(IDC_LOG_ALLBRANCH))->SetCheck(FALSE);
986 OnBnClickedAllBranch();
988 else if (cmd == 3)
990 ((CButton*)GetDlgItem(IDC_LOG_ALLBRANCH))->SetCheck(TRUE);
991 OnBnClickedAllBranch();
993 else if (cmd >= offset)
995 SetRange(m_History.GetEntry(cmd - offset));
996 m_History.AddEntry(m_LogList.m_sRange);
997 m_History.Save();
998 ((CButton*)GetDlgItem(IDC_LOG_ALLBRANCH))->SetCheck(FALSE);
999 OnBnClickedAllBranch();
1001 return;
1003 int selCount = m_LogList.GetSelectedCount();
1004 if ((selCount == 1)&&(pWnd == GetDlgItem(IDC_MSGVIEW)))
1006 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1007 int selIndex = -1;
1008 if (pos)
1009 selIndex = m_LogList.GetNextSelectedItem(pos);
1011 GitRev *pRev = ((GitRev*)m_LogList.m_arShownList[selIndex]);
1013 if ((point.x == -1) && (point.y == -1))
1015 CRect rect;
1016 GetDlgItem(IDC_MSGVIEW)->GetClientRect(&rect);
1017 ClientToScreen(&rect);
1018 point = rect.CenterPoint();
1020 CString sMenuItemText;
1021 CIconMenu popup;
1022 if (popup.CreatePopupMenu())
1024 // add the 'default' entries
1025 sMenuItemText.LoadString(IDS_SCIEDIT_COPY);
1026 popup.AppendMenu(MF_STRING | MF_ENABLED, WM_COPY, sMenuItemText);
1027 sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);
1028 popup.AppendMenu(MF_STRING | MF_ENABLED, EM_SETSEL, sMenuItemText);
1029 sMenuItemText.LoadString(IDS_EDIT_NOTES);
1030 popup.AppendMenuIcon( CGitLogList::ID_EDITNOTE, sMenuItemText, IDI_EDIT);
1032 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1033 switch (cmd)
1035 case 0:
1036 break; // no command selected
1037 case EM_SETSEL:
1038 case WM_COPY:
1039 ::SendMessage(GetDlgItem(IDC_MSGVIEW)->GetSafeHwnd(), cmd, 0, -1);
1040 break;
1041 case CGitLogList::ID_EDITNOTE:
1042 CAppUtils::EditNote(pRev);
1043 this->FillLogMessageCtrl(true);
1044 break;
1050 void CLogDlg::OnOK()
1052 // since the log dialog is also used to select revisions for other
1053 // dialogs, we have to do some work before closing this dialog
1054 if (GetFocus() != GetDlgItem(IDOK))
1055 return; // if the "OK" button doesn't have the focus, do nothing: this prevents closing the dialog when pressing enter
1057 m_LogList.SafeTerminateAsyncDiffThread();
1058 if (this->IsThreadRunning())
1060 m_LogList.SafeTerminateThread();
1062 m_gravatar.SafeTerminateGravatarThread();
1063 UpdateData();
1064 // check that one and only one row is selected
1065 if (m_LogList.GetSelectedCount() == 1)
1067 // get the selected row
1068 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1069 int selIndex = m_LogList.GetNextSelectedItem(pos);
1070 if (selIndex < m_LogList.m_arShownList.GetCount())
1072 // all ok, pick up the revision
1073 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.SafeGetAt(selIndex));
1074 // extract the hash
1075 m_sSelectedHash = pLogEntry->m_CommitHash;
1078 UpdateData(FALSE);
1079 SaveSplitterPos();
1080 __super::OnOK();
1082 #if 0
1083 if (!GetDlgItem(IDOK)->IsWindowVisible() && GetFocus() != GetDlgItem(IDCANCEL))
1084 return; // the Cancel button works as the OK button. But if the cancel button has not the focus, do nothing.
1086 CString temp;
1087 CString buttontext;
1088 GetDlgItemText(IDOK, buttontext);
1089 temp.LoadString(IDS_MSGBOX_CANCEL);
1090 if (temp.Compare(buttontext) != 0)
1091 __super::OnOK(); // only exit if the button text matches, and that will match only if the thread isn't running anymore
1092 m_bCancelled = TRUE;
1093 m_selectedRevs.Clear();
1094 m_selectedRevsOneRange.Clear();
1095 if (m_pNotifyWindow)
1097 int selIndex = m_LogList.GetSelectionMark();
1098 if (selIndex >= 0)
1100 PLOGENTRYDATA pLogEntry = NULL;
1101 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1102 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
1103 m_selectedRevs.AddRevision(pLogEntry->Rev);
1104 git_revnum_t lowerRev = pLogEntry->Rev;
1105 git_revnum_t higherRev = lowerRev;
1106 while (pos)
1108 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
1109 git_revnum_t rev = pLogEntry->Rev;
1110 m_selectedRevs.AddRevision(pLogEntry->Rev);
1111 if (lowerRev > rev)
1112 lowerRev = rev;
1113 if (higherRev < rev)
1114 higherRev = rev;
1116 if (m_sFilterText.IsEmpty() && m_nSortColumn == 0 && IsSelectionContinuous())
1118 m_selectedRevsOneRange.AddRevRange(lowerRev, higherRev);
1120 BOOL bSentMessage = FALSE;
1121 if (m_LogList.GetSelectedCount() == 1)
1123 // if only one revision is selected, check if the path/url with which the dialog was started
1124 // was directly affected in that revision. If it was, then check if our path was copied from somewhere.
1125 // if it was copied, use the copy from revision as lowerRev
1126 if ((pLogEntry)&&(pLogEntry->pArChangedPaths)&&(lowerRev == higherRev))
1128 CString sUrl = m_path.GetGitPathString();
1129 if (!m_path.IsUrl())
1131 sUrl = GetURLFromPath(m_path);
1133 sUrl = sUrl.Mid(m_sRepositoryRoot.GetLength());
1134 for (int cp = 0; cp < pLogEntry->pArChangedPaths->GetCount(); ++cp)
1136 LogChangedPath * pData = pLogEntry->pArChangedPaths->SafeGetAt(cp);
1137 if (pData)
1139 if (sUrl.Compare(pData->sPath) == 0)
1141 if (!pData->sCopyFromPath.IsEmpty())
1143 lowerRev = pData->lCopyFromRev;
1144 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART), lowerRev);
1145 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND), higherRev);
1146 m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);
1147 bSentMessage = TRUE;
1154 if ( !bSentMessage )
1156 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART | MERGE_REVSELECTMINUSONE), lowerRev);
1157 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND | MERGE_REVSELECTMINUSONE), higherRev);
1158 m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);
1159 if (m_selectedRevsOneRange.GetCount())
1160 m_pNotifyWindow->SendMessage(WM_REVLISTONERANGE, 0, (LPARAM)&m_selectedRevsOneRange);
1164 UpdateData();
1165 CRegDWORD reg = CRegDWORD(_T("Software\\TortoiseGit\\ShowAllEntry"));
1166 SaveSplitterPos();
1167 #endif
1170 void CLogDlg::DoDiffFromLog(INT_PTR selIndex, GitRev* rev1, GitRev* rev2, bool /*blame*/, bool /*unified*/)
1172 DialogEnableWindow(IDOK, FALSE);
1173 // SetPromptApp(&theApp);
1174 theApp.DoWaitCursor(1);
1176 CString temppath;
1177 GetTempPath(temppath);
1179 CString file1;
1180 file1.Format(_T("%s%s_%s%s"),
1181 temppath,
1182 (*m_currentChangedArray)[selIndex].GetBaseFilename(),
1183 rev1->m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()),
1184 (*m_currentChangedArray)[selIndex].GetFileExtension());
1186 CString file2;
1187 file2.Format(_T("%s\\%s_%s%s"),
1188 temppath,
1189 (*m_currentChangedArray)[selIndex].GetBaseFilename(),
1190 rev2->m_CommitHash.ToString().Left(g_Git.GetShortHASHLength()),
1191 (*m_currentChangedArray)[selIndex].GetFileExtension());
1193 CString cmd;
1194 CTGitPath &path = (CTGitPath &)(*m_currentChangedArray)[selIndex];
1196 g_Git.GetOneFile(rev1->m_CommitHash.ToString(), path, file1);
1197 g_Git.GetOneFile(rev2->m_CommitHash.ToString(), path, file2);
1199 CAppUtils::DiffFlags flags;
1200 CAppUtils::StartExtDiff(file1,file2,_T("A"),_T("B"),
1201 g_Git.m_CurrentDir + _T("\\") + path.GetWinPathString(), g_Git.m_CurrentDir + _T("\\") + path.GetWinPathString(),
1202 rev1->m_CommitHash.ToString(), rev2->m_CommitHash.ToString(),
1203 flags);
1205 theApp.DoWaitCursor(-1);
1206 EnableOKButton();
1209 BOOL CLogDlg::PreTranslateMessage(MSG* pMsg)
1211 // Skip Ctrl-C when copying text out of the log message or search filter
1212 bool bSkipAccelerator = (pMsg->message == WM_KEYDOWN && (pMsg->wParam == 'C' || pMsg->wParam == VK_INSERT) && (GetFocus() == GetDlgItem(IDC_MSGVIEW) || GetFocus() == GetDlgItem(IDC_SEARCHEDIT)) && GetKeyState(VK_CONTROL) & 0x8000);
1213 if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
1215 if (GetFocus()==GetDlgItem(IDC_LOGLIST))
1217 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1219 m_LogList.DiffSelectedRevWithPrevious();
1220 return TRUE;
1223 if (GetFocus() == GetDlgItem(IDC_SEARCHEDIT))
1225 KillTimer(LOGFILTER_TIMER);
1226 m_limit = 0;
1227 m_LogList.Refresh(FALSE);
1228 FillLogMessageCtrl(false);
1231 if (m_hAccel && !bSkipAccelerator)
1233 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
1234 if (ret)
1235 return TRUE;
1238 if(::IsWindow(m_tooltips.m_hWnd))
1239 m_tooltips.RelayEvent(pMsg);
1240 return __super::PreTranslateMessage(pMsg);
1244 BOOL CLogDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1246 //if (this->IsThreadRunning())
1247 if(m_LogList.m_bNoDispUpdates)
1249 // only show the wait cursor over the list control
1250 if ((pWnd)&&
1251 ((pWnd == GetDlgItem(IDC_LOGLIST))||
1252 (pWnd == GetDlgItem(IDC_MSGVIEW))||
1253 (pWnd == GetDlgItem(IDC_LOGMSG))))
1255 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
1256 SetCursor(hCur);
1257 return TRUE;
1260 if ((pWnd) && (pWnd == GetDlgItem(IDC_MSGVIEW)))
1261 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
1263 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
1264 SetCursor(hCur);
1265 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
1268 void CLogDlg::OnLvnItemchangedLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1270 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1271 *pResult = 0;
1272 //if (this->IsThreadRunning())
1273 if(m_LogList.m_bNoDispUpdates)
1274 return;
1275 if (pNMLV->iItem >= 0)
1277 this->m_LogList.m_nSearchIndex = pNMLV->iItem;
1278 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.SafeGetAt(pNMLV->iItem));
1279 m_LogList.m_lastSelectedHash = pLogEntry->m_CommitHash;
1280 if (pNMLV->iSubItem != 0)
1281 return;
1282 if ((pNMLV->iItem == m_LogList.m_arShownList.GetCount()))
1284 // remove the selected state
1285 if (pNMLV->uChanged & LVIF_STATE)
1287 m_LogList.SetItemState(pNMLV->iItem, 0, LVIS_SELECTED);
1288 FillLogMessageCtrl();
1289 UpdateData(FALSE);
1290 UpdateLogInfoLabel();
1292 return;
1294 if (pNMLV->uChanged & LVIF_STATE)
1296 FillLogMessageCtrl();
1297 UpdateData(FALSE);
1300 else
1302 m_LogList.m_lastSelectedHash.Empty();
1303 FillLogMessageCtrl();
1304 UpdateData(FALSE);
1306 EnableOKButton();
1307 UpdateLogInfoLabel();
1310 void CLogDlg::OnLvnItemchangedLogmsg(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
1312 UpdateLogInfoLabel();
1315 void CLogDlg::OnEnLinkMsgview(NMHDR *pNMHDR, LRESULT *pResult)
1317 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1318 if (pEnLink->msg == WM_LBUTTONUP)
1320 CString url, msg;
1321 GetDlgItemText(IDC_MSGVIEW, msg);
1322 msg.Replace(_T("\r\n"), _T("\n"));
1323 url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax-pEnLink->chrg.cpMin);
1324 int pos = 0;
1325 if (LookLikeGitHash(url, pos))
1327 bool found = false;
1328 for (int i = 0; i < m_LogList.m_arShownList.GetCount(); ++i)
1330 GitRev *rev = (GitRev *)m_LogList.m_arShownList.SafeGetAt(i);
1331 if (!rev) continue;
1332 if (rev->m_CommitHash.ToString().Left(url.GetLength()) == url)
1334 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1335 if (pos)
1337 int index = m_LogList.GetNextSelectedItem(pos);
1338 if (index > 0)
1339 m_LogList.SetItemState(index, 0, LVIS_SELECTED);
1341 m_LogList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1342 m_LogList.EnsureVisible(i, FALSE);
1343 m_LogList.SetSelectionMark(i);
1344 found = true;
1345 break;
1349 if (!found)
1350 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_PROC_LOG_JUMPNOTFOUND, IDS_APPNAME, 1, IDI_INFORMATION, IDS_OKBUTTON, 0, 0, _T("NoJumpNotFoundWarning"), IDS_MSGBOX_DONOTSHOWAGAIN);
1352 else
1354 if (!::PathIsURL(url))
1356 url = m_LogList.m_ProjectProperties.GetBugIDUrl(url);
1357 url = GetAbsoluteUrlFromRelativeUrl(url);
1359 if (!url.IsEmpty())
1360 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1363 *pResult = 0;
1366 void CLogDlg::OnBnClickedStatbutton()
1368 if (this->IsThreadRunning())
1369 return;
1370 if (m_LogList.m_arShownList.IsEmpty() || m_LogList.m_arShownList.GetCount() == 1 && m_LogList.m_bShowWC)
1371 return; // nothing or just the working copy changes are shown, so no statistics.
1372 // the statistics dialog expects the log entries to be sorted by date
1373 SortByColumn(3, false);
1375 CStatGraphDlg dlg;
1376 m_LogList.RecalculateShownList(&dlg.m_ShowList);
1378 dlg.m_path = m_orgPath;
1379 dlg.DoModal();
1380 // restore the previous sorting
1381 SortByColumn(m_nSortColumn, m_bAscending);
1382 OnTimer(LOGFILTER_TIMER);
1385 void CLogDlg::MoveToSameTop(CWnd *pWndRef, CWnd *pWndTarget)
1387 CRect rcWndPicAuthor, rcWndMsgView;
1388 pWndRef->GetWindowRect(rcWndMsgView);
1389 ScreenToClient(rcWndMsgView);
1390 pWndTarget->GetWindowRect(rcWndPicAuthor);
1391 ScreenToClient(rcWndPicAuthor);
1392 int diff = rcWndMsgView.top - rcWndPicAuthor.top;
1393 rcWndPicAuthor.top += diff;
1394 rcWndPicAuthor.bottom += diff;
1395 pWndTarget->MoveWindow(rcWndPicAuthor);
1398 void CLogDlg::DoSizeV1(int delta)
1401 RemoveAnchor(IDC_LOGLIST);
1402 RemoveAnchor(IDC_SPLITTERTOP);
1403 RemoveAnchor(IDC_MSGVIEW);
1404 RemoveAnchor(IDC_PIC_AUTHOR);
1405 RemoveAnchor(IDC_SPLITTERBOTTOM);
1406 RemoveAnchor(IDC_LOGMSG);
1407 CSplitterControl::ChangeHeight(&m_LogList, delta, CW_TOPALIGN);
1408 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), -delta, CW_BOTTOMALIGN);
1409 MoveToSameTop(GetDlgItem(IDC_MSGVIEW), GetDlgItem(IDC_PIC_AUTHOR));
1410 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
1411 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
1412 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
1413 AddAnchor(IDC_PIC_AUTHOR, TOP_RIGHT);
1414 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
1415 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
1416 ArrangeLayout();
1417 AdjustMinSize();
1418 SetSplitterRange();
1419 m_LogList.Invalidate();
1420 GetDlgItem(IDC_MSGVIEW)->Invalidate();
1421 m_gravatar.Invalidate();
1425 void CLogDlg::DoSizeV2(int delta)
1428 RemoveAnchor(IDC_LOGLIST);
1429 RemoveAnchor(IDC_SPLITTERTOP);
1430 RemoveAnchor(IDC_MSGVIEW);
1431 RemoveAnchor(IDC_PIC_AUTHOR);
1432 RemoveAnchor(IDC_SPLITTERBOTTOM);
1433 RemoveAnchor(IDC_LOGMSG);
1434 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), delta, CW_TOPALIGN);
1435 CSplitterControl::ChangeHeight(&m_ChangedFileListCtrl, -delta, CW_BOTTOMALIGN);
1436 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
1437 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
1438 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
1439 AddAnchor(IDC_PIC_AUTHOR, TOP_RIGHT);
1440 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
1441 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
1442 ArrangeLayout();
1443 AdjustMinSize();
1444 SetSplitterRange();
1445 GetDlgItem(IDC_MSGVIEW)->Invalidate();
1446 m_ChangedFileListCtrl.Invalidate();
1447 m_gravatar.Invalidate();
1451 void CLogDlg::AdjustMinSize()
1453 // adjust the minimum size of the dialog to prevent the resizing from
1454 // moving the list control too far down.
1455 CRect rcChgListView;
1456 m_ChangedFileListCtrl.GetClientRect(rcChgListView);
1457 CRect rcLogList;
1458 m_LogList.GetClientRect(rcLogList);
1460 SetMinTrackSize(CSize(m_DlgOrigRect.Width(),
1461 m_DlgOrigRect.Height()-m_ChgOrigRect.Height()-m_LogListOrigRect.Height()-m_MsgViewOrigRect.Height()
1462 +rcChgListView.Height()+rcLogList.Height()+60));
1465 LRESULT CLogDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
1467 switch (message) {
1468 case WM_NOTIFY:
1469 if (wParam == IDC_SPLITTERTOP)
1471 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
1472 DoSizeV1(pHdr->delta);
1474 else if (wParam == IDC_SPLITTERBOTTOM)
1476 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
1477 DoSizeV2(pHdr->delta);
1479 break;
1482 return CResizableDialog::DefWindowProc(message, wParam, lParam);
1485 void CLogDlg::SetSplitterRange()
1487 if ((m_LogList)&&(m_ChangedFileListCtrl))
1489 CRect rcTop;
1490 m_LogList.GetWindowRect(rcTop);
1491 ScreenToClient(rcTop);
1492 CRect rcMiddle;
1493 GetDlgItem(IDC_MSGVIEW)->GetWindowRect(rcMiddle);
1494 ScreenToClient(rcMiddle);
1495 m_wndSplitter1.SetRange(rcTop.top+30, rcMiddle.bottom-20);
1496 CRect rcBottom;
1497 m_ChangedFileListCtrl.GetWindowRect(rcBottom);
1498 ScreenToClient(rcBottom);
1499 m_wndSplitter2.SetRange(rcMiddle.top+30, rcBottom.bottom-20);
1503 LRESULT CLogDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1505 // FIXME: x64 version would get this function called with unexpected parameters.
1506 if (!lParam)
1507 return 0;
1509 RECT * rect = (LPRECT)lParam;
1510 CPoint point;
1511 CString temp;
1512 point = CPoint(rect->left, rect->bottom);
1513 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_LogList.m_SelectedFilters & x ? MF_CHECKED : MF_UNCHECKED))
1514 CMenu popup;
1515 if (popup.CreatePopupMenu())
1517 temp.LoadString(IDS_LOG_FILTER_SUBJECT);
1518 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_SUBJECT), LOGFILTER_SUBJECT, temp);
1520 temp.LoadString(IDS_LOG_FILTER_MESSAGES);
1521 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_MESSAGES), LOGFILTER_MESSAGES, temp);
1523 temp.LoadString(IDS_LOG_FILTER_PATHS);
1524 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_PATHS), LOGFILTER_PATHS, temp);
1526 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1527 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1529 temp.LoadString(IDS_LOG_FILTER_EMAILS);
1530 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_EMAILS), LOGFILTER_EMAILS, temp);
1532 temp.LoadString(IDS_LOG_FILTER_REVS);
1533 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1535 temp.LoadString(IDS_LOG_FILTER_REFNAME);
1536 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REFNAME), LOGFILTER_REFNAME, temp);
1538 if (m_LogList.m_bShowBugtraqColumn == TRUE) {
1539 temp.LoadString(IDS_LOG_FILTER_BUGIDS);
1540 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_BUGID), LOGFILTER_BUGID, temp);
1543 popup.AppendMenu(MF_SEPARATOR, NULL);
1545 temp.LoadString(IDS_LOG_FILTER_REGEX);
1546 popup.AppendMenu(MF_STRING | MF_ENABLED | (m_bFilterWithRegex ? MF_CHECKED : MF_UNCHECKED), LOGFILTER_REGEX, temp);
1548 m_tooltips.Pop();
1549 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1550 if (selection != 0)
1553 if (selection == LOGFILTER_REGEX)
1555 m_bFilterWithRegex = !m_bFilterWithRegex;
1556 CRegDWORD b = CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
1557 b = m_bFilterWithRegex;
1558 m_LogList.m_bFilterWithRegex = m_bFilterWithRegex;
1559 SetFilterCueText();
1560 CheckRegexpTooltip();
1562 else
1564 m_LogList.m_SelectedFilters ^= selection;
1565 SetFilterCueText();
1567 // Reload only if a search text is entered
1568 if (!m_LogList.m_sFilterText.IsEmpty())
1569 SetTimer(LOGFILTER_TIMER, 1000, NULL);
1572 return 0L;
1575 LRESULT CLogDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1578 KillTimer(LOGFILTER_TIMER);
1580 m_LogList.m_sFilterText.Empty();
1581 UpdateData(FALSE);
1582 theApp.DoWaitCursor(1);
1583 CStoreSelection storeselection(this);
1584 FillLogMessageCtrl(false);
1586 m_LogList.RemoveFilter();
1588 Refresh();
1590 CTime begin,end;
1591 m_LogList.GetTimeRange(begin,end);
1592 m_DateFrom.SetTime(&begin);
1593 m_DateTo.SetTime(&end);
1595 theApp.DoWaitCursor(-1);
1596 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
1597 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
1598 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
1599 UpdateLogInfoLabel();
1601 return 0L;
1605 void CLogDlg::SetFilterCueText()
1607 CString temp(MAKEINTRESOURCE(IDS_LOG_FILTER_BY));
1608 temp += _T(" ");
1610 if (m_LogList.m_SelectedFilters & LOGFILTER_SUBJECT)
1612 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_SUBJECT));
1615 if (m_LogList.m_SelectedFilters & LOGFILTER_MESSAGES)
1617 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1618 temp += _T(", ");
1619 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_MESSAGES));
1622 if (m_LogList.m_SelectedFilters & LOGFILTER_PATHS)
1624 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1625 temp += _T(", ");
1626 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_PATHS));
1629 if (m_LogList.m_SelectedFilters & LOGFILTER_AUTHORS)
1631 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1632 temp += _T(", ");
1633 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_AUTHORS));
1636 if (m_LogList.m_SelectedFilters & LOGFILTER_EMAILS)
1638 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1639 temp += _T(", ");
1640 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_EMAILS));
1643 if (m_LogList.m_SelectedFilters & LOGFILTER_REVS)
1645 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1646 temp += _T(", ");
1647 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REVS));
1650 if (m_LogList.m_SelectedFilters & LOGFILTER_REFNAME)
1652 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1653 temp += _T(", ");
1654 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_REFNAME));
1657 if (m_LogList.m_SelectedFilters & LOGFILTER_BUGID)
1659 if (temp.ReverseFind(_T(' ')) != temp.GetLength() - 1)
1660 temp += _T(", ");
1661 temp += CString(MAKEINTRESOURCE(IDS_LOG_FILTER_BUGIDS));
1664 // to make the cue banner text appear more to the right of the edit control
1665 temp = _T(" ")+temp;
1666 m_cFilter.SetCueBanner(temp.TrimRight());
1669 bool CLogDlg::Validate(LPCTSTR string)
1671 if (!m_bFilterWithRegex)
1672 return true;
1673 std::tr1::wregex pat;
1674 return m_LogList.ValidateRegexp(string, pat, false);
1678 void CLogDlg::OnTimer(UINT_PTR nIDEvent)
1680 if (nIDEvent == LOGFTIME_TIMER)
1682 KillTimer(LOGFTIME_TIMER);
1683 m_limit = 0;
1684 m_LogList.Refresh(FALSE);
1685 FillLogMessageCtrl(false);
1688 if (nIDEvent == LOGFILTER_TIMER)
1690 KillTimer(LOGFILTER_TIMER);
1691 m_limit = 0;
1692 m_LogList.Refresh(FALSE);
1693 FillLogMessageCtrl(false);
1695 #if 0
1696 /* we will use git built-in grep to filter log */
1697 if (this->IsThreadRunning())
1699 // thread still running! So just restart the timer.
1700 SetTimer(LOGFILTER_TIMER, 1000, NULL);
1701 return;
1703 CWnd * focusWnd = GetFocus();
1704 bool bSetFocusToFilterControl = ((focusWnd != GetDlgItem(IDC_DATEFROM))&&(focusWnd != GetDlgItem(IDC_DATETO))
1705 && (focusWnd != GetDlgItem(IDC_LOGLIST)));
1706 if (m_LogList.m_sFilterText.IsEmpty())
1708 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));
1709 // do not return here!
1710 // we also need to run the filter if the filter text is empty:
1711 // 1. to clear an existing filter
1712 // 2. to rebuild the m_arShownList after sorting
1714 theApp.DoWaitCursor(1);
1715 CStoreSelection storeselection(this);
1716 KillTimer(LOGFILTER_TIMER);
1717 FillLogMessageCtrl(false);
1719 // now start filter the log list
1720 m_LogList.StartFilter();
1722 if ( m_LogList.GetItemCount()==1 )
1724 m_LogList.SetSelectionMark(0);
1725 m_LogList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
1727 theApp.DoWaitCursor(-1);
1728 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
1729 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
1730 if (bSetFocusToFilterControl)
1731 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
1732 UpdateLogInfoLabel();
1733 #endif
1734 } // if (nIDEvent == LOGFILTER_TIMER)
1735 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty() || m_LogList.m_arShownList.GetCount() == 1 && m_LogList.m_bShowWC))));
1736 __super::OnTimer(nIDEvent);
1739 void CLogDlg::OnDtnDatetimechangeDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1743 CTime _time;
1744 m_DateTo.GetTime(_time);
1746 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 23, 59, 59);
1747 if (time.GetTime() != m_LogList.m_To)
1749 m_LogList.m_To = (DWORD)time.GetTime();
1750 SetTimer(LOGFTIME_TIMER, 10, NULL);
1753 catch (...)
1755 CMessageBox::Show(NULL,_T("Invalidate Parameter"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1758 *pResult = 0;
1761 void CLogDlg::OnDtnDatetimechangeDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1766 CTime _time;
1767 m_DateFrom.GetTime(_time);
1769 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 0, 0, 0);
1770 if (time.GetTime() != m_LogList.m_From)
1772 m_LogList.m_From = (DWORD)time.GetTime();
1773 SetTimer(LOGFTIME_TIMER, 10, NULL);
1776 catch (...)
1778 CMessageBox::Show(NULL,_T("Invalidate Parameter"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1781 *pResult = 0;
1784 void CLogDlg::OnCbnSelchangeJumpType()
1786 // reserved for future use
1789 void CLogDlg::OnBnClickedJumpUp()
1791 int sel = m_JumpType.GetCurSel();
1792 if (sel < 0) return;
1793 JumpType jumpType = (JumpType)sel;
1795 CString strValue;
1796 CGitHash hashValue;
1797 int index = -1;
1798 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1799 if (pos)
1801 index = m_LogList.GetNextSelectedItem(pos);
1802 if (index == 0) return;
1804 GitRev* data = (GitRev*)m_LogList.m_arShownList.SafeGetAt(index);
1805 if (jumpType == JumpType_AuthorEmail)
1806 strValue = data->GetAuthorEmail();
1807 else if (jumpType == JumpType_CommitterEmail)
1808 strValue = data->GetCommitterEmail();
1809 else if (jumpType == JumpType_Parent1)
1810 hashValue = data->m_CommitHash;
1811 else if (jumpType == JumpType_Parent2)
1812 hashValue = data->m_CommitHash;
1813 else if (jumpType == JumpType_TagFF)
1814 hashValue = data->m_CommitHash;
1815 else if (jumpType == JumpType_BranchFF)
1816 hashValue = data->m_CommitHash;
1818 m_LogList.SetItemState(index, 0, LVIS_SELECTED);
1820 else
1821 return;
1823 while (pos)
1825 index = m_LogList.GetNextSelectedItem(pos);
1826 m_LogList.SetItemState(index, 0, LVIS_SELECTED);
1828 m_LogList.SetSelectionMark(-1);
1830 for (int i = index - 1; i >= 0; i--)
1832 bool found = false;
1833 GitRev* data = (GitRev*)m_LogList.m_arShownList.SafeGetAt(i);
1834 if (jumpType == JumpType_AuthorEmail)
1835 found = strValue == data->GetAuthorEmail();
1836 else if (jumpType == JumpType_CommitterEmail)
1837 found = strValue == data->GetCommitterEmail();
1838 else if (jumpType == JumpType_MergePoint)
1839 found = data->ParentsCount() > 1;
1840 else if (jumpType == JumpType_Parent1)
1842 if (data->m_ParentHash.size() > 0)
1843 found = data->m_ParentHash[0] == hashValue;
1845 else if (jumpType == JumpType_Parent2)
1847 if (data->m_ParentHash.size() > 1)
1848 found = data->m_ParentHash[1] == hashValue;
1850 else if (jumpType == JumpType_Tag || jumpType == JumpType_TagFF)
1852 STRING_VECTOR refList = m_LogList.m_HashMap[data->m_CommitHash];
1853 for (size_t j = 0; j < refList.size(); ++j)
1855 if (refList[j].Left(10) == _T("refs/tags/"))
1857 found = true;
1858 break;
1862 if (found && jumpType == JumpType_TagFF)
1863 found = g_Git.IsFastForward(hashValue, data->m_CommitHash);
1865 else if (jumpType == JumpType_Branch || jumpType == JumpType_BranchFF)
1867 STRING_VECTOR refList = m_LogList.m_HashMap[data->m_CommitHash];
1868 for (size_t j = 0; j < refList.size(); ++j)
1870 if (refList[j].Left(11) == _T("refs/heads/") || refList[j].Left(13) == _T("refs/remotes/"))
1872 found = true;
1873 break;
1877 if (found && jumpType == JumpType_BranchFF)
1878 found = g_Git.IsFastForward(hashValue, data->m_CommitHash);
1881 if (found)
1883 m_LogList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1884 m_LogList.EnsureVisible(i, FALSE);
1885 m_LogList.SetSelectionMark(i);
1886 return;
1890 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_PROC_LOG_JUMPNOTFOUND, IDS_APPNAME, 1, IDI_INFORMATION, IDS_OKBUTTON, 0, 0, _T("NoJumpNotFoundWarning"), IDS_MSGBOX_DONOTSHOWAGAIN);
1893 void CLogDlg::OnBnClickedJumpDown()
1895 int jumpType = m_JumpType.GetCurSel();
1896 if (jumpType < 0) return;
1898 CString strValue;
1899 CGitHash hashValue;
1900 int index = -1;
1901 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1902 if (pos)
1904 index = m_LogList.GetNextSelectedItem(pos);
1905 if (index == 0) return;
1907 GitRev* data = (GitRev*)m_LogList.m_arShownList.SafeGetAt(index);
1908 if (jumpType == JumpType_AuthorEmail)
1909 strValue = data->GetAuthorEmail();
1910 else if (jumpType == JumpType_CommitterEmail)
1911 strValue = data->GetCommitterEmail();
1912 else if (jumpType == JumpType_Parent1)
1914 if (data->m_ParentHash.size() > 0)
1915 hashValue = data->m_ParentHash.at(0);
1916 else
1917 return;
1919 else if (jumpType == JumpType_Parent2)
1921 if (data->m_ParentHash.size() > 1)
1922 hashValue = data->m_ParentHash.at(1);
1923 else
1924 return;
1926 else if (jumpType == JumpType_TagFF)
1927 hashValue = data->m_CommitHash;
1928 else if (jumpType == JumpType_BranchFF)
1929 hashValue = data->m_CommitHash;
1931 m_LogList.SetItemState(index, 0, LVIS_SELECTED);
1933 else
1934 return;
1936 while (pos)
1938 index = m_LogList.GetNextSelectedItem(pos);
1939 m_LogList.SetItemState(index, 0, LVIS_SELECTED);
1941 m_LogList.SetSelectionMark(-1);
1943 for (int i = index + 1; i < m_LogList.GetItemCount(); ++i)
1945 bool found = false;
1946 GitRev* data = (GitRev*)m_LogList.m_arShownList.SafeGetAt(i);
1947 if (jumpType == JumpType_AuthorEmail)
1948 found = strValue == data->GetAuthorEmail();
1949 else if (jumpType == JumpType_CommitterEmail)
1950 found = strValue == data->GetCommitterEmail();
1951 else if (jumpType == JumpType_MergePoint)
1952 found = data->ParentsCount() > 1;
1953 else if (jumpType == JumpType_Parent1)
1954 found = data->m_CommitHash == hashValue;
1955 else if (jumpType == JumpType_Parent2)
1956 found = data->m_CommitHash == hashValue;
1957 else if (jumpType == JumpType_Tag || jumpType == JumpType_TagFF)
1959 STRING_VECTOR refList = m_LogList.m_HashMap[data->m_CommitHash];
1960 for (size_t j = 0; j < refList.size(); ++j)
1962 if (refList[j].Left(10) == _T("refs/tags/"))
1964 found = true;
1965 break;
1969 if (found && jumpType == JumpType_TagFF)
1970 found = g_Git.IsFastForward(data->m_CommitHash, hashValue);
1972 else if (jumpType == JumpType_Branch || jumpType == JumpType_BranchFF)
1974 STRING_VECTOR refList = m_LogList.m_HashMap[data->m_CommitHash];
1975 for (size_t j = 0; j < refList.size(); ++j)
1977 if (refList[j].Left(11) == _T("refs/heads/") || refList[j].Left(13) == _T("refs/remotes/"))
1979 found = true;
1980 break;
1984 if (found && jumpType == JumpType_BranchFF)
1985 found = g_Git.IsFastForward(data->m_CommitHash, hashValue);
1988 if (found)
1990 m_LogList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1991 m_LogList.EnsureVisible(i, FALSE);
1992 m_LogList.SetSelectionMark(i);
1993 return;
1997 CMessageBox::ShowCheck(GetSafeHwnd(), IDS_PROC_LOG_JUMPNOTFOUND, IDS_APPNAME, 1, IDI_INFORMATION, IDS_OKBUTTON, 0, 0, _T("NoJumpNotFoundWarning"), IDS_MSGBOX_DONOTSHOWAGAIN);
2000 CTGitPathList CLogDlg::GetChangedPathsFromSelectedRevisions(bool /*bRelativePaths*/ /* = false */, bool /*bUseFilter*/ /* = true */)
2002 CTGitPathList pathList;
2003 #if 0
2005 if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))
2007 m_sRepositoryRoot = GetRepositoryRoot(m_path);
2009 if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))
2010 return pathList;
2012 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2013 if (pos != NULL)
2015 while (pos)
2017 int nextpos = m_LogList.GetNextSelectedItem(pos);
2018 if (nextpos >= m_arShownList.GetCount())
2019 continue;
2020 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(nextpos));
2021 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
2022 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
2024 LogChangedPath * cpath = cpatharray->SafeGetAt(cpPathIndex);
2025 if (cpath == NULL)
2026 continue;
2027 CTGitPath path;
2028 if (!bRelativePaths)
2029 path.SetFromGit(m_sRepositoryRoot);
2030 path.AppendPathString(cpath->sPath);
2031 if ((!bUseFilter)||
2032 ((m_cHidePaths.GetState() & 0x0003)!=BST_CHECKED)||
2033 (cpath->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0))
2034 pathList.AddPath(path);
2039 pathList.RemoveDuplicates();
2040 #endif
2041 return pathList;
2044 void CLogDlg::SortByColumn(int /*nSortColumn*/, bool /*bAscending*/)
2046 #if 0
2047 switch(nSortColumn)
2049 case 0: // Revision
2051 if(bAscending)
2052 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscRevSort());
2053 else
2054 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescRevSort());
2056 break;
2057 case 1: // action
2059 if(bAscending)
2060 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscActionSort());
2061 else
2062 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescActionSort());
2064 break;
2065 case 2: // Author
2067 if(bAscending)
2068 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscAuthorSort());
2069 else
2070 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescAuthorSort());
2072 break;
2073 case 3: // Date
2075 if(bAscending)
2076 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscDateSort());
2077 else
2078 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescDateSort());
2080 break;
2081 case 4: // Message or bug id
2082 if (m_bShowBugtraqColumn)
2084 if(bAscending)
2085 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscBugIDSort());
2086 else
2087 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescBugIDSort());
2088 break;
2090 // fall through here
2091 case 5: // Message
2093 if(bAscending)
2094 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscMessageSort());
2095 else
2096 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescMessageSort());
2098 break;
2099 default:
2100 ATLASSERT(0);
2101 break;
2103 #endif
2106 void CLogDlg::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)
2108 if (this->IsThreadRunning())
2109 return; //no sorting while the arrays are filled
2110 #if 0
2111 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
2112 const int nColumn = pNMLV->iSubItem;
2113 m_bAscending = nColumn == m_nSortColumn ? !m_bAscending : TRUE;
2114 m_nSortColumn = nColumn;
2115 SortByColumn(m_nSortColumn, m_bAscending);
2116 SetSortArrow(&m_LogList, m_nSortColumn, !!m_bAscending);
2117 SortShownListArray();
2118 m_LogList.Invalidate();
2119 UpdateLogInfoLabel();
2120 #else
2121 UNREFERENCED_PARAMETER(pNMHDR);
2122 #endif
2123 *pResult = 0;
2125 CLogOrdering orderDlg;
2126 if (orderDlg.DoModal() == IDOK)
2127 Refresh();
2130 void CLogDlg::SortShownListArray()
2132 // make sure the shown list still matches the filter after sorting.
2133 OnTimer(LOGFILTER_TIMER);
2134 // clear the selection states
2135 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2136 while (pos)
2138 m_LogList.SetItemState(m_LogList.GetNextSelectedItem(pos), 0, LVIS_SELECTED);
2140 m_LogList.SetSelectionMark(-1);
2143 void CLogDlg::SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
2145 if (control == NULL)
2146 return;
2147 // set the sort arrow
2148 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
2149 HDITEM HeaderItem = {0};
2150 HeaderItem.mask = HDI_FORMAT;
2151 for (int i=0; i<pHeader->GetItemCount(); ++i)
2153 pHeader->GetItem(i, &HeaderItem);
2154 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
2155 pHeader->SetItem(i, &HeaderItem);
2157 if (nColumn >= 0)
2159 pHeader->GetItem(nColumn, &HeaderItem);
2160 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
2161 pHeader->SetItem(nColumn, &HeaderItem);
2165 int CLogDlg::m_nSortColumnPathList = 0;
2166 bool CLogDlg::m_bAscendingPathList = false;
2168 void CLogDlg::OnBnClickedHidepaths()
2170 FillLogMessageCtrl();
2171 m_ChangedFileListCtrl.Invalidate();
2174 void CLogDlg::UpdateLogInfoLabel()
2177 CGitHash rev1 ;
2178 CGitHash rev2 ;
2179 long selectedrevs = 0;
2180 long selectedfiles = 0;
2181 int count = (int)m_LogList.m_arShownList.GetCount();
2182 int start = 0;
2183 if (count)
2185 rev1 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.SafeGetAt(0)))->m_CommitHash;
2186 if(this->m_LogList.m_bShowWC && rev1.IsEmpty()&&(count>1))
2187 start = 1;
2188 rev1 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.SafeGetAt(start)))->m_CommitHash;
2189 //pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_arShownList.GetCount()-1));
2190 rev2 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.SafeGetAt(count-1)))->m_CommitHash;
2191 selectedrevs = m_LogList.GetSelectedCount();
2192 if (selectedrevs)
2193 selectedfiles = m_ChangedFileListCtrl.GetSelectedCount();
2195 CString sTemp;
2196 sTemp.Format(IDS_PROC_LOG_STATS,
2197 count - start,
2198 rev2.ToString().Left(g_Git.GetShortHASHLength()), rev1.ToString().Left(g_Git.GetShortHASHLength()), selectedrevs, selectedfiles);
2200 if(selectedrevs == 1)
2202 CString str=m_ChangedFileListCtrl.GetStatisticsString(true);
2203 str.Replace(_T('\n'), _T(' '));
2204 sTemp += _T("\r\n") + str;
2206 m_sLogInfo = sTemp;
2208 UpdateData(FALSE);
2211 void CLogDlg::OnDtnDropdownDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2213 // the date control should not show the "today" button
2214 CMonthCalCtrl * pCtrl = m_DateFrom.GetMonthCalCtrl();
2215 if (pCtrl)
2216 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));
2217 *pResult = 0;
2220 void CLogDlg::OnDtnDropdownDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2222 // the date control should not show the "today" button
2223 CMonthCalCtrl * pCtrl = m_DateTo.GetMonthCalCtrl();
2224 if (pCtrl)
2225 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));
2226 *pResult = 0;
2229 void CLogDlg::OnSize(UINT nType, int cx, int cy)
2231 __super::OnSize(nType, cx, cy);
2232 //set range
2233 SetSplitterRange();
2236 void CLogDlg::OnRefresh()
2239 m_limit = 0;
2240 this->m_LogProgress.SetPos(0);
2242 Refresh (false);
2246 void CLogDlg::OnFocusFilter()
2248 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
2251 void CLogDlg::OnEditCopy()
2253 if (GetFocus() == &m_ChangedFileListCtrl)
2254 CopyChangedSelectionToClipBoard();
2255 else
2256 m_LogList.CopySelectionToClipBoard();
2259 CString CLogDlg::GetAbsoluteUrlFromRelativeUrl(const CString& url)
2261 // is the URL a relative one?
2262 if (url.Left(2).Compare(_T("^/")) == 0)
2264 // URL is relative to the repository root
2265 CString url1 = m_sRepositoryRoot + url.Mid(1);
2266 TCHAR buf[INTERNET_MAX_URL_LENGTH];
2267 DWORD len = url.GetLength();
2268 if (UrlCanonicalize((LPCTSTR)url1, buf, &len, 0) == S_OK)
2269 return CString(buf, len);
2270 return url1;
2272 else if (url[0] == '/')
2274 // URL is relative to the server's hostname
2275 CString sHost;
2276 // find the server's hostname
2277 int schemepos = m_sRepositoryRoot.Find(_T("//"));
2278 if (schemepos >= 0)
2280 sHost = m_sRepositoryRoot.Left(m_sRepositoryRoot.Find('/', schemepos+3));
2281 CString url1 = sHost + url;
2282 TCHAR buf[INTERNET_MAX_URL_LENGTH];
2283 DWORD len = url.GetLength();
2284 if (UrlCanonicalize((LPCTSTR)url, buf, &len, 0) == S_OK)
2285 return CString(buf, len);
2286 return url1;
2289 return url;
2292 void CLogDlg::ShowGravatar()
2294 m_gravatar.EnableGravatar(m_bShowGravatar);
2295 if (m_gravatar.IsGravatarEnabled())
2297 RECT rect, rect2;
2298 GetDlgItem(IDC_MSGVIEW)->GetWindowRect(&rect);
2299 ScreenToClient(&rect);
2300 m_gravatar.GetWindowRect(&rect2);
2301 ScreenToClient(&rect2);
2302 rect.right = rect2.left;
2303 GetDlgItem(IDC_MSGVIEW)->MoveWindow(&rect);
2304 m_gravatar.ShowWindow(SW_SHOW);
2306 else
2308 RECT rect, rect2;
2309 GetDlgItem(IDC_MSGVIEW)->GetWindowRect(&rect);
2310 ScreenToClient(&rect);
2311 m_gravatar.GetWindowRect(&rect2);
2312 ScreenToClient(&rect2);
2313 rect.right = rect2.right;
2314 GetDlgItem(IDC_MSGVIEW)->MoveWindow(&rect);
2315 m_gravatar.ShowWindow(SW_HIDE);
2320 void CLogDlg::OnEnChangeSearchedit()
2322 UpdateData();
2323 if (m_LogList.m_sFilterText.IsEmpty())
2325 CStoreSelection storeselection(this);
2326 // clear the filter, i.e. make all entries appear
2327 theApp.DoWaitCursor(1);
2328 KillTimer(LOGFILTER_TIMER);
2329 FillLogMessageCtrl(false);
2331 Refresh();
2332 //m_LogList.StartFilter();
2333 #if 0
2334 InterlockedExchange(&m_bNoDispUpdates, TRUE);
2335 m_arShownList.RemoveAll();
2336 for (DWORD i=0; i<m_logEntries.size(); ++i)
2338 if (IsEntryInDateRange(i))
2339 m_arShownList.Add(m_logEntries[i]);
2341 InterlockedExchange(&m_bNoDispUpdates, FALSE);
2342 m_LogList.DeleteAllItems();
2343 m_LogList.SetItemCountEx(ShownCountWithStopped());
2344 m_LogList.RedrawItems(0, ShownCountWithStopped());
2345 m_LogList.SetRedraw(false);
2346 ResizeAllListCtrlCols();
2347 m_LogList.SetRedraw(true);
2348 #endif
2349 theApp.DoWaitCursor(-1);
2350 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
2351 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
2352 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
2353 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));
2354 return;
2356 if (Validate(m_LogList.m_sFilterText))
2357 SetTimer(LOGFILTER_TIMER, 1000, NULL);
2358 else
2359 KillTimer(LOGFILTER_TIMER);
2363 void CLogDlg::OnBnClickedAllBranch()
2365 this->UpdateData();
2367 if(this->m_bAllBranch)
2368 m_LogList.m_ShowMask|=CGit::LOG_INFO_ALL_BRANCH;
2369 else
2370 m_LogList.m_ShowMask&=~CGit::LOG_INFO_ALL_BRANCH;
2372 OnRefresh();
2374 FillLogMessageCtrl(false);
2377 void CLogDlg::OnBnClickedFollowRenames()
2379 if(m_bFollowRenames)
2381 m_LogList.m_ShowMask |= CGit::LOG_INFO_FOLLOW;
2382 if (m_bAllBranch)
2385 m_bAllBranch = FALSE;
2386 m_LogList.m_ShowMask &=~ CGit::LOG_INFO_ALL_BRANCH;
2390 else
2391 m_LogList.m_ShowMask &= ~CGit::LOG_INFO_FOLLOW;
2393 DialogEnableWindow(IDC_LOG_ALLBRANCH, !m_bFollowRenames);
2394 DialogEnableWindow(IDC_SHOWWHOLEPROJECT, !m_bFollowRenames);
2396 OnRefresh();
2398 FillLogMessageCtrl(false);
2401 void CLogDlg::HandleShowLabels(bool var, int flag)
2403 if (var)
2404 m_LogList.m_ShowRefMask |= flag;
2405 else
2406 m_LogList.m_ShowRefMask &= ~flag;
2408 if ((m_LogList.m_ShowFilter & CGitLogListBase::FILTERSHOW_REFS) && !(m_LogList.m_ShowFilter & CGitLogListBase::FILTERSHOW_ANYCOMMIT))
2410 // Remove commits where labels are not shown.
2411 OnRefresh();
2412 FillLogMessageCtrl(false);
2414 else
2416 // Just redraw
2417 m_LogList.Invalidate();
2421 void CLogDlg::OnBnClickedCompressedGraph()
2423 UpdateData();
2425 if (m_iCompressedGraph == 2)
2426 m_LogList.m_ShowFilter = CGitLogListBase::FILTERSHOW_REFS;
2427 else if (m_iCompressedGraph == 1)
2428 m_LogList.m_ShowFilter = static_cast<CGitLogListBase::FilterShow>(CGitLogListBase::FILTERSHOW_REFS | CGitLogListBase::FILTERSHOW_MERGEPOINTS);
2429 else
2430 m_LogList.m_ShowFilter = CGitLogListBase::FILTERSHOW_ALL;
2432 OnRefresh();
2433 FillLogMessageCtrl(false);
2436 void CLogDlg::OnBnClickedBrowseRef()
2438 CString newRef = CBrowseRefsDlg::PickRef(false, m_LogList.GetRange(), gPickRef_All, true);
2439 if(newRef.IsEmpty())
2440 return;
2442 m_History.AddEntry(newRef);
2443 m_History.Save();
2445 SetRange(newRef);
2446 ((CButton*)GetDlgItem(IDC_LOG_ALLBRANCH))->SetCheck(0);
2448 OnBnClickedAllBranch();
2451 void CLogDlg::ShowStartRef()
2453 //Show ref name on top
2454 if(!::IsWindow(m_hWnd))
2455 return;
2456 if(m_bAllBranch)
2458 m_staticRef.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_LOG_ALLBRANCHES)));
2459 m_staticRef.Invalidate(TRUE);
2460 m_tooltips.DelTool(GetDlgItem(IDC_STATIC_REF));
2461 return;
2464 CString showStartRef = m_LogList.GetRange();
2465 if (showStartRef.IsEmpty() || showStartRef == _T("HEAD"))
2467 showStartRef.Empty();
2468 //Ref name is HEAD
2469 if (g_Git.Run(L"git.exe symbolic-ref HEAD", &showStartRef, NULL, CP_UTF8))
2470 showStartRef = CString(MAKEINTRESOURCE(IDS_PROC_LOG_NOBRANCH));
2471 showStartRef.Trim(L"\r\n\t ");
2475 showStartRef = g_Git.StripRefName(showStartRef);
2477 m_staticRef.SetWindowText(showStartRef);
2478 CWnd *pWnd = GetDlgItem(IDC_STATIC_REF);
2479 m_tooltips.AddTool(pWnd, showStartRef);
2480 m_staticRef.Invalidate(TRUE);
2483 void CLogDlg::SetRange(const CString& range)
2485 m_LogList.SetRange(range);
2487 ShowStartRef();
2490 static void AppendMenuChecked(CMenu &menu, UINT nTextID, UINT_PTR nItemID, BOOL checked = FALSE, BOOL enabled = TRUE)
2492 CString text;
2493 text.LoadString(nTextID);
2494 menu.AppendMenu(MF_STRING | (enabled ? MF_ENABLED : MF_DISABLED) | (checked ? MF_CHECKED : MF_UNCHECKED), nItemID, text);
2497 #define WALKBEHAVIOUR_FIRSTPARENT 1
2498 #define WALKBEHAVIOUR_FOLLOWRENAMES 2
2499 #define WALKBEHAVIOUR_COMPRESSEDGRAPH 3
2500 #define WALKBEHAVIOUR_LABELEDCOMMITS 4
2501 #define WALKBEHAVIOUR_NOMERGES 5
2503 void CLogDlg::OnBnClickedWalkBehaviour()
2505 CMenu popup;
2506 if (popup.CreatePopupMenu())
2508 AppendMenuChecked(popup, IDS_WALKBEHAVIOUR_FIRSTPARENT, WALKBEHAVIOUR_FIRSTPARENT, m_bFirstParent);
2509 AppendMenuChecked(popup, IDS_WALKBEHAVIOUR_NOMERGES, WALKBEHAVIOUR_NOMERGES, m_bNoMerges);
2510 AppendMenuChecked(popup, IDS_WALKBEHAVIOUR_FOLLOWRENAMES, WALKBEHAVIOUR_FOLLOWRENAMES, m_bFollowRenames, !(m_path.IsEmpty() || m_path.IsDirectory()));
2511 popup.AppendMenu(MF_SEPARATOR, NULL);
2512 AppendMenuChecked(popup, IDS_WALKBEHAVIOUR_COMPRESSED, WALKBEHAVIOUR_COMPRESSEDGRAPH, m_iCompressedGraph == 1);
2513 AppendMenuChecked(popup, IDS_WALKBEHAVIOUR_LABELEDCOMMITS, WALKBEHAVIOUR_LABELEDCOMMITS, m_iCompressedGraph == 2);
2515 m_tooltips.Pop();
2516 RECT rect;
2517 GetDlgItem(IDC_WALKBEHAVIOUR)->GetWindowRect(&rect);
2518 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.top, this, 0);
2519 switch (selection)
2521 case WALKBEHAVIOUR_FIRSTPARENT:
2522 m_bFirstParent = !m_bFirstParent;
2523 OnBnClickedFirstParent();
2524 break;
2525 case WALKBEHAVIOUR_NOMERGES:
2526 m_bNoMerges = !m_bNoMerges;
2527 OnBnClickedFirstParent(); // OnBnClickedFirstParent handles both cases: m_bFirstParent and m_bNoMerges
2528 break;
2529 case WALKBEHAVIOUR_FOLLOWRENAMES:
2530 m_bFollowRenames = !m_bFollowRenames;
2531 OnBnClickedFollowRenames();
2532 break;
2533 case WALKBEHAVIOUR_COMPRESSEDGRAPH:
2534 m_iCompressedGraph = (m_iCompressedGraph == 1 ? 0 : 1);
2535 OnBnClickedCompressedGraph();
2536 break;
2537 case WALKBEHAVIOUR_LABELEDCOMMITS:
2538 m_iCompressedGraph = (m_iCompressedGraph == 2 ? 0 : 2);
2539 OnBnClickedCompressedGraph();
2540 break;
2541 default:
2542 break;
2544 m_bWalkBehavior = (m_bFirstParent || m_bNoMerges || m_bFollowRenames || m_iCompressedGraph);
2545 UpdateData(FALSE);
2549 #define VIEW_HIDEPATHS 1
2550 #define VIEW_GRAYPATHS 2
2551 #define VIEW_SHOWTAGS 3
2552 #define VIEW_SHOWLOCALBRANCHES 4
2553 #define VIEW_SHOWREMOTEBRANCHES 5
2554 #define VIEW_SHOWGRAVATAR 6
2556 void CLogDlg::OnBnClickedView()
2558 CMenu popup;
2559 if (popup.CreatePopupMenu())
2561 AppendMenuChecked(popup, IDS_SHOWFILES_HIDEPATHS, VIEW_HIDEPATHS, m_iHidePaths == 1);
2562 AppendMenuChecked(popup, IDS_SHOWFILES_GRAYPATHS, VIEW_GRAYPATHS, m_iHidePaths == 2);
2563 popup.AppendMenu(MF_SEPARATOR, NULL);
2564 CMenu showLabelsMenu;
2565 if (showLabelsMenu.CreatePopupMenu())
2567 AppendMenuChecked(showLabelsMenu, IDS_VIEW_SHOWTAGLABELS, VIEW_SHOWTAGS, m_bShowTags);
2568 AppendMenuChecked(showLabelsMenu, IDS_VIEW_SHOWLOCALBRANCHLABELS, VIEW_SHOWLOCALBRANCHES, m_bShowLocalBranches);
2569 AppendMenuChecked(showLabelsMenu, IDS_VIEW_SHOWREMOTEBRANCHLABELS, VIEW_SHOWREMOTEBRANCHES, m_bShowRemoteBranches);
2570 popup.AppendMenu(MF_STRING | MF_POPUP, (UINT)showLabelsMenu.m_hMenu, (CString)MAKEINTRESOURCE(IDS_VIEW_LABELS));
2572 popup.AppendMenu(MF_SEPARATOR, NULL);
2573 AppendMenuChecked(popup, IDS_VIEW_SHOWGRAVATAR, VIEW_SHOWGRAVATAR, m_bShowGravatar);
2575 m_tooltips.Pop();
2576 RECT rect;
2577 GetDlgItem(IDC_VIEW)->GetWindowRect(&rect);
2578 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, rect.left, rect.top, this, 0);
2579 switch (selection)
2581 case VIEW_HIDEPATHS:
2582 if (m_iHidePaths == 1)
2583 m_iHidePaths = 0;
2584 else
2585 m_iHidePaths = 1;
2586 OnBnClickedHidepaths();
2587 break;
2588 case VIEW_GRAYPATHS:
2589 if (m_iHidePaths == 2)
2590 m_iHidePaths = 0;
2591 else
2592 m_iHidePaths = 2;
2593 OnBnClickedHidepaths();
2594 break;
2595 case VIEW_SHOWTAGS:
2596 m_bShowTags = !m_bShowTags;
2597 HandleShowLabels(m_bShowTags, LOGLIST_SHOWTAGS);
2598 break;
2599 case VIEW_SHOWLOCALBRANCHES:
2600 m_bShowLocalBranches = !m_bShowLocalBranches;
2601 HandleShowLabels(m_bShowLocalBranches, LOGLIST_SHOWLOCALBRANCHES);
2602 break;
2603 case VIEW_SHOWREMOTEBRANCHES:
2604 m_bShowRemoteBranches = !m_bShowRemoteBranches;
2605 HandleShowLabels(m_bShowRemoteBranches, LOGLIST_SHOWREMOTEBRANCHES);
2606 break;
2607 case VIEW_SHOWGRAVATAR:
2609 m_bShowGravatar = !m_bShowGravatar;
2610 ShowGravatar();
2611 m_gravatar.Init();
2612 CString email;
2613 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2614 if (pos)
2616 int selIndex = m_LogList.GetNextSelectedItem(pos);
2617 int moreSel = m_LogList.GetNextSelectedItem(pos);
2618 if (moreSel < 0)
2620 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.SafeGetAt(selIndex));
2621 if (pLogEntry)
2622 email = pLogEntry->GetAuthorEmail();
2625 m_gravatar.LoadGravatar(email);
2626 break;
2628 default:
2629 break;
2634 void CLogDlg::OnBnClickedFirstParent()
2636 if(this->m_bFirstParent)
2637 m_LogList.m_ShowMask|=CGit::LOG_INFO_FIRST_PARENT;
2638 else
2639 m_LogList.m_ShowMask&=~CGit::LOG_INFO_FIRST_PARENT;
2641 if (m_bNoMerges)
2642 m_LogList.m_ShowMask |= CGit::LOG_INFO_NO_MERGE;
2643 else
2644 m_LogList.m_ShowMask &= ~CGit::LOG_INFO_NO_MERGE;
2646 OnRefresh();
2648 FillLogMessageCtrl(false);
2652 void CLogDlg::OnBnClickShowWholeProject()
2654 this->UpdateData();
2656 if(this->m_bWholeProject)
2658 m_LogList.m_Path.Reset();
2659 SetDlgTitle();
2661 else
2662 m_LogList.m_Path=m_path;
2664 SetDlgTitle();
2666 OnRefresh();
2668 FillLogMessageCtrl(false);
2672 LRESULT CLogDlg::OnRefLogChanged(WPARAM /*wParam*/, LPARAM /*lParam*/)
2674 ShowStartRef();
2675 return 0;
2678 LRESULT CLogDlg::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
2680 m_pTaskbarList.Release();
2681 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
2682 SetUUIDOverlayIcon(m_hWnd);
2683 return 0;