1 // TortoiseGit - a Windows shell extension for easy version control
2 // Copyright (C) 2003-2008 - TortoiseGit
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License for more details.
12 // You should have received a copy of the GNU General Public License
13 // along with this program; if not, write to the Free Software Foundation,
14 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 #include "TortoiseProc.h"
21 #include "SVNProgressDlg.h"
22 #include "ProgressDlg.h"
23 //#include "RepositoryBrowser.h"
24 //#include "CopyDlg.h"
25 #include "StatGraphDlg.h"
27 #include "MessageBox.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "UnicodeUtils.h"
34 //#include "GitInfo.h"
35 //#include "GitDiff.h"
37 //#include "RevisionRangeDlg.h"
38 //#include "BrowseFolder.h"
39 //#include "BlameDlg.h"
41 //#include "GitHelpers.h"
42 #include "GitStatus.h"
43 //#include "LogDlgHelper.h"
44 //#include "CachedLogInfo.h"
45 //#include "RepositoryInfo.h"
46 //#include "EditPropertiesDlg.h"
47 #include "FileDiffDlg.h"
48 #include "BrowseRefsDlg.h"
51 IMPLEMENT_DYNAMIC(CLogDlg
, CResizableStandAloneDialog
)
52 CLogDlg::CLogDlg(CWnd
* pParent
/*=NULL*/)
53 : CResizableStandAloneDialog(CLogDlg::IDD
, pParent
)
56 , m_currentChangedArray(NULL
)
61 , m_bSelectionMustBeContinuous(false)
67 , m_pNotifyWindow(NULL
)
74 , m_bIncludeMerges(FALSE
)
77 m_bFilterWithRegex
= !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE
);
80 str
=g_Git
.m_CurrentDir
;
81 str
.Replace(_T(":"),_T("_"));
82 str
=CString(_T("Software\\TortoiseGit\\LogDialog\\AllBranch\\"))+str
;
84 m_regbAllBranch
=CRegDWORD(str
,FALSE
);
86 m_bAllBranch
=m_regbAllBranch
;
89 m_bWholeProject
=FALSE
;
95 m_regbAllBranch
=m_bAllBranch
;
97 m_CurrentFilteredChangedArray
.RemoveAll();
101 void CLogDlg::DoDataExchange(CDataExchange
* pDX
)
103 CResizableStandAloneDialog::DoDataExchange(pDX
);
104 DDX_Control(pDX
, IDC_LOGLIST
, m_LogList
);
105 DDX_Control(pDX
, IDC_LOGMSG
, m_ChangedFileListCtrl
);
106 DDX_Control(pDX
, IDC_PROGRESS
, m_LogProgress
);
107 DDX_Control(pDX
, IDC_SPLITTERTOP
, m_wndSplitter1
);
108 DDX_Control(pDX
, IDC_SPLITTERBOTTOM
, m_wndSplitter2
);
109 DDX_Text(pDX
, IDC_SEARCHEDIT
, m_LogList
.m_sFilterText
);
110 DDX_Control(pDX
, IDC_DATEFROM
, m_DateFrom
);
111 DDX_Control(pDX
, IDC_DATETO
, m_DateTo
);
112 DDX_Control(pDX
, IDC_HIDEPATHS
, m_cHidePaths
);
113 DDX_Text(pDX
, IDC_LOGINFO
, m_sLogInfo
);
114 DDX_Check(pDX
, IDC_LOG_FIRSTPARENT
, m_bFirstParent
);
115 DDX_Check(pDX
, IDC_LOG_ALLBRANCH
,m_bAllBranch
);
116 DDX_Check(pDX
, IDC_SHOWWHOLEPROJECT
,m_bWholeProject
);
117 DDX_Control(pDX
, IDC_SEARCHEDIT
, m_cFilter
);
118 DDX_Control(pDX
, IDC_STATIC_REF
, m_staticRef
);
121 BEGIN_MESSAGE_MAP(CLogDlg
, CResizableStandAloneDialog
)
122 //ON_BN_CLICKED(IDC_GETALL, OnBnClickedGetall)
123 //ON_NOTIFY(NM_DBLCLK, IDC_LOGMSG, OnNMDblclkChangedFileList)
126 ON_BN_CLICKED(IDHELP
, OnBnClickedHelp
)
127 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_LOGLIST
, OnLvnItemchangedLoglist
)
128 ON_NOTIFY(EN_LINK
, IDC_MSGVIEW
, OnEnLinkMsgview
)
129 ON_BN_CLICKED(IDC_STATBUTTON
, OnBnClickedStatbutton
)
132 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED
, OnClickedInfoIcon
)
133 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED
, OnClickedCancelFilter
)
135 ON_MESSAGE(MSG_LOAD_PERCENTAGE
,OnLogListLoading
)
137 ON_EN_CHANGE(IDC_SEARCHEDIT
, OnEnChangeSearchedit
)
139 ON_NOTIFY(DTN_DATETIMECHANGE
, IDC_DATETO
, OnDtnDatetimechangeDateto
)
140 ON_NOTIFY(DTN_DATETIMECHANGE
, IDC_DATEFROM
, OnDtnDatetimechangeDatefrom
)
141 ON_BN_CLICKED(IDC_SHOWWHOLEPROJECT
, OnBnClickShowWholeProject
)
142 //ON_NOTIFY(NM_CUSTOMDRAW, IDC_LOGMSG, OnNMCustomdrawChangedFileList)
143 //ON_NOTIFY(LVN_GETDISPINFO, IDC_LOGMSG, OnLvnGetdispinfoChangedFileList)
144 ON_NOTIFY(LVN_COLUMNCLICK
,IDC_LOGLIST
, OnLvnColumnclick
)
145 //ON_NOTIFY(LVN_COLUMNCLICK, IDC_LOGMSG, OnLvnColumnclickChangedFileList)
146 ON_BN_CLICKED(IDC_HIDEPATHS
, OnBnClickedHidepaths
)
147 ON_COMMAND(MSG_FETCHED_DIFF
, OnBnClickedHidepaths
)
148 ON_BN_CLICKED(IDC_LOG_ALLBRANCH
, OnBnClickedAllBranch
)
150 ON_NOTIFY(DTN_DROPDOWN
, IDC_DATEFROM
, &CLogDlg::OnDtnDropdownDatefrom
)
151 ON_NOTIFY(DTN_DROPDOWN
, IDC_DATETO
, &CLogDlg::OnDtnDropdownDateto
)
153 ON_BN_CLICKED(IDC_LOG_FIRSTPARENT
, &CLogDlg::OnBnClickedFirstParent
)
154 ON_BN_CLICKED(IDC_REFRESH
, &CLogDlg::OnBnClickedRefresh
)
155 // ON_BN_CLICKED(IDC_BUTTON_BROWSE_REF, &CLogDlg::OnBnClickedBrowseRef)
156 ON_STN_CLICKED(IDC_STATIC_REF
, &CLogDlg::OnBnClickedBrowseRef
)
157 ON_COMMAND(ID_LOGDLG_REFRESH
, &CLogDlg::OnBnClickedRefresh
)
158 ON_COMMAND(ID_LOGDLG_FIND
, &CLogDlg::OnFind
)
159 ON_COMMAND(ID_LOGDLG_FOCUSFILTER
, &CLogDlg::OnFocusFilter
)
160 ON_COMMAND(ID_EDIT_COPY
, &CLogDlg::OnEditCopy
)
161 ON_MESSAGE(MSG_REFLOG_CHANGED
, OnRefLogChanged
)
164 void CLogDlg::SetParams(const CTGitPath
& orgPath
, const CTGitPath
& path
, CString pegrev
, CString startrev
, CString endrev
, int limit
/* = FALSE */)
169 this->m_LogList
.m_startrev
= startrev
;
170 m_LogRevision
= startrev
;
171 this->m_LogList
.m_endrev
= endrev
;
173 if(!endrev
.IsEmpty())
174 this->SetStartRef(endrev
);
176 m_hasWC
= !path
.IsUrl();
178 if (::IsWindow(m_hWnd
))
182 BOOL
CLogDlg::OnInitDialog()
185 CResizableStandAloneDialog::OnInitDialog();
187 m_hAccel
= LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_LOGDLG
));
190 // use the state of the "stop on copy/rename" option from the last time
193 // set the font to use in the log message view, configured in the settings dialog
194 CAppUtils::CreateFontForLogs(m_logFont
);
195 GetDlgItem(IDC_MSGVIEW
)->SetFont(&m_logFont
);
196 // automatically detect URLs in the log message and turn them into links
197 GetDlgItem(IDC_MSGVIEW
)->SendMessage(EM_AUTOURLDETECT
, TRUE
, NULL
);
198 // make the log message rich edit control send a message when the mouse pointer is over a link
199 GetDlgItem(IDC_MSGVIEW
)->SendMessage(EM_SETEVENTMASK
, NULL
, ENM_LINK
);
200 //m_LogList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);
202 // the "hide unrelated paths" checkbox should be indeterminate
203 m_cHidePaths
.SetCheck(BST_INDETERMINATE
);
206 //theme.SetWindowTheme(m_LogList.GetSafeHwnd(), L"Explorer", NULL);
207 //theme.SetWindowTheme(m_ChangedFileListCtrl.GetSafeHwnd(), L"Explorer", NULL);
209 // set up the columns
210 m_LogList
.DeleteAllItems();
212 m_LogList
.m_Path
=m_path
;
213 m_LogList
.m_bShowWC
= true;
214 m_LogList
.InsertGitColumn();
216 m_ChangedFileListCtrl
.Init(SVNSLC_COLEXT
| SVNSLC_COLSTATUS
|SVNSLC_COLADD
|SVNSLC_COLDEL
, _T("LogDlg"),(SVNSLC_POPALL
^ (SVNSLC_POPCOMMIT
)),false);
218 GetDlgItem(IDC_LOGLIST
)->UpdateData(FALSE
);
221 m_sMessageBuf
.Preallocate(100000);
223 // set the dialog title to "Log - path/to/whatever/we/show/the/log/for"
226 m_tooltips
.Create(this);
227 CheckRegexpTooltip();
231 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
232 m_cFilter
.SetCancelBitmaps(IDI_CANCELNORMAL
, IDI_CANCELPRESSED
);
233 m_cFilter
.SetInfoIcon(IDI_LOGFILTER
);
234 m_cFilter
.SetValidator(this);
236 AdjustControlSize(IDC_HIDEPATHS
);
237 AdjustControlSize(IDC_LOG_FIRSTPARENT
);
238 AdjustControlSize(IDC_LOG_ALLBRANCH
);
240 GetClientRect(m_DlgOrigRect
);
241 m_LogList
.GetClientRect(m_LogListOrigRect
);
242 GetDlgItem(IDC_MSGVIEW
)->GetClientRect(m_MsgViewOrigRect
);
243 m_ChangedFileListCtrl
.GetClientRect(m_ChgOrigRect
);
245 m_DateFrom
.SendMessage(DTM_SETMCSTYLE
, 0, MCS_WEEKNUMBERS
|MCS_NOTODAY
|MCS_NOTRAILINGDATES
|MCS_NOSELCHANGEONNAV
);
246 m_DateTo
.SendMessage(DTM_SETMCSTYLE
, 0, MCS_WEEKNUMBERS
|MCS_NOTODAY
|MCS_NOTRAILINGDATES
|MCS_NOSELCHANGEONNAV
);
248 m_staticRef
.SetURL(CString());
251 AddAnchor(IDC_STATIC_REF
, TOP_LEFT
);
252 //AddAnchor(IDC_BUTTON_BROWSE_REF, TOP_LEFT);
253 AddAnchor(IDC_FROMLABEL
, TOP_LEFT
);
254 AddAnchor(IDC_DATEFROM
, TOP_LEFT
);
255 AddAnchor(IDC_TOLABEL
, TOP_LEFT
);
256 AddAnchor(IDC_DATETO
, TOP_LEFT
);
259 AddAnchor(IDC_SEARCHEDIT
, TOP_LEFT
, TOP_RIGHT
);
261 AddAnchor(IDC_LOGLIST
, TOP_LEFT
, TOP_RIGHT
);
262 AddAnchor(IDC_SPLITTERTOP
, TOP_LEFT
, TOP_RIGHT
);
263 AddAnchor(IDC_MSGVIEW
, TOP_LEFT
, BOTTOM_RIGHT
);
264 AddAnchor(IDC_SPLITTERBOTTOM
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
265 AddAnchor(IDC_LOGMSG
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
267 AddAnchor(IDC_LOGINFO
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
268 AddAnchor(IDC_HIDEPATHS
, BOTTOM_LEFT
);
269 AddAnchor(IDC_LOG_ALLBRANCH
,BOTTOM_LEFT
);
270 AddAnchor(IDC_LOG_FIRSTPARENT
, BOTTOM_LEFT
);
271 //AddAnchor(IDC_GETALL, BOTTOM_LEFT);
272 AddAnchor(IDC_SHOWWHOLEPROJECT
, BOTTOM_LEFT
);
273 AddAnchor(IDC_REFRESH
, BOTTOM_LEFT
);
274 AddAnchor(IDC_STATBUTTON
, BOTTOM_RIGHT
);
275 AddAnchor(IDC_PROGRESS
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
276 AddAnchor(IDOK
, BOTTOM_RIGHT
);
277 AddAnchor(IDCANCEL
, BOTTOM_RIGHT
);
278 AddAnchor(IDHELP
, BOTTOM_RIGHT
);
280 if(this->m_bAllBranch
)
281 m_LogList
.m_ShowMask
|=CGit::LOG_INFO_ALL_BRANCH
;
283 m_LogList
.m_ShowMask
&=~CGit::LOG_INFO_ALL_BRANCH
;
285 // SetPromptParentWindow(m_hWnd);
288 CenterWindow(CWnd::FromHandle(hWndExplorer
));
289 EnableSaveRestore(_T("LogDlg"));
291 DWORD yPos1
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
292 DWORD yPos2
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
293 RECT rcDlg
, rcLogList
, rcChgMsg
;
294 GetClientRect(&rcDlg
);
295 m_LogList
.GetWindowRect(&rcLogList
);
296 ScreenToClient(&rcLogList
);
297 m_ChangedFileListCtrl
.GetWindowRect(&rcChgMsg
);
298 ScreenToClient(&rcChgMsg
);
302 m_wndSplitter1
.GetWindowRect(&rectSplitter
);
303 ScreenToClient(&rectSplitter
);
304 int delta
= yPos1
- rectSplitter
.top
;
306 if ((rcLogList
.bottom
+ delta
> rcLogList
.top
)&&(rcLogList
.bottom
+ delta
< rcChgMsg
.bottom
- 30))
308 m_wndSplitter1
.SetWindowPos(NULL
, 0, yPos1
, 0, 0, SWP_NOSIZE
);
315 m_wndSplitter2
.GetWindowRect(&rectSplitter
);
316 ScreenToClient(&rectSplitter
);
317 int delta
= yPos2
- rectSplitter
.top
;
319 if ((rcChgMsg
.top
+ delta
< rcChgMsg
.bottom
)&&(rcChgMsg
.top
+ delta
> rcLogList
.top
+ 30))
321 m_wndSplitter2
.SetWindowPos(NULL
, 0, yPos2
, 0, 0, SWP_NOSIZE
);
329 // the dialog is used to select revisions
330 // enable the OK button if appropriate
335 // the dialog is used to just view log messages
336 // hide the OK button and set text on Cancel button to OK
337 GetDlgItemText(IDOK
, temp
);
338 SetDlgItemText(IDCANCEL
, temp
);
339 GetDlgItem(IDOK
)->ShowWindow(SW_HIDE
);
342 m_mergedRevs
.clear();
344 // first start a thread to obtain the log messages without
345 // blocking the dialog
347 //m_tFrom = (DWORD)-1;
350 m_LogList
.FetchLogAsync(this);
352 GetDlgItem(IDC_LOGLIST
)->SetFocus();
358 LRESULT
CLogDlg::OnLogListLoading(WPARAM wParam
, LPARAM
/*lParam*/)
362 if( cur
== GITLOG_START
)
365 temp
.LoadString(IDS_PROGRESSWAIT
);
367 this->m_LogList
.ShowText(temp
, true);
369 // We use a progress bar while getting the logs
370 m_LogProgress
.SetRange32(0, 100);
371 m_LogProgress
.SetPos(0);
373 GetDlgItem(IDC_PROGRESS
)->ShowWindow(TRUE
);
375 //DialogEnableWindow(IDC_GETALL, FALSE);
376 //DialogEnableWindow(IDC_SHOWWHOLEPROJECT, FALSE);
377 //DialogEnableWindow(IDC_LOG_FIRSTPARENT, FALSE);
378 DialogEnableWindow(IDC_STATBUTTON
, FALSE
);
379 //DialogEnableWindow(IDC_REFRESH, FALSE);
380 DialogEnableWindow(IDC_HIDEPATHS
,FALSE
);
383 else if( cur
== GITLOG_END
)
385 if(this->m_LogList
.HasText())
387 this->m_LogList
.ClearText();
389 UpdateLogInfoLabel();
393 DialogEnableWindow(IDC_SHOWWHOLEPROJECT
, TRUE
);
395 //DialogEnableWindow(IDC_GETALL, TRUE);
396 DialogEnableWindow(IDC_STATBUTTON
, TRUE
);
397 DialogEnableWindow(IDC_REFRESH
, TRUE
);
398 DialogEnableWindow(IDC_HIDEPATHS
,TRUE
);
400 // PostMessage(WM_TIMER, LOGFILTER_TIMER);
401 GetDlgItem(IDC_PROGRESS
)->ShowWindow(FALSE
);
402 //CTime time=m_LogList.GetOldestTime();
404 m_LogList
.GetTimeRange(begin
,end
);
406 if(m_LogList
.m_From
== -1)
407 m_DateFrom
.SetTime(&begin
);
409 if(m_LogList
.m_To
== -1)
410 m_DateTo
.SetTime(&end
);
415 if(this->m_LogList
.HasText())
417 this->m_LogList
.ClearText();
418 this->m_LogList
.Invalidate();
420 UpdateLogInfoLabel();
421 m_LogProgress
.SetPos(cur
);
425 void CLogDlg::SetDlgTitle(bool bOffline
)
427 if (m_sTitle
.IsEmpty())
428 GetWindowText(m_sTitle
);
433 if (m_orgPath
.IsUrl())
434 sTemp
.Format(IDS_LOG_DLGTITLEOFFLINE
, (LPCTSTR
)m_sTitle
, (LPCTSTR
)m_orgPath
.GetUIPathString());
435 else if (m_orgPath
.IsDirectory())
436 sTemp
.Format(IDS_LOG_DLGTITLEOFFLINE
, (LPCTSTR
)m_sTitle
, (LPCTSTR
)m_orgPath
.GetWinPathString());
438 sTemp
.Format(IDS_LOG_DLGTITLEOFFLINE
, (LPCTSTR
)m_sTitle
, (LPCTSTR
)m_orgPath
.GetFilename());
439 SetWindowText(sTemp
);
443 if (m_orgPath
.IsUrl())
444 SetWindowText(m_sTitle
+ _T(" - ") + m_orgPath
.GetUIPathString());
445 else if (m_orgPath
.IsEmpty())
446 SetWindowText(m_sTitle
+ _T(" - ") + CString(_T("Whole Project")));
447 else if (m_orgPath
.IsDirectory())
448 SetWindowText(m_sTitle
+ _T(" - ") + m_orgPath
.GetWinPathString());
450 SetWindowText(m_sTitle
+ _T(" - ") + m_orgPath
.GetFilename());
454 void CLogDlg::CheckRegexpTooltip()
456 CWnd
*pWnd
= GetDlgItem(IDC_SEARCHEDIT
);
457 // Since tooltip describes regexp features, show it only if regexps are enabled.
458 if (m_bFilterWithRegex
)
460 m_tooltips
.AddTool(pWnd
, IDS_LOG_FILTER_REGEX_TT
);
463 m_tooltips
.DelTool(pWnd
);
466 void CLogDlg::EnableOKButton()
470 // the dialog is used to select revisions
471 if (m_bSelectionMustBeSingle
)
473 // enable OK button if only a single revision is selected
474 DialogEnableWindow(IDOK
, (m_LogList
.GetSelectedCount()==1));
476 else if (m_bSelectionMustBeContinuous
)
477 DialogEnableWindow(IDOK
, (m_LogList
.GetSelectedCount()!=0)&&(m_LogList
.IsSelectionContinuous()));
479 DialogEnableWindow(IDOK
, m_LogList
.GetSelectedCount()!=0);
482 DialogEnableWindow(IDOK
, TRUE
);
485 CString
CLogDlg::GetTagInfo(GitRev
* pLogEntry
)
490 if(m_LogList
.m_HashMap
.find(pLogEntry
->m_CommitHash
) != m_LogList
.m_HashMap
.end())
492 STRING_VECTOR
&vector
= m_LogList
.m_HashMap
[pLogEntry
->m_CommitHash
];
493 for(int i
=0;i
<vector
.size();i
++)
495 if(vector
[i
].Find(_T("refs/tags/")) == 0 )
497 CString tag
= vector
[i
];
498 int start
= vector
[i
].Find(_T("^{}"));
504 cmd
.Format(_T("git.exe cat-file tag %s"), tag
);
506 if(g_Git
.Run(cmd
, &output
, CP_UTF8
) == 0 )
512 if(!output
.IsEmpty())
514 output
= _T("\n*Tag Info*\n\n") + output
;
520 void CLogDlg::FillLogMessageCtrl(bool bShow
/* = true*/)
522 // we fill here the log message rich edit control,
523 // and also populate the changed files list control
524 // according to the selected revision(s).
526 CRichEditCtrl
* pMsgView
= (CRichEditCtrl
*)GetDlgItem(IDC_MSGVIEW
);
527 // empty the log message view
528 pMsgView
->SetWindowText(_T(" "));
529 // empty the changed files list
530 m_ChangedFileListCtrl
.SetRedraw(FALSE
);
531 // InterlockedExchange(&m_bNoDispUpdates, TRUE);
532 m_currentChangedArray
= NULL
;
533 //m_ChangedFileListCtrl.SetExtendedStyle ( LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER );
534 m_ChangedFileListCtrl
.DeleteAllItems();
536 // if we're not here to really show a selected revision, just
537 // get out of here after clearing the views, which is what is intended
538 // if that flag is not set.
542 m_ChangedFileListCtrl
.Invalidate();
543 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
544 m_ChangedFileListCtrl
.SetRedraw(TRUE
);
548 // depending on how many revisions are selected, we have to do different
550 int selCount
= m_LogList
.GetSelectedCount();
553 // if nothing is selected, we have nothing more to do
554 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
555 m_ChangedFileListCtrl
.SetRedraw(TRUE
);
558 else if (selCount
== 1)
560 // if one revision is selected, we have to fill the log message view
561 // with the corresponding log message, and also fill the changed files
563 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
564 int selIndex
= m_LogList
.GetNextSelectedItem(pos
);
565 if (selIndex
>= m_LogList
.m_arShownList
.GetCount())
567 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
568 m_ChangedFileListCtrl
.SetRedraw(TRUE
);
571 GitRev
* pLogEntry
= reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(selIndex
));
574 // set the log message text
575 pMsgView
->SetWindowText(_T("Commit:")+pLogEntry
->m_CommitHash
.ToString()+_T("\r\n\r\n"));
576 // turn bug ID's into links if the bugtraq: properties have been set
577 // and we can find a match of those in the log message
579 pMsgView
->SetSel(-1,-1);
581 SecureZeroMemory(&format
, sizeof(CHARFORMAT2
));
582 format
.cbSize
= sizeof(CHARFORMAT2
);
583 format
.dwMask
= CFM_BOLD
;
584 format
.dwEffects
= CFE_BOLD
;
585 pMsgView
->SendMessage(EM_SETCHARFORMAT
, SCF_SELECTION
, (LPARAM
)&format
);
587 CString msg
=_T("* ");
588 msg
+=pLogEntry
->GetSubject();
589 pMsgView
->ReplaceSel(msg
);
591 pMsgView
->SetSel(-1,-1);
592 format
.dwEffects
= 0;
593 pMsgView
->SendMessage(EM_SETCHARFORMAT
, SCF_SELECTION
, (LPARAM
)&format
);
596 msg
+=pLogEntry
->GetBody();
598 if(!pLogEntry
->m_Notes
.IsEmpty())
600 msg
+= _T("\n*Notes* ");
601 msg
+= pLogEntry
->m_Notes
;
605 msg
+=GetTagInfo(pLogEntry
);
607 pMsgView
->ReplaceSel(msg
);
610 pMsgView
->GetWindowText(text
);
611 // the rich edit control doesn't count the CR char!
612 // to be exact: CRLF is treated as one char.
613 text
.Replace(_T("\r"), _T(""));
615 m_LogList
.m_ProjectProperties
.FindBugID(text
, pMsgView
);
616 CAppUtils::FormatTextInRichEditControl(pMsgView
);
618 int HidePaths
=m_cHidePaths
.GetState() & 0x0003;
619 CString matchpath
=this->m_path
.GetGitPathString();
621 int count
= pLogEntry
->GetFiles(&m_LogList
).GetCount();
622 for(int i
=0;i
<count
&& (!matchpath
.IsEmpty());i
++)
624 if( m_bWholeProject
)
627 ((CTGitPath
&)pLogEntry
->GetFiles(&m_LogList
)[i
]).m_Action
&= ~(CTGitPath::LOGACTIONS_HIDE
|CTGitPath::LOGACTIONS_GRAY
);
629 if(pLogEntry
->GetFiles(&m_LogList
)[i
].GetGitPathString().Left(matchpath
.GetLength()) != matchpath
)
631 if(HidePaths
==BST_CHECKED
)
632 ((CTGitPath
&)pLogEntry
->GetFiles(&m_LogList
)[i
]).m_Action
|= CTGitPath::LOGACTIONS_HIDE
;
633 if(HidePaths
==BST_INDETERMINATE
)
634 ((CTGitPath
&)pLogEntry
->GetFiles(&m_LogList
)[i
]).m_Action
|= CTGitPath::LOGACTIONS_GRAY
;
638 m_ChangedFileListCtrl
.UpdateWithGitPathList(pLogEntry
->GetFiles(&m_LogList
));
639 m_ChangedFileListCtrl
.m_CurrentVersion
=pLogEntry
->m_CommitHash
;
640 m_ChangedFileListCtrl
.Show(SVNSLC_SHOWVERSIONED
);
642 m_ChangedFileListCtrl
.SetBusyString(_T("Fetch Changed File..."));
644 if(!pLogEntry
->m_IsDiffFiles
)
645 m_ChangedFileListCtrl
.SetBusy(TRUE
);
647 m_ChangedFileListCtrl
.SetBusy(FALSE
);
649 m_ChangedFileListCtrl
.SetRedraw(TRUE
);
656 // more than one revision is selected:
657 // the log message view must be emptied
658 // the changed files list contains all the changed paths from all
659 // selected revisions, with 'doubles' removed
660 m_currentChangedPathList
= GetChangedPathsFromSelectedRevisions(true);
664 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
666 if (m_currentChangedArray
)
668 m_ChangedFileListCtrl
.SetItemCountEx(m_currentChangedArray
->GetCount());
669 m_ChangedFileListCtrl
.RedrawItems(0, m_currentChangedArray
->GetCount());
671 else if (m_currentChangedPathList
.GetCount())
673 m_ChangedFileListCtrl
.SetItemCountEx(m_currentChangedPathList
.GetCount());
674 m_ChangedFileListCtrl
.RedrawItems(0, m_currentChangedPathList
.GetCount());
678 m_ChangedFileListCtrl
.SetItemCountEx(0);
679 m_ChangedFileListCtrl
.Invalidate();
682 // sort according to the settings
683 if (m_nSortColumnPathList
> 0)
684 SetSortArrow(&m_ChangedFileListCtrl
, m_nSortColumnPathList
, m_bAscendingPathList
);
686 SetSortArrow(&m_ChangedFileListCtrl
, -1, false);
687 m_ChangedFileListCtrl
.SetRedraw(TRUE
);
691 void CLogDlg::OnBnClickedRefresh()
696 void CLogDlg::Refresh (bool clearfilter
/*autoGoOnline*/)
699 m_LogList
.Refresh(clearfilter
);
700 FillLogMessageCtrl(false);
705 BOOL
CLogDlg::Cancel()
710 void CLogDlg::SaveSplitterPos()
714 CRegDWORD regPos1
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
715 CRegDWORD regPos2
= CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
717 m_wndSplitter1
.GetWindowRect(&rectSplitter
);
718 ScreenToClient(&rectSplitter
);
719 regPos1
= rectSplitter
.top
;
720 m_wndSplitter2
.GetWindowRect(&rectSplitter
);
721 ScreenToClient(&rectSplitter
);
722 regPos2
= rectSplitter
.top
;
726 void CLogDlg::OnCancel()
728 // canceling means stopping the working thread if it's still running.
729 m_LogList
.SafeTerminateAsyncDiffThread();
730 if (this->IsThreadRunning())
732 m_LogList
.SafeTerminateThread();
740 CString
CLogDlg::MakeShortMessage(const CString
& message
)
742 bool bFoundShort
= true;
743 CString sShortMessage
= m_LogList
.m_ProjectProperties
.GetLogSummary(message
);
744 if (sShortMessage
.IsEmpty())
747 sShortMessage
= message
;
749 // Remove newlines and tabs 'cause those are not shown nicely in the list control
750 sShortMessage
.Replace(_T("\r"), _T(""));
751 sShortMessage
.Replace(_T("\t"), _T(" "));
753 // Suppose the first empty line separates 'summary' from the rest of the message.
754 int found
= sShortMessage
.Find(_T("\n\n"));
755 // To avoid too short 'short' messages
756 // (e.g. if the message looks something like "Bugfix:\n\n*done this\n*done that")
757 // only use the empty newline as a separator if it comes after at least 15 chars.
758 if ((!bFoundShort
)&&(found
>= 15))
760 sShortMessage
= sShortMessage
.Left(found
);
762 sShortMessage
.Replace('\n', ' ');
763 return sShortMessage
;
766 BOOL
CLogDlg::Log(git_revnum_t
/*rev*/, const CString
& /*author*/, const CString
& /*date*/, const CString
& /*message*/, LogChangedPathArray
* /*cpaths*/, int /*filechanges*/, BOOL
/*copies*/, DWORD
/*actions*/, BOOL
/*haschildren*/)
769 if (rev
== SVN_INVALID_REVNUM
)
775 // this is the callback function which receives the data for every revision we ask the log for
776 // we store this information here one by one.
778 if (m_startrev
== -1)
783 m_LogProgress
.SetPos(m_limit
- m_limitcounter
);
785 else if (m_startrev
.IsNumber() && m_startrev
.IsNumber())
786 m_LogProgress
.SetPos((git_revnum_t
)m_startrev
-rev
+(git_revnum_t
)m_endrev
);
787 __time64_t ttime
= time
/1000000L;
788 if (m_tTo
< (DWORD
)ttime
)
789 m_tTo
= (DWORD
)ttime
;
790 if (m_tFrom
> (DWORD
)ttime
)
791 m_tFrom
= (DWORD
)ttime
;
792 if ((m_lowestRev
> rev
)||(m_lowestRev
< 0))
794 // Add as many characters from the log message to the list control
795 PLOGENTRYDATA pLogItem
= new LOGENTRYDATA
;
796 pLogItem
->bCopies
= !!copies
;
798 // find out if this item was copied in the revision
799 BOOL copiedself
= FALSE
;
802 for (INT_PTR cpPathIndex
= 0; cpPathIndex
< cpaths
->GetCount(); ++cpPathIndex
)
804 LogChangedPath
* cpath
= cpaths
->SafeGetAt(cpPathIndex
);
805 if (!cpath
->sCopyFromPath
.IsEmpty() && (cpath
->sPath
.Compare(m_sSelfRelativeURL
) == 0))
807 // note: this only works if the log is fetched top-to-bottom
808 // but since we do that, it shouldn't be a problem
809 m_sSelfRelativeURL
= cpath
->sCopyFromPath
;
815 pLogItem
->bCopiedSelf
= copiedself
;
816 pLogItem
->tmDate
= ttime
;
817 pLogItem
->sAuthor
= author
;
818 pLogItem
->sDate
= date
;
819 pLogItem
->sShortMessage
= MakeShortMessage(message
);
820 pLogItem
->dwFileChanges
= filechanges
;
821 pLogItem
->actions
= actions
;
822 pLogItem
->haschildren
= haschildren
;
823 pLogItem
->childStackDepth
= m_childCounter
;
824 m_maxChild
= max(m_childCounter
, m_maxChild
);
827 pLogItem
->sBugIDs
= m_ProjectProperties
.FindBugID(message
).Trim();
829 // split multi line log entries and concatenate them
830 // again but this time with \r\n as line separators
831 // so that the edit control recognizes them
834 if (message
.GetLength()>0)
836 m_sMessageBuf
= message
;
837 m_sMessageBuf
.Replace(_T("\n\r"), _T("\n"));
838 m_sMessageBuf
.Replace(_T("\r\n"), _T("\n"));
839 if (m_sMessageBuf
.Right(1).Compare(_T("\n"))==0)
840 m_sMessageBuf
= m_sMessageBuf
.Left(m_sMessageBuf
.GetLength()-1);
843 m_sMessageBuf
.Empty();
844 pLogItem
->sMessage
= m_sMessageBuf
;
847 // move-construct path array
849 pLogItem
->pArChangedPaths
= new LogChangedPathArray (*cpaths
);
852 catch (CException
* e
)
854 ::MessageBox(NULL
, _T("not enough memory!"), _T("TortoiseGit"), MB_ICONERROR
);
858 m_logEntries
.push_back(pLogItem
);
859 m_arShownList
.Add(pLogItem
);
865 //this is the thread function which calls the subversion function
870 void CLogDlg::CopyChangedSelectionToClipBoard()
873 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
875 return; // nothing is selected, get out of here
879 // CGitRev* pLogEntry = reinterpret_cast<CGitRev* >(m_LogList.m_arShownList.SafeGetAt(m_LogList.GetNextSelectedItem(pos)));
882 POSITION pos
= m_ChangedFileListCtrl
.GetFirstSelectedItemPosition();
885 int nItem
= m_ChangedFileListCtrl
.GetNextSelectedItem(pos
);
886 CTGitPath
*path
= (CTGitPath
*)m_ChangedFileListCtrl
.GetItemData(nItem
);
888 sPaths
+= path
->GetGitPathString();
889 sPaths
+= _T("\r\n");
895 // only one revision is selected in the log dialog top pane
896 // but multiple items could be selected in the changed items list
897 POSITION pos
= m_ChangedFileListCtrl
.GetFirstSelectedItemPosition();
900 int nItem
= m_ChangedFileListCtrl
.GetNextSelectedItem(pos
);
901 LogChangedPath
* changedlogpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(nItem
);
903 if ((m_cHidePaths
.GetState() & 0x0003)==BST_CHECKED
)
905 // some items are hidden! So find out which item the user really selected
906 INT_PTR selRealIndex
= -1;
907 for (INT_PTR hiddenindex
=0; hiddenindex
<pLogEntry
->pArChangedPaths
->GetCount(); ++hiddenindex
)
909 if (pLogEntry
->pArChangedPaths
->SafeGetAt(hiddenindex
)->sPath
.Left(m_sRelativeRoot
.GetLength()).Compare(m_sRelativeRoot
)==0)
911 if (selRealIndex
== nItem
)
913 changedlogpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(hiddenindex
);
920 sPaths
+= changedlogpath
->sPath
;
921 sPaths
+= _T("\r\n");
927 CStringUtils::WriteAsciiStringToClipboard(sPaths
, GetSafeHwnd());
931 BOOL
CLogDlg::IsDiffPossible(LogChangedPath
* /*changedpath*/, git_revnum_t rev
)
934 CString added
, deleted
;
935 if (changedpath
== NULL
)
938 if ((rev
> 1)&&(changedpath
->action
!= LOGACTIONS_DELETED
))
940 if (changedpath
->action
== LOGACTIONS_ADDED
) // file is added
942 if (changedpath
->lCopyFromRev
== 0)
943 return FALSE
; // but file was not added with history
951 void CLogDlg::OnContextMenu(CWnd
* pWnd
, CPoint point
)
953 // we have two separate context menus:
954 // one shown on the log message list control,
955 // the other shown in the changed-files list control
956 int selCount
= m_LogList
.GetSelectedCount();
957 if (pWnd
== &m_LogList
)
959 //ShowContextMenuForRevisions(pWnd, point);
961 else if (pWnd
== &m_ChangedFileListCtrl
)
963 //ShowContextMenuForChangedpaths(pWnd, point);
965 else if ((selCount
== 1)&&(pWnd
== GetDlgItem(IDC_MSGVIEW
)))
967 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
970 selIndex
= m_LogList
.GetNextSelectedItem(pos
);
972 GitRev
*pRev
= ((GitRev
*)m_LogList
.m_arShownList
[selIndex
]);
974 if ((point
.x
== -1) && (point
.y
== -1))
977 GetDlgItem(IDC_MSGVIEW
)->GetClientRect(&rect
);
978 ClientToScreen(&rect
);
979 point
= rect
.CenterPoint();
981 CString sMenuItemText
;
983 if (popup
.CreatePopupMenu())
985 // add the 'default' entries
986 sMenuItemText
.LoadString(IDS_SCIEDIT_COPY
);
987 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, WM_COPY
, sMenuItemText
);
988 sMenuItemText
.LoadString(IDS_SCIEDIT_SELECTALL
);
989 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, EM_SETSEL
, sMenuItemText
);
990 sMenuItemText
.LoadString(IDS_EDIT_NOTES
);
991 popup
.AppendMenuIcon( CGitLogList::ID_EDITNOTE
, sMenuItemText
, IDI_EDIT
);
995 // popup.AppendMenu(MF_SEPARATOR);
996 // sMenuItemText.LoadString(IDS_LOG_POPUP_EDITLOG);
997 // popup.AppendMenu(MF_STRING | MF_ENABLED, CGitLogList::ID_EDITAUTHOR, sMenuItemText);
1000 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
1004 break; // no command selected
1007 ::SendMessage(GetDlgItem(IDC_MSGVIEW
)->GetSafeHwnd(), cmd
, 0, -1);
1009 case CGitLogList::ID_EDITNOTE
:
1010 CAppUtils::EditNote(pRev
);
1011 this->FillLogMessageCtrl(true);
1018 void CLogDlg::OnOK()
1020 // since the log dialog is also used to select revisions for other
1021 // dialogs, we have to do some work before closing this dialog
1022 if (GetFocus() != GetDlgItem(IDOK
))
1023 return; // if the "OK" button doesn't have the focus, do nothing: this prevents closing the dialog when pressing enter
1025 m_LogList
.SafeTerminateAsyncDiffThread();
1026 if (this->IsThreadRunning())
1028 m_LogList
.SafeTerminateThread();
1031 // check that one and only one row is selected
1032 if (m_LogList
.GetSelectedCount() == 1)
1034 // get the selected row
1035 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
1036 int selIndex
= m_LogList
.GetNextSelectedItem(pos
);
1037 if (selIndex
< m_LogList
.m_arShownList
.GetCount())
1039 // all ok, pick up the revision
1040 GitRev
* pLogEntry
= reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(selIndex
));
1042 m_sSelectedHash
= pLogEntry
->m_CommitHash
;
1050 if (!GetDlgItem(IDOK
)->IsWindowVisible() && GetFocus() != GetDlgItem(IDCANCEL
))
1051 return; // the Cancel button works as the OK button. But if the cancel button has not the focus, do nothing.
1055 GetDlgItemText(IDOK
, buttontext
);
1056 temp
.LoadString(IDS_MSGBOX_CANCEL
);
1057 if (temp
.Compare(buttontext
) != 0)
1058 __super::OnOK(); // only exit if the button text matches, and that will match only if the thread isn't running anymore
1059 m_bCancelled
= TRUE
;
1060 m_selectedRevs
.Clear();
1061 m_selectedRevsOneRange
.Clear();
1062 if (m_pNotifyWindow
)
1064 int selIndex
= m_LogList
.GetSelectionMark();
1067 PLOGENTRYDATA pLogEntry
= NULL
;
1068 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
1069 pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetNextSelectedItem(pos
)));
1070 m_selectedRevs
.AddRevision(pLogEntry
->Rev
);
1071 git_revnum_t lowerRev
= pLogEntry
->Rev
;
1072 git_revnum_t higherRev
= lowerRev
;
1075 pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetNextSelectedItem(pos
)));
1076 git_revnum_t rev
= pLogEntry
->Rev
;
1077 m_selectedRevs
.AddRevision(pLogEntry
->Rev
);
1080 if (higherRev
< rev
)
1083 if (m_sFilterText
.IsEmpty() && m_nSortColumn
== 0 && IsSelectionContinuous())
1085 m_selectedRevsOneRange
.AddRevRange(lowerRev
, higherRev
);
1087 BOOL bSentMessage
= FALSE
;
1088 if (m_LogList
.GetSelectedCount() == 1)
1090 // if only one revision is selected, check if the path/url with which the dialog was started
1091 // was directly affected in that revision. If it was, then check if our path was copied from somewhere.
1092 // if it was copied, use the copy from revision as lowerRev
1093 if ((pLogEntry
)&&(pLogEntry
->pArChangedPaths
)&&(lowerRev
== higherRev
))
1095 CString sUrl
= m_path
.GetGitPathString();
1096 if (!m_path
.IsUrl())
1098 sUrl
= GetURLFromPath(m_path
);
1100 sUrl
= sUrl
.Mid(m_sRepositoryRoot
.GetLength());
1101 for (int cp
= 0; cp
< pLogEntry
->pArChangedPaths
->GetCount(); ++cp
)
1103 LogChangedPath
* pData
= pLogEntry
->pArChangedPaths
->SafeGetAt(cp
);
1106 if (sUrl
.Compare(pData
->sPath
) == 0)
1108 if (!pData
->sCopyFromPath
.IsEmpty())
1110 lowerRev
= pData
->lCopyFromRev
;
1111 m_pNotifyWindow
->SendMessage(WM_REVSELECTED
, m_wParam
& (MERGE_REVSELECTSTART
), lowerRev
);
1112 m_pNotifyWindow
->SendMessage(WM_REVSELECTED
, m_wParam
& (MERGE_REVSELECTEND
), higherRev
);
1113 m_pNotifyWindow
->SendMessage(WM_REVLIST
, m_selectedRevs
.GetCount(), (LPARAM
)&m_selectedRevs
);
1114 bSentMessage
= TRUE
;
1121 if ( !bSentMessage
)
1123 m_pNotifyWindow
->SendMessage(WM_REVSELECTED
, m_wParam
& (MERGE_REVSELECTSTART
| MERGE_REVSELECTMINUSONE
), lowerRev
);
1124 m_pNotifyWindow
->SendMessage(WM_REVSELECTED
, m_wParam
& (MERGE_REVSELECTEND
| MERGE_REVSELECTMINUSONE
), higherRev
);
1125 m_pNotifyWindow
->SendMessage(WM_REVLIST
, m_selectedRevs
.GetCount(), (LPARAM
)&m_selectedRevs
);
1126 if (m_selectedRevsOneRange
.GetCount())
1127 m_pNotifyWindow
->SendMessage(WM_REVLISTONERANGE
, 0, (LPARAM
)&m_selectedRevsOneRange
);
1132 CRegDWORD reg
= CRegDWORD(_T("Software\\TortoiseGit\\ShowAllEntry"));
1133 reg
= m_btnShow
.GetCurrentEntry();
1138 void CLogDlg::OnNMDblclkChangedFileList(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
1140 // a double click on an entry in the changed-files list has happened
1146 void CLogDlg::DiffSelectedFile()
1149 if (m_bThreadRunning
)
1151 UpdateLogInfoLabel();
1152 INT_PTR selIndex
= m_ChangedFileListCtrl
.GetSelectionMark();
1155 if (m_ChangedFileListCtrl
.GetSelectedCount() == 0)
1157 // find out if there's an entry selected in the log list
1158 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
1159 PLOGENTRYDATA pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetNextSelectedItem(pos
)));
1160 git_revnum_t rev1
= pLogEntry
->Rev
;
1161 git_revnum_t rev2
= rev1
;
1166 // there's at least a second entry selected in the log list: several revisions selected!
1167 pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetNextSelectedItem(pos
)));
1170 rev1
= max(rev1
,(long)pLogEntry
->Rev
);
1171 rev2
= min(rev2
,(long)pLogEntry
->Rev
);
1175 // now we have both revisions selected in the log list, so we can do a diff of the selected
1176 // entry in the changed files list with these two revisions.
1177 DoDiffFromLog(selIndex
, rev1
, rev2
, false, false);
1182 // nothing or only one revision selected in the log list
1183 LogChangedPath
* changedpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(selIndex
);
1185 if ((m_cHidePaths
.GetState() & 0x0003)==BST_CHECKED
)
1187 // some items are hidden! So find out which item the user really clicked on
1188 INT_PTR selRealIndex
= -1;
1189 for (INT_PTR hiddenindex
=0; hiddenindex
<pLogEntry
->pArChangedPaths
->GetCount(); ++hiddenindex
)
1191 if (pLogEntry
->pArChangedPaths
->SafeGetAt(hiddenindex
)->sPath
.Left(m_sRelativeRoot
.GetLength()).Compare(m_sRelativeRoot
)==0)
1193 if (selRealIndex
== selIndex
)
1195 selIndex
= hiddenindex
;
1196 changedpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(selIndex
);
1202 if (IsDiffPossible(changedpath
, rev1
))
1204 // diffs with renamed files are possible
1205 if ((changedpath
)&&(!changedpath
->sCopyFromPath
.IsEmpty()))
1206 rev2
= changedpath
->lCopyFromRev
;
1209 // if the path was modified but the parent path was 'added with history'
1210 // then we have to use the copy from revision of the parent path
1211 CTGitPath cpath
= CTGitPath(changedpath
->sPath
);
1212 for (int flist
= 0; flist
< pLogEntry
->pArChangedPaths
->GetCount(); ++flist
)
1214 CTGitPath p
= CTGitPath(pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->sPath
);
1215 if (p
.IsAncestorOf(cpath
))
1217 if (!pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->sCopyFromPath
.IsEmpty())
1218 rev2
= pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->lCopyFromRev
;
1222 DoDiffFromLog(selIndex
, rev1
, rev2
, false, false);
1226 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath
->sPath
));
1227 CTGitPath tempfile2
= CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath
->sPath
));
1229 // deleted files must be opened from the revision before the deletion
1230 if (changedpath
->action
== LOGACTIONS_DELETED
)
1232 m_bCancelled
= false;
1234 CProgressDlg progDlg
;
1235 progDlg
.SetTitle(IDS_APPNAME
);
1236 progDlg
.SetAnimation(IDR_DOWNLOAD
);
1238 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, (LPCTSTR
)(m_sRepositoryRoot
+ changedpath
->sPath
), (LPCTSTR
)r
.ToString());
1239 progDlg
.SetLine(1, sInfoLine
, true);
1240 SetAndClearProgressInfo(&progDlg
);
1241 progDlg
.ShowModeless(m_hWnd
);
1243 if (!Cat(CTGitPath(m_sRepositoryRoot
+ changedpath
->sPath
), r
, r
, tempfile
))
1245 m_bCancelled
= false;
1246 if (!Cat(CTGitPath(m_sRepositoryRoot
+ changedpath
->sPath
), GitRev::REV_HEAD
, r
, tempfile
))
1249 SetAndClearProgressInfo((HWND
)NULL
);
1250 CMessageBox::Show(m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1255 SetAndClearProgressInfo((HWND
)NULL
);
1257 CString sName1
, sName2
;
1258 sName1
.Format(_T("%s - Revision %ld"), (LPCTSTR
)CPathUtils::GetFileNameFromPath(changedpath
->sPath
), (git_revnum_t
)rev1
);
1259 sName2
.Format(_T("%s - Revision %ld"), (LPCTSTR
)CPathUtils::GetFileNameFromPath(changedpath
->sPath
), (git_revnum_t
)rev1
-1);
1260 CAppUtils::DiffFlags flags
;
1261 flags
.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
1262 if (changedpath
->action
== LOGACTIONS_DELETED
)
1263 CAppUtils::StartExtDiff(tempfile
, tempfile2
, sName2
, sName1
, flags
);
1265 CAppUtils::StartExtDiff(tempfile2
, tempfile
, sName2
, sName1
, flags
);
1272 void CLogDlg::DoDiffFromLog(INT_PTR selIndex
, GitRev
* rev1
, GitRev
* rev2
, bool /*blame*/, bool /*unified*/)
1274 DialogEnableWindow(IDOK
, FALSE
);
1275 // SetPromptApp(&theApp);
1276 theApp
.DoWaitCursor(1);
1279 GetTempPath(temppath
);
1282 file1
.Format(_T("%s%s_%s%s"),
1284 (*m_currentChangedArray
)[selIndex
].GetBaseFilename(),
1285 rev1
->m_CommitHash
.ToString().Left(6),
1286 (*m_currentChangedArray
)[selIndex
].GetFileExtension());
1289 file2
.Format(_T("%s\\%s_%s%s"),
1291 (*m_currentChangedArray
)[selIndex
].GetBaseFilename(),
1292 rev2
->m_CommitHash
.ToString().Left(6),
1293 (*m_currentChangedArray
)[selIndex
].GetFileExtension());
1297 g_Git
.GetOneFile(rev1
->m_CommitHash
.ToString(), (CTGitPath
&)(*m_currentChangedArray
)[selIndex
],file1
);
1299 g_Git
.GetOneFile(rev2
->m_CommitHash
.ToString(), (CTGitPath
&)(*m_currentChangedArray
)[selIndex
],file2
);
1301 CAppUtils::DiffFlags flags
;
1302 CAppUtils::StartExtDiff(file1
,file2
,_T("A"),_T("B"),flags
);
1307 if (Git::PathIsURL(m_path
))
1309 filepath
= m_path
.GetGitPathString();
1313 filepath
= GetURLFromPath(m_path
);
1314 if (filepath
.IsEmpty())
1316 theApp
.DoWaitCursor(-1);
1318 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
1319 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
1320 TRACE(_T("could not retrieve the URL of the file!\n"));
1322 theApp
.DoWaitCursor(-11);
1326 m_bCancelled
= FALSE
;
1327 filepath
= GetRepositoryRoot(CTGitPath(filepath
));
1329 CString firstfile
, secondfile
;
1330 if (m_LogList
.GetSelectedCount()==1)
1332 int s
= m_LogList
.GetSelectionMark();
1333 PLOGENTRYDATA pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(s
));
1334 LogChangedPath
* changedpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(selIndex
);
1335 firstfile
= changedpath
->sPath
;
1336 secondfile
= firstfile
;
1337 if ((rev2
== rev1
-1)&&(changedpath
->lCopyFromRev
> 0)) // is it an added file with history?
1339 secondfile
= changedpath
->sCopyFromPath
;
1340 rev2
= changedpath
->lCopyFromRev
;
1345 firstfile
= m_currentChangedPathList
[selIndex
].GetGitPathString();
1346 secondfile
= firstfile
;
1349 firstfile
= filepath
+ firstfile
.Trim();
1350 secondfile
= filepath
+ secondfile
.Trim();
1352 GitDiff
diff(this, this->m_hWnd
, true);
1353 diff
.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT
) & 0x8000));
1354 diff
.SetHEADPeg(m_LogRevision
);
1358 diff
.ShowUnifiedDiff(CTGitPath(secondfile
), rev2
, CTGitPath(firstfile
), rev1
);
1360 CAppUtils::StartShowUnifiedDiff(m_hWnd
, CTGitPath(secondfile
), rev2
, CTGitPath(firstfile
), rev1
, GitRev(), m_LogRevision
);
1364 if (diff
.ShowCompare(CTGitPath(secondfile
), rev2
, CTGitPath(firstfile
), rev1
, GitRev(), false, blame
))
1366 if (firstfile
.Compare(secondfile
)==0)
1368 git_revnum_t baseRev
= 0;
1369 diff
.DiffProps(CTGitPath(firstfile
), rev2
, rev1
, baseRev
);
1376 theApp
.DoWaitCursor(-1);
1380 BOOL
CLogDlg::Open(bool /*bOpenWith*/,CString changedpath
, git_revnum_t rev
)
1383 DialogEnableWindow(IDOK
, FALSE
);
1384 SetPromptApp(&theApp
);
1385 theApp
.DoWaitCursor(1);
1387 if (Git::PathIsURL(m_path
))
1389 filepath
= m_path
.GetGitPathString();
1393 filepath
= GetURLFromPath(m_path
);
1394 if (filepath
.IsEmpty())
1396 theApp
.DoWaitCursor(-1);
1398 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
1399 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
1400 TRACE(_T("could not retrieve the URL of the file!\n"));
1405 m_bCancelled
= false;
1406 filepath
= GetRepositoryRoot(CTGitPath(filepath
));
1407 filepath
+= changedpath
;
1409 CProgressDlg progDlg
;
1410 progDlg
.SetTitle(IDS_APPNAME
);
1411 progDlg
.SetAnimation(IDR_DOWNLOAD
);
1413 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, (LPCTSTR
)filepath
, (LPCTSTR
)GitRev(rev
).ToString());
1414 progDlg
.SetLine(1, sInfoLine
, true);
1415 SetAndClearProgressInfo(&progDlg
);
1416 progDlg
.ShowModeless(m_hWnd
);
1418 CTGitPath tempfile
= CTempFiles::Instance().GetTempFilePath(false, CTGitPath(filepath
), rev
);
1419 m_bCancelled
= false;
1420 if (!Cat(CTGitPath(filepath
), GitRev(rev
), rev
, tempfile
))
1423 SetAndClearProgressInfo((HWND
)NULL
);
1424 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1426 theApp
.DoWaitCursor(-1);
1430 SetAndClearProgressInfo((HWND
)NULL
);
1431 SetFileAttributes(tempfile
.GetWinPath(), FILE_ATTRIBUTE_READONLY
);
1434 int ret
= (int)ShellExecute(this->m_hWnd
, NULL
, tempfile
.GetWinPath(), NULL
, NULL
, SW_SHOWNORMAL
);
1435 if (ret
<= HINSTANCE_ERROR
)
1440 CString cmd
= _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1441 cmd
+= tempfile
.GetWinPathString() + _T(" ");
1442 CAppUtils::LaunchApplication(cmd
, NULL
, false);
1445 theApp
.DoWaitCursor(-1);
1450 void CLogDlg::EditAuthor(const CLogDataVector
& /*logs*/)
1455 if (logs
.size() == 0)
1457 DialogEnableWindow(IDOK
, FALSE
);
1458 SetPromptApp(&theApp
);
1459 theApp
.DoWaitCursor(1);
1460 if (Git::PathIsURL(m_path
))
1461 url
= m_path
.GetGitPathString();
1464 url
= GetURLFromPath(m_path
);
1466 name
= Git_PROP_REVISION_AUTHOR
;
1468 CString value
= RevPropertyGet(name
, CTGitPath(url
), logs
[0]->Rev
);
1469 CString sOldValue
= value
;
1470 value
.Replace(_T("\n"), _T("\r\n"));
1471 CInputDlg
dlg(this);
1472 dlg
.m_sHintText
.LoadString(IDS_LOG_AUTHOR
);
1473 dlg
.m_sInputText
= value
;
1474 dlg
.m_sTitle
.LoadString(IDS_LOG_AUTHOREDITTITLE
);
1475 dlg
.m_pProjectProperties
= &m_ProjectProperties
;
1476 dlg
.m_bUseLogWidth
= false;
1477 if (dlg
.DoModal() == IDOK
)
1479 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1481 LogCache::CCachedLogInfo
* toUpdate
= GetLogCache (CTGitPath (m_sRepositoryRoot
));
1483 CProgressDlg progDlg
;
1484 progDlg
.SetTitle(IDS_APPNAME
);
1485 progDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT
)));
1486 progDlg
.SetTime(true);
1487 progDlg
.SetShowProgressBar(true);
1488 progDlg
.ShowModeless(m_hWnd
);
1489 for (DWORD i
=0; i
<logs
.size(); ++i
)
1491 if (!RevPropertySet(name
, dlg
.m_sInputText
, sOldValue
, CTGitPath(url
), logs
[i
]->Rev
))
1494 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1500 logs
[i
]->sAuthor
= dlg
.m_sInputText
;
1501 m_LogList
.Invalidate();
1503 // update the log cache
1505 if (toUpdate
!= NULL
)
1507 // log caching is active
1509 LogCache::CCachedLogInfo newInfo
;
1510 newInfo
.Insert ( logs
[i
]->Rev
1511 , (const char*) CUnicodeUtils::GetUTF8 (logs
[i
]->sAuthor
)
1514 , LogCache::CRevisionInfoContainer::HAS_AUTHOR
);
1516 toUpdate
->Update (newInfo
);
1519 progDlg
.SetProgress64(i
, logs
.size());
1523 theApp
.DoWaitCursor(-1);
1528 void CLogDlg::EditLogMessage(int /*index*/)
1534 DialogEnableWindow(IDOK
, FALSE
);
1535 SetPromptApp(&theApp
);
1536 theApp
.DoWaitCursor(1);
1537 if (Git::PathIsURL(m_path
))
1538 url
= m_path
.GetGitPathString();
1541 url
= GetURLFromPath(m_path
);
1543 name
= Git_PROP_REVISION_LOG
;
1545 PLOGENTRYDATA pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(index
));
1546 m_bCancelled
= FALSE
;
1547 CString value
= RevPropertyGet(name
, CTGitPath(url
), pLogEntry
->Rev
);
1548 CString sOldValue
= value
;
1549 value
.Replace(_T("\n"), _T("\r\n"));
1550 CInputDlg
dlg(this);
1551 dlg
.m_sHintText
.LoadString(IDS_LOG_MESSAGE
);
1552 dlg
.m_sInputText
= value
;
1553 dlg
.m_sTitle
.LoadString(IDS_LOG_MESSAGEEDITTITLE
);
1554 dlg
.m_pProjectProperties
= &m_ProjectProperties
;
1555 dlg
.m_bUseLogWidth
= true;
1556 if (dlg
.DoModal() == IDOK
)
1558 dlg
.m_sInputText
.Replace(_T("\r"), _T(""));
1559 if (!RevPropertySet(name
, dlg
.m_sInputText
, sOldValue
, CTGitPath(url
), pLogEntry
->Rev
))
1561 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
1565 pLogEntry
->sShortMessage
= MakeShortMessage(dlg
.m_sInputText
);
1566 // split multi line log entries and concatenate them
1567 // again but this time with \r\n as line separators
1568 // so that the edit control recognizes them
1569 if (dlg
.m_sInputText
.GetLength()>0)
1571 m_sMessageBuf
= dlg
.m_sInputText
;
1572 dlg
.m_sInputText
.Replace(_T("\n\r"), _T("\n"));
1573 dlg
.m_sInputText
.Replace(_T("\r\n"), _T("\n"));
1574 if (dlg
.m_sInputText
.Right(1).Compare(_T("\n"))==0)
1575 dlg
.m_sInputText
= dlg
.m_sInputText
.Left(dlg
.m_sInputText
.GetLength()-1);
1578 dlg
.m_sInputText
.Empty();
1579 pLogEntry
->sMessage
= dlg
.m_sInputText
;
1580 pLogEntry
->sBugIDs
= m_ProjectProperties
.FindBugID(dlg
.m_sInputText
);
1581 CWnd
* pMsgView
= GetDlgItem(IDC_MSGVIEW
);
1582 pMsgView
->SetWindowText(_T(" "));
1583 pMsgView
->SetWindowText(dlg
.m_sInputText
);
1584 m_ProjectProperties
.FindBugID(dlg
.m_sInputText
, pMsgView
);
1585 m_LogList
.Invalidate();
1587 // update the log cache
1588 LogCache::CCachedLogInfo
* toUpdate
= GetLogCache(CTGitPath (m_sRepositoryRoot
));
1589 if (toUpdate
!= NULL
)
1591 // log caching is active
1593 LogCache::CCachedLogInfo newInfo
;
1594 newInfo
.Insert( pLogEntry
->Rev
1596 , (const char*) CUnicodeUtils::GetUTF8 (pLogEntry
->sMessage
)
1598 , LogCache::CRevisionInfoContainer::HAS_COMMENT
);
1600 toUpdate
->Update(newInfo
);
1604 theApp
.DoWaitCursor(-1);
1609 BOOL
CLogDlg::PreTranslateMessage(MSG
* pMsg
)
1611 // Skip Ctrl-C when copying text out of the log message or search filter
1612 BOOL bSkipAccelerator
= ( pMsg
->message
== WM_KEYDOWN
&& pMsg
->wParam
=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW
) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT
) ) && GetKeyState(VK_CONTROL
)&0x8000 );
1613 if (pMsg
->message
== WM_KEYDOWN
&& pMsg
->wParam
=='\r')
1615 if (GetFocus()==GetDlgItem(IDC_LOGLIST
))
1617 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE
))
1619 m_LogList
.DiffSelectedRevWithPrevious();
1624 if (m_hAccel
&& !bSkipAccelerator
)
1626 int ret
= TranslateAccelerator(m_hWnd
, m_hAccel
, pMsg
);
1631 if(::IsWindow(m_tooltips
.m_hWnd
))
1632 m_tooltips
.RelayEvent(pMsg
);
1633 return __super::PreTranslateMessage(pMsg
);
1637 BOOL
CLogDlg::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
1639 //if (this->IsThreadRunning())
1640 if(m_LogList
.m_bNoDispUpdates
)
1642 // only show the wait cursor over the list control
1644 ((pWnd
== GetDlgItem(IDC_LOGLIST
))||
1645 (pWnd
== GetDlgItem(IDC_MSGVIEW
))||
1646 (pWnd
== GetDlgItem(IDC_LOGMSG
))))
1648 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_WAIT
));
1653 if ((pWnd
) && (pWnd
== GetDlgItem(IDC_MSGVIEW
)))
1654 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
1656 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
));
1658 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
1661 void CLogDlg::OnBnClickedHelp()
1666 void CLogDlg::OnLvnItemchangedLoglist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1668 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
1670 //if (this->IsThreadRunning())
1671 if(m_LogList
.m_bNoDispUpdates
)
1673 if (pNMLV
->iItem
>= 0)
1675 this->m_LogList
.m_nSearchIndex
= pNMLV
->iItem
;
1676 if (pNMLV
->iSubItem
!= 0)
1678 if ((pNMLV
->iItem
== m_LogList
.m_arShownList
.GetCount()))
1680 // remove the selected state
1681 if (pNMLV
->uChanged
& LVIF_STATE
)
1683 m_LogList
.SetItemState(pNMLV
->iItem
, 0, LVIS_SELECTED
);
1684 FillLogMessageCtrl();
1686 UpdateLogInfoLabel();
1690 if (pNMLV
->uChanged
& LVIF_STATE
)
1692 FillLogMessageCtrl();
1698 FillLogMessageCtrl();
1702 UpdateLogInfoLabel();
1705 void CLogDlg::OnEnLinkMsgview(NMHDR
*pNMHDR
, LRESULT
*pResult
)
1707 ENLINK
*pEnLink
= reinterpret_cast<ENLINK
*>(pNMHDR
);
1708 if (pEnLink
->msg
== WM_LBUTTONUP
)
1711 GetDlgItemText(IDC_MSGVIEW
, msg
);
1712 msg
.Replace(_T("\r\n"), _T("\n"));
1713 url
= msg
.Mid(pEnLink
->chrg
.cpMin
, pEnLink
->chrg
.cpMax
-pEnLink
->chrg
.cpMin
);
1714 if (!::PathIsURL(url
))
1716 url
= m_LogList
.m_ProjectProperties
.GetBugIDUrl(url
);
1717 url
= GetAbsoluteUrlFromRelativeUrl(url
);
1720 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
1728 class CCommitPointer
1731 CCommitPointer():m_cont(NULL
){}
1732 CCommitPointer(const CCommitPointer
& P_Right
)
1738 CCommitPointer
& operator = (const CCommitPointer
& P_Right
)
1742 (*m_cont
->m_parDates
)[m_place
] = P_Right
.GetDate();
1743 (*m_cont
->m_parFileChanges
)[m_place
] = P_Right
.GetChanges();
1744 (*m_cont
->m_parAuthors
)[m_place
] = P_Right
.GetAuthor();
1748 m_Date
= P_Right
.GetDate();
1749 m_Changes
= P_Right
.GetChanges();
1750 m_csAuthor
= P_Right
.GetAuthor();
1755 void Clone(const CCommitPointer
& P_Right
)
1757 m_cont
= P_Right
.m_cont
;
1758 m_place
= P_Right
.m_place
;
1759 m_Date
= P_Right
.m_Date
;
1760 m_Changes
= P_Right
.m_Changes
;
1761 m_csAuthor
= P_Right
.m_csAuthor
;
1764 DWORD
GetDate() const {return IsPointer() ? (*m_cont
->m_parDates
)[m_place
] : m_Date
;}
1765 DWORD
GetChanges() const {return IsPointer() ? (*m_cont
->m_parFileChanges
)[m_place
] : m_Changes
;}
1766 CString
GetAuthor() const {return IsPointer() ? (*m_cont
->m_parAuthors
)[m_place
] : m_csAuthor
;}
1768 bool IsPointer() const {return m_cont
!= NULL
;}
1770 CDateSorter
* m_cont
;
1779 class iterator
: public std::iterator
<std::random_access_iterator_tag
, CCommitPointer
>
1782 CCommitPointer m_ptr
;
1785 iterator(const iterator
& P_Right
){*this = P_Right
;}
1786 iterator
& operator=(const iterator
& P_Right
)
1788 m_ptr
.Clone(P_Right
.m_ptr
);
1792 CCommitPointer
& operator*(){return m_ptr
;}
1793 CCommitPointer
* operator->(){return &m_ptr
;}
1794 const CCommitPointer
& operator*()const{return m_ptr
;}
1795 const CCommitPointer
* operator->()const{return &m_ptr
;}
1797 iterator
& operator+=(size_t P_iOffset
){m_ptr
.m_place
+= P_iOffset
;return *this;}
1798 iterator
& operator-=(size_t P_iOffset
){m_ptr
.m_place
-= P_iOffset
;return *this;}
1799 iterator
operator+(size_t P_iOffset
)const{iterator
it(*this); it
+= P_iOffset
;return it
;}
1800 iterator
operator-(size_t P_iOffset
)const{iterator
it(*this); it
-= P_iOffset
;return it
;}
1802 iterator
& operator++(){++m_ptr
.m_place
;return *this;}
1803 iterator
& operator--(){--m_ptr
.m_place
;return *this;}
1804 iterator
operator++(int){iterator
it(*this);++*this;return it
;}
1805 iterator
operator--(int){iterator
it(*this);--*this;return it
;}
1807 size_t operator-(const iterator
& P_itRight
)const{return m_ptr
.m_place
- P_itRight
->m_place
;}
1809 bool operator<(const iterator
& P_itRight
)const{return m_ptr
.m_place
< P_itRight
->m_place
;}
1810 bool operator!=(const iterator
& P_itRight
)const{return m_ptr
.m_place
!= P_itRight
->m_place
;}
1811 bool operator==(const iterator
& P_itRight
)const{return m_ptr
.m_place
== P_itRight
->m_place
;}
1812 bool operator>(const iterator
& P_itRight
)const{return m_ptr
.m_place
> P_itRight
->m_place
;}
1824 it
->m_place
= m_parDates
->GetCount();
1829 CDWordArray
* m_parDates
;
1830 CDWordArray
* m_parFileChanges
;
1831 CStringArray
* m_parAuthors
;
1834 class CDateSorterLess
1837 bool operator () (const CDateSorter::CCommitPointer
& P_Left
, const CDateSorter::CCommitPointer
& P_Right
) const
1839 return P_Left
.GetDate() > P_Right
.GetDate(); //Last date first
1846 void CLogDlg::OnBnClickedStatbutton()
1848 if (this->IsThreadRunning())
1850 if (m_LogList
.m_arShownList
.IsEmpty())
1851 return; // nothing is shown, so no statistics.
1852 // the statistics dialog expects the log entries to be sorted by date
1853 SortByColumn(3, false);
1854 CThreadSafePtrArray
shownlist(NULL
);
1855 m_LogList
.RecalculateShownList(&shownlist
);
1856 // create arrays which are aware of the current filter
1857 CStringArray m_arAuthorsFiltered
;
1858 CDWordArray m_arDatesFiltered
;
1859 CDWordArray m_arFileChangesFiltered
;
1860 for (INT_PTR i
=0; i
<shownlist
.GetCount(); ++i
)
1862 GitRev
* pLogEntry
= reinterpret_cast<GitRev
*>(shownlist
.SafeGetAt(i
));
1864 // do not take working dir changes into statistics
1865 if (pLogEntry
->m_CommitHash
.IsEmpty()) {
1869 CString strAuthor
= pLogEntry
->GetAuthorName();
1870 if ( strAuthor
.IsEmpty() )
1872 strAuthor
.LoadString(IDS_STATGRAPH_EMPTYAUTHOR
);
1874 m_arAuthorsFiltered
.Add(strAuthor
);
1875 m_arDatesFiltered
.Add(pLogEntry
->GetAuthorDate().GetTime());
1876 m_arFileChangesFiltered
.Add(pLogEntry
->GetFiles(&m_LogList
).GetCount());
1879 CDateSorter W_Sorter
;
1880 W_Sorter
.m_parAuthors
= &m_arAuthorsFiltered
;
1881 W_Sorter
.m_parDates
= &m_arDatesFiltered
;
1882 W_Sorter
.m_parFileChanges
= &m_arFileChangesFiltered
;
1883 std::sort(W_Sorter
.begin(), W_Sorter
.end(), CDateSorterLess());
1886 dlg
.m_parAuthors
= &m_arAuthorsFiltered
;
1887 dlg
.m_parDates
= &m_arDatesFiltered
;
1888 dlg
.m_parFileChanges
= &m_arFileChangesFiltered
;
1889 dlg
.m_path
= m_orgPath
;
1891 // restore the previous sorting
1892 SortByColumn(m_nSortColumn
, m_bAscending
);
1893 OnTimer(LOGFILTER_TIMER
);
1896 void CLogDlg::DoSizeV1(int delta
)
1899 RemoveAnchor(IDC_LOGLIST
);
1900 RemoveAnchor(IDC_SPLITTERTOP
);
1901 RemoveAnchor(IDC_MSGVIEW
);
1902 RemoveAnchor(IDC_SPLITTERBOTTOM
);
1903 RemoveAnchor(IDC_LOGMSG
);
1904 CSplitterControl::ChangeHeight(&m_LogList
, delta
, CW_TOPALIGN
);
1905 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW
), -delta
, CW_BOTTOMALIGN
);
1906 AddAnchor(IDC_LOGLIST
, TOP_LEFT
, TOP_RIGHT
);
1907 AddAnchor(IDC_SPLITTERTOP
, TOP_LEFT
, TOP_RIGHT
);
1908 AddAnchor(IDC_MSGVIEW
, TOP_LEFT
, BOTTOM_RIGHT
);
1909 AddAnchor(IDC_SPLITTERBOTTOM
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
1910 AddAnchor(IDC_LOGMSG
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
1914 m_LogList
.Invalidate();
1915 GetDlgItem(IDC_MSGVIEW
)->Invalidate();
1919 void CLogDlg::DoSizeV2(int delta
)
1922 RemoveAnchor(IDC_LOGLIST
);
1923 RemoveAnchor(IDC_SPLITTERTOP
);
1924 RemoveAnchor(IDC_MSGVIEW
);
1925 RemoveAnchor(IDC_SPLITTERBOTTOM
);
1926 RemoveAnchor(IDC_LOGMSG
);
1927 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW
), delta
, CW_TOPALIGN
);
1928 CSplitterControl::ChangeHeight(&m_ChangedFileListCtrl
, -delta
, CW_BOTTOMALIGN
);
1929 AddAnchor(IDC_LOGLIST
, TOP_LEFT
, TOP_RIGHT
);
1930 AddAnchor(IDC_SPLITTERTOP
, TOP_LEFT
, TOP_RIGHT
);
1931 AddAnchor(IDC_MSGVIEW
, TOP_LEFT
, BOTTOM_RIGHT
);
1932 AddAnchor(IDC_SPLITTERBOTTOM
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
1933 AddAnchor(IDC_LOGMSG
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
1937 GetDlgItem(IDC_MSGVIEW
)->Invalidate();
1938 m_ChangedFileListCtrl
.Invalidate();
1942 void CLogDlg::AdjustMinSize()
1944 // adjust the minimum size of the dialog to prevent the resizing from
1945 // moving the list control too far down.
1946 CRect rcChgListView
;
1947 m_ChangedFileListCtrl
.GetClientRect(rcChgListView
);
1949 m_LogList
.GetClientRect(rcLogList
);
1951 SetMinTrackSize(CSize(m_DlgOrigRect
.Width(),
1952 m_DlgOrigRect
.Height()-m_ChgOrigRect
.Height()-m_LogListOrigRect
.Height()-m_MsgViewOrigRect
.Height()
1953 +rcChgListView
.Height()+rcLogList
.Height()+60));
1956 LRESULT
CLogDlg::DefWindowProc(UINT message
, WPARAM wParam
, LPARAM lParam
)
1960 if (wParam
== IDC_SPLITTERTOP
)
1962 SPC_NMHDR
* pHdr
= (SPC_NMHDR
*) lParam
;
1963 DoSizeV1(pHdr
->delta
);
1965 else if (wParam
== IDC_SPLITTERBOTTOM
)
1967 SPC_NMHDR
* pHdr
= (SPC_NMHDR
*) lParam
;
1968 DoSizeV2(pHdr
->delta
);
1973 return CResizableDialog::DefWindowProc(message
, wParam
, lParam
);
1976 void CLogDlg::SetSplitterRange()
1978 if ((m_LogList
)&&(m_ChangedFileListCtrl
))
1981 m_LogList
.GetWindowRect(rcTop
);
1982 ScreenToClient(rcTop
);
1984 GetDlgItem(IDC_MSGVIEW
)->GetWindowRect(rcMiddle
);
1985 ScreenToClient(rcMiddle
);
1986 m_wndSplitter1
.SetRange(rcTop
.top
+30, rcMiddle
.bottom
-20);
1988 m_ChangedFileListCtrl
.GetWindowRect(rcBottom
);
1989 ScreenToClient(rcBottom
);
1990 m_wndSplitter2
.SetRange(rcMiddle
.top
+30, rcBottom
.bottom
-20);
1994 LRESULT
CLogDlg::OnClickedInfoIcon(WPARAM
/*wParam*/, LPARAM lParam
)
1996 // FIXME: x64 version would get this function called with unexpected parameters.
2000 RECT
* rect
= (LPRECT
)lParam
;
2003 point
= CPoint(rect
->left
, rect
->bottom
);
2004 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_LogList.m_nSelectedFilter == x ? MF_CHECKED : MF_UNCHECKED))
2006 if (popup
.CreatePopupMenu())
2008 temp
.LoadString(IDS_LOG_FILTER_ALL
);
2009 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_ALL
), LOGFILTER_ALL
, temp
);
2011 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2013 temp
.LoadString(IDS_LOG_FILTER_MESSAGES
);
2014 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_MESSAGES
), LOGFILTER_MESSAGES
, temp
);
2016 //Path support because we use git grep to filter message
2017 temp.LoadString(IDS_LOG_FILTER_PATHS);
2018 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_PATHS), LOGFILTER_PATHS, temp);
2020 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
2021 popup
.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS
), LOGFILTER_AUTHORS
, temp
);
2023 /* //We use git grep to filter message
2024 temp.LoadString(IDS_LOG_FILTER_REVS);
2025 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
2026 if (m_LogList.m_bShowBugtraqColumn == true) {
2027 temp.LoadString(IDS_LOG_FILTER_BUGIDS);
2028 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_BUGID), LOGFILTER_BUGID, temp);
2031 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2033 temp
.LoadString(IDS_LOG_FILTER_REGEX
);
2034 popup
.AppendMenu(MF_STRING
| MF_ENABLED
| (m_bFilterWithRegex
? MF_CHECKED
: MF_UNCHECKED
), LOGFILTER_REGEX
, temp
);
2037 int selection
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
2041 if (selection
== LOGFILTER_REGEX
)
2043 m_bFilterWithRegex
= !m_bFilterWithRegex
;
2044 CRegDWORD b
= CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE
);
2045 b
= m_bFilterWithRegex
;
2046 m_LogList
.m_bFilterWithRegex
= m_bFilterWithRegex
;
2048 CheckRegexpTooltip();
2052 m_LogList
.m_nSelectedFilter
= selection
;
2055 SetTimer(LOGFILTER_TIMER
, 1000, NULL
);
2061 LRESULT
CLogDlg::OnClickedCancelFilter(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
2064 KillTimer(LOGFILTER_TIMER
);
2066 m_LogList
.m_sFilterText
.Empty();
2068 theApp
.DoWaitCursor(1);
2069 CStoreSelection
storeselection(this);
2070 FillLogMessageCtrl(false);
2072 m_LogList
.RemoveFilter();
2075 m_LogList
.GetTimeRange(begin
,end
);
2076 m_DateFrom
.SetTime(&begin
);
2077 m_DateTo
.SetTime(&end
);
2079 theApp
.DoWaitCursor(-1);
2080 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_HIDE
);
2081 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_SHOW
);
2082 GetDlgItem(IDC_SEARCHEDIT
)->SetFocus();
2083 UpdateLogInfoLabel();
2089 void CLogDlg::SetFilterCueText()
2092 switch (m_LogList
.m_nSelectedFilter
)
2095 temp
.LoadString(IDS_LOG_FILTER_ALL
);
2097 case LOGFILTER_MESSAGES
:
2098 temp
.LoadString(IDS_LOG_FILTER_MESSAGES
);
2100 case LOGFILTER_PATHS
:
2101 temp
.LoadString(IDS_LOG_FILTER_PATHS
);
2103 case LOGFILTER_AUTHORS
:
2104 temp
.LoadString(IDS_LOG_FILTER_AUTHORS
);
2106 case LOGFILTER_REVS
:
2107 temp
.LoadString(IDS_LOG_FILTER_REVS
);
2110 // to make the cue banner text appear more to the right of the edit control
2111 temp
= _T(" ")+temp
;
2112 m_cFilter
.SetCueBanner(temp
);
2115 bool CLogDlg::Validate(LPCTSTR string
)
2117 if (!m_bFilterWithRegex
)
2120 return m_LogList
.ValidateRegexp(string
, pat
, false);
2124 void CLogDlg::OnTimer(UINT_PTR nIDEvent
)
2126 if (nIDEvent
== LOGFTIME_TIMER
)
2128 KillTimer(LOGFTIME_TIMER
);
2130 m_LogList
.Refresh(FALSE
);
2131 FillLogMessageCtrl(false);
2134 if (nIDEvent
== LOGFILTER_TIMER
)
2136 KillTimer(LOGFILTER_TIMER
);
2138 m_LogList
.Refresh(FALSE
);
2139 FillLogMessageCtrl(false);
2142 /* we will use git built-in grep to filter log */
2143 if (this->IsThreadRunning())
2145 // thread still running! So just restart the timer.
2146 SetTimer(LOGFILTER_TIMER
, 1000, NULL
);
2149 CWnd
* focusWnd
= GetFocus();
2150 bool bSetFocusToFilterControl
= ((focusWnd
!= GetDlgItem(IDC_DATEFROM
))&&(focusWnd
!= GetDlgItem(IDC_DATETO
))
2151 && (focusWnd
!= GetDlgItem(IDC_LOGLIST
)));
2152 if (m_LogList
.m_sFilterText
.IsEmpty())
2154 DialogEnableWindow(IDC_STATBUTTON
, !(((this->IsThreadRunning())||(m_LogList
.m_arShownList
.IsEmpty()))));
2155 // do not return here!
2156 // we also need to run the filter if the filter text is empty:
2157 // 1. to clear an existing filter
2158 // 2. to rebuild the m_arShownList after sorting
2160 theApp
.DoWaitCursor(1);
2161 CStoreSelection
storeselection(this);
2162 KillTimer(LOGFILTER_TIMER
);
2163 FillLogMessageCtrl(false);
2165 // now start filter the log list
2166 m_LogList
.StartFilter();
2168 if ( m_LogList
.GetItemCount()==1 )
2170 m_LogList
.SetSelectionMark(0);
2171 m_LogList
.SetItemState(0, LVIS_SELECTED
, LVIS_SELECTED
);
2173 theApp
.DoWaitCursor(-1);
2174 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_HIDE
);
2175 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_SHOW
);
2176 if (bSetFocusToFilterControl
)
2177 GetDlgItem(IDC_SEARCHEDIT
)->SetFocus();
2178 UpdateLogInfoLabel();
2180 } // if (nIDEvent == LOGFILTER_TIMER)
2181 DialogEnableWindow(IDC_STATBUTTON
, !(((this->IsThreadRunning())||(m_LogList
.m_arShownList
.IsEmpty()))));
2182 __super::OnTimer(nIDEvent
);
2185 void CLogDlg::OnDtnDatetimechangeDateto(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
2190 m_DateTo
.GetTime(_time
);
2192 CTime
time(_time
.GetYear(), _time
.GetMonth(), _time
.GetDay(), 23, 59, 59);
2193 if (time
.GetTime() != m_LogList
.m_To
)
2195 m_LogList
.m_To
= (DWORD
)time
.GetTime();
2196 SetTimer(LOGFTIME_TIMER
, 10, NULL
);
2201 CMessageBox::Show(NULL
,_T("Invalidate Parameter"),_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
2207 void CLogDlg::OnDtnDatetimechangeDatefrom(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
2213 m_DateFrom
.GetTime(_time
);
2215 CTime
time(_time
.GetYear(), _time
.GetMonth(), _time
.GetDay(), 0, 0, 0);
2216 if (time
.GetTime() != m_LogList
.m_From
)
2218 m_LogList
.m_From
= (DWORD
)time
.GetTime();
2219 SetTimer(LOGFTIME_TIMER
, 10, NULL
);
2224 CMessageBox::Show(NULL
,_T("Invalidate Parameter"),_T("TortoiseGit"),MB_OK
|MB_ICONERROR
);
2232 CTGitPathList
CLogDlg::GetChangedPathsFromSelectedRevisions(bool /*bRelativePaths*/ /* = false */, bool /*bUseFilter*/ /* = true */)
2234 CTGitPathList pathList
;
2237 if (m_sRepositoryRoot
.IsEmpty() && (bRelativePaths
== false))
2239 m_sRepositoryRoot
= GetRepositoryRoot(m_path
);
2241 if (m_sRepositoryRoot
.IsEmpty() && (bRelativePaths
== false))
2244 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
2249 int nextpos
= m_LogList
.GetNextSelectedItem(pos
);
2250 if (nextpos
>= m_arShownList
.GetCount())
2252 PLOGENTRYDATA pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(nextpos
));
2253 LogChangedPathArray
* cpatharray
= pLogEntry
->pArChangedPaths
;
2254 for (INT_PTR cpPathIndex
= 0; cpPathIndex
<cpatharray
->GetCount(); ++cpPathIndex
)
2256 LogChangedPath
* cpath
= cpatharray
->SafeGetAt(cpPathIndex
);
2260 if (!bRelativePaths
)
2261 path
.SetFromGit(m_sRepositoryRoot
);
2262 path
.AppendPathString(cpath
->sPath
);
2264 ((m_cHidePaths
.GetState() & 0x0003)!=BST_CHECKED
)||
2265 (cpath
->sPath
.Left(m_sRelativeRoot
.GetLength()).Compare(m_sRelativeRoot
)==0))
2266 pathList
.AddPath(path
);
2271 pathList
.RemoveDuplicates();
2276 void CLogDlg::SortByColumn(int /*nSortColumn*/, bool /*bAscending*/)
2284 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscRevSort());
2286 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescRevSort());
2292 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscActionSort());
2294 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescActionSort());
2300 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscAuthorSort());
2302 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescAuthorSort());
2308 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscDateSort());
2310 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescDateSort());
2313 case 4: // Message or bug id
2314 if (m_bShowBugtraqColumn
)
2317 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscBugIDSort());
2319 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescBugIDSort());
2322 // fall through here
2326 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::AscMessageSort());
2328 std::sort(m_logEntries
.begin(), m_logEntries
.end(), CLogDataVector::DescMessageSort());
2338 void CLogDlg::OnLvnColumnclick(NMHDR
*pNMHDR
, LRESULT
*pResult
)
2340 if (this->IsThreadRunning())
2341 return; //no sorting while the arrays are filled
2343 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
2344 const int nColumn
= pNMLV
->iSubItem
;
2345 m_bAscending
= nColumn
== m_nSortColumn
? !m_bAscending
: TRUE
;
2346 m_nSortColumn
= nColumn
;
2347 SortByColumn(m_nSortColumn
, m_bAscending
);
2348 SetSortArrow(&m_LogList
, m_nSortColumn
, !!m_bAscending
);
2349 SortShownListArray();
2350 m_LogList
.Invalidate();
2351 UpdateLogInfoLabel();
2353 UNREFERENCED_PARAMETER(pNMHDR
);
2358 void CLogDlg::SortShownListArray()
2360 // make sure the shown list still matches the filter after sorting.
2361 OnTimer(LOGFILTER_TIMER
);
2362 // clear the selection states
2363 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
2366 m_LogList
.SetItemState(m_LogList
.GetNextSelectedItem(pos
), 0, LVIS_SELECTED
);
2368 m_LogList
.SetSelectionMark(-1);
2371 void CLogDlg::SetSortArrow(CListCtrl
* control
, int nColumn
, bool bAscending
)
2373 if (control
== NULL
)
2375 // set the sort arrow
2376 CHeaderCtrl
* pHeader
= control
->GetHeaderCtrl();
2377 HDITEM HeaderItem
= {0};
2378 HeaderItem
.mask
= HDI_FORMAT
;
2379 for (int i
=0; i
<pHeader
->GetItemCount(); ++i
)
2381 pHeader
->GetItem(i
, &HeaderItem
);
2382 HeaderItem
.fmt
&= ~(HDF_SORTDOWN
| HDF_SORTUP
);
2383 pHeader
->SetItem(i
, &HeaderItem
);
2387 pHeader
->GetItem(nColumn
, &HeaderItem
);
2388 HeaderItem
.fmt
|= (bAscending
? HDF_SORTUP
: HDF_SORTDOWN
);
2389 pHeader
->SetItem(nColumn
, &HeaderItem
);
2392 void CLogDlg::OnLvnColumnclickChangedFileList(NMHDR
* /*pNMHDR*/, LRESULT
* /*pResult*/)
2395 if (this->IsThreadRunning())
2396 return; //no sorting while the arrays are filled
2397 if (m_currentChangedArray
== NULL
)
2399 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
2400 const int nColumn
= pNMLV
->iSubItem
;
2401 m_bAscendingPathList
= nColumn
== m_nSortColumnPathList
? !m_bAscendingPathList
: TRUE
;
2402 m_nSortColumnPathList
= nColumn
;
2403 // qsort(m_currentChangedArray->GetData(), m_currentChangedArray->GetSize(), sizeof(LogChangedPath*), (GENERICCOMPAREFN)SortCompare);
2405 SetSortArrow(&m_ChangedFileListCtrl
, m_nSortColumnPathList
, m_bAscendingPathList
);
2406 m_ChangedFileListCtrl
.Invalidate();
2411 int CLogDlg::m_nSortColumnPathList
= 0;
2412 bool CLogDlg::m_bAscendingPathList
= false;
2414 int CLogDlg::SortCompare(const void * /*pElem1*/, const void * /*pElem2*/)
2417 LogChangedPath
* cpath1
= *((LogChangedPath
**)pElem1
);
2418 LogChangedPath
* cpath2
= *((LogChangedPath
**)pElem2
);
2420 if (m_bAscendingPathList
)
2421 std::swap (cpath1
, cpath2
);
2424 switch (m_nSortColumnPathList
)
2427 cmp
= cpath2
->GetAction().Compare(cpath1
->GetAction());
2432 cmp
= cpath2
->sPath
.CompareNoCase(cpath1
->sPath
);
2436 case 2: // copy from path
2437 cmp
= cpath2
->sCopyFromPath
.Compare(cpath1
->sCopyFromPath
);
2441 case 3: // copy from revision
2442 return cpath2
->lCopyFromRev
> cpath1
->lCopyFromRev
;
2448 void CLogDlg::OnBnClickedHidepaths()
2450 FillLogMessageCtrl();
2451 m_ChangedFileListCtrl
.Invalidate();
2456 void CLogDlg::OnBnClickedCheckStoponcopy()
2459 if (!GetDlgItem(IDC_GETALL
)->IsWindowEnabled())
2462 // ignore old fetch limits when switching
2463 // between copy-following and stop-on-copy
2464 // (otherwise stop-on-copy will limit what
2465 // we see immediately after switching to
2470 // now, restart the query
2476 void CLogDlg::UpdateLogInfoLabel()
2481 long selectedrevs
= 0;
2482 int count
=m_LogList
.m_arShownList
.GetCount();
2486 rev1
= (reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(0)))->m_CommitHash
;
2487 if(this->m_LogList
.m_bShowWC
&& rev1
.IsEmpty()&&(count
>1))
2489 rev1
= (reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(start
)))->m_CommitHash
;
2490 //pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.SafeGetAt(m_arShownList.GetCount()-1));
2491 rev2
= (reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(count
-1)))->m_CommitHash
;
2492 selectedrevs
= m_LogList
.GetSelectedCount();
2495 sTemp
.Format(_T("Showing %ld revision(s), from revision %s to revision %s - %ld revision(s) selected\r\n"),
2497 rev2
.ToString().Left(6), rev1
.ToString().Left(6), selectedrevs
);
2499 if(selectedrevs
== 1)
2501 CString str
=m_ChangedFileListCtrl
.GetStatisticsString(true);
2502 str
.Replace(_T('\n'), _T(' '));
2511 void CLogDlg::ShowContextMenuForChangedpaths(CWnd
* /*pWnd*/, CPoint point
)
2514 int selIndex
= m_ChangedFileListCtrl
.GetSelectionMark();
2515 if ((point
.x
== -1) && (point
.y
== -1))
2518 m_ChangedFileListCtrl
.GetItemRect(selIndex
, &rect
, LVIR_LABEL
);
2519 m_ChangedFileListCtrl
.ClientToScreen(&rect
);
2520 point
= rect
.CenterPoint();
2524 int s
= m_LogList
.GetSelectionMark();
2527 std::vector
<CString
> changedpaths
;
2528 std::vector
<LogChangedPath
*> changedlogpaths
;
2529 POSITION pos
= m_LogList
.GetFirstSelectedItemPosition();
2531 return; // nothing is selected, get out of here
2533 bool bOneRev
= true;
2534 int sel
=m_LogList
.GetNextSelectedItem(pos
);
2535 GitRev
* pLogEntry
= reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(sel
));
2536 GitRev
* rev1
= pLogEntry
;
2537 GitRev
* rev2
= reinterpret_cast<GitRev
*>(m_LogList
.m_arShownList
.SafeGetAt(sel
+1));
2539 bool bOneRev
= true;
2544 pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetNextSelectedItem(pos
)));
2547 rev1
= max(rev1
,(git_revnum_t
)pLogEntry
->Rev
);
2548 rev2
= min(rev2
,(git_revnum_t
)pLogEntry
->Rev
);
2554 POSITION pos
= m_ChangedFileListCtrl
.GetFirstSelectedItemPosition();
2557 int nItem
= m_ChangedFileListCtrl
.GetNextSelectedItem(pos
);
2558 changedpaths
.push_back(m_currentChangedPathList
[nItem
].GetGitPathString());
2563 // only one revision is selected in the log dialog top pane
2564 // but multiple items could be selected in the changed items list
2567 POSITION pos
= m_ChangedFileListCtrl
.GetFirstSelectedItemPosition();
2570 int nItem
= m_ChangedFileListCtrl
.GetNextSelectedItem(pos
);
2571 LogChangedPath
* changedlogpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(nItem
);
2573 if (m_ChangedFileListCtrl
.GetSelectedCount() == 1)
2575 if ((changedlogpath
)&&(!changedlogpath
->sCopyFromPath
.IsEmpty()))
2576 rev2
= changedlogpath
->lCopyFromRev
;
2579 // if the path was modified but the parent path was 'added with history'
2580 // then we have to use the copy from revision of the parent path
2581 CTGitPath cpath
= CTGitPath(changedlogpath
->sPath
);
2582 for (int flist
= 0; flist
< pLogEntry
->pArChangedPaths
->GetCount(); ++flist
)
2584 CTGitPath p
= CTGitPath(pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->sPath
);
2585 if (p
.IsAncestorOf(cpath
))
2587 if (!pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->sCopyFromPath
.IsEmpty())
2588 rev2
= pLogEntry
->pArChangedPaths
->SafeGetAt(flist
)->lCopyFromRev
;
2593 if ((m_cHidePaths
.GetState() & 0x0003)==BST_CHECKED
)
2595 // some items are hidden! So find out which item the user really clicked on
2596 INT_PTR selRealIndex
= -1;
2597 for (INT_PTR hiddenindex
=0; hiddenindex
<pLogEntry
->pArChangedPaths
->GetCount(); ++hiddenindex
)
2599 if (pLogEntry
->pArChangedPaths
->SafeGetAt(hiddenindex
)->sPath
.Left(m_sRelativeRoot
.GetLength()).Compare(m_sRelativeRoot
)==0)
2601 if (selRealIndex
== nItem
)
2603 selIndex
= hiddenindex
;
2604 changedlogpath
= pLogEntry
->pArChangedPaths
->SafeGetAt(selIndex
);
2611 changedpaths
.push_back(changedlogpath
->sPath
);
2612 changedlogpaths
.push_back(changedlogpath
);
2617 //entry is selected, now show the popup menu
2619 if (popup
.CreatePopupMenu())
2621 bool bEntryAdded
= false;
2622 if (m_ChangedFileListCtrl
.GetSelectedCount() == 1)
2624 // if ((!bOneRev)||(IsDiffPossible(changedlogpaths[0], rev1)))
2626 popup
.AppendMenuIcon(CGitLogList::ID_DIFF
, IDS_LOG_POPUP_DIFF
, IDI_DIFF
);
2627 popup
.AppendMenuIcon(CGitLogList::ID_BLAMEDIFF
, IDS_LOG_POPUP_BLAMEDIFF
, IDI_BLAME
);
2628 popup
.SetDefaultItem(CGitLogList::ID_DIFF
, FALSE
);
2629 popup
.AppendMenuIcon(CGitLogList::ID_GNUDIFF1
, IDS_LOG_POPUP_GNUDIFF_CH
, IDI_DIFF
);
2632 // if (rev2 == rev1-1)
2635 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2636 popup
.AppendMenuIcon(CGitLogList::ID_OPEN
, IDS_LOG_POPUP_OPEN
, IDI_OPEN
);
2637 popup
.AppendMenuIcon(CGitLogList::ID_OPENWITH
, IDS_LOG_POPUP_OPENWITH
, IDI_OPEN
);
2638 popup
.AppendMenuIcon(CGitLogList::ID_BLAME
, IDS_LOG_POPUP_BLAME
, IDI_BLAME
);
2639 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2641 popup
.AppendMenuIcon(CGitLogList::ID_REVERTREV
, IDS_LOG_POPUP_REVERTREV
, IDI_REVERT
);
2642 popup
.AppendMenuIcon(CGitLogList::ID_POPPROPS
, IDS_REPOBROWSE_SHOWPROP
, IDI_PROPERTIES
); // "Show Properties"
2643 popup
.AppendMenuIcon(CGitLogList::ID_LOG
, IDS_MENULOG
, IDI_LOG
); // "Show Log"
2644 popup
.AppendMenuIcon(CGitLogList::ID_GETMERGELOGS
, IDS_LOG_POPUP_GETMERGELOGS
, IDI_LOG
); // "Show merge log"
2645 popup
.AppendMenuIcon(CGitLogList::ID_SAVEAS
, IDS_LOG_POPUP_SAVE
, IDI_SAVEAS
);
2647 if (!m_ProjectProperties
.sWebViewerPathRev
.IsEmpty())
2649 popup
.AppendMenu(MF_SEPARATOR
, NULL
);
2650 popup
.AppendMenuIcon(CGitLogList::ID_VIEWPATHREV
, IDS_LOG_POPUP_VIEWPATHREV
);
2652 if (popup
.GetDefaultItem(0,FALSE
)==-1)
2653 popup
.SetDefaultItem(CGitLogList::ID_OPEN
, FALSE
);
2656 else if (changedlogpaths
.size())
2658 // more than one entry is selected
2659 popup
.AppendMenuIcon(CGitLogList::ID_SAVEAS
, IDS_LOG_POPUP_SAVE
);
2665 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, point
.x
, point
.y
, this, 0);
2666 bool bOpenWith
= false;
2667 bool bMergeLog
= false;
2668 m_bCancelled
= false;
2672 case CGitLogList::ID_DIFF
:
2674 DoDiffFromLog(selIndex
, rev1
, rev2
, false, false);
2680 DoDiffFromLog(selIndex
, rev1
, rev2
, true, false);
2685 DoDiffFromLog(selIndex
, rev1
, rev2
, false, true);
2690 SetPromptApp(&theApp
);
2691 theApp
.DoWaitCursor(1);
2693 if (Git::PathIsURL(m_path
))
2695 sUrl
= m_path
.GetGitPathString();
2699 sUrl
= GetURLFromPath(m_path
);
2702 theApp
.DoWaitCursor(-1);
2704 temp
.Format(IDS_ERR_NOURLOFFILE
, m_path
.GetWinPath());
2705 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
2707 theApp
.DoWaitCursor(-1);
2711 // find the working copy path of the selected item from the URL
2712 m_bCancelled
= false;
2713 CString sUrlRoot
= GetRepositoryRoot(CTGitPath(sUrl
));
2715 CString fileURL
= changedpaths
[0];
2716 fileURL
= sUrlRoot
+ fileURL
.Trim();
2717 // firstfile = (e.g.) http://mydomain.com/repos/trunk/folder/file1
2718 // sUrl = http://mydomain.com/repos/trunk/folder
2719 CString sUnescapedUrl
= CPathUtils::PathUnescape(sUrl
);
2720 // find out until which char the urls are identical
2722 while ((i
<fileURL
.GetLength())&&(i
<sUnescapedUrl
.GetLength())&&(fileURL
[i
]==sUnescapedUrl
[i
]))
2724 int leftcount
= m_path
.GetWinPathString().GetLength()-(sUnescapedUrl
.GetLength()-i
);
2725 CString wcPath
= m_path
.GetWinPathString().Left(leftcount
);
2726 wcPath
+= fileURL
.Mid(i
);
2727 wcPath
.Replace('/', '\\');
2728 CGitProgressDlg dlg
;
2729 if (changedlogpaths
[0]->action
== LOGACTIONS_DELETED
)
2731 // a deleted path! Since the path isn't there anymore, merge
2732 // won't work. So just do a copy url->wc
2733 dlg
.SetCommand(CGitProgressDlg::GitProgress_Copy
);
2734 dlg
.SetPathList(CTGitPathList(CTGitPath(fileURL
)));
2736 dlg
.SetRevision(rev2
);
2740 if (!PathFileExists(wcPath
))
2742 // seems the path got renamed
2743 // tell the user how to work around this.
2744 CMessageBox::Show(this->m_hWnd
, IDS_LOG_REVERTREV_ERROR
, IDS_APPNAME
, MB_ICONERROR
);
2746 theApp
.DoWaitCursor(-1);
2749 dlg
.SetCommand(CGitProgressDlg::GitProgress_Merge
);
2750 dlg
.SetPathList(CTGitPathList(CTGitPath(wcPath
)));
2751 dlg
.SetUrl(fileURL
);
2752 dlg
.SetSecondUrl(fileURL
);
2753 GitRevRangeArray revarray
;
2754 revarray
.AddRevRange(rev1
, rev2
);
2755 dlg
.SetRevisionRanges(revarray
);
2758 msg
.Format(IDS_LOG_REVERT_CONFIRM
, (LPCTSTR
)wcPath
);
2759 if (CMessageBox::Show(this->m_hWnd
, msg
, _T("TortoiseGit"), MB_YESNO
| MB_ICONQUESTION
) == IDYES
)
2763 theApp
.DoWaitCursor(-1);
2768 DialogEnableWindow(IDOK
, FALSE
);
2769 SetPromptApp(&theApp
);
2770 theApp
.DoWaitCursor(1);
2772 if (Git::PathIsURL(m_path
))
2774 filepath
= m_path
.GetGitPathString();
2778 filepath
= GetURLFromPath(m_path
);
2779 if (filepath
.IsEmpty())
2781 theApp
.DoWaitCursor(-1);
2783 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
2784 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
2785 TRACE(_T("could not retrieve the URL of the file!\n"));
2790 filepath
= GetRepositoryRoot(CTGitPath(filepath
));
2791 filepath
+= changedpaths
[0];
2794 dlg
.m_Path
= CTGitPath(filepath
);
2797 theApp
.DoWaitCursor(-1);
2802 DialogEnableWindow(IDOK
, FALSE
);
2803 SetPromptApp(&theApp
);
2804 theApp
.DoWaitCursor(1);
2806 if (Git::PathIsURL(m_path
))
2808 filepath
= m_path
.GetGitPathString();
2812 filepath
= GetURLFromPath(m_path
);
2813 if (filepath
.IsEmpty())
2815 theApp
.DoWaitCursor(-1);
2817 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
2818 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
2819 TRACE(_T("could not retrieve the URL of the file!\n"));
2824 m_bCancelled
= false;
2825 CString sRoot
= GetRepositoryRoot(CTGitPath(filepath
));
2826 // if more than one entry is selected, we save them
2827 // one by one into a folder the user has selected
2828 bool bTargetSelected
= false;
2829 CTGitPath TargetPath
;
2830 if (m_ChangedFileListCtrl
.GetSelectedCount() > 1)
2832 CBrowseFolder browseFolder
;
2833 browseFolder
.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_SAVEFOLDERTOHINT
)));
2834 browseFolder
.m_style
= BIF_EDITBOX
| BIF_NEWDIALOGSTYLE
| BIF_RETURNFSANCESTORS
| BIF_RETURNONLYFSDIRS
;
2835 CString strSaveAsDirectory
;
2836 if (browseFolder
.Show(GetSafeHwnd(), strSaveAsDirectory
) == CBrowseFolder::OK
)
2838 TargetPath
= CTGitPath(strSaveAsDirectory
);
2839 bTargetSelected
= true;
2844 // Display the Open dialog box.
2845 CString revFilename
;
2847 temp
= CPathUtils::GetFileNameFromPath(changedpaths
[0]);
2848 int rfind
= temp
.ReverseFind('.');
2850 revFilename
.Format(_T("%s-%ld%s"), (LPCTSTR
)temp
.Left(rfind
), rev1
, (LPCTSTR
)temp
.Mid(rfind
));
2852 revFilename
.Format(_T("%s-%ld"), (LPCTSTR
)temp
, rev1
);
2853 bTargetSelected
= CAppUtils::FileOpenSave(revFilename
, NULL
, IDS_LOG_POPUP_SAVE
, IDS_COMMONFILEFILTER
, false, m_hWnd
);
2854 TargetPath
.SetFromWin(revFilename
);
2856 if (bTargetSelected
)
2858 CProgressDlg progDlg
;
2859 progDlg
.SetTitle(IDS_APPNAME
);
2860 progDlg
.SetAnimation(IDR_DOWNLOAD
);
2861 for (std::vector
<LogChangedPath
*>::iterator it
= changedlogpaths
.begin(); it
!= changedlogpaths
.end(); ++it
)
2863 GitRev getrev
= ((*it
)->action
== LOGACTIONS_DELETED
) ? rev2
: rev1
;
2866 sInfoLine
.Format(IDS_PROGRESSGETFILEREVISION
, (LPCTSTR
)filepath
, (LPCTSTR
)getrev
.ToString());
2867 progDlg
.SetLine(1, sInfoLine
, true);
2868 SetAndClearProgressInfo(&progDlg
);
2869 progDlg
.ShowModeless(m_hWnd
);
2871 CTGitPath tempfile
= TargetPath
;
2872 if (changedpaths
.size() > 1)
2874 // if multiple items are selected, then the TargetPath
2875 // points to a folder and we have to append the filename
2876 // to save to to that folder.
2877 CString sName
= (*it
)->sPath
;
2878 int slashpos
= sName
.ReverseFind('/');
2880 sName
= sName
.Mid(slashpos
);
2881 tempfile
.AppendPathString(sName
);
2882 // one problem here:
2883 // a user could have selected multiple items which
2884 // have the same filename but reside in different
2885 // directories, e.g.
2888 // in that case, the second 'file1' will overwrite
2889 // the already saved 'file1'.
2891 // we could maybe find the common root of all selected
2892 // items and then create sub folders to save those files
2894 // But I think we should just leave it that way: to check
2895 // out multiple items at once, the better way is still to
2896 // use the export command from the top pane of the log dialog.
2898 filepath
= sRoot
+ (*it
)->sPath
;
2899 if (!Cat(CTGitPath(filepath
), getrev
, getrev
, tempfile
))
2902 SetAndClearProgressInfo((HWND
)NULL
);
2903 CMessageBox::Show(this->m_hWnd
, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
2905 theApp
.DoWaitCursor(-1);
2910 SetAndClearProgressInfo((HWND
)NULL
);
2913 theApp
.DoWaitCursor(-1);
2920 GitRev getrev
= pLogEntry
->pArChangedPaths
->SafeGetAt(selIndex
)->action
== LOGACTIONS_DELETED
? rev2
: rev1
;
2921 Open(bOpenWith
,changedpaths
[0],getrev
);
2927 if (Git::PathIsURL(m_path
))
2929 filepath
= m_path
.GetGitPathString();
2933 filepath
= GetURLFromPath(m_path
);
2934 if (filepath
.IsEmpty())
2936 theApp
.DoWaitCursor(-1);
2938 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
2939 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
2940 TRACE(_T("could not retrieve the URL of the file!\n"));
2945 filepath
= GetRepositoryRoot(CTGitPath(filepath
));
2946 filepath
+= changedpaths
[0];
2949 if (dlg
.DoModal() == IDOK
)
2954 tempfile
= blame
.BlameToTempFile(CTGitPath(filepath
), dlg
.StartRev
, dlg
.EndRev
, dlg
.EndRev
, logfile
, _T(""), dlg
.m_bIncludeMerge
, TRUE
, TRUE
);
2955 if (!tempfile
.IsEmpty())
2957 if (dlg
.m_bTextView
)
2959 //open the default text editor for the result file
2960 CAppUtils::StartTextViewer(tempfile
);
2964 CString sParams
= _T("/path:\"") + filepath
+ _T("\" ");
2965 if(!CAppUtils::LaunchTortoiseBlame(tempfile
, logfile
, CPathUtils::GetFileNameFromPath(filepath
),sParams
))
2973 CMessageBox::Show(this->m_hWnd
, blame
.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR
);
2978 case ID_GETMERGELOGS
:
2983 DialogEnableWindow(IDOK
, FALSE
);
2984 SetPromptApp(&theApp
);
2985 theApp
.DoWaitCursor(1);
2987 if (Git::PathIsURL(m_path
))
2989 filepath
= m_path
.GetGitPathString();
2993 filepath
= GetURLFromPath(m_path
);
2994 if (filepath
.IsEmpty())
2996 theApp
.DoWaitCursor(-1);
2998 temp
.Format(IDS_ERR_NOURLOFFILE
, (LPCTSTR
)filepath
);
2999 CMessageBox::Show(this->m_hWnd
, temp
, _T("TortoiseGit"), MB_ICONERROR
);
3000 TRACE(_T("could not retrieve the URL of the file!\n"));
3005 m_bCancelled
= false;
3006 filepath
= GetRepositoryRoot(CTGitPath(filepath
));
3007 filepath
+= changedpaths
[0];
3008 git_revnum_t logrev
= rev1
;
3009 if (changedlogpaths
[0]->action
== LOGACTIONS_DELETED
)
3011 // if the item got deleted in this revision,
3012 // fetch the log from the previous revision where it
3017 sCmd
.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%ld"), (LPCTSTR
)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR
)filepath
, logrev
);
3019 sCmd
+= _T(" /merge");
3020 CAppUtils::LaunchApplication(sCmd
, NULL
, false);
3022 theApp
.DoWaitCursor(-1);
3025 case ID_VIEWPATHREV
:
3027 PLOGENTRYDATA pLogEntry
= reinterpret_cast<PLOGENTRYDATA
>(m_arShownList
.SafeGetAt(m_LogList
.GetSelectionMark()));
3028 GitRev rev
= pLogEntry
->Rev
;
3029 CString relurl
= changedpaths
[0];
3030 CString url
= m_ProjectProperties
.sWebViewerPathRev
;
3031 url
.Replace(_T("%REVISION%"), rev
.ToString());
3032 url
.Replace(_T("%PATH%"), relurl
);
3033 relurl
= relurl
.Mid(relurl
.Find('/'));
3034 url
.Replace(_T("%PATH1%"), relurl
);
3036 ShellExecute(this->m_hWnd
, _T("open"), url
, NULL
, NULL
, SW_SHOWDEFAULT
);
3044 } // if (popup.CreatePopupMenu())
3048 void CLogDlg::OnDtnDropdownDatefrom(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
3050 // the date control should not show the "today" button
3051 CMonthCalCtrl
* pCtrl
= m_DateFrom
.GetMonthCalCtrl();
3053 SetWindowLongPtr(pCtrl
->GetSafeHwnd(), GWL_STYLE
, LONG_PTR(pCtrl
->GetStyle() | MCS_NOTODAY
));
3057 void CLogDlg::OnDtnDropdownDateto(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
3059 // the date control should not show the "today" button
3060 CMonthCalCtrl
* pCtrl
= m_DateTo
.GetMonthCalCtrl();
3062 SetWindowLongPtr(pCtrl
->GetSafeHwnd(), GWL_STYLE
, LONG_PTR(pCtrl
->GetStyle() | MCS_NOTODAY
));
3066 void CLogDlg::OnSize(UINT nType
, int cx
, int cy
)
3068 __super::OnSize(nType
, cx
, cy
);
3073 void CLogDlg::OnRefresh()
3075 //if (GetDlgItem(IDC_GETALL)->IsWindowEnabled())
3079 this->m_LogProgress
.SetPos(0);
3087 void CLogDlg::OnFocusFilter()
3089 GetDlgItem(IDC_SEARCHEDIT
)->SetFocus();
3092 void CLogDlg::OnEditCopy()
3094 if (GetFocus() == &m_ChangedFileListCtrl
)
3095 CopyChangedSelectionToClipBoard();
3097 m_LogList
.CopySelectionToClipBoard();
3100 CString
CLogDlg::GetAbsoluteUrlFromRelativeUrl(const CString
& url
)
3102 // is the URL a relative one?
3103 if (url
.Left(2).Compare(_T("^/")) == 0)
3105 // URL is relative to the repository root
3106 CString url1
= m_sRepositoryRoot
+ url
.Mid(1);
3107 TCHAR buf
[INTERNET_MAX_URL_LENGTH
];
3108 DWORD len
= url
.GetLength();
3109 if (UrlCanonicalize((LPCTSTR
)url1
, buf
, &len
, 0) == S_OK
)
3110 return CString(buf
, len
);
3113 else if (url
[0] == '/')
3115 // URL is relative to the server's hostname
3117 // find the server's hostname
3118 int schemepos
= m_sRepositoryRoot
.Find(_T("//"));
3121 sHost
= m_sRepositoryRoot
.Left(m_sRepositoryRoot
.Find('/', schemepos
+3));
3122 CString url1
= sHost
+ url
;
3123 TCHAR buf
[INTERNET_MAX_URL_LENGTH
];
3124 DWORD len
= url
.GetLength();
3125 if (UrlCanonicalize((LPCTSTR
)url
, buf
, &len
, 0) == S_OK
)
3126 return CString(buf
, len
);
3134 void CLogDlg::OnEnChangeSearchedit()
3137 if (m_LogList
.m_sFilterText
.IsEmpty())
3139 CStoreSelection
storeselection(this);
3140 // clear the filter, i.e. make all entries appear
3141 theApp
.DoWaitCursor(1);
3142 KillTimer(LOGFILTER_TIMER
);
3143 FillLogMessageCtrl(false);
3146 //m_LogList.StartFilter();
3148 InterlockedExchange(&m_bNoDispUpdates
, TRUE
);
3149 m_arShownList
.RemoveAll();
3150 for (DWORD i
=0; i
<m_logEntries
.size(); ++i
)
3152 if (IsEntryInDateRange(i
))
3153 m_arShownList
.Add(m_logEntries
[i
]);
3155 InterlockedExchange(&m_bNoDispUpdates
, FALSE
);
3156 m_LogList
.DeleteAllItems();
3157 m_LogList
.SetItemCountEx(ShownCountWithStopped());
3158 m_LogList
.RedrawItems(0, ShownCountWithStopped());
3159 m_LogList
.SetRedraw(false);
3160 ResizeAllListCtrlCols();
3161 m_LogList
.SetRedraw(true);
3163 theApp
.DoWaitCursor(-1);
3164 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_HIDE
);
3165 GetDlgItem(IDC_SEARCHEDIT
)->ShowWindow(SW_SHOW
);
3166 GetDlgItem(IDC_SEARCHEDIT
)->SetFocus();
3167 DialogEnableWindow(IDC_STATBUTTON
, !(((this->IsThreadRunning())||(m_LogList
.m_arShownList
.IsEmpty()))));
3170 if (Validate(m_LogList
.m_sFilterText
))
3171 SetTimer(LOGFILTER_TIMER
, 1000, NULL
);
3173 KillTimer(LOGFILTER_TIMER
);
3177 void CLogDlg::OnBnClickedAllBranch()
3181 if(this->m_bAllBranch
)
3182 m_LogList
.m_ShowMask
|=CGit::LOG_INFO_ALL_BRANCH
;
3184 m_LogList
.m_ShowMask
&=~CGit::LOG_INFO_ALL_BRANCH
;
3188 FillLogMessageCtrl(false);
3191 void CLogDlg::OnBnClickedBrowseRef()
3193 CString newRef
= CBrowseRefsDlg::PickRef(false,m_LogList
.GetStartRef());
3194 if(newRef
.IsEmpty())
3197 SetStartRef(newRef
);
3198 ((CButton
*)GetDlgItem(IDC_LOG_ALLBRANCH
))->SetCheck(0);
3200 OnBnClickedAllBranch();
3203 void CLogDlg::ShowStartRef()
3205 //Show ref name on top
3206 if(!::IsWindow(m_hWnd
))
3210 m_staticRef
.SetWindowText(L
"<All Branches>");
3211 m_staticRef
.Invalidate(TRUE
);
3215 CString showStartRef
= m_LogList
.GetStartRef();
3216 if(showStartRef
.IsEmpty())
3219 if( g_Git
.Run(L
"git symbolic-ref HEAD",&showStartRef
,CP_UTF8
) )
3220 showStartRef
= _T("<No branch>");
3221 showStartRef
.Trim(L
"\r\n\t ");
3225 if(wcsncmp(showStartRef
,L
"refs/",5) == 0)
3226 showStartRef
= showStartRef
.Mid(5);
3227 if(wcsncmp(showStartRef
,L
"heads/",6) == 0)
3228 showStartRef
= showStartRef
.Mid(6);
3230 m_staticRef
.SetWindowText(showStartRef
);
3231 m_staticRef
.Invalidate(TRUE
);
3234 void CLogDlg::SetStartRef(const CString
& StartRef
)
3236 m_LogList
.SetStartRef(StartRef
);
3243 void CLogDlg::OnBnClickedFirstParent()
3247 if(this->m_bFirstParent
)
3248 m_LogList
.m_ShowMask
|=CGit::LOG_INFO_FIRST_PARENT
;
3250 m_LogList
.m_ShowMask
&=~CGit::LOG_INFO_FIRST_PARENT
;
3254 FillLogMessageCtrl(false);
3258 void CLogDlg::OnBnClickShowWholeProject()
3262 if(this->m_bWholeProject
)
3264 m_LogList
.m_Path
.Reset();
3265 SetWindowText(m_sTitle
+ _T(" - ") + CString(_T("Whole Project")));
3269 m_LogList
.m_Path
=m_path
;
3270 if(!m_path
.IsEmpty())
3271 SetWindowText(m_sTitle
+ _T(" - ") + m_path
.GetGitPathString());
3276 FillLogMessageCtrl(false);
3280 LRESULT
CLogDlg::OnRefLogChanged(WPARAM wParam
, LPARAM lParam
)
3282 UNREFERENCED_PARAMETER(wParam
);
3283 UNREFERENCED_PARAMETER(lParam
);