Show Bug ID link at log dialog
[TortoiseGit.git] / src / TortoiseProc / LogDlg.cpp
blob35276cebe07c9f62a00ba2c4094146213ca31205
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.
16 #include "stdafx.h"
17 #include "TortoiseProc.h"
18 #include "cursor.h"
19 #include "InputDlg.h"
20 #include "PropDlg.h"
21 #include "SVNProgressDlg.h"
22 #include "ProgressDlg.h"
23 //#include "RepositoryBrowser.h"
24 //#include "CopyDlg.h"
25 #include "StatGraphDlg.h"
26 #include "Logdlg.h"
27 #include "MessageBox.h"
28 #include "Registry.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "UnicodeUtils.h"
33 #include "TempFile.h"
34 //#include "GitInfo.h"
35 //#include "GitDiff.h"
36 #include "IconMenu.h"
37 //#include "RevisionRangeDlg.h"
38 //#include "BrowseFolder.h"
39 //#include "BlameDlg.h"
40 //#include "Blame.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"
50 const UINT CLogDlg::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
53 IMPLEMENT_DYNAMIC(CLogDlg, CResizableStandAloneDialog)
54 CLogDlg::CLogDlg(CWnd* pParent /*=NULL*/)
55 : CResizableStandAloneDialog(CLogDlg::IDD, pParent)
56 , m_logcounter(0)
57 , m_nSearchIndex(0)
58 , m_wParam(0)
59 , m_currentChangedArray(NULL)
60 , m_nSortColumn(0)
61 , m_bShowedAll(false)
62 , m_bSelect(false)
64 , m_bSelectionMustBeContinuous(false)
65 , m_bShowBugtraqColumn(false)
66 , m_lowestRev(_T(""))
68 , m_sLogInfo(_T(""))
69 , m_pFindDialog(NULL)
70 , m_bCancelled(FALSE)
71 , m_pNotifyWindow(NULL)
73 , m_bAscending(FALSE)
75 , m_limit(0)
76 , m_childCounter(0)
77 , m_maxChild(0)
78 , m_bIncludeMerges(FALSE)
79 , m_hAccel(NULL)
81 m_bFilterWithRegex = !!CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
82 m_bAllBranch=FALSE;
83 m_bFirstParent=FALSE;
84 m_bWholeProject=FALSE;
87 CLogDlg::~CLogDlg()
90 m_CurrentFilteredChangedArray.RemoveAll();
94 void CLogDlg::DoDataExchange(CDataExchange* pDX)
96 CResizableStandAloneDialog::DoDataExchange(pDX);
97 DDX_Control(pDX, IDC_LOGLIST, m_LogList);
98 DDX_Control(pDX, IDC_LOGMSG, m_ChangedFileListCtrl);
99 DDX_Control(pDX, IDC_PROGRESS, m_LogProgress);
100 DDX_Control(pDX, IDC_SPLITTERTOP, m_wndSplitter1);
101 DDX_Control(pDX, IDC_SPLITTERBOTTOM, m_wndSplitter2);
102 DDX_Text(pDX, IDC_SEARCHEDIT, m_LogList.m_sFilterText);
103 DDX_Control(pDX, IDC_DATEFROM, m_DateFrom);
104 DDX_Control(pDX, IDC_DATETO, m_DateTo);
105 DDX_Control(pDX, IDC_HIDEPATHS, m_cHidePaths);
106 DDX_Text(pDX, IDC_LOGINFO, m_sLogInfo);
107 DDX_Check(pDX, IDC_LOG_FIRSTPARENT, m_bFirstParent);
108 DDX_Check(pDX, IDC_LOG_ALLBRANCH,m_bAllBranch);
109 DDX_Check(pDX, IDC_SHOWWHOLEPROJECT,m_bWholeProject);
110 DDX_Control(pDX, IDC_SEARCHEDIT, m_cFilter);
113 BEGIN_MESSAGE_MAP(CLogDlg, CResizableStandAloneDialog)
114 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
115 //ON_BN_CLICKED(IDC_GETALL, OnBnClickedGetall)
116 //ON_NOTIFY(NM_DBLCLK, IDC_LOGMSG, OnNMDblclkChangedFileList)
117 ON_WM_CONTEXTMENU()
118 ON_WM_SETCURSOR()
119 ON_BN_CLICKED(IDHELP, OnBnClickedHelp)
120 ON_NOTIFY(LVN_ITEMCHANGED, IDC_LOGLIST, OnLvnItemchangedLoglist)
121 ON_NOTIFY(EN_LINK, IDC_MSGVIEW, OnEnLinkMsgview)
122 ON_BN_CLICKED(IDC_STATBUTTON, OnBnClickedStatbutton)
125 ON_MESSAGE(WM_FILTEREDIT_INFOCLICKED, OnClickedInfoIcon)
126 ON_MESSAGE(WM_FILTEREDIT_CANCELCLICKED, OnClickedCancelFilter)
128 ON_MESSAGE(MSG_LOAD_PERCENTAGE,OnLogListLoading)
130 ON_EN_CHANGE(IDC_SEARCHEDIT, OnEnChangeSearchedit)
131 ON_WM_TIMER()
132 ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATETO, OnDtnDatetimechangeDateto)
133 ON_NOTIFY(DTN_DATETIMECHANGE, IDC_DATEFROM, OnDtnDatetimechangeDatefrom)
134 ON_BN_CLICKED(IDC_SHOWWHOLEPROJECT, OnBnClickShowWholeProject)
135 //ON_NOTIFY(NM_CUSTOMDRAW, IDC_LOGMSG, OnNMCustomdrawChangedFileList)
136 //ON_NOTIFY(LVN_GETDISPINFO, IDC_LOGMSG, OnLvnGetdispinfoChangedFileList)
137 ON_NOTIFY(LVN_COLUMNCLICK,IDC_LOGLIST , OnLvnColumnclick)
138 //ON_NOTIFY(LVN_COLUMNCLICK, IDC_LOGMSG, OnLvnColumnclickChangedFileList)
139 ON_BN_CLICKED(IDC_HIDEPATHS, OnBnClickedHidepaths)
140 ON_BN_CLICKED(IDC_LOG_ALLBRANCH,OnBnClickedAllBranch)
142 ON_NOTIFY(DTN_DROPDOWN, IDC_DATEFROM, &CLogDlg::OnDtnDropdownDatefrom)
143 ON_NOTIFY(DTN_DROPDOWN, IDC_DATETO, &CLogDlg::OnDtnDropdownDateto)
144 ON_WM_SIZE()
145 ON_BN_CLICKED(IDC_LOG_FIRSTPARENT, &CLogDlg::OnBnClickedFirstParent)
146 ON_BN_CLICKED(IDC_REFRESH, &CLogDlg::OnBnClickedRefresh)
147 ON_BN_CLICKED(IDC_BUTTON_BROWSE_REF, &CLogDlg::OnBnClickedBrowseRef)
148 ON_COMMAND(ID_LOGDLG_REFRESH,&CLogDlg::OnRefresh)
149 ON_COMMAND(ID_LOGDLG_FIND,&CLogDlg::OnFind)
150 ON_COMMAND(ID_LOGDLG_FOCUSFILTER,&CLogDlg::OnFocusFilter)
151 ON_COMMAND(ID_EDIT_COPY, &CLogDlg::OnEditCopy)
152 END_MESSAGE_MAP()
154 void CLogDlg::SetParams(const CTGitPath& path, GitRev pegrev, GitRev startrev, GitRev endrev, int limit /* = FALSE */)
156 m_path = path;
157 m_pegrev = pegrev;
158 m_startrev = startrev;
159 m_LogRevision = startrev;
160 m_endrev = endrev;
161 m_hasWC = !path.IsUrl();
162 m_limit = limit;
163 if (::IsWindow(m_hWnd))
164 UpdateData(FALSE);
167 BOOL CLogDlg::OnInitDialog()
169 CString temp;
170 CResizableStandAloneDialog::OnInitDialog();
172 m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_LOGDLG));
175 // use the state of the "stop on copy/rename" option from the last time
176 UpdateData(FALSE);
178 // set the font to use in the log message view, configured in the settings dialog
179 CAppUtils::CreateFontForLogs(m_logFont);
180 GetDlgItem(IDC_MSGVIEW)->SetFont(&m_logFont);
181 // automatically detect URLs in the log message and turn them into links
182 GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_AUTOURLDETECT, TRUE, NULL);
183 // make the log message rich edit control send a message when the mouse pointer is over a link
184 GetDlgItem(IDC_MSGVIEW)->SendMessage(EM_SETEVENTMASK, NULL, ENM_LINK);
185 //m_LogList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);
187 // the "hide unrelated paths" checkbox should be indeterminate
188 m_cHidePaths.SetCheck(BST_INDETERMINATE);
191 // if there is a working copy, load the project properties
192 // to get information about the bugtraq: integration
193 if (m_hasWC)
194 m_ProjectProperties.ReadProps(m_path);
196 // the bugtraq issue id column is only shown if the bugtraq:url or bugtraq:regex is set
197 if ((!m_ProjectProperties.sUrl.IsEmpty())||(!m_ProjectProperties.sCheckRe.IsEmpty()))
198 m_bShowBugtraqColumn = true;
200 //theme.SetWindowTheme(m_LogList.GetSafeHwnd(), L"Explorer", NULL);
201 //theme.SetWindowTheme(m_ChangedFileListCtrl.GetSafeHwnd(), L"Explorer", NULL);
203 // set up the columns
204 m_LogList.DeleteAllItems();
205 m_LogList.InsertGitColumn();
207 m_ChangedFileListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLSTATUS |SVNSLC_COLADD|SVNSLC_COLDEL , _T("LogDlg"),(SVNSLC_POPALL ^ (SVNSLC_POPCOMMIT|SVNSLC_POPREVERT)),false);
209 GetDlgItem(IDC_LOGLIST)->UpdateData(FALSE);
211 m_logcounter = 0;
212 m_sMessageBuf.Preallocate(100000);
214 // set the dialog title to "Log - path/to/whatever/we/show/the/log/for"
215 SetDlgTitle(false);
217 m_tooltips.Create(this);
218 CheckRegexpTooltip();
220 SetSplitterRange();
222 // the filter control has a 'cancel' button (the red 'X'), we need to load its bitmap
223 m_cFilter.SetCancelBitmaps(IDI_CANCELNORMAL, IDI_CANCELPRESSED);
224 m_cFilter.SetInfoIcon(IDI_LOGFILTER);
225 m_cFilter.SetValidator(this);
227 AdjustControlSize(IDC_HIDEPATHS);
228 AdjustControlSize(IDC_LOG_FIRSTPARENT);
229 AdjustControlSize(IDC_LOG_ALLBRANCH);
231 GetClientRect(m_DlgOrigRect);
232 m_LogList.GetClientRect(m_LogListOrigRect);
233 GetDlgItem(IDC_MSGVIEW)->GetClientRect(m_MsgViewOrigRect);
234 m_ChangedFileListCtrl.GetClientRect(m_ChgOrigRect);
236 m_DateFrom.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);
237 m_DateTo.SendMessage(DTM_SETMCSTYLE, 0, MCS_WEEKNUMBERS|MCS_NOTODAY|MCS_NOTRAILINGDATES|MCS_NOSELCHANGEONNAV);
239 // resizable stuff
240 AddAnchor(IDC_STATIC_REF, TOP_LEFT);
241 AddAnchor(IDC_BUTTON_BROWSE_REF, TOP_LEFT);
242 AddAnchor(IDC_FROMLABEL, TOP_LEFT);
243 AddAnchor(IDC_DATEFROM, TOP_LEFT);
244 AddAnchor(IDC_TOLABEL, TOP_LEFT);
245 AddAnchor(IDC_DATETO, TOP_LEFT);
247 SetFilterCueText();
248 AddAnchor(IDC_SEARCHEDIT, TOP_LEFT, TOP_RIGHT);
250 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
251 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
252 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
253 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
254 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
256 AddAnchor(IDC_LOGINFO, BOTTOM_LEFT, BOTTOM_RIGHT);
257 AddAnchor(IDC_HIDEPATHS, BOTTOM_LEFT);
258 AddAnchor(IDC_LOG_ALLBRANCH,BOTTOM_LEFT);
259 AddAnchor(IDC_LOG_FIRSTPARENT, BOTTOM_LEFT);
260 //AddAnchor(IDC_GETALL, BOTTOM_LEFT);
261 AddAnchor(IDC_SHOWWHOLEPROJECT, BOTTOM_LEFT);
262 AddAnchor(IDC_REFRESH, BOTTOM_LEFT);
263 AddAnchor(IDC_STATBUTTON, BOTTOM_RIGHT);
264 AddAnchor(IDC_PROGRESS, BOTTOM_LEFT, BOTTOM_RIGHT);
265 AddAnchor(IDOK, BOTTOM_RIGHT);
266 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
267 AddAnchor(IDHELP, BOTTOM_RIGHT);
269 // SetPromptParentWindow(m_hWnd);
271 if (hWndExplorer)
272 CenterWindow(CWnd::FromHandle(hWndExplorer));
273 EnableSaveRestore(_T("LogDlg"));
275 DWORD yPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
276 DWORD yPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
277 RECT rcDlg, rcLogList, rcChgMsg;
278 GetClientRect(&rcDlg);
279 m_LogList.GetWindowRect(&rcLogList);
280 ScreenToClient(&rcLogList);
281 m_ChangedFileListCtrl.GetWindowRect(&rcChgMsg);
282 ScreenToClient(&rcChgMsg);
283 if (yPos1)
285 RECT rectSplitter;
286 m_wndSplitter1.GetWindowRect(&rectSplitter);
287 ScreenToClient(&rectSplitter);
288 int delta = yPos1 - rectSplitter.top;
290 if ((rcLogList.bottom + delta > rcLogList.top)&&(rcLogList.bottom + delta < rcChgMsg.bottom - 30))
292 m_wndSplitter1.SetWindowPos(NULL, 0, yPos1, 0, 0, SWP_NOSIZE);
293 DoSizeV1(delta);
296 if (yPos2)
298 RECT rectSplitter;
299 m_wndSplitter2.GetWindowRect(&rectSplitter);
300 ScreenToClient(&rectSplitter);
301 int delta = yPos2 - rectSplitter.top;
303 if ((rcChgMsg.top + delta < rcChgMsg.bottom)&&(rcChgMsg.top + delta > rcLogList.top + 30))
305 m_wndSplitter2.SetWindowPos(NULL, 0, yPos2, 0, 0, SWP_NOSIZE);
306 DoSizeV2(delta);
311 if (m_bSelect)
313 // the dialog is used to select revisions
314 // enable the OK button if appropriate
315 EnableOKButton();
317 else
319 // the dialog is used to just view log messages
320 // hide the OK button and set text on Cancel button to OK
321 GetDlgItemText(IDOK, temp);
322 SetDlgItemText(IDCANCEL, temp);
323 GetDlgItem(IDOK)->ShowWindow(SW_HIDE);
326 m_mergedRevs.clear();
328 // first start a thread to obtain the log messages without
329 // blocking the dialog
330 //m_tTo = 0;
331 //m_tFrom = (DWORD)-1;
333 m_LogList.m_Path=m_path;
334 m_LogList.FetchLogAsync(this);
336 GetDlgItem(IDC_LOGLIST)->SetFocus();
338 ShowStartRef();
339 return FALSE;
342 LRESULT CLogDlg::OnLogListLoading(WPARAM wParam, LPARAM /*lParam*/)
344 int cur=(int)wParam;
346 if( cur == GITLOG_START )
348 CString temp;
349 temp.LoadString(IDS_PROGRESSWAIT);
351 this->m_LogList.ShowText(temp, true);
353 // We use a progress bar while getting the logs
354 m_LogProgress.SetRange32(0, 100);
355 m_LogProgress.SetPos(0);
357 GetDlgItem(IDC_PROGRESS)->ShowWindow(TRUE);
359 //DialogEnableWindow(IDC_GETALL, FALSE);
360 //DialogEnableWindow(IDC_SHOWWHOLEPROJECT, FALSE);
361 //DialogEnableWindow(IDC_LOG_FIRSTPARENT, FALSE);
362 DialogEnableWindow(IDC_STATBUTTON, FALSE);
363 DialogEnableWindow(IDC_REFRESH, FALSE);
364 DialogEnableWindow(IDC_HIDEPATHS,FALSE);
366 }else if( cur == GITLOG_END)
369 //if (!m_bShowedAll)
370 DialogEnableWindow(IDC_SHOWWHOLEPROJECT, TRUE);
372 //DialogEnableWindow(IDC_GETALL, TRUE);
373 DialogEnableWindow(IDC_LOG_FIRSTPARENT, TRUE);
374 DialogEnableWindow(IDC_STATBUTTON, TRUE);
375 DialogEnableWindow(IDC_REFRESH, TRUE);
376 DialogEnableWindow(IDC_HIDEPATHS,TRUE);
378 // PostMessage(WM_TIMER, LOGFILTER_TIMER);
379 GetDlgItem(IDC_PROGRESS)->ShowWindow(FALSE);
380 //CTime time=m_LogList.GetOldestTime();
381 CTime begin,end;
382 m_LogList.GetTimeRange(begin,end);
383 m_DateFrom.SetTime(&begin);
384 m_DateTo.SetTime(&end);
388 }else
390 if(this->m_LogList.HasText())
392 this->m_LogList.ClearText();
393 UpdateLogInfoLabel();
395 m_LogProgress.SetPos(cur);
397 return 0;
399 void CLogDlg::SetDlgTitle(bool bOffline)
401 if (m_sTitle.IsEmpty())
402 GetWindowText(m_sTitle);
404 if (bOffline)
406 CString sTemp;
407 if (m_path.IsUrl())
408 sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetUIPathString());
409 else if (m_path.IsDirectory())
410 sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetWinPathString());
411 else
412 sTemp.Format(IDS_LOG_DLGTITLEOFFLINE, (LPCTSTR)m_sTitle, (LPCTSTR)m_path.GetFilename());
413 SetWindowText(sTemp);
415 else
417 if (m_path.IsUrl())
418 SetWindowText(m_sTitle + _T(" - ") + m_path.GetUIPathString());
419 else if (m_path.IsEmpty())
420 SetWindowText(m_sTitle + _T(" - ") + CString(_T("Whole Project")));
421 else if (m_path.IsDirectory())
422 SetWindowText(m_sTitle + _T(" - ") + m_path.GetWinPathString());
423 else
424 SetWindowText(m_sTitle + _T(" - ") + m_path.GetFilename());
428 void CLogDlg::CheckRegexpTooltip()
430 CWnd *pWnd = GetDlgItem(IDC_SEARCHEDIT);
431 // Since tooltip describes regexp features, show it only if regexps are enabled.
432 if (m_bFilterWithRegex)
434 m_tooltips.AddTool(pWnd, IDS_LOG_FILTER_REGEX_TT);
436 else
437 m_tooltips.DelTool(pWnd);
440 void CLogDlg::EnableOKButton()
442 if (m_bSelect)
444 // the dialog is used to select revisions
445 if (m_bSelectionMustBeSingle)
447 // enable OK button if only a single revision is selected
448 DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()==1));
450 else if (m_bSelectionMustBeContinuous)
451 DialogEnableWindow(IDOK, (m_LogList.GetSelectedCount()!=0)&&(m_LogList.IsSelectionContinuous()));
452 else
453 DialogEnableWindow(IDOK, m_LogList.GetSelectedCount()!=0);
455 else
456 DialogEnableWindow(IDOK, TRUE);
459 void CLogDlg::FillLogMessageCtrl(bool bShow /* = true*/)
461 // we fill here the log message rich edit control,
462 // and also populate the changed files list control
463 // according to the selected revision(s).
465 CWnd * pMsgView = GetDlgItem(IDC_MSGVIEW);
466 // empty the log message view
467 pMsgView->SetWindowText(_T(" "));
468 // empty the changed files list
469 m_ChangedFileListCtrl.SetRedraw(FALSE);
470 // InterlockedExchange(&m_bNoDispUpdates, TRUE);
471 m_currentChangedArray = NULL;
472 //m_ChangedFileListCtrl.SetExtendedStyle ( LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER );
473 m_ChangedFileListCtrl.DeleteAllItems();
475 // if we're not here to really show a selected revision, just
476 // get out of here after clearing the views, which is what is intended
477 // if that flag is not set.
478 if (!bShow)
480 // force a redraw
481 m_ChangedFileListCtrl.Invalidate();
482 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
483 m_ChangedFileListCtrl.SetRedraw(TRUE);
484 return;
487 // depending on how many revisions are selected, we have to do different
488 // tasks.
489 int selCount = m_LogList.GetSelectedCount();
490 if (selCount == 0)
492 // if nothing is selected, we have nothing more to do
493 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
494 m_ChangedFileListCtrl.SetRedraw(TRUE);
495 return;
497 else if (selCount == 1)
499 // if one revision is selected, we have to fill the log message view
500 // with the corresponding log message, and also fill the changed files
501 // list fully.
502 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
503 int selIndex = m_LogList.GetNextSelectedItem(pos);
504 if (selIndex >= m_LogList.m_arShownList.GetCount())
506 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
507 m_ChangedFileListCtrl.SetRedraw(TRUE);
508 return;
510 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(selIndex));
512 if(!pLogEntry->m_IsFull)
514 pMsgView->SetWindowText(_T("load ..."));
515 }else
517 // set the log message text
518 pMsgView->SetWindowText(_T("Commit:")+pLogEntry->m_CommitHash+_T("\r\n\r\n* ")+pLogEntry->m_Subject+_T("\n\n")+pLogEntry->m_Body);
519 // turn bug ID's into links if the bugtraq: properties have been set
520 // and we can find a match of those in the log message
522 CString text;
523 pMsgView->GetWindowText(text);
524 // the rich edit control doesn't count the CR char!
525 // to be exact: CRLF is treated as one char.
526 text.Replace(_T("\r"), _T(""));
528 m_ProjectProperties.FindBugID(text, pMsgView);
529 CAppUtils::FormatTextInRichEditControl(pMsgView);
531 int HidePaths=m_cHidePaths.GetState() & 0x0003;
532 CString matchpath=this->m_path.GetGitPathString();
534 for(int i=0;i<pLogEntry->m_Files.GetCount() && (!matchpath.IsEmpty());i++)
536 if( m_bWholeProject )
537 break;
539 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action &= ~(CTGitPath::LOGACTIONS_HIDE|CTGitPath::LOGACTIONS_GRAY);
541 if(pLogEntry->m_Files[i].GetGitPathString().Left(matchpath.GetLength()) != matchpath)
543 if(HidePaths==BST_CHECKED)
544 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action |= CTGitPath::LOGACTIONS_HIDE;
545 if(HidePaths==BST_INDETERMINATE)
546 ((CTGitPath&)pLogEntry->m_Files[i]).m_Action |= CTGitPath::LOGACTIONS_GRAY;
549 m_ChangedFileListCtrl.UpdateWithGitPathList(pLogEntry->m_Files);
550 m_ChangedFileListCtrl.m_CurrentVersion=pLogEntry->m_CommitHash;
551 m_ChangedFileListCtrl.Show(SVNSLC_SHOWVERSIONED);
553 m_ChangedFileListCtrl.SetRedraw(TRUE);
554 return;
558 else
560 // more than one revision is selected:
561 // the log message view must be emptied
562 // the changed files list contains all the changed paths from all
563 // selected revisions, with 'doubles' removed
564 m_currentChangedPathList = GetChangedPathsFromSelectedRevisions(true);
567 // redraw the views
568 // InterlockedExchange(&m_bNoDispUpdates, FALSE);
569 #if 0
570 if (m_currentChangedArray)
572 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedArray->GetCount());
573 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedArray->GetCount());
575 else if (m_currentChangedPathList.GetCount())
577 m_ChangedFileListCtrl.SetItemCountEx(m_currentChangedPathList.GetCount());
578 m_ChangedFileListCtrl.RedrawItems(0, m_currentChangedPathList.GetCount());
580 else
582 m_ChangedFileListCtrl.SetItemCountEx(0);
583 m_ChangedFileListCtrl.Invalidate();
585 #endif
586 // sort according to the settings
587 if (m_nSortColumnPathList > 0)
588 SetSortArrow(&m_ChangedFileListCtrl, m_nSortColumnPathList, m_bAscendingPathList);
589 else
590 SetSortArrow(&m_ChangedFileListCtrl, -1, false);
591 m_ChangedFileListCtrl.SetRedraw(TRUE);
595 void CLogDlg::OnBnClickedRefresh()
597 m_limit = 0;
598 Refresh (true);
601 void CLogDlg::Refresh (bool /*autoGoOnline*/)
603 m_LogList.Refresh();
608 BOOL CLogDlg::Cancel()
610 return m_bCancelled;
613 void CLogDlg::SaveSplitterPos()
615 if (!IsIconic())
617 CRegDWORD regPos1 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer1"));
618 CRegDWORD regPos2 = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\LogDlgSizer2"));
619 RECT rectSplitter;
620 m_wndSplitter1.GetWindowRect(&rectSplitter);
621 ScreenToClient(&rectSplitter);
622 regPos1 = rectSplitter.top;
623 m_wndSplitter2.GetWindowRect(&rectSplitter);
624 ScreenToClient(&rectSplitter);
625 regPos2 = rectSplitter.top;
629 void CLogDlg::OnCancel()
631 // canceling means stopping the working thread if it's still running.
632 if (this->IsThreadRunning())
634 m_LogList.TerminateThread();
636 UpdateData();
638 SaveSplitterPos();
639 __super::OnCancel();
642 CString CLogDlg::MakeShortMessage(const CString& message)
644 bool bFoundShort = true;
645 CString sShortMessage = m_ProjectProperties.GetLogSummary(message);
646 if (sShortMessage.IsEmpty())
648 bFoundShort = false;
649 sShortMessage = message;
651 // Remove newlines and tabs 'cause those are not shown nicely in the list control
652 sShortMessage.Replace(_T("\r"), _T(""));
653 sShortMessage.Replace(_T("\t"), _T(" "));
655 // Suppose the first empty line separates 'summary' from the rest of the message.
656 int found = sShortMessage.Find(_T("\n\n"));
657 // To avoid too short 'short' messages
658 // (e.g. if the message looks something like "Bugfix:\n\n*done this\n*done that")
659 // only use the empty newline as a separator if it comes after at least 15 chars.
660 if ((!bFoundShort)&&(found >= 15))
662 sShortMessage = sShortMessage.Left(found);
664 sShortMessage.Replace('\n', ' ');
665 return sShortMessage;
668 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*/)
670 #if 0
671 if (rev == SVN_INVALID_REVNUM)
673 m_childCounter--;
674 return TRUE;
677 // this is the callback function which receives the data for every revision we ask the log for
678 // we store this information here one by one.
679 m_logcounter += 1;
680 if (m_startrev == -1)
681 m_startrev = rev;
682 if (m_limit != 0)
684 m_limitcounter--;
685 m_LogProgress.SetPos(m_limit - m_limitcounter);
687 else if (m_startrev.IsNumber() && m_startrev.IsNumber())
688 m_LogProgress.SetPos((git_revnum_t)m_startrev-rev+(git_revnum_t)m_endrev);
689 __time64_t ttime = time/1000000L;
690 if (m_tTo < (DWORD)ttime)
691 m_tTo = (DWORD)ttime;
692 if (m_tFrom > (DWORD)ttime)
693 m_tFrom = (DWORD)ttime;
694 if ((m_lowestRev > rev)||(m_lowestRev < 0))
695 m_lowestRev = rev;
696 // Add as many characters from the log message to the list control
697 PLOGENTRYDATA pLogItem = new LOGENTRYDATA;
698 pLogItem->bCopies = !!copies;
700 // find out if this item was copied in the revision
701 BOOL copiedself = FALSE;
702 if (copies)
704 for (INT_PTR cpPathIndex = 0; cpPathIndex < cpaths->GetCount(); ++cpPathIndex)
706 LogChangedPath * cpath = cpaths->GetAt(cpPathIndex);
707 if (!cpath->sCopyFromPath.IsEmpty() && (cpath->sPath.Compare(m_sSelfRelativeURL) == 0))
709 // note: this only works if the log is fetched top-to-bottom
710 // but since we do that, it shouldn't be a problem
711 m_sSelfRelativeURL = cpath->sCopyFromPath;
712 copiedself = TRUE;
713 break;
717 pLogItem->bCopiedSelf = copiedself;
718 pLogItem->tmDate = ttime;
719 pLogItem->sAuthor = author;
720 pLogItem->sDate = date;
721 pLogItem->sShortMessage = MakeShortMessage(message);
722 pLogItem->dwFileChanges = filechanges;
723 pLogItem->actions = actions;
724 pLogItem->haschildren = haschildren;
725 pLogItem->childStackDepth = m_childCounter;
726 m_maxChild = max(m_childCounter, m_maxChild);
727 if (haschildren)
728 m_childCounter++;
729 pLogItem->sBugIDs = m_ProjectProperties.FindBugID(message).Trim();
731 // split multi line log entries and concatenate them
732 // again but this time with \r\n as line separators
733 // so that the edit control recognizes them
736 if (message.GetLength()>0)
738 m_sMessageBuf = message;
739 m_sMessageBuf.Replace(_T("\n\r"), _T("\n"));
740 m_sMessageBuf.Replace(_T("\r\n"), _T("\n"));
741 if (m_sMessageBuf.Right(1).Compare(_T("\n"))==0)
742 m_sMessageBuf = m_sMessageBuf.Left(m_sMessageBuf.GetLength()-1);
744 else
745 m_sMessageBuf.Empty();
746 pLogItem->sMessage = m_sMessageBuf;
747 pLogItem->Rev = rev;
749 // move-construct path array
751 pLogItem->pArChangedPaths = new LogChangedPathArray (*cpaths);
752 cpaths->RemoveAll();
754 catch (CException * e)
756 ::MessageBox(NULL, _T("not enough memory!"), _T("TortoiseGit"), MB_ICONERROR);
757 e->Delete();
758 m_bCancelled = TRUE;
760 m_logEntries.push_back(pLogItem);
761 m_arShownList.Add(pLogItem);
762 #endif
763 return TRUE;
766 GitRev g_rev;
767 //this is the thread function which calls the subversion function
772 void CLogDlg::CopyChangedSelectionToClipBoard()
775 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
776 if (pos == NULL)
777 return; // nothing is selected, get out of here
779 CString sPaths;
781 // CGitRev* pLogEntry = reinterpret_cast<CGitRev* >(m_LogList.m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
782 // if (pos)
784 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
785 while (pos)
787 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
788 CTGitPath *path = (CTGitPath*)m_ChangedFileListCtrl.GetItemData(nItem);
789 if(path)
790 sPaths += path->GetGitPathString();
791 sPaths += _T("\r\n");
794 #if 0
795 else
797 // only one revision is selected in the log dialog top pane
798 // but multiple items could be selected in the changed items list
799 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
800 while (pos)
802 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
803 LogChangedPath * changedlogpath = pLogEntry->pArChangedPaths->GetAt(nItem);
805 if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)
807 // some items are hidden! So find out which item the user really selected
808 INT_PTR selRealIndex = -1;
809 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)
811 if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
812 selRealIndex++;
813 if (selRealIndex == nItem)
815 changedlogpath = pLogEntry->pArChangedPaths->GetAt(hiddenindex);
816 break;
820 if (changedlogpath)
822 sPaths += changedlogpath->sPath;
823 sPaths += _T("\r\n");
827 #endif
828 sPaths.Trim();
829 CStringUtils::WriteAsciiStringToClipboard(sPaths, GetSafeHwnd());
833 BOOL CLogDlg::IsDiffPossible(LogChangedPath * /*changedpath*/, git_revnum_t rev)
835 #if 0
836 CString added, deleted;
837 if (changedpath == NULL)
838 return false;
840 if ((rev > 1)&&(changedpath->action != LOGACTIONS_DELETED))
842 if (changedpath->action == LOGACTIONS_ADDED) // file is added
844 if (changedpath->lCopyFromRev == 0)
845 return FALSE; // but file was not added with history
847 return TRUE;
849 #endif
850 return FALSE;
853 void CLogDlg::OnContextMenu(CWnd* pWnd, CPoint point)
855 // we have two separate context menus:
856 // one shown on the log message list control,
857 // the other shown in the changed-files list control
858 int selCount = m_LogList.GetSelectedCount();
859 if (pWnd == &m_LogList)
861 //ShowContextMenuForRevisions(pWnd, point);
863 else if (pWnd == &m_ChangedFileListCtrl)
865 //ShowContextMenuForChangedpaths(pWnd, point);
867 else if ((selCount == 1)&&(pWnd == GetDlgItem(IDC_MSGVIEW)))
869 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
870 int selIndex = -1;
871 if (pos)
872 selIndex = m_LogList.GetNextSelectedItem(pos);
873 if ((point.x == -1) && (point.y == -1))
875 CRect rect;
876 GetDlgItem(IDC_MSGVIEW)->GetClientRect(&rect);
877 ClientToScreen(&rect);
878 point = rect.CenterPoint();
880 CString sMenuItemText;
881 CMenu popup;
882 if (popup.CreatePopupMenu())
884 // add the 'default' entries
885 sMenuItemText.LoadString(IDS_SCIEDIT_COPY);
886 popup.AppendMenu(MF_STRING | MF_ENABLED, WM_COPY, sMenuItemText);
887 sMenuItemText.LoadString(IDS_SCIEDIT_SELECTALL);
888 popup.AppendMenu(MF_STRING | MF_ENABLED, EM_SETSEL, sMenuItemText);
890 //if (selIndex >= 0)
892 // popup.AppendMenu(MF_SEPARATOR);
893 // sMenuItemText.LoadString(IDS_LOG_POPUP_EDITLOG);
894 // popup.AppendMenu(MF_STRING | MF_ENABLED, CGitLogList::ID_EDITAUTHOR, sMenuItemText);
897 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
898 switch (cmd)
900 case 0:
901 break; // no command selected
902 case EM_SETSEL:
903 case WM_COPY:
904 ::SendMessage(GetDlgItem(IDC_MSGVIEW)->GetSafeHwnd(), cmd, 0, -1);
905 break;
906 case CGitLogList::ID_EDITAUTHOR:
907 EditLogMessage(selIndex);
908 break;
915 LRESULT CLogDlg::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
917 #if 0
918 ASSERT(m_pFindDialog != NULL);
920 if (m_pFindDialog->IsTerminating())
922 // invalidate the handle identifying the dialog box.
923 m_pFindDialog = NULL;
924 return 0;
927 if(m_pFindDialog->FindNext())
929 //read data from dialog
930 CString FindText = m_pFindDialog->GetFindString();
931 bool bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
932 bool bFound = false;
933 tr1::wregex pat;
934 bool bRegex = ValidateRegexp(FindText, pat, bMatchCase);
936 tr1::regex_constants::match_flag_type flags = tr1::regex_constants::match_not_null;
938 int i;
939 for (i = this->m_nSearchIndex; i<m_arShownList.GetCount()&&!bFound; i++)
941 if (bRegex)
943 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
945 if (regex_search(wstring((LPCTSTR)pLogEntry->sMessage), pat, flags))
947 bFound = true;
948 break;
950 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
951 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
953 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
954 if (regex_search(wstring((LPCTSTR)cpath->sCopyFromPath), pat, flags))
956 bFound = true;
957 --i;
958 break;
960 if (regex_search(wstring((LPCTSTR)cpath->sPath), pat, flags))
962 bFound = true;
963 --i;
964 break;
968 else
970 if (bMatchCase)
972 if (m_logEntries[i]->sMessage.Find(FindText) >= 0)
974 bFound = true;
975 break;
977 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
978 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
979 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
981 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
982 if (cpath->sCopyFromPath.Find(FindText)>=0)
984 bFound = true;
985 --i;
986 break;
988 if (cpath->sPath.Find(FindText)>=0)
990 bFound = true;
991 --i;
992 break;
996 else
998 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
999 CString msg = pLogEntry->sMessage;
1000 msg = msg.MakeLower();
1001 CString find = FindText.MakeLower();
1002 if (msg.Find(find) >= 0)
1004 bFound = TRUE;
1005 break;
1007 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
1008 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
1010 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
1011 CString lowerpath = cpath->sCopyFromPath;
1012 lowerpath.MakeLower();
1013 if (lowerpath.Find(find)>=0)
1015 bFound = TRUE;
1016 --i;
1017 break;
1019 lowerpath = cpath->sPath;
1020 lowerpath.MakeLower();
1021 if (lowerpath.Find(find)>=0)
1023 bFound = TRUE;
1024 --i;
1025 break;
1030 } // for (i = this->m_nSearchIndex; i<m_arShownList.GetItemCount()&&!bFound; i++)
1031 if (bFound)
1033 this->m_nSearchIndex = (i+1);
1034 m_LogList.EnsureVisible(i, FALSE);
1035 m_LogList.SetItemState(m_LogList.GetSelectionMark(), 0, LVIS_SELECTED);
1036 m_LogList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
1037 m_LogList.SetSelectionMark(i);
1038 FillLogMessageCtrl();
1039 UpdateData(FALSE);
1040 m_nSearchIndex++;
1041 if (m_nSearchIndex >= m_arShownList.GetCount())
1042 m_nSearchIndex = (int)m_arShownList.GetCount()-1;
1044 } // if(m_pFindDialog->FindNext())
1045 UpdateLogInfoLabel();
1046 #endif
1047 return 0;
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
1058 if (this->IsThreadRunning())
1060 m_LogList.TerminateThread();
1062 UpdateData();
1063 // check that one and only one row is selected
1064 if (m_LogList.GetSelectedCount() == 1)
1066 // get the selected row
1067 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1068 int selIndex = m_LogList.GetNextSelectedItem(pos);
1069 if (selIndex < m_LogList.m_arShownList.GetCount())
1071 // all ok, pick up the revision
1072 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(selIndex));
1073 // extract the hash
1074 m_sSelectedHash = pLogEntry->m_CommitHash;
1077 UpdateData(FALSE);
1078 SaveSplitterPos();
1079 __super::OnOK();
1081 #if 0
1082 if (!GetDlgItem(IDOK)->IsWindowVisible() && GetFocus() != GetDlgItem(IDCANCEL))
1083 return; // the Cancel button works as the OK button. But if the cancel button has not the focus, do nothing.
1085 CString temp;
1086 CString buttontext;
1087 GetDlgItemText(IDOK, buttontext);
1088 temp.LoadString(IDS_MSGBOX_CANCEL);
1089 if (temp.Compare(buttontext) != 0)
1090 __super::OnOK(); // only exit if the button text matches, and that will match only if the thread isn't running anymore
1091 m_bCancelled = TRUE;
1092 m_selectedRevs.Clear();
1093 m_selectedRevsOneRange.Clear();
1094 if (m_pNotifyWindow)
1096 int selIndex = m_LogList.GetSelectionMark();
1097 if (selIndex >= 0)
1099 PLOGENTRYDATA pLogEntry = NULL;
1100 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1101 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1102 m_selectedRevs.AddRevision(pLogEntry->Rev);
1103 git_revnum_t lowerRev = pLogEntry->Rev;
1104 git_revnum_t higherRev = lowerRev;
1105 while (pos)
1107 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1108 git_revnum_t rev = pLogEntry->Rev;
1109 m_selectedRevs.AddRevision(pLogEntry->Rev);
1110 if (lowerRev > rev)
1111 lowerRev = rev;
1112 if (higherRev < rev)
1113 higherRev = rev;
1115 if (m_sFilterText.IsEmpty() && m_nSortColumn == 0 && IsSelectionContinuous())
1117 m_selectedRevsOneRange.AddRevRange(lowerRev, higherRev);
1119 BOOL bSentMessage = FALSE;
1120 if (m_LogList.GetSelectedCount() == 1)
1122 // if only one revision is selected, check if the path/url with which the dialog was started
1123 // was directly affected in that revision. If it was, then check if our path was copied from somewhere.
1124 // if it was copied, use the copy from revision as lowerRev
1125 if ((pLogEntry)&&(pLogEntry->pArChangedPaths)&&(lowerRev == higherRev))
1127 CString sUrl = m_path.GetGitPathString();
1128 if (!m_path.IsUrl())
1130 sUrl = GetURLFromPath(m_path);
1132 sUrl = sUrl.Mid(m_sRepositoryRoot.GetLength());
1133 for (int cp = 0; cp < pLogEntry->pArChangedPaths->GetCount(); ++cp)
1135 LogChangedPath * pData = pLogEntry->pArChangedPaths->GetAt(cp);
1136 if (pData)
1138 if (sUrl.Compare(pData->sPath) == 0)
1140 if (!pData->sCopyFromPath.IsEmpty())
1142 lowerRev = pData->lCopyFromRev;
1143 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART), lowerRev);
1144 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND), higherRev);
1145 m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);
1146 bSentMessage = TRUE;
1153 if ( !bSentMessage )
1155 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTSTART | MERGE_REVSELECTMINUSONE), lowerRev);
1156 m_pNotifyWindow->SendMessage(WM_REVSELECTED, m_wParam & (MERGE_REVSELECTEND | MERGE_REVSELECTMINUSONE), higherRev);
1157 m_pNotifyWindow->SendMessage(WM_REVLIST, m_selectedRevs.GetCount(), (LPARAM)&m_selectedRevs);
1158 if (m_selectedRevsOneRange.GetCount())
1159 m_pNotifyWindow->SendMessage(WM_REVLISTONERANGE, 0, (LPARAM)&m_selectedRevsOneRange);
1163 UpdateData();
1164 CRegDWORD reg = CRegDWORD(_T("Software\\TortoiseGit\\ShowAllEntry"));
1165 reg = m_btnShow.GetCurrentEntry();
1166 SaveSplitterPos();
1167 #endif
1170 void CLogDlg::OnNMDblclkChangedFileList(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1172 // a double click on an entry in the changed-files list has happened
1173 *pResult = 0;
1175 DiffSelectedFile();
1178 void CLogDlg::DiffSelectedFile()
1180 #if 0
1181 if (m_bThreadRunning)
1182 return;
1183 UpdateLogInfoLabel();
1184 INT_PTR selIndex = m_ChangedFileListCtrl.GetSelectionMark();
1185 if (selIndex < 0)
1186 return;
1187 if (m_ChangedFileListCtrl.GetSelectedCount() == 0)
1188 return;
1189 // find out if there's an entry selected in the log list
1190 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1191 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1192 git_revnum_t rev1 = pLogEntry->Rev;
1193 git_revnum_t rev2 = rev1;
1194 if (pos)
1196 while (pos)
1198 // there's at least a second entry selected in the log list: several revisions selected!
1199 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1200 if (pLogEntry)
1202 rev1 = max(rev1,(long)pLogEntry->Rev);
1203 rev2 = min(rev2,(long)pLogEntry->Rev);
1206 rev2--;
1207 // now we have both revisions selected in the log list, so we can do a diff of the selected
1208 // entry in the changed files list with these two revisions.
1209 DoDiffFromLog(selIndex, rev1, rev2, false, false);
1211 else
1213 rev2 = rev1-1;
1214 // nothing or only one revision selected in the log list
1215 LogChangedPath * changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);
1217 if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)
1219 // some items are hidden! So find out which item the user really clicked on
1220 INT_PTR selRealIndex = -1;
1221 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)
1223 if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
1224 selRealIndex++;
1225 if (selRealIndex == selIndex)
1227 selIndex = hiddenindex;
1228 changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);
1229 break;
1234 if (IsDiffPossible(changedpath, rev1))
1236 // diffs with renamed files are possible
1237 if ((changedpath)&&(!changedpath->sCopyFromPath.IsEmpty()))
1238 rev2 = changedpath->lCopyFromRev;
1239 else
1241 // if the path was modified but the parent path was 'added with history'
1242 // then we have to use the copy from revision of the parent path
1243 CTGitPath cpath = CTGitPath(changedpath->sPath);
1244 for (int flist = 0; flist < pLogEntry->pArChangedPaths->GetCount(); ++flist)
1246 CTGitPath p = CTGitPath(pLogEntry->pArChangedPaths->GetAt(flist)->sPath);
1247 if (p.IsAncestorOf(cpath))
1249 if (!pLogEntry->pArChangedPaths->GetAt(flist)->sCopyFromPath.IsEmpty())
1250 rev2 = pLogEntry->pArChangedPaths->GetAt(flist)->lCopyFromRev;
1254 DoDiffFromLog(selIndex, rev1, rev2, false, false);
1256 else
1258 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath->sPath));
1259 CTGitPath tempfile2 = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(changedpath->sPath));
1260 GitRev r = rev1;
1261 // deleted files must be opened from the revision before the deletion
1262 if (changedpath->action == LOGACTIONS_DELETED)
1263 r = rev1-1;
1264 m_bCancelled = false;
1266 CProgressDlg progDlg;
1267 progDlg.SetTitle(IDS_APPNAME);
1268 progDlg.SetAnimation(IDR_DOWNLOAD);
1269 CString sInfoLine;
1270 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)(m_sRepositoryRoot + changedpath->sPath), (LPCTSTR)r.ToString());
1271 progDlg.SetLine(1, sInfoLine, true);
1272 SetAndClearProgressInfo(&progDlg);
1273 progDlg.ShowModeless(m_hWnd);
1275 if (!Cat(CTGitPath(m_sRepositoryRoot + changedpath->sPath), r, r, tempfile))
1277 m_bCancelled = false;
1278 if (!Cat(CTGitPath(m_sRepositoryRoot + changedpath->sPath), GitRev::REV_HEAD, r, tempfile))
1280 progDlg.Stop();
1281 SetAndClearProgressInfo((HWND)NULL);
1282 CMessageBox::Show(m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1283 return;
1286 progDlg.Stop();
1287 SetAndClearProgressInfo((HWND)NULL);
1289 CString sName1, sName2;
1290 sName1.Format(_T("%s - Revision %ld"), (LPCTSTR)CPathUtils::GetFileNameFromPath(changedpath->sPath), (git_revnum_t)rev1);
1291 sName2.Format(_T("%s - Revision %ld"), (LPCTSTR)CPathUtils::GetFileNameFromPath(changedpath->sPath), (git_revnum_t)rev1-1);
1292 CAppUtils::DiffFlags flags;
1293 flags.AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1294 if (changedpath->action == LOGACTIONS_DELETED)
1295 CAppUtils::StartExtDiff(tempfile, tempfile2, sName2, sName1, flags);
1296 else
1297 CAppUtils::StartExtDiff(tempfile2, tempfile, sName2, sName1, flags);
1300 #endif
1304 void CLogDlg::DoDiffFromLog(INT_PTR selIndex, GitRev* rev1, GitRev* rev2, bool blame, bool unified)
1306 DialogEnableWindow(IDOK, FALSE);
1307 // SetPromptApp(&theApp);
1308 theApp.DoWaitCursor(1);
1310 CString temppath;
1311 GetTempPath(temppath);
1313 CString file1;
1314 file1.Format(_T("%s%s_%s%s"),
1315 temppath,
1316 (*m_currentChangedArray)[selIndex].GetBaseFilename(),
1317 rev1->m_CommitHash.Left(6),
1318 (*m_currentChangedArray)[selIndex].GetFileExtension());
1320 CString file2;
1321 file2.Format(_T("%s\\%s_%s%s"),
1322 temppath,
1323 (*m_currentChangedArray)[selIndex].GetBaseFilename(),
1324 rev2->m_CommitHash.Left(6),
1325 (*m_currentChangedArray)[selIndex].GetFileExtension());
1327 CString cmd;
1329 cmd.Format(_T("git.exe cat-file -p %s:%s"),rev1->m_CommitHash,(*m_currentChangedArray)[selIndex].GetGitPathString());
1330 g_Git.RunLogFile(cmd,file1);
1331 cmd.Format(_T("git.exe cat-file -p %s:%s"),rev2->m_CommitHash,(*m_currentChangedArray)[selIndex].GetGitPathString());
1332 g_Git.RunLogFile(cmd,file2);
1334 CAppUtils::DiffFlags flags;
1335 CAppUtils::StartExtDiff(file1,file2,_T("A"),_T("B"),flags);
1337 #if 0
1338 //get the filename
1339 CString filepath;
1340 if (Git::PathIsURL(m_path))
1342 filepath = m_path.GetGitPathString();
1344 else
1346 filepath = GetURLFromPath(m_path);
1347 if (filepath.IsEmpty())
1349 theApp.DoWaitCursor(-1);
1350 CString temp;
1351 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
1352 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
1353 TRACE(_T("could not retrieve the URL of the file!\n"));
1354 EnableOKButton();
1355 theApp.DoWaitCursor(-11);
1356 return; //exit
1359 m_bCancelled = FALSE;
1360 filepath = GetRepositoryRoot(CTGitPath(filepath));
1362 CString firstfile, secondfile;
1363 if (m_LogList.GetSelectedCount()==1)
1365 int s = m_LogList.GetSelectionMark();
1366 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(s));
1367 LogChangedPath * changedpath = pLogEntry->pArChangedPaths->GetAt(selIndex);
1368 firstfile = changedpath->sPath;
1369 secondfile = firstfile;
1370 if ((rev2 == rev1-1)&&(changedpath->lCopyFromRev > 0)) // is it an added file with history?
1372 secondfile = changedpath->sCopyFromPath;
1373 rev2 = changedpath->lCopyFromRev;
1376 else
1378 firstfile = m_currentChangedPathList[selIndex].GetGitPathString();
1379 secondfile = firstfile;
1382 firstfile = filepath + firstfile.Trim();
1383 secondfile = filepath + secondfile.Trim();
1385 GitDiff diff(this, this->m_hWnd, true);
1386 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1387 diff.SetHEADPeg(m_LogRevision);
1388 if (unified)
1390 if (PromptShown())
1391 diff.ShowUnifiedDiff(CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1);
1392 else
1393 CAppUtils::StartShowUnifiedDiff(m_hWnd, CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1, GitRev(), m_LogRevision);
1395 else
1397 if (diff.ShowCompare(CTGitPath(secondfile), rev2, CTGitPath(firstfile), rev1, GitRev(), false, blame))
1399 if (firstfile.Compare(secondfile)==0)
1401 git_revnum_t baseRev = 0;
1402 diff.DiffProps(CTGitPath(firstfile), rev2, rev1, baseRev);
1407 #endif
1409 theApp.DoWaitCursor(-1);
1410 EnableOKButton();
1413 BOOL CLogDlg::Open(bool /*bOpenWith*/,CString changedpath, git_revnum_t rev)
1415 #if 0
1416 DialogEnableWindow(IDOK, FALSE);
1417 SetPromptApp(&theApp);
1418 theApp.DoWaitCursor(1);
1419 CString filepath;
1420 if (Git::PathIsURL(m_path))
1422 filepath = m_path.GetGitPathString();
1424 else
1426 filepath = GetURLFromPath(m_path);
1427 if (filepath.IsEmpty())
1429 theApp.DoWaitCursor(-1);
1430 CString temp;
1431 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
1432 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
1433 TRACE(_T("could not retrieve the URL of the file!\n"));
1434 EnableOKButton();
1435 return FALSE;
1438 m_bCancelled = false;
1439 filepath = GetRepositoryRoot(CTGitPath(filepath));
1440 filepath += changedpath;
1442 CProgressDlg progDlg;
1443 progDlg.SetTitle(IDS_APPNAME);
1444 progDlg.SetAnimation(IDR_DOWNLOAD);
1445 CString sInfoLine;
1446 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)filepath, (LPCTSTR)GitRev(rev).ToString());
1447 progDlg.SetLine(1, sInfoLine, true);
1448 SetAndClearProgressInfo(&progDlg);
1449 progDlg.ShowModeless(m_hWnd);
1451 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, CTGitPath(filepath), rev);
1452 m_bCancelled = false;
1453 if (!Cat(CTGitPath(filepath), GitRev(rev), rev, tempfile))
1455 progDlg.Stop();
1456 SetAndClearProgressInfo((HWND)NULL);
1457 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1458 EnableOKButton();
1459 theApp.DoWaitCursor(-1);
1460 return FALSE;
1462 progDlg.Stop();
1463 SetAndClearProgressInfo((HWND)NULL);
1464 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1465 if (!bOpenWith)
1467 int ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
1468 if (ret <= HINSTANCE_ERROR)
1469 bOpenWith = true;
1471 if (bOpenWith)
1473 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1474 cmd += tempfile.GetWinPathString() + _T(" ");
1475 CAppUtils::LaunchApplication(cmd, NULL, false);
1477 EnableOKButton();
1478 theApp.DoWaitCursor(-1);
1479 #endif
1480 return TRUE;
1483 void CLogDlg::EditAuthor(const CLogDataVector& /*logs*/)
1485 #if 0
1486 CString url;
1487 CString name;
1488 if (logs.size() == 0)
1489 return;
1490 DialogEnableWindow(IDOK, FALSE);
1491 SetPromptApp(&theApp);
1492 theApp.DoWaitCursor(1);
1493 if (Git::PathIsURL(m_path))
1494 url = m_path.GetGitPathString();
1495 else
1497 url = GetURLFromPath(m_path);
1499 name = Git_PROP_REVISION_AUTHOR;
1501 CString value = RevPropertyGet(name, CTGitPath(url), logs[0]->Rev);
1502 CString sOldValue = value;
1503 value.Replace(_T("\n"), _T("\r\n"));
1504 CInputDlg dlg(this);
1505 dlg.m_sHintText.LoadString(IDS_LOG_AUTHOR);
1506 dlg.m_sInputText = value;
1507 dlg.m_sTitle.LoadString(IDS_LOG_AUTHOREDITTITLE);
1508 dlg.m_pProjectProperties = &m_ProjectProperties;
1509 dlg.m_bUseLogWidth = false;
1510 if (dlg.DoModal() == IDOK)
1512 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1514 LogCache::CCachedLogInfo* toUpdate
1515 = GetLogCache (CTGitPath (m_sRepositoryRoot));
1517 CProgressDlg progDlg;
1518 progDlg.SetTitle(IDS_APPNAME);
1519 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PROGRESSWAIT)));
1520 progDlg.SetTime(true);
1521 progDlg.SetShowProgressBar(true);
1522 progDlg.ShowModeless(m_hWnd);
1523 for (DWORD i=0; i<logs.size(); ++i)
1525 if (!RevPropertySet(name, dlg.m_sInputText, sOldValue, CTGitPath(url), logs[i]->Rev))
1527 progDlg.Stop();
1528 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1529 break;
1531 else
1534 logs[i]->sAuthor = dlg.m_sInputText;
1535 m_LogList.Invalidate();
1537 // update the log cache
1539 if (toUpdate != NULL)
1541 // log caching is active
1543 LogCache::CCachedLogInfo newInfo;
1544 newInfo.Insert ( logs[i]->Rev
1545 , (const char*) CUnicodeUtils::GetUTF8 (logs[i]->sAuthor)
1546 , ""
1548 , LogCache::CRevisionInfoContainer::HAS_AUTHOR);
1550 toUpdate->Update (newInfo);
1553 progDlg.SetProgress64(i, logs.size());
1555 progDlg.Stop();
1557 theApp.DoWaitCursor(-1);
1558 EnableOKButton();
1559 #endif
1562 void CLogDlg::EditLogMessage(int /*index*/)
1564 #if 0
1565 CString url;
1566 CString name;
1567 DialogEnableWindow(IDOK, FALSE);
1568 SetPromptApp(&theApp);
1569 theApp.DoWaitCursor(1);
1570 if (Git::PathIsURL(m_path))
1571 url = m_path.GetGitPathString();
1572 else
1574 url = GetURLFromPath(m_path);
1576 name = Git_PROP_REVISION_LOG;
1578 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(index));
1579 m_bCancelled = FALSE;
1580 CString value = RevPropertyGet(name, CTGitPath(url), pLogEntry->Rev);
1581 CString sOldValue = value;
1582 value.Replace(_T("\n"), _T("\r\n"));
1583 CInputDlg dlg(this);
1584 dlg.m_sHintText.LoadString(IDS_LOG_MESSAGE);
1585 dlg.m_sInputText = value;
1586 dlg.m_sTitle.LoadString(IDS_LOG_MESSAGEEDITTITLE);
1587 dlg.m_pProjectProperties = &m_ProjectProperties;
1588 dlg.m_bUseLogWidth = true;
1589 if (dlg.DoModal() == IDOK)
1591 dlg.m_sInputText.Replace(_T("\r"), _T(""));
1592 if (!RevPropertySet(name, dlg.m_sInputText, sOldValue, CTGitPath(url), pLogEntry->Rev))
1594 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1596 else
1598 pLogEntry->sShortMessage = MakeShortMessage(dlg.m_sInputText);
1599 // split multi line log entries and concatenate them
1600 // again but this time with \r\n as line separators
1601 // so that the edit control recognizes them
1602 if (dlg.m_sInputText.GetLength()>0)
1604 m_sMessageBuf = dlg.m_sInputText;
1605 dlg.m_sInputText.Replace(_T("\n\r"), _T("\n"));
1606 dlg.m_sInputText.Replace(_T("\r\n"), _T("\n"));
1607 if (dlg.m_sInputText.Right(1).Compare(_T("\n"))==0)
1608 dlg.m_sInputText = dlg.m_sInputText.Left(dlg.m_sInputText.GetLength()-1);
1610 else
1611 dlg.m_sInputText.Empty();
1612 pLogEntry->sMessage = dlg.m_sInputText;
1613 pLogEntry->sBugIDs = m_ProjectProperties.FindBugID(dlg.m_sInputText);
1614 CWnd * pMsgView = GetDlgItem(IDC_MSGVIEW);
1615 pMsgView->SetWindowText(_T(" "));
1616 pMsgView->SetWindowText(dlg.m_sInputText);
1617 m_ProjectProperties.FindBugID(dlg.m_sInputText, pMsgView);
1618 m_LogList.Invalidate();
1620 // update the log cache
1622 LogCache::CCachedLogInfo* toUpdate
1623 = GetLogCache (CTGitPath (m_sRepositoryRoot));
1624 if (toUpdate != NULL)
1626 // log caching is active
1628 LogCache::CCachedLogInfo newInfo;
1629 newInfo.Insert ( pLogEntry->Rev
1630 , ""
1631 , (const char*) CUnicodeUtils::GetUTF8 (pLogEntry->sMessage)
1633 , LogCache::CRevisionInfoContainer::HAS_COMMENT);
1635 toUpdate->Update (newInfo);
1639 theApp.DoWaitCursor(-1);
1640 EnableOKButton();
1641 #endif
1644 BOOL CLogDlg::PreTranslateMessage(MSG* pMsg)
1646 // Skip Ctrl-C when copying text out of the log message or search filter
1647 BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );
1648 if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
1650 if (GetFocus()==GetDlgItem(IDC_LOGLIST))
1652 if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1654 m_LogList.DiffSelectedRevWithPrevious();
1655 return TRUE;
1658 if (GetFocus()==GetDlgItem(IDC_LOGMSG))
1660 DiffSelectedFile();
1661 return TRUE;
1664 if (m_hAccel && !bSkipAccelerator)
1666 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
1667 if (ret)
1668 return TRUE;
1671 if(::IsWindow(m_tooltips.m_hWnd))
1672 m_tooltips.RelayEvent(pMsg);
1673 return __super::PreTranslateMessage(pMsg);
1677 BOOL CLogDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1679 //if (this->IsThreadRunning())
1680 if(m_LogList.m_bNoDispUpdates)
1682 // only show the wait cursor over the list control
1683 if ((pWnd)&&
1684 ((pWnd == GetDlgItem(IDC_LOGLIST))||
1685 (pWnd == GetDlgItem(IDC_MSGVIEW))||
1686 (pWnd == GetDlgItem(IDC_LOGMSG))))
1688 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT));
1689 SetCursor(hCur);
1690 return TRUE;
1693 if ((pWnd) && (pWnd == GetDlgItem(IDC_MSGVIEW)))
1694 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
1696 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
1697 SetCursor(hCur);
1698 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
1701 void CLogDlg::OnBnClickedHelp()
1703 OnHelp();
1706 void CLogDlg::OnLvnItemchangedLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1708 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1709 *pResult = 0;
1710 //if (this->IsThreadRunning())
1711 if(m_LogList.m_bNoDispUpdates)
1712 return;
1713 if (pNMLV->iItem >= 0)
1715 m_nSearchIndex = pNMLV->iItem;
1716 if (pNMLV->iSubItem != 0)
1717 return;
1718 if ((pNMLV->iItem == m_LogList.m_arShownList.GetCount()))
1720 // remove the selected state
1721 if (pNMLV->uChanged & LVIF_STATE)
1723 m_LogList.SetItemState(pNMLV->iItem, 0, LVIS_SELECTED);
1724 FillLogMessageCtrl();
1725 UpdateData(FALSE);
1726 UpdateLogInfoLabel();
1728 return;
1730 if (pNMLV->uChanged & LVIF_STATE)
1732 FillLogMessageCtrl();
1733 UpdateData(FALSE);
1736 else
1738 FillLogMessageCtrl();
1739 UpdateData(FALSE);
1741 EnableOKButton();
1742 UpdateLogInfoLabel();
1745 void CLogDlg::OnEnLinkMsgview(NMHDR *pNMHDR, LRESULT *pResult)
1747 ENLINK *pEnLink = reinterpret_cast<ENLINK *>(pNMHDR);
1748 if (pEnLink->msg == WM_LBUTTONUP)
1750 CString url, msg;
1751 GetDlgItemText(IDC_MSGVIEW, msg);
1752 msg.Replace(_T("\r\n"), _T("\n"));
1753 url = msg.Mid(pEnLink->chrg.cpMin, pEnLink->chrg.cpMax-pEnLink->chrg.cpMin);
1754 if (!::PathIsURL(url))
1756 url = m_ProjectProperties.GetBugIDUrl(url);
1757 url = GetAbsoluteUrlFromRelativeUrl(url);
1759 if (!url.IsEmpty())
1760 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
1762 *pResult = 0;
1765 void CLogDlg::OnBnClickedStatbutton()
1768 if (this->IsThreadRunning())
1769 return;
1770 if (m_LogList.m_arShownList.IsEmpty())
1771 return; // nothing is shown, so no statistics.
1772 // the statistics dialog expects the log entries to be sorted by date
1773 SortByColumn(3, false);
1774 CPtrArray shownlist;
1775 m_LogList.RecalculateShownList(&shownlist);
1776 // create arrays which are aware of the current filter
1777 CStringArray m_arAuthorsFiltered;
1778 CDWordArray m_arDatesFiltered;
1779 CDWordArray m_arFileChangesFiltered;
1780 for (INT_PTR i=0; i<shownlist.GetCount(); ++i)
1782 GitRev* pLogEntry = reinterpret_cast<GitRev*>(shownlist.GetAt(i));
1783 CString strAuthor = pLogEntry->m_AuthorName;
1784 if ( strAuthor.IsEmpty() )
1786 strAuthor.LoadString(IDS_STATGRAPH_EMPTYAUTHOR);
1788 m_arAuthorsFiltered.Add(strAuthor);
1789 m_arDatesFiltered.Add(pLogEntry->m_AuthorDate.GetTime());
1790 m_arFileChangesFiltered.Add(pLogEntry->m_Files.GetCount());
1792 CStatGraphDlg dlg;
1793 dlg.m_parAuthors = &m_arAuthorsFiltered;
1794 dlg.m_parDates = &m_arDatesFiltered;
1795 dlg.m_parFileChanges = &m_arFileChangesFiltered;
1796 dlg.m_path = m_path;
1797 dlg.DoModal();
1798 // restore the previous sorting
1799 SortByColumn(m_nSortColumn, m_bAscending);
1800 OnTimer(LOGFILTER_TIMER);
1804 void CLogDlg::DoSizeV1(int delta)
1807 RemoveAnchor(IDC_LOGLIST);
1808 RemoveAnchor(IDC_SPLITTERTOP);
1809 RemoveAnchor(IDC_MSGVIEW);
1810 RemoveAnchor(IDC_SPLITTERBOTTOM);
1811 RemoveAnchor(IDC_LOGMSG);
1812 CSplitterControl::ChangeHeight(&m_LogList, delta, CW_TOPALIGN);
1813 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), -delta, CW_BOTTOMALIGN);
1814 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
1815 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
1816 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
1817 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
1818 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
1819 ArrangeLayout();
1820 AdjustMinSize();
1821 SetSplitterRange();
1822 m_LogList.Invalidate();
1823 GetDlgItem(IDC_MSGVIEW)->Invalidate();
1827 void CLogDlg::DoSizeV2(int delta)
1830 RemoveAnchor(IDC_LOGLIST);
1831 RemoveAnchor(IDC_SPLITTERTOP);
1832 RemoveAnchor(IDC_MSGVIEW);
1833 RemoveAnchor(IDC_SPLITTERBOTTOM);
1834 RemoveAnchor(IDC_LOGMSG);
1835 CSplitterControl::ChangeHeight(GetDlgItem(IDC_MSGVIEW), delta, CW_TOPALIGN);
1836 CSplitterControl::ChangeHeight(&m_ChangedFileListCtrl, -delta, CW_BOTTOMALIGN);
1837 AddAnchor(IDC_LOGLIST, TOP_LEFT, TOP_RIGHT);
1838 AddAnchor(IDC_SPLITTERTOP, TOP_LEFT, TOP_RIGHT);
1839 AddAnchor(IDC_MSGVIEW, TOP_LEFT, BOTTOM_RIGHT);
1840 AddAnchor(IDC_SPLITTERBOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT);
1841 AddAnchor(IDC_LOGMSG, BOTTOM_LEFT, BOTTOM_RIGHT);
1842 ArrangeLayout();
1843 AdjustMinSize();
1844 SetSplitterRange();
1845 GetDlgItem(IDC_MSGVIEW)->Invalidate();
1846 m_ChangedFileListCtrl.Invalidate();
1850 void CLogDlg::AdjustMinSize()
1852 // adjust the minimum size of the dialog to prevent the resizing from
1853 // moving the list control too far down.
1854 CRect rcChgListView;
1855 m_ChangedFileListCtrl.GetClientRect(rcChgListView);
1856 CRect rcLogList;
1857 m_LogList.GetClientRect(rcLogList);
1859 SetMinTrackSize(CSize(m_DlgOrigRect.Width(),
1860 m_DlgOrigRect.Height()-m_ChgOrigRect.Height()-m_LogListOrigRect.Height()-m_MsgViewOrigRect.Height()
1861 +rcChgListView.Height()+rcLogList.Height()+60));
1864 LRESULT CLogDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
1866 switch (message) {
1867 case WM_NOTIFY:
1868 if (wParam == IDC_SPLITTERTOP)
1870 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
1871 DoSizeV1(pHdr->delta);
1873 else if (wParam == IDC_SPLITTERBOTTOM)
1875 SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;
1876 DoSizeV2(pHdr->delta);
1878 break;
1881 return CResizableDialog::DefWindowProc(message, wParam, lParam);
1884 void CLogDlg::SetSplitterRange()
1886 if ((m_LogList)&&(m_ChangedFileListCtrl))
1888 CRect rcTop;
1889 m_LogList.GetWindowRect(rcTop);
1890 ScreenToClient(rcTop);
1891 CRect rcMiddle;
1892 GetDlgItem(IDC_MSGVIEW)->GetWindowRect(rcMiddle);
1893 ScreenToClient(rcMiddle);
1894 m_wndSplitter1.SetRange(rcTop.top+30, rcMiddle.bottom-20);
1895 CRect rcBottom;
1896 m_ChangedFileListCtrl.GetWindowRect(rcBottom);
1897 ScreenToClient(rcBottom);
1898 m_wndSplitter2.SetRange(rcMiddle.top+30, rcBottom.bottom-20);
1902 LRESULT CLogDlg::OnClickedInfoIcon(WPARAM /*wParam*/, LPARAM lParam)
1904 // FIXME: x64 version would get this function called with unexpected parameters.
1905 if (!lParam)
1906 return 0;
1908 RECT * rect = (LPRECT)lParam;
1909 CPoint point;
1910 CString temp;
1911 point = CPoint(rect->left, rect->bottom);
1912 #define LOGMENUFLAGS(x) (MF_STRING | MF_ENABLED | (m_LogList.m_nSelectedFilter == x ? MF_CHECKED : MF_UNCHECKED))
1913 CMenu popup;
1914 if (popup.CreatePopupMenu())
1916 temp.LoadString(IDS_LOG_FILTER_ALL);
1917 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_ALL), LOGFILTER_ALL, temp);
1919 popup.AppendMenu(MF_SEPARATOR, NULL);
1921 temp.LoadString(IDS_LOG_FILTER_MESSAGES);
1922 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_MESSAGES), LOGFILTER_MESSAGES, temp);
1923 temp.LoadString(IDS_LOG_FILTER_PATHS);
1924 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_PATHS), LOGFILTER_PATHS, temp);
1925 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
1926 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_AUTHORS), LOGFILTER_AUTHORS, temp);
1927 temp.LoadString(IDS_LOG_FILTER_REVS);
1928 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_REVS), LOGFILTER_REVS, temp);
1929 temp.LoadString(IDS_LOG_FILTER_BUGIDS);
1930 popup.AppendMenu(LOGMENUFLAGS(LOGFILTER_BUGID), LOGFILTER_BUGID, temp);
1932 popup.AppendMenu(MF_SEPARATOR, NULL);
1934 temp.LoadString(IDS_LOG_FILTER_REGEX);
1935 popup.AppendMenu(MF_STRING | MF_ENABLED | (m_bFilterWithRegex ? MF_CHECKED : MF_UNCHECKED), LOGFILTER_REGEX, temp);
1937 m_tooltips.Pop();
1938 int selection = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1939 if (selection != 0)
1942 if (selection == LOGFILTER_REGEX)
1944 m_bFilterWithRegex = !m_bFilterWithRegex;
1945 CRegDWORD b = CRegDWORD(_T("Software\\TortoiseGit\\UseRegexFilter"), TRUE);
1946 b = m_bFilterWithRegex;
1947 CheckRegexpTooltip();
1949 else
1951 m_LogList.m_nSelectedFilter = selection;
1952 SetFilterCueText();
1954 SetTimer(LOGFILTER_TIMER, 1000, NULL);
1957 return 0L;
1960 LRESULT CLogDlg::OnClickedCancelFilter(WPARAM /*wParam*/, LPARAM /*lParam*/)
1963 KillTimer(LOGFILTER_TIMER);
1965 m_LogList.m_sFilterText.Empty();
1966 UpdateData(FALSE);
1967 theApp.DoWaitCursor(1);
1968 CStoreSelection storeselection(this);
1969 FillLogMessageCtrl(false);
1971 m_LogList.RemoveFilter();
1973 CTime begin,end;
1974 m_LogList.GetTimeRange(begin,end);
1975 m_DateFrom.SetTime(&begin);
1976 m_DateTo.SetTime(&end);
1978 theApp.DoWaitCursor(-1);
1979 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
1980 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
1981 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
1982 UpdateLogInfoLabel();
1984 return 0L;
1988 void CLogDlg::SetFilterCueText()
1990 CString temp;
1991 switch (m_LogList.m_nSelectedFilter)
1993 case LOGFILTER_ALL:
1994 temp.LoadString(IDS_LOG_FILTER_ALL);
1995 break;
1996 case LOGFILTER_MESSAGES:
1997 temp.LoadString(IDS_LOG_FILTER_MESSAGES);
1998 break;
1999 case LOGFILTER_PATHS:
2000 temp.LoadString(IDS_LOG_FILTER_PATHS);
2001 break;
2002 case LOGFILTER_AUTHORS:
2003 temp.LoadString(IDS_LOG_FILTER_AUTHORS);
2004 break;
2005 case LOGFILTER_REVS:
2006 temp.LoadString(IDS_LOG_FILTER_REVS);
2007 break;
2009 // to make the cue banner text appear more to the right of the edit control
2010 temp = _T(" ")+temp;
2011 m_cFilter.SetCueBanner(temp);
2014 bool CLogDlg::Validate(LPCTSTR string)
2016 if (!m_bFilterWithRegex)
2017 return true;
2018 tr1::wregex pat;
2019 return m_LogList.ValidateRegexp(string, pat, false);
2023 void CLogDlg::OnTimer(UINT_PTR nIDEvent)
2025 if (nIDEvent == LOGFILTER_TIMER)
2027 if (this->IsThreadRunning())
2029 // thread still running! So just restart the timer.
2030 SetTimer(LOGFILTER_TIMER, 1000, NULL);
2031 return;
2033 CWnd * focusWnd = GetFocus();
2034 bool bSetFocusToFilterControl = ((focusWnd != GetDlgItem(IDC_DATEFROM))&&(focusWnd != GetDlgItem(IDC_DATETO))
2035 && (focusWnd != GetDlgItem(IDC_LOGLIST)));
2036 if (m_LogList.m_sFilterText.IsEmpty())
2038 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));
2039 // do not return here!
2040 // we also need to run the filter if the filter text is empty:
2041 // 1. to clear an existing filter
2042 // 2. to rebuild the m_arShownList after sorting
2044 theApp.DoWaitCursor(1);
2045 CStoreSelection storeselection(this);
2046 KillTimer(LOGFILTER_TIMER);
2047 FillLogMessageCtrl(false);
2049 // now start filter the log list
2050 m_LogList.StartFilter();
2052 if ( m_LogList.GetItemCount()==1 )
2054 m_LogList.SetSelectionMark(0);
2055 m_LogList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
2057 theApp.DoWaitCursor(-1);
2058 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
2059 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
2060 if (bSetFocusToFilterControl)
2061 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
2062 UpdateLogInfoLabel();
2063 } // if (nIDEvent == LOGFILTER_TIMER)
2064 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));
2065 __super::OnTimer(nIDEvent);
2068 void CLogDlg::OnDtnDatetimechangeDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2070 CTime _time;
2071 m_DateTo.GetTime(_time);
2074 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 23, 59, 59);
2075 if (time.GetTime() != m_LogList.m_To.GetTime())
2077 m_LogList.m_To = (DWORD)time.GetTime();
2078 SetTimer(LOGFILTER_TIMER, 10, NULL);
2081 catch (CAtlException)
2085 *pResult = 0;
2088 void CLogDlg::OnDtnDatetimechangeDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2090 CTime _time;
2091 m_DateFrom.GetTime(_time);
2094 CTime time(_time.GetYear(), _time.GetMonth(), _time.GetDay(), 0, 0, 0);
2095 if (time.GetTime() != m_LogList.m_From.GetTime())
2097 m_LogList.m_From = (DWORD)time.GetTime();
2098 SetTimer(LOGFILTER_TIMER, 10, NULL);
2101 catch (CAtlException)
2105 *pResult = 0;
2110 CTGitPathList CLogDlg::GetChangedPathsFromSelectedRevisions(bool /*bRelativePaths*/ /* = false */, bool /*bUseFilter*/ /* = true */)
2112 CTGitPathList pathList;
2113 #if 0
2115 if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))
2117 m_sRepositoryRoot = GetRepositoryRoot(m_path);
2119 if (m_sRepositoryRoot.IsEmpty() && (bRelativePaths == false))
2120 return pathList;
2122 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2123 if (pos != NULL)
2125 while (pos)
2127 int nextpos = m_LogList.GetNextSelectedItem(pos);
2128 if (nextpos >= m_arShownList.GetCount())
2129 continue;
2130 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(nextpos));
2131 LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
2132 for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
2134 LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
2135 if (cpath == NULL)
2136 continue;
2137 CTGitPath path;
2138 if (!bRelativePaths)
2139 path.SetFromGit(m_sRepositoryRoot);
2140 path.AppendPathString(cpath->sPath);
2141 if ((!bUseFilter)||
2142 ((m_cHidePaths.GetState() & 0x0003)!=BST_CHECKED)||
2143 (cpath->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0))
2144 pathList.AddPath(path);
2149 pathList.RemoveDuplicates();
2150 #endif
2151 return pathList;
2154 void CLogDlg::SortByColumn(int /*nSortColumn*/, bool /*bAscending*/)
2156 #if 0
2157 switch(nSortColumn)
2159 case 0: // Revision
2161 if(bAscending)
2162 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscRevSort());
2163 else
2164 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescRevSort());
2166 break;
2167 case 1: // action
2169 if(bAscending)
2170 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscActionSort());
2171 else
2172 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescActionSort());
2174 break;
2175 case 2: // Author
2177 if(bAscending)
2178 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscAuthorSort());
2179 else
2180 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescAuthorSort());
2182 break;
2183 case 3: // Date
2185 if(bAscending)
2186 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscDateSort());
2187 else
2188 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescDateSort());
2190 break;
2191 case 4: // Message or bug id
2192 if (m_bShowBugtraqColumn)
2194 if(bAscending)
2195 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscBugIDSort());
2196 else
2197 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescBugIDSort());
2198 break;
2200 // fall through here
2201 case 5: // Message
2203 if(bAscending)
2204 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::AscMessageSort());
2205 else
2206 std::sort(m_logEntries.begin(), m_logEntries.end(), CLogDataVector::DescMessageSort());
2208 break;
2209 default:
2210 ATLASSERT(0);
2211 break;
2213 #endif
2216 void CLogDlg::OnLvnColumnclick(NMHDR *pNMHDR, LRESULT *pResult)
2218 if (this->IsThreadRunning())
2219 return; //no sorting while the arrays are filled
2220 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
2221 const int nColumn = pNMLV->iSubItem;
2222 m_bAscending = nColumn == m_nSortColumn ? !m_bAscending : TRUE;
2223 m_nSortColumn = nColumn;
2224 SortByColumn(m_nSortColumn, m_bAscending);
2225 SetSortArrow(&m_LogList, m_nSortColumn, !!m_bAscending);
2226 SortShownListArray();
2227 m_LogList.Invalidate();
2228 UpdateLogInfoLabel();
2230 *pResult = 0;
2233 void CLogDlg::SortShownListArray()
2235 // make sure the shown list still matches the filter after sorting.
2236 OnTimer(LOGFILTER_TIMER);
2237 // clear the selection states
2238 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2239 while (pos)
2241 m_LogList.SetItemState(m_LogList.GetNextSelectedItem(pos), 0, LVIS_SELECTED);
2243 m_LogList.SetSelectionMark(-1);
2246 void CLogDlg::SetSortArrow(CListCtrl * control, int nColumn, bool bAscending)
2248 if (control == NULL)
2249 return;
2250 // set the sort arrow
2251 CHeaderCtrl * pHeader = control->GetHeaderCtrl();
2252 HDITEM HeaderItem = {0};
2253 HeaderItem.mask = HDI_FORMAT;
2254 for (int i=0; i<pHeader->GetItemCount(); ++i)
2256 pHeader->GetItem(i, &HeaderItem);
2257 HeaderItem.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
2258 pHeader->SetItem(i, &HeaderItem);
2260 if (nColumn >= 0)
2262 pHeader->GetItem(nColumn, &HeaderItem);
2263 HeaderItem.fmt |= (bAscending ? HDF_SORTUP : HDF_SORTDOWN);
2264 pHeader->SetItem(nColumn, &HeaderItem);
2267 void CLogDlg::OnLvnColumnclickChangedFileList(NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/)
2269 #if 0
2270 if (this->IsThreadRunning())
2271 return; //no sorting while the arrays are filled
2272 if (m_currentChangedArray == NULL)
2273 return;
2274 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
2275 const int nColumn = pNMLV->iSubItem;
2276 m_bAscendingPathList = nColumn == m_nSortColumnPathList ? !m_bAscendingPathList : TRUE;
2277 m_nSortColumnPathList = nColumn;
2278 // qsort(m_currentChangedArray->GetData(), m_currentChangedArray->GetSize(), sizeof(LogChangedPath*), (GENERICCOMPAREFN)SortCompare);
2280 SetSortArrow(&m_ChangedFileListCtrl, m_nSortColumnPathList, m_bAscendingPathList);
2281 m_ChangedFileListCtrl.Invalidate();
2282 *pResult = 0;
2283 #endif
2286 int CLogDlg::m_nSortColumnPathList = 0;
2287 bool CLogDlg::m_bAscendingPathList = false;
2289 int CLogDlg::SortCompare(const void * /*pElem1*/, const void * /*pElem2*/)
2291 #if 0
2292 LogChangedPath * cpath1 = *((LogChangedPath**)pElem1);
2293 LogChangedPath * cpath2 = *((LogChangedPath**)pElem2);
2295 if (m_bAscendingPathList)
2296 std::swap (cpath1, cpath2);
2298 int cmp = 0;
2299 switch (m_nSortColumnPathList)
2301 case 0: // action
2302 cmp = cpath2->GetAction().Compare(cpath1->GetAction());
2303 if (cmp)
2304 return cmp;
2305 // fall through
2306 case 1: // path
2307 cmp = cpath2->sPath.CompareNoCase(cpath1->sPath);
2308 if (cmp)
2309 return cmp;
2310 // fall through
2311 case 2: // copy from path
2312 cmp = cpath2->sCopyFromPath.Compare(cpath1->sCopyFromPath);
2313 if (cmp)
2314 return cmp;
2315 // fall through
2316 case 3: // copy from revision
2317 return cpath2->lCopyFromRev > cpath1->lCopyFromRev;
2319 #endif
2320 return 0;
2323 void CLogDlg::OnBnClickedHidepaths()
2325 FillLogMessageCtrl();
2326 m_ChangedFileListCtrl.Invalidate();
2331 void CLogDlg::OnBnClickedCheckStoponcopy()
2333 #if 0
2334 if (!GetDlgItem(IDC_GETALL)->IsWindowEnabled())
2335 return;
2337 // ignore old fetch limits when switching
2338 // between copy-following and stop-on-copy
2339 // (otherwise stop-on-copy will limit what
2340 // we see immediately after switching to
2341 // copy-following)
2343 m_endrev = 0;
2345 // now, restart the query
2346 #endif
2347 Refresh();
2351 void CLogDlg::UpdateLogInfoLabel()
2354 git_revnum_t rev1 ;
2355 git_revnum_t rev2 ;
2356 long selectedrevs = 0;
2357 int count =m_LogList.m_arShownList.GetCount();
2358 if (count)
2360 rev1 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.GetAt(0)))->m_CommitHash;
2361 //pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_arShownList.GetCount()-1));
2362 rev2 = (reinterpret_cast<GitRev*>(m_LogList.m_arShownList.GetAt(count-1)))->m_CommitHash;
2363 selectedrevs = m_LogList.GetSelectedCount();
2365 CString sTemp;
2366 sTemp.Format(_T("Showing %ld revision(s), from revision %s to revision %s - %ld revision(s) selected"), count, rev2.Left(6), rev1.Left(6), selectedrevs);
2367 m_sLogInfo = sTemp;
2369 UpdateData(FALSE);
2372 #if 0
2373 void CLogDlg::ShowContextMenuForChangedpaths(CWnd* /*pWnd*/, CPoint point)
2376 int selIndex = m_ChangedFileListCtrl.GetSelectionMark();
2377 if ((point.x == -1) && (point.y == -1))
2379 CRect rect;
2380 m_ChangedFileListCtrl.GetItemRect(selIndex, &rect, LVIR_LABEL);
2381 m_ChangedFileListCtrl.ClientToScreen(&rect);
2382 point = rect.CenterPoint();
2384 if (selIndex < 0)
2385 return;
2386 int s = m_LogList.GetSelectionMark();
2387 if (s < 0)
2388 return;
2389 std::vector<CString> changedpaths;
2390 std::vector<LogChangedPath*> changedlogpaths;
2391 POSITION pos = m_LogList.GetFirstSelectedItemPosition();
2392 if (pos == NULL)
2393 return; // nothing is selected, get out of here
2395 bool bOneRev = true;
2396 int sel=m_LogList.GetNextSelectedItem(pos);
2397 GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(sel));
2398 GitRev * rev1 = pLogEntry;
2399 GitRev * rev2 = reinterpret_cast<GitRev *>(m_LogList.m_arShownList.GetAt(sel+1));
2400 #if 0
2401 bool bOneRev = true;
2402 if (pos)
2404 while (pos)
2406 pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
2407 if (pLogEntry)
2409 rev1 = max(rev1,(git_revnum_t)pLogEntry->Rev);
2410 rev2 = min(rev2,(git_revnum_t)pLogEntry->Rev);
2411 bOneRev = false;
2414 if (!bOneRev)
2415 rev2--;
2416 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
2417 while (pos)
2419 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
2420 changedpaths.push_back(m_currentChangedPathList[nItem].GetGitPathString());
2423 else
2425 // only one revision is selected in the log dialog top pane
2426 // but multiple items could be selected in the changed items list
2427 rev2 = rev1-1;
2429 POSITION pos = m_ChangedFileListCtrl.GetFirstSelectedItemPosition();
2430 while (pos)
2432 int nItem = m_ChangedFileListCtrl.GetNextSelectedItem(pos);
2433 LogChangedPath * changedlogpath = pLogEntry->pArChangedPaths->GetAt(nItem);
2435 if (m_ChangedFileListCtrl.GetSelectedCount() == 1)
2437 if ((changedlogpath)&&(!changedlogpath->sCopyFromPath.IsEmpty()))
2438 rev2 = changedlogpath->lCopyFromRev;
2439 else
2441 // if the path was modified but the parent path was 'added with history'
2442 // then we have to use the copy from revision of the parent path
2443 CTGitPath cpath = CTGitPath(changedlogpath->sPath);
2444 for (int flist = 0; flist < pLogEntry->pArChangedPaths->GetCount(); ++flist)
2446 CTGitPath p = CTGitPath(pLogEntry->pArChangedPaths->GetAt(flist)->sPath);
2447 if (p.IsAncestorOf(cpath))
2449 if (!pLogEntry->pArChangedPaths->GetAt(flist)->sCopyFromPath.IsEmpty())
2450 rev2 = pLogEntry->pArChangedPaths->GetAt(flist)->lCopyFromRev;
2455 if ((m_cHidePaths.GetState() & 0x0003)==BST_CHECKED)
2457 // some items are hidden! So find out which item the user really clicked on
2458 INT_PTR selRealIndex = -1;
2459 for (INT_PTR hiddenindex=0; hiddenindex<pLogEntry->pArChangedPaths->GetCount(); ++hiddenindex)
2461 if (pLogEntry->pArChangedPaths->GetAt(hiddenindex)->sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
2462 selRealIndex++;
2463 if (selRealIndex == nItem)
2465 selIndex = hiddenindex;
2466 changedlogpath = pLogEntry->pArChangedPaths->GetAt(selIndex);
2467 break;
2471 if (changedlogpath)
2473 changedpaths.push_back(changedlogpath->sPath);
2474 changedlogpaths.push_back(changedlogpath);
2478 #endif
2479 //entry is selected, now show the popup menu
2480 CIconMenu popup;
2481 if (popup.CreatePopupMenu())
2483 bool bEntryAdded = false;
2484 if (m_ChangedFileListCtrl.GetSelectedCount() == 1)
2486 // if ((!bOneRev)||(IsDiffPossible(changedlogpaths[0], rev1)))
2488 popup.AppendMenuIcon(CGitLogList::ID_DIFF, IDS_LOG_POPUP_DIFF, IDI_DIFF);
2489 popup.AppendMenuIcon(CGitLogList::ID_BLAMEDIFF, IDS_LOG_POPUP_BLAMEDIFF, IDI_BLAME);
2490 popup.SetDefaultItem(CGitLogList::ID_DIFF, FALSE);
2491 popup.AppendMenuIcon(CGitLogList::ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
2492 bEntryAdded = true;
2494 // if (rev2 == rev1-1)
2496 if (bEntryAdded)
2497 popup.AppendMenu(MF_SEPARATOR, NULL);
2498 popup.AppendMenuIcon(CGitLogList::ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
2499 popup.AppendMenuIcon(CGitLogList::ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
2500 popup.AppendMenuIcon(CGitLogList::ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);
2501 popup.AppendMenu(MF_SEPARATOR, NULL);
2502 if (m_hasWC)
2503 popup.AppendMenuIcon(CGitLogList::ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
2504 popup.AppendMenuIcon(CGitLogList::ID_POPPROPS, IDS_REPOBROWSE_SHOWPROP, IDI_PROPERTIES); // "Show Properties"
2505 popup.AppendMenuIcon(CGitLogList::ID_LOG, IDS_MENULOG, IDI_LOG); // "Show Log"
2506 popup.AppendMenuIcon(CGitLogList::ID_GETMERGELOGS, IDS_LOG_POPUP_GETMERGELOGS, IDI_LOG); // "Show merge log"
2507 popup.AppendMenuIcon(CGitLogList::ID_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);
2508 bEntryAdded = true;
2509 if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
2511 popup.AppendMenu(MF_SEPARATOR, NULL);
2512 popup.AppendMenuIcon(CGitLogList::ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
2514 if (popup.GetDefaultItem(0,FALSE)==-1)
2515 popup.SetDefaultItem(CGitLogList::ID_OPEN, FALSE);
2518 else if (changedlogpaths.size())
2520 // more than one entry is selected
2521 popup.AppendMenuIcon(CGitLogList::ID_SAVEAS, IDS_LOG_POPUP_SAVE);
2522 bEntryAdded = true;
2525 if (!bEntryAdded)
2526 return;
2527 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
2528 bool bOpenWith = false;
2529 bool bMergeLog = false;
2530 m_bCancelled = false;
2532 switch (cmd)
2534 case CGitLogList::ID_DIFF:
2536 DoDiffFromLog(selIndex, rev1, rev2, false, false);
2538 break;
2539 #if 0
2540 case ID_BLAMEDIFF:
2542 DoDiffFromLog(selIndex, rev1, rev2, true, false);
2544 break;
2545 case ID_GNUDIFF1:
2547 DoDiffFromLog(selIndex, rev1, rev2, false, true);
2549 break;
2550 case ID_REVERTREV:
2552 SetPromptApp(&theApp);
2553 theApp.DoWaitCursor(1);
2554 CString sUrl;
2555 if (Git::PathIsURL(m_path))
2557 sUrl = m_path.GetGitPathString();
2559 else
2561 sUrl = GetURLFromPath(m_path);
2562 if (sUrl.IsEmpty())
2564 theApp.DoWaitCursor(-1);
2565 CString temp;
2566 temp.Format(IDS_ERR_NOURLOFFILE, m_path.GetWinPath());
2567 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2568 EnableOKButton();
2569 theApp.DoWaitCursor(-1);
2570 break; //exit
2573 // find the working copy path of the selected item from the URL
2574 m_bCancelled = false;
2575 CString sUrlRoot = GetRepositoryRoot(CTGitPath(sUrl));
2577 CString fileURL = changedpaths[0];
2578 fileURL = sUrlRoot + fileURL.Trim();
2579 // firstfile = (e.g.) http://mydomain.com/repos/trunk/folder/file1
2580 // sUrl = http://mydomain.com/repos/trunk/folder
2581 CString sUnescapedUrl = CPathUtils::PathUnescape(sUrl);
2582 // find out until which char the urls are identical
2583 int i=0;
2584 while ((i<fileURL.GetLength())&&(i<sUnescapedUrl.GetLength())&&(fileURL[i]==sUnescapedUrl[i]))
2585 i++;
2586 int leftcount = m_path.GetWinPathString().GetLength()-(sUnescapedUrl.GetLength()-i);
2587 CString wcPath = m_path.GetWinPathString().Left(leftcount);
2588 wcPath += fileURL.Mid(i);
2589 wcPath.Replace('/', '\\');
2590 CGitProgressDlg dlg;
2591 if (changedlogpaths[0]->action == LOGACTIONS_DELETED)
2593 // a deleted path! Since the path isn't there anymore, merge
2594 // won't work. So just do a copy url->wc
2595 dlg.SetCommand(CGitProgressDlg::GitProgress_Copy);
2596 dlg.SetPathList(CTGitPathList(CTGitPath(fileURL)));
2597 dlg.SetUrl(wcPath);
2598 dlg.SetRevision(rev2);
2600 else
2602 if (!PathFileExists(wcPath))
2604 // seems the path got renamed
2605 // tell the user how to work around this.
2606 CMessageBox::Show(this->m_hWnd, IDS_LOG_REVERTREV_ERROR, IDS_APPNAME, MB_ICONERROR);
2607 EnableOKButton();
2608 theApp.DoWaitCursor(-1);
2609 break; //exit
2611 dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
2612 dlg.SetPathList(CTGitPathList(CTGitPath(wcPath)));
2613 dlg.SetUrl(fileURL);
2614 dlg.SetSecondUrl(fileURL);
2615 GitRevRangeArray revarray;
2616 revarray.AddRevRange(rev1, rev2);
2617 dlg.SetRevisionRanges(revarray);
2619 CString msg;
2620 msg.Format(IDS_LOG_REVERT_CONFIRM, (LPCTSTR)wcPath);
2621 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
2623 dlg.DoModal();
2625 theApp.DoWaitCursor(-1);
2627 break;
2628 case ID_POPPROPS:
2630 DialogEnableWindow(IDOK, FALSE);
2631 SetPromptApp(&theApp);
2632 theApp.DoWaitCursor(1);
2633 CString filepath;
2634 if (Git::PathIsURL(m_path))
2636 filepath = m_path.GetGitPathString();
2638 else
2640 filepath = GetURLFromPath(m_path);
2641 if (filepath.IsEmpty())
2643 theApp.DoWaitCursor(-1);
2644 CString temp;
2645 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
2646 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2647 TRACE(_T("could not retrieve the URL of the file!\n"));
2648 EnableOKButton();
2649 break;
2652 filepath = GetRepositoryRoot(CTGitPath(filepath));
2653 filepath += changedpaths[0];
2654 CPropDlg dlg;
2655 dlg.m_rev = rev1;
2656 dlg.m_Path = CTGitPath(filepath);
2657 dlg.DoModal();
2658 EnableOKButton();
2659 theApp.DoWaitCursor(-1);
2661 break;
2662 case ID_SAVEAS:
2664 DialogEnableWindow(IDOK, FALSE);
2665 SetPromptApp(&theApp);
2666 theApp.DoWaitCursor(1);
2667 CString filepath;
2668 if (Git::PathIsURL(m_path))
2670 filepath = m_path.GetGitPathString();
2672 else
2674 filepath = GetURLFromPath(m_path);
2675 if (filepath.IsEmpty())
2677 theApp.DoWaitCursor(-1);
2678 CString temp;
2679 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
2680 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2681 TRACE(_T("could not retrieve the URL of the file!\n"));
2682 EnableOKButton();
2683 break;
2686 m_bCancelled = false;
2687 CString sRoot = GetRepositoryRoot(CTGitPath(filepath));
2688 // if more than one entry is selected, we save them
2689 // one by one into a folder the user has selected
2690 bool bTargetSelected = false;
2691 CTGitPath TargetPath;
2692 if (m_ChangedFileListCtrl.GetSelectedCount() > 1)
2694 CBrowseFolder browseFolder;
2695 browseFolder.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_SAVEFOLDERTOHINT)));
2696 browseFolder.m_style = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
2697 CString strSaveAsDirectory;
2698 if (browseFolder.Show(GetSafeHwnd(), strSaveAsDirectory) == CBrowseFolder::OK)
2700 TargetPath = CTGitPath(strSaveAsDirectory);
2701 bTargetSelected = true;
2704 else
2706 // Display the Open dialog box.
2707 CString revFilename;
2708 CString temp;
2709 temp = CPathUtils::GetFileNameFromPath(changedpaths[0]);
2710 int rfind = temp.ReverseFind('.');
2711 if (rfind > 0)
2712 revFilename.Format(_T("%s-%ld%s"), (LPCTSTR)temp.Left(rfind), rev1, (LPCTSTR)temp.Mid(rfind));
2713 else
2714 revFilename.Format(_T("%s-%ld"), (LPCTSTR)temp, rev1);
2715 bTargetSelected = CAppUtils::FileOpenSave(revFilename, NULL, IDS_LOG_POPUP_SAVE, IDS_COMMONFILEFILTER, false, m_hWnd);
2716 TargetPath.SetFromWin(revFilename);
2718 if (bTargetSelected)
2720 CProgressDlg progDlg;
2721 progDlg.SetTitle(IDS_APPNAME);
2722 progDlg.SetAnimation(IDR_DOWNLOAD);
2723 for (std::vector<LogChangedPath*>::iterator it = changedlogpaths.begin(); it!= changedlogpaths.end(); ++it)
2725 GitRev getrev = ((*it)->action == LOGACTIONS_DELETED) ? rev2 : rev1;
2727 CString sInfoLine;
2728 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, (LPCTSTR)filepath, (LPCTSTR)getrev.ToString());
2729 progDlg.SetLine(1, sInfoLine, true);
2730 SetAndClearProgressInfo(&progDlg);
2731 progDlg.ShowModeless(m_hWnd);
2733 CTGitPath tempfile = TargetPath;
2734 if (changedpaths.size() > 1)
2736 // if multiple items are selected, then the TargetPath
2737 // points to a folder and we have to append the filename
2738 // to save to to that folder.
2739 CString sName = (*it)->sPath;
2740 int slashpos = sName.ReverseFind('/');
2741 if (slashpos >= 0)
2742 sName = sName.Mid(slashpos);
2743 tempfile.AppendPathString(sName);
2744 // one problem here:
2745 // a user could have selected multiple items which
2746 // have the same filename but reside in different
2747 // directories, e.g.
2748 // /folder1/file1
2749 // /folder2/file1
2750 // in that case, the second 'file1' will overwrite
2751 // the already saved 'file1'.
2753 // we could maybe find the common root of all selected
2754 // items and then create sub folders to save those files
2755 // there.
2756 // But I think we should just leave it that way: to check
2757 // out multiple items at once, the better way is still to
2758 // use the export command from the top pane of the log dialog.
2760 filepath = sRoot + (*it)->sPath;
2761 if (!Cat(CTGitPath(filepath), getrev, getrev, tempfile))
2763 progDlg.Stop();
2764 SetAndClearProgressInfo((HWND)NULL);
2765 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
2766 EnableOKButton();
2767 theApp.DoWaitCursor(-1);
2768 break;
2771 progDlg.Stop();
2772 SetAndClearProgressInfo((HWND)NULL);
2774 EnableOKButton();
2775 theApp.DoWaitCursor(-1);
2777 break;
2778 case ID_OPENWITH:
2779 bOpenWith = true;
2780 case ID_OPEN:
2782 GitRev getrev = pLogEntry->pArChangedPaths->GetAt(selIndex)->action == LOGACTIONS_DELETED ? rev2 : rev1;
2783 Open(bOpenWith,changedpaths[0],getrev);
2785 break;
2786 case ID_BLAME:
2788 CString filepath;
2789 if (Git::PathIsURL(m_path))
2791 filepath = m_path.GetGitPathString();
2793 else
2795 filepath = GetURLFromPath(m_path);
2796 if (filepath.IsEmpty())
2798 theApp.DoWaitCursor(-1);
2799 CString temp;
2800 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
2801 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2802 TRACE(_T("could not retrieve the URL of the file!\n"));
2803 EnableOKButton();
2804 break;
2807 filepath = GetRepositoryRoot(CTGitPath(filepath));
2808 filepath += changedpaths[0];
2809 CBlameDlg dlg;
2810 dlg.EndRev = rev1;
2811 if (dlg.DoModal() == IDOK)
2813 CBlame blame;
2814 CString tempfile;
2815 CString logfile;
2816 tempfile = blame.BlameToTempFile(CTGitPath(filepath), dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
2817 if (!tempfile.IsEmpty())
2819 if (dlg.m_bTextView)
2821 //open the default text editor for the result file
2822 CAppUtils::StartTextViewer(tempfile);
2824 else
2826 CString sParams = _T("/path:\"") + filepath + _T("\" ");
2827 if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(filepath),sParams))
2829 break;
2833 else
2835 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
2839 break;
2840 case ID_GETMERGELOGS:
2841 bMergeLog = true;
2842 // fall through
2843 case ID_LOG:
2845 DialogEnableWindow(IDOK, FALSE);
2846 SetPromptApp(&theApp);
2847 theApp.DoWaitCursor(1);
2848 CString filepath;
2849 if (Git::PathIsURL(m_path))
2851 filepath = m_path.GetGitPathString();
2853 else
2855 filepath = GetURLFromPath(m_path);
2856 if (filepath.IsEmpty())
2858 theApp.DoWaitCursor(-1);
2859 CString temp;
2860 temp.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)filepath);
2861 CMessageBox::Show(this->m_hWnd, temp, _T("TortoiseGit"), MB_ICONERROR);
2862 TRACE(_T("could not retrieve the URL of the file!\n"));
2863 EnableOKButton();
2864 break;
2867 m_bCancelled = false;
2868 filepath = GetRepositoryRoot(CTGitPath(filepath));
2869 filepath += changedpaths[0];
2870 git_revnum_t logrev = rev1;
2871 if (changedlogpaths[0]->action == LOGACTIONS_DELETED)
2873 // if the item got deleted in this revision,
2874 // fetch the log from the previous revision where it
2875 // still existed.
2876 logrev--;
2878 CString sCmd;
2879 sCmd.Format(_T("\"%s\" /command:log /path:\"%s\" /startrev:%ld"), (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")), (LPCTSTR)filepath, logrev);
2880 if (bMergeLog)
2881 sCmd += _T(" /merge");
2882 CAppUtils::LaunchApplication(sCmd, NULL, false);
2883 EnableOKButton();
2884 theApp.DoWaitCursor(-1);
2886 break;
2887 case ID_VIEWPATHREV:
2889 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetSelectionMark()));
2890 GitRev rev = pLogEntry->Rev;
2891 CString relurl = changedpaths[0];
2892 CString url = m_ProjectProperties.sWebViewerPathRev;
2893 url.Replace(_T("%REVISION%"), rev.ToString());
2894 url.Replace(_T("%PATH%"), relurl);
2895 relurl = relurl.Mid(relurl.Find('/'));
2896 url.Replace(_T("%PATH1%"), relurl);
2897 if (!url.IsEmpty())
2898 ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
2900 break;
2901 #endif
2902 default:
2903 break;
2904 } // switch (cmd)
2906 } // if (popup.CreatePopupMenu())
2908 #endif
2910 void CLogDlg::OnDtnDropdownDatefrom(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2912 // the date control should not show the "today" button
2913 CMonthCalCtrl * pCtrl = m_DateFrom.GetMonthCalCtrl();
2914 if (pCtrl)
2915 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));
2916 *pResult = 0;
2919 void CLogDlg::OnDtnDropdownDateto(NMHDR * /*pNMHDR*/, LRESULT *pResult)
2921 // the date control should not show the "today" button
2922 CMonthCalCtrl * pCtrl = m_DateTo.GetMonthCalCtrl();
2923 if (pCtrl)
2924 SetWindowLongPtr(pCtrl->GetSafeHwnd(), GWL_STYLE, LONG_PTR(pCtrl->GetStyle() | MCS_NOTODAY));
2925 *pResult = 0;
2928 void CLogDlg::OnSize(UINT nType, int cx, int cy)
2930 __super::OnSize(nType, cx, cy);
2931 //set range
2932 SetSplitterRange();
2935 void CLogDlg::OnRefresh()
2937 //if (GetDlgItem(IDC_GETALL)->IsWindowEnabled())
2938 ShowStartRef();
2940 m_limit = 0;
2941 this->m_LogProgress.SetPos(0);
2943 Refresh (true);
2947 void CLogDlg::OnFind()
2949 if (!m_pFindDialog)
2951 m_pFindDialog = new CFindReplaceDialog();
2952 m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
2956 void CLogDlg::OnFocusFilter()
2958 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
2961 void CLogDlg::OnEditCopy()
2963 if (GetFocus() == &m_ChangedFileListCtrl)
2964 CopyChangedSelectionToClipBoard();
2965 else
2966 m_LogList.CopySelectionToClipBoard();
2969 CString CLogDlg::GetAbsoluteUrlFromRelativeUrl(const CString& url)
2971 // is the URL a relative one?
2972 if (url.Left(2).Compare(_T("^/")) == 0)
2974 // URL is relative to the repository root
2975 CString url1 = m_sRepositoryRoot + url.Mid(1);
2976 TCHAR buf[INTERNET_MAX_URL_LENGTH];
2977 DWORD len = url.GetLength();
2978 if (UrlCanonicalize((LPCTSTR)url1, buf, &len, 0) == S_OK)
2979 return CString(buf, len);
2980 return url1;
2982 else if (url[0] == '/')
2984 // URL is relative to the server's hostname
2985 CString sHost;
2986 // find the server's hostname
2987 int schemepos = m_sRepositoryRoot.Find(_T("//"));
2988 if (schemepos >= 0)
2990 sHost = m_sRepositoryRoot.Left(m_sRepositoryRoot.Find('/', schemepos+3));
2991 CString url1 = sHost + url;
2992 TCHAR buf[INTERNET_MAX_URL_LENGTH];
2993 DWORD len = url.GetLength();
2994 if (UrlCanonicalize((LPCTSTR)url, buf, &len, 0) == S_OK)
2995 return CString(buf, len);
2996 return url1;
2999 return url;
3003 void CLogDlg::OnEnChangeSearchedit()
3005 UpdateData();
3006 if (m_LogList.m_sFilterText.IsEmpty())
3008 CStoreSelection storeselection(this);
3009 // clear the filter, i.e. make all entries appear
3010 theApp.DoWaitCursor(1);
3011 KillTimer(LOGFILTER_TIMER);
3012 FillLogMessageCtrl(false);
3013 m_LogList.StartFilter();
3014 #if 0
3015 InterlockedExchange(&m_bNoDispUpdates, TRUE);
3016 m_arShownList.RemoveAll();
3017 for (DWORD i=0; i<m_logEntries.size(); ++i)
3019 if (IsEntryInDateRange(i))
3020 m_arShownList.Add(m_logEntries[i]);
3022 InterlockedExchange(&m_bNoDispUpdates, FALSE);
3023 m_LogList.DeleteAllItems();
3024 m_LogList.SetItemCountEx(ShownCountWithStopped());
3025 m_LogList.RedrawItems(0, ShownCountWithStopped());
3026 m_LogList.SetRedraw(false);
3027 ResizeAllListCtrlCols();
3028 m_LogList.SetRedraw(true);
3029 #endif
3030 theApp.DoWaitCursor(-1);
3031 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_HIDE);
3032 GetDlgItem(IDC_SEARCHEDIT)->ShowWindow(SW_SHOW);
3033 GetDlgItem(IDC_SEARCHEDIT)->SetFocus();
3034 DialogEnableWindow(IDC_STATBUTTON, !(((this->IsThreadRunning())||(m_LogList.m_arShownList.IsEmpty()))));
3035 return;
3037 if (Validate(m_LogList.m_sFilterText))
3038 SetTimer(LOGFILTER_TIMER, 1000, NULL);
3039 else
3040 KillTimer(LOGFILTER_TIMER);
3044 void CLogDlg::OnBnClickedAllBranch()
3046 this->UpdateData();
3048 if(this->m_bAllBranch)
3049 m_LogList.m_ShowMask|=CGit::LOG_INFO_ALL_BRANCH;
3050 else
3051 m_LogList.m_ShowMask&=~CGit::LOG_INFO_ALL_BRANCH;
3053 OnRefresh();
3055 FillLogMessageCtrl(false);
3058 void CLogDlg::OnBnClickedBrowseRef()
3060 CString newRef = CBrowseRefsDlg::PickRef(false,m_LogList.GetStartRef());
3061 if(newRef.IsEmpty())
3062 return;
3064 SetStartRef(newRef);
3065 ((CButton*)GetDlgItem(IDC_LOG_ALLBRANCH))->SetCheck(0);
3067 OnBnClickedAllBranch();
3070 void CLogDlg::ShowStartRef()
3072 //Show ref name on top
3073 if(!::IsWindow(m_hWnd))
3074 return;
3075 if(m_bAllBranch)
3077 GetDlgItem(IDC_STATIC_REF)->SetWindowText(L"<All Branches>");
3078 return;
3081 CString showStartRef = m_LogList.GetStartRef();
3082 if(showStartRef.IsEmpty())
3084 //Ref name is HEAD
3085 g_Git.Run(L"git symbolic-ref HEAD",&showStartRef,CP_UTF8);
3086 showStartRef.Trim(L"\r\n\t ");
3090 if(wcsncmp(showStartRef,L"refs/",5) == 0)
3091 showStartRef = showStartRef.Mid(5);
3092 if(wcsncmp(showStartRef,L"heads/",6) == 0)
3093 showStartRef = showStartRef.Mid(6);
3095 GetDlgItem(IDC_STATIC_REF)->SetWindowText(showStartRef);
3098 void CLogDlg::SetStartRef(const CString& StartRef)
3100 m_LogList.SetStartRef(StartRef);
3102 ShowStartRef();
3107 void CLogDlg::OnBnClickedFirstParent()
3109 this->UpdateData();
3111 if(this->m_bFirstParent)
3112 m_LogList.m_ShowMask|=CGit::LOG_INFO_FIRST_PARENT;
3113 else
3114 m_LogList.m_ShowMask&=~CGit::LOG_INFO_FIRST_PARENT;
3116 OnRefresh();
3118 FillLogMessageCtrl(false);
3122 void CLogDlg::OnBnClickShowWholeProject()
3124 this->UpdateData();
3126 if(this->m_bWholeProject)
3128 m_LogList.m_Path.Reset();
3129 SetWindowText(m_sTitle + _T(" - ") + CString(_T("Whole Project")));
3131 else
3133 m_LogList.m_Path=m_path;
3134 if(!m_path.IsEmpty())
3135 SetWindowText(m_sTitle + _T(" - ") + m_path.GetGitPathString());
3138 OnRefresh();
3140 FillLogMessageCtrl(false);