-// GitLogList.cpp : implementation file\r
-//\r
-\r
-#include "stdafx.h"\r
-#include "TortoiseProc.h"\r
-#include "GitLogList.h"\r
-#include "GitRev.h"\r
-//#include "VssStyle.h"\r
-#include "IconMenu.h"\r
-// CGitLogList\r
-#include "cursor.h"\r
-#include "InputDlg.h"\r
-#include "PropDlg.h"\r
-#include "SVNProgressDlg.h"\r
-#include "ProgressDlg.h"\r
-//#include "RepositoryBrowser.h"\r
-//#include "CopyDlg.h"\r
-//#include "StatGraphDlg.h"\r
-#include "Logdlg.h"\r
-#include "MessageBox.h"\r
-#include "Registry.h"\r
-#include "AppUtils.h"\r
-#include "PathUtils.h"\r
-#include "StringUtils.h"\r
-#include "UnicodeUtils.h"\r
-#include "TempFile.h"\r
-//#include "GitInfo.h"\r
-//#include "GitDiff.h"\r
-#include "IconMenu.h"\r
-//#include "RevisionRangeDlg.h"\r
-//#include "BrowseFolder.h"\r
-//#include "BlameDlg.h"\r
-//#include "Blame.h"\r
-//#include "GitHelpers.h"\r
-#include "GitStatus.h"\r
-//#include "LogDlgHelper.h"\r
-//#include "CachedLogInfo.h"\r
-//#include "RepositoryInfo.h"\r
-//#include "EditPropertiesDlg.h"\r
-#include "FileDiffDlg.h"\r
-\r
-\r
-\r
-\r
-IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)\r
-\r
-CGitLogList::CGitLogList():CHintListCtrl()\r
- ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)\r
- ,m_nSearchIndex(0)\r
- ,m_bNoDispUpdates(FALSE)\r
- , m_bThreadRunning(FALSE)\r
- , m_bStrictStopped(false)\r
- , m_pStoreSelection(NULL)\r
-{\r
- // use the default GUI font, create a copy of it and\r
- // change the copy to BOLD (leave the rest of the font\r
- // the same)\r
- HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);\r
- LOGFONT lf = {0};\r
- GetObject(hFont, sizeof(LOGFONT), &lf);\r
- lf.lfWeight = FW_BOLD;\r
- m_boldFont = CreateFontIndirect(&lf);\r
-\r
- m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
- m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
- m_hAddedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
- m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);\r
-\r
- g_Git.GetMapHashToFriendName(m_HashMap);\r
-}\r
-\r
-CGitLogList::~CGitLogList()\r
-{\r
- InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
-\r
- DestroyIcon(m_hModifiedIcon);\r
- DestroyIcon(m_hReplacedIcon);\r
- DestroyIcon(m_hAddedIcon);\r
- DestroyIcon(m_hDeletedIcon);\r
- m_logEntries.ClearAll();\r
-\r
- if (m_boldFont)\r
- DeleteObject(m_boldFont);\r
-\r
- if ( m_pStoreSelection )\r
- {\r
- delete m_pStoreSelection;\r
- m_pStoreSelection = NULL;\r
- }\r
-}\r
-\r
-\r
-BEGIN_MESSAGE_MAP(CGitLogList, CHintListCtrl)\r
- ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)\r
- ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)\r
- ON_WM_CONTEXTMENU()\r
- ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)\r
- ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)\r
- ON_WM_CREATE()\r
-END_MESSAGE_MAP()\r
-\r
-int CGitLogList:: OnCreate(LPCREATESTRUCT lpCreateStruct)\r
-{\r
- PreSubclassWindow();\r
- return CHintListCtrl::OnCreate(lpCreateStruct);\r
-}\r
-\r
-void CGitLogList::PreSubclassWindow()\r
-{\r
- SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);\r
- // load the icons for the action columns\r
- m_Theme.SetWindowTheme(GetSafeHwnd(), L"Explorer", NULL);\r
- CHintListCtrl::PreSubclassWindow();\r
-}\r
-\r
-void CGitLogList::InsertGitColumn()\r
-{\r
- CString temp;\r
-\r
- int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
- \r
- while (c>=0)\r
- DeleteColumn(c--);\r
- temp.LoadString(IDS_LOG_GRAPH);\r
-\r
- InsertColumn(this->LOGLIST_GRAPH, temp);\r
- \r
-#if 0 \r
- // make the revision column right aligned\r
- LVCOLUMN Column;\r
- Column.mask = LVCF_FMT;\r
- Column.fmt = LVCFMT_RIGHT;\r
- SetColumn(0, &Column); \r
-#endif \r
-// CString log;\r
-// g_Git.GetLog(log);\r
-\r
- temp.LoadString(IDS_LOG_ACTIONS);\r
- InsertColumn(this->LOGLIST_ACTION, temp);\r
- \r
- temp.LoadString(IDS_LOG_MESSAGE);\r
- InsertColumn(this->LOGLIST_MESSAGE, temp);\r
- \r
- temp.LoadString(IDS_LOG_AUTHOR);\r
- InsertColumn(this->LOGLIST_AUTHOR, temp);\r
- \r
- temp.LoadString(IDS_LOG_DATE);\r
- InsertColumn(this->LOGLIST_DATE, temp);\r
- \r
-\r
- if (m_bShowBugtraqColumn)\r
- {\r
-// temp = m_ProjectProperties.sLabel;\r
- if (temp.IsEmpty())\r
- temp.LoadString(IDS_LOG_BUGIDS);\r
- InsertColumn(this->LOGLIST_BUG, temp);\r
-\r
- }\r
- \r
- SetRedraw(false);\r
- ResizeAllListCtrlCols();\r
- SetRedraw(true);\r
-\r
-}\r
-\r
-void CGitLogList::ResizeAllListCtrlCols()\r
-{\r
-\r
- const int nMinimumWidth = ICONITEMBORDER+16*4;\r
- int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;\r
- int nItemCount = GetItemCount();\r
- TCHAR textbuf[MAX_PATH];\r
- CHeaderCtrl * pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));\r
- if (pHdrCtrl)\r
- {\r
- for (int col = 0; col <= maxcol; col++)\r
- {\r
- HDITEM hdi = {0};\r
- hdi.mask = HDI_TEXT;\r
- hdi.pszText = textbuf;\r
- hdi.cchTextMax = sizeof(textbuf);\r
- pHdrCtrl->GetItem(col, &hdi);\r
- int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin\r
- for (int index = 0; index<nItemCount; ++index)\r
- {\r
- // get the width of the string and add 14 pixels for the column separator and margins\r
- int linewidth = GetStringWidth(GetItemText(index, col)) + 14;\r
- if (index < m_arShownList.GetCount())\r
- {\r
- GitRev * pCurLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));\r
- if ((pCurLogEntry)&&(pCurLogEntry->m_CommitHash == m_wcRev.m_CommitHash))\r
- {\r
- // set the bold font and ask for the string width again\r
- SendMessage(WM_SETFONT, (WPARAM)m_boldFont, NULL);\r
- linewidth = GetStringWidth(GetItemText(index, col)) + 14;\r
- // restore the system font\r
- SendMessage(WM_SETFONT, NULL, NULL);\r
- }\r
- }\r
- if (index == 0)\r
- {\r
- // add the image size\r
- CImageList * pImgList = GetImageList(LVSIL_SMALL);\r
- if ((pImgList)&&(pImgList->GetImageCount()))\r
- {\r
- IMAGEINFO imginfo;\r
- pImgList->GetImageInfo(0, &imginfo);\r
- linewidth += (imginfo.rcImage.right - imginfo.rcImage.left);\r
- linewidth += 3; // 3 pixels between icon and text\r
- }\r
- }\r
- if (cx < linewidth)\r
- cx = linewidth;\r
- }\r
- // Adjust columns "Actions" containing icons\r
- if (col == this->LOGLIST_ACTION)\r
- {\r
- if (cx < nMinimumWidth)\r
- {\r
- cx = nMinimumWidth;\r
- }\r
- }\r
- \r
- if (col == this->LOGLIST_MESSAGE)\r
- {\r
- if (cx > LOGLIST_MESSAGE_MAX)\r
- {\r
- cx = LOGLIST_MESSAGE_MAX;\r
- }\r
-\r
- }\r
- // keep the bug id column small\r
- if ((col == 4)&&(m_bShowBugtraqColumn))\r
- {\r
- if (cx > (int)(DWORD)m_regMaxBugIDColWidth)\r
- {\r
- cx = (int)(DWORD)m_regMaxBugIDColWidth;\r
- }\r
- }\r
-\r
- SetColumnWidth(col, cx);\r
- }\r
- }\r
-\r
-}\r
-BOOL CGitLogList::GetShortName(CString ref, CString &shortname,CString prefix)\r
-{\r
- if(ref.Left(prefix.GetLength()) == prefix)\r
- {\r
- shortname = ref.Right(ref.GetLength()-prefix.GetLength());\r
- return TRUE;\r
- }\r
- return FALSE;\r
-}\r
-void CGitLogList::FillBackGround(HDC hdc, int Index,CRect &rect)\r
-{ \r
- HBRUSH brush;\r
- LVITEM rItem;\r
- SecureZeroMemory(&rItem, sizeof(LVITEM));\r
- rItem.mask = LVIF_STATE;\r
- rItem.iItem = Index;\r
- rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
- GetItem(&rItem);\r
-\r
- if (m_Theme.IsAppThemed() && m_bVista)\r
- {\r
- m_Theme.Open(m_hWnd, L"Explorer");\r
- int state = LISS_NORMAL;\r
- if (rItem.state & LVIS_SELECTED)\r
- {\r
- if (::GetFocus() == m_hWnd)\r
- state |= LISS_SELECTED;\r
- else\r
- state |= LISS_SELECTEDNOTFOCUS;\r
- }\r
- else\r
- {\r
-#if 0\r
- if (pLogEntry->bCopiedSelf)\r
- {\r
- // unfortunately, the pLVCD->nmcd.uItemState does not contain valid\r
- // information at this drawing stage. But we can check the whether the\r
- // previous stage changed the background color of the item\r
- if (pLVCD->clrTextBk == GetSysColor(COLOR_MENU))\r
- {\r
- HBRUSH brush;\r
- brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));\r
- if (brush)\r
- {\r
- ::FillRect(pLVCD->nmcd.hdc, &rect, brush);\r
- ::DeleteObject(brush);\r
- }\r
- }\r
- }\r
-#endif\r
- }\r
-\r
- if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTDETAIL, state))\r
- m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);\r
-\r
- m_Theme.DrawBackground(hdc, LVP_LISTDETAIL, state, &rect, NULL);\r
- }\r
- else\r
- {\r
- HBRUSH brush;\r
- if (rItem.state & LVIS_SELECTED)\r
- {\r
- if (::GetFocus() == m_hWnd)\r
- brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));\r
- else\r
- brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));\r
- }\r
- else\r
- {\r
- //if (pLogEntry->bCopiedSelf)\r
- // brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));\r
- //else\r
- brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));\r
- }\r
- if (brush == NULL)\r
- return;\r
-\r
- ::FillRect(hdc, &rect, brush);\r
- ::DeleteObject(brush);\r
- \r
- }\r
-}\r
-\r
-void CGitLogList::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)\r
-{\r
- GitRev* data = (GitRev*)m_arShownList.GetAt(index);\r
- CRect rt=rect;\r
- LVITEM rItem;\r
- SecureZeroMemory(&rItem, sizeof(LVITEM));\r
- rItem.mask = LVIF_STATE;\r
- rItem.iItem = index;\r
- rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;\r
- GetItem(&rItem);\r
-\r
- for(int i=0;i<m_HashMap[data->m_CommitHash].size();i++)\r
- {\r
- CString str;\r
- str=m_HashMap[data->m_CommitHash][i];\r
- \r
- CString shortname;\r
- HBRUSH brush=0;\r
- shortname=_T("");\r
- if(GetShortName(str,shortname,_T("refs/heads/")))\r
- {\r
- brush = ::CreateSolidBrush(RGB(0xff, 0, 0));\r
- }else if(GetShortName(str,shortname,_T("refs/remotes/")))\r
- {\r
- brush = ::CreateSolidBrush(RGB(0xff, 0xff, 0));\r
- }\r
- else if(GetShortName(str,shortname,_T("refs/tags/")))\r
- {\r
- brush = ::CreateSolidBrush(RGB(0, 0, 0xff));\r
- }\r
-\r
- if(!shortname.IsEmpty())\r
- {\r
- SIZE size;\r
- memset(&size,0,sizeof(SIZE));\r
- GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);\r
- \r
- rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);\r
- rt.right+=4;\r
- ::FillRect(hdc, &rt, brush);\r
- if (rItem.state & LVIS_SELECTED)\r
- {\r
- COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT)); \r
- ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);\r
- ::SetTextColor(hdc,clrOld); \r
- }else\r
- {\r
- ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);\r
- }\r
-\r
- \r
- ::MoveToEx(hdc,rt.left,rt.top,NULL);\r
- ::LineTo(hdc,rt.right,rt.top);\r
- ::LineTo(hdc,rt.right,rt.bottom);\r
- ::LineTo(hdc,rt.left,rt.bottom);\r
- ::LineTo(hdc,rt.left,rt.top);\r
- \r
- rt.left=rt.right+3;\r
- }\r
- if(brush)\r
- ::DeleteObject(brush);\r
- } \r
- rt.right=rect.right;\r
-\r
- if (rItem.state & LVIS_SELECTED)\r
- {\r
- COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT)); \r
- ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);\r
- ::SetTextColor(hdc,clrOld); \r
- }else\r
- {\r
- ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);\r
- }\r
- \r
-}\r
-\r
-void CGitLogList::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
-{\r
-\r
- NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );\r
- // Take the default processing unless we set this to something else below.\r
- *pResult = CDRF_DODEFAULT;\r
-\r
- if (m_bNoDispUpdates)\r
- return;\r
-\r
- switch (pLVCD->nmcd.dwDrawStage)\r
- {\r
- case CDDS_PREPAINT:\r
- {\r
- *pResult = CDRF_NOTIFYITEMDRAW;\r
- return;\r
- }\r
- break;\r
- case CDDS_ITEMPREPAINT:\r
- {\r
- // This is the prepaint stage for an item. Here's where we set the\r
- // item's text color. \r
- \r
- // Tell Windows to send draw notifications for each subitem.\r
- *pResult = CDRF_NOTIFYSUBITEMDRAW;\r
-\r
- COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);\r
-\r
- if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
- {\r
- GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
- if (data)\r
- {\r
-#if 0\r
- if (data->bCopiedSelf)\r
- {\r
- // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)\r
- if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))\r
- pLVCD->clrTextBk = GetSysColor(COLOR_MENU);\r
- }\r
-\r
- if (data->bCopies)\r
- crText = m_Colors.GetColor(CColors::Modified);\r
-#endif\r
-// if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))\r
-// crText = GetSysColor(COLOR_GRAYTEXT);\r
-// if (data->Rev == m_wcRev)\r
-// {\r
-// SelectObject(pLVCD->nmcd.hdc, m_boldFont);\r
- // We changed the font, so we're returning CDRF_NEWFONT. This\r
- // tells the control to recalculate the extent of the text.\r
-// *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;\r
-// }\r
- }\r
- }\r
- if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
- {\r
- if (m_bStrictStopped)\r
- crText = GetSysColor(COLOR_GRAYTEXT);\r
- }\r
- // Store the color back in the NMLVCUSTOMDRAW struct.\r
- pLVCD->clrText = crText;\r
- return;\r
- }\r
- break;\r
- case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:\r
- {\r
- if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))\r
- {\r
- pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);\r
- }\r
- if (pLVCD->iSubItem == LOGLIST_MESSAGE)\r
- {\r
- if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
- {\r
- GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);\r
- if(m_HashMap[data->m_CommitHash].size()!=0)\r
- {\r
- CRect rect;\r
- GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
- \r
- FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
- DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);\r
-\r
- *pResult = CDRF_SKIPDEFAULT;\r
- return;\r
-\r
- }\r
- }\r
- }\r
- if (pLVCD->iSubItem == 1)\r
- {\r
- *pResult = CDRF_DODEFAULT;\r
-\r
- if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)\r
- return;\r
-\r
- int nIcons = 0;\r
- int iconwidth = ::GetSystemMetrics(SM_CXSMICON);\r
- int iconheight = ::GetSystemMetrics(SM_CYSMICON);\r
-\r
- GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));\r
- CRect rect;\r
- GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);\r
- // Get the selected state of the\r
- // item being drawn. \r
-\r
- // Fill the background\r
- FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);\r
- \r
- // Draw the icon(s) into the compatible DC\r
- if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)\r
- ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
- nIcons++;\r
-\r
- if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_ADDED)\r
- ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
- nIcons++;\r
-\r
- if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)\r
- ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
- nIcons++;\r
-\r
- if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)\r
- ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);\r
- nIcons++;\r
- *pResult = CDRF_SKIPDEFAULT;\r
- return;\r
- }\r
- }\r
- break;\r
- }\r
- *pResult = CDRF_DODEFAULT;\r
-\r
-}\r
-\r
-// CGitLogList message handlers\r
-\r
-void CGitLogList::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
-{\r
- NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);\r
-\r
- // Create a pointer to the item\r
- LV_ITEM* pItem = &(pDispInfo)->item;\r
-\r
- // Do the list need text information?\r
- if (!(pItem->mask & LVIF_TEXT))\r
- return;\r
-\r
- // By default, clear text buffer.\r
- lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);\r
-\r
- bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();\r
- \r
- *pResult = 0;\r
- if (m_bNoDispUpdates || m_bThreadRunning || bOutOfRange)\r
- return;\r
-\r
- // Which item number?\r
- int itemid = pItem->iItem;\r
- GitRev * pLogEntry = NULL;\r
- if (itemid < m_arShownList.GetCount())\r
- pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));\r
- \r
- // Which column?\r
- switch (pItem->iSubItem)\r
- {\r
- case this->LOGLIST_GRAPH: //Graphic\r
- if (pLogEntry)\r
- {\r
- }\r
- break;\r
- case this->LOGLIST_ACTION: //action -- no text in the column\r
- break;\r
- case this->LOGLIST_MESSAGE: //Message\r
- if (pLogEntry)\r
- lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);\r
- break;\r
- case this->LOGLIST_AUTHOR: //Author\r
- if (pLogEntry)\r
- lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);\r
- break;\r
- case this->LOGLIST_DATE: //Date\r
- if (pLogEntry)\r
- lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorDate.Format(_T("%Y-%m-%d %H:%M")), pItem->cchTextMax);\r
- break;\r
- \r
- case 5:\r
-\r
- break;\r
- default:\r
- ASSERT(false);\r
- }\r
-}\r
-\r
-void CGitLogList::OnContextMenu(CWnd* pWnd, CPoint point)\r
-{\r
-\r
- int selIndex = GetSelectionMark();\r
- if (selIndex < 0)\r
- return; // nothing selected, nothing to do with a context menu\r
-\r
- // if the user selected the info text telling about not all revisions shown due to\r
- // the "stop on copy/rename" option, we also don't show the context menu\r
- if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))\r
- return;\r
-\r
- // if the context menu is invoked through the keyboard, we have to use\r
- // a calculated position on where to anchor the menu on\r
- if ((point.x == -1) && (point.y == -1))\r
- {\r
- CRect rect;\r
- GetItemRect(selIndex, &rect, LVIR_LABEL);\r
- ClientToScreen(&rect);\r
- point = rect.CenterPoint();\r
- }\r
- m_nSearchIndex = selIndex;\r
- m_bCancelled = FALSE;\r
-\r
- // calculate some information the context menu commands can use\r
-// CString pathURL = GetURLFromPath(m_path);\r
-\r
- POSITION pos = GetFirstSelectedItemPosition();\r
- int indexNext = GetNextSelectedItem(pos);\r
- if (indexNext < 0)\r
- return;\r
-\r
- GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));\r
-#if 0\r
- GitRev revSelected = pSelLogEntry->Rev;\r
- GitRev revPrevious = git_revnum_t(revSelected)-1;\r
- if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))\r
- {\r
- for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)\r
- {\r
- LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);\r
- if (changedpath->lCopyFromRev)\r
- revPrevious = changedpath->lCopyFromRev;\r
- }\r
- }\r
- GitRev revSelected2;\r
- if (pos)\r
- {\r
- PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
- revSelected2 = pLogEntry->Rev;\r
- }\r
- bool bAllFromTheSameAuthor = true;\r
- CString firstAuthor;\r
- CLogDataVector selEntries;\r
- GitRev revLowest, revHighest;\r
- GitRevRangeArray revisionRanges;\r
- {\r
- POSITION pos = GetFirstSelectedItemPosition();\r
- PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
- revisionRanges.AddRevision(pLogEntry->Rev);\r
- selEntries.push_back(pLogEntry);\r
- firstAuthor = pLogEntry->sAuthor;\r
- revLowest = pLogEntry->Rev;\r
- revHighest = pLogEntry->Rev;\r
- while (pos)\r
- {\r
- pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
- revisionRanges.AddRevision(pLogEntry->Rev);\r
- selEntries.push_back(pLogEntry);\r
- if (firstAuthor.Compare(pLogEntry->sAuthor))\r
- bAllFromTheSameAuthor = false;\r
- revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);\r
- revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);\r
- }\r
- }\r
-\r
-#endif\r
-\r
- int FirstSelect=-1, LastSelect=-1;\r
- pos = GetFirstSelectedItemPosition();\r
- FirstSelect = GetNextSelectedItem(pos);\r
- while(pos)\r
- {\r
- LastSelect = GetNextSelectedItem(pos);\r
- }\r
- //entry is selected, now show the popup menu\r
- CIconMenu popup;\r
- if (popup.CreatePopupMenu())\r
- {\r
- if (GetSelectedCount() == 1)\r
- {\r
-#if 0\r
- if (!m_path.IsDirectory())\r
- {\r
- if (m_hasWC)\r
- {\r
- popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
- popup.AppendMenuIcon(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);\r
- }\r
- popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);\r
- popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);\r
- popup.AppendMenu(MF_SEPARATOR, NULL);\r
- popup.AppendMenuIcon(ID_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);\r
- popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);\r
- popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);\r
- popup.AppendMenuIcon(ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);\r
- popup.AppendMenu(MF_SEPARATOR, NULL);\r
- }\r
- else\r
-#endif \r
- {\r
- if (m_hasWC)\r
- {\r
- popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);\r
- // TODO:\r
- // TortoiseMerge could be improved to take a /blame switch\r
- // and then not 'cat' the files from a unified diff but\r
- // blame then.\r
- // But until that's implemented, the context menu entry for\r
- // this feature is commented out.\r
- //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);\r
- }\r
- popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);\r
- popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);\r
- popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);\r
- popup.AppendMenu(MF_SEPARATOR, NULL);\r
- }\r
-\r
-// if (!m_ProjectProperties.sWebViewerRev.IsEmpty())\r
-// {\r
-// popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);\r
-// }\r
-// if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())\r
-// {\r
-// popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);\r
-// }\r
-// if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||\r
-// (!m_ProjectProperties.sWebViewerRev.IsEmpty()))\r
-// {\r
-// popup.AppendMenu(MF_SEPARATOR, NULL);\r
-// }\r
-\r
-// popup.AppendMenuIcon(ID_REPOBROWSE, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);\r
-// popup.AppendMenuIcon(ID_COPY, IDS_LOG_POPUP_COPY);\r
-// if (m_hasWC)\r
-// popup.AppendMenuIcon(ID_UPDATE, IDS_LOG_POPUP_UPDATE, IDI_UPDATE);\r
- if (m_hasWC)\r
- popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);\r
- if (m_hasWC)\r
- popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);\r
-// if (m_hasWC)\r
-// popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREV, IDI_MERGE);\r
- popup.AppendMenuIcon(ID_CHECKOUT, IDS_MENUCHECKOUT, IDI_CHECKOUT);\r
- popup.AppendMenuIcon(ID_EXPORT, IDS_MENUEXPORT, IDI_EXPORT);\r
- popup.AppendMenu(MF_SEPARATOR, NULL);\r
- }\r
- else if (GetSelectedCount() >= 2)\r
- {\r
- bool bAddSeparator = false;\r
- if (IsSelectionContinuous() || (GetSelectedCount() == 2))\r
- {\r
- popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);\r
- }\r
- if (GetSelectedCount() == 2)\r
- {\r
- popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);\r
- popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);\r
- bAddSeparator = true;\r
- }\r
- if (m_hasWC)\r
- {\r
- popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);\r
-// if (m_hasWC)\r
-// popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);\r
- bAddSeparator = true;\r
- }\r
- if (bAddSeparator)\r
- popup.AppendMenu(MF_SEPARATOR, NULL);\r
- }\r
-#if 0\r
-// if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))\r
-// {\r
-// popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);\r
-// }\r
-// if (GetSelectedCount() == 1)\r
-// {\r
-// popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);\r
-// popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"\r
-// popup.AppendMenu(MF_SEPARATOR, NULL);\r
-// }\r
-#endif\r
- if (GetSelectedCount() != 0)\r
- {\r
- popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);\r
- }\r
- popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);\r
-\r
- int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);\r
-// DialogEnableWindow(IDOK, FALSE);\r
-// SetPromptApp(&theApp);\r
- theApp.DoWaitCursor(1);\r
- bool bOpenWith = false;\r
-\r
- switch (cmd)\r
- {\r
- case ID_GNUDIFF1:\r
- {\r
- CString tempfile=GetTempFile();\r
- CString cmd;\r
- GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
- cmd.Format(_T("git.cmd diff-tree -r -p --stat %s"),r1->m_CommitHash);\r
- g_Git.RunLogFile(cmd,tempfile);\r
- CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);\r
- }\r
- break;\r
-\r
- case ID_GNUDIFF2:\r
- {\r
- CString tempfile=GetTempFile();\r
- CString cmd;\r
- GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
- GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
- cmd.Format(_T("git.cmd diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);\r
- g_Git.RunLogFile(cmd,tempfile);\r
- CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));\r
-\r
- }\r
- break;\r
-\r
- case ID_COMPARETWO:\r
- {\r
- GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));\r
- GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));\r
- CFileDiffDlg dlg;\r
- dlg.SetDiff(NULL,*r1,*r2);\r
- dlg.DoModal();\r
- \r
- }\r
- break;\r
-\r
-#if 0\r
- case ID_GNUDIFF1:\r
- {\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, this->m_hWnd, true);\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowUnifiedDiff(m_path, revPrevious, m_path, revSelected);\r
- }\r
- else\r
- CAppUtils::StartShowUnifiedDiff(m_hWnd, m_path, revPrevious, m_path, revSelected, GitRev(), m_LogRevision);\r
- }\r
- break;\r
-\r
- case ID_GNUDIFF2:\r
- {\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, this->m_hWnd, true);\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowUnifiedDiff(m_path, revSelected2, m_path, revSelected);\r
- }\r
- else\r
- CAppUtils::StartShowUnifiedDiff(m_hWnd, m_path, revSelected2, m_path, revSelected, GitRev(), m_LogRevision);\r
- }\r
- break;\r
- case ID_REVERTREV:\r
- {\r
- // we need an URL to complete this command, so error out if we can't get an URL\r
- if (pathURL.IsEmpty())\r
- {\r
- CString strMessage;\r
- strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));\r
- CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);\r
- TRACE(_T("could not retrieve the URL of the folder!\n"));\r
- break; //exit\r
- }\r
- CString msg;\r
- msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());\r
- if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)\r
- {\r
- CGitProgressDlg dlg;\r
- dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);\r
- dlg.SetPathList(CTGitPathList(m_path));\r
- dlg.SetUrl(pathURL);\r
- dlg.SetSecondUrl(pathURL);\r
- revisionRanges.AdjustForMerge(true);\r
- dlg.SetRevisionRanges(revisionRanges);\r
- dlg.SetPegRevision(m_LogRevision);\r
- dlg.DoModal();\r
- }\r
- }\r
- break;\r
- case ID_MERGEREV:\r
- {\r
- // we need an URL to complete this command, so error out if we can't get an URL\r
- if (pathURL.IsEmpty())\r
- {\r
- CString strMessage;\r
- strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));\r
- CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);\r
- TRACE(_T("could not retrieve the URL of the folder!\n"));\r
- break; //exit\r
- }\r
-\r
- CString path = m_path.GetWinPathString();\r
- bool bGotSavePath = false;\r
- if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))\r
- {\r
- bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());\r
- }\r
- else\r
- {\r
- CBrowseFolder folderBrowser;\r
- folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));\r
- bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);\r
- }\r
- if (bGotSavePath)\r
- {\r
- CGitProgressDlg dlg;\r
- dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);\r
- dlg.SetPathList(CTGitPathList(CTGitPath(path)));\r
- dlg.SetUrl(pathURL);\r
- dlg.SetSecondUrl(pathURL);\r
- revisionRanges.AdjustForMerge(false);\r
- dlg.SetRevisionRanges(revisionRanges);\r
- dlg.SetPegRevision(m_LogRevision);\r
- dlg.DoModal();\r
- }\r
- }\r
- break;\r
- case ID_REVERTTOREV:\r
- {\r
- // we need an URL to complete this command, so error out if we can't get an URL\r
- if (pathURL.IsEmpty())\r
- {\r
- CString strMessage;\r
- strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));\r
- CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);\r
- TRACE(_T("could not retrieve the URL of the folder!\n"));\r
- break; //exit\r
- }\r
-\r
- CString msg;\r
- msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());\r
- if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)\r
- {\r
- CGitProgressDlg dlg;\r
- dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);\r
- dlg.SetPathList(CTGitPathList(m_path));\r
- dlg.SetUrl(pathURL);\r
- dlg.SetSecondUrl(pathURL);\r
- GitRevRangeArray revarray;\r
- revarray.AddRevRange(GitRev::REV_HEAD, revSelected);\r
- dlg.SetRevisionRanges(revarray);\r
- dlg.SetPegRevision(m_LogRevision);\r
- dlg.DoModal();\r
- }\r
- }\r
- break;\r
- case ID_COPY:\r
- {\r
- // we need an URL to complete this command, so error out if we can't get an URL\r
- if (pathURL.IsEmpty())\r
- {\r
- CString strMessage;\r
- strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));\r
- CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);\r
- TRACE(_T("could not retrieve the URL of the folder!\n"));\r
- break; //exit\r
- }\r
-\r
- CCopyDlg dlg;\r
- dlg.m_URL = pathURL;\r
- dlg.m_path = m_path;\r
- dlg.m_CopyRev = revSelected;\r
- if (dlg.DoModal() == IDOK)\r
- {\r
- // should we show a progress dialog here? Copies are done really fast\r
- // and without much network traffic.\r
- if (!Copy(CTGitPathList(CTGitPath(pathURL)), CTGitPath(dlg.m_URL), dlg.m_CopyRev, dlg.m_CopyRev, dlg.m_sLogMessage))\r
- CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
- else\r
- CMessageBox::Show(this->m_hWnd, IDS_LOG_COPY_SUCCESS, IDS_APPNAME, MB_ICONINFORMATION);\r
- }\r
- } \r
- break;\r
- case ID_COMPARE:\r
- {\r
- //user clicked on the menu item "compare with working copy"\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, m_hWnd, true);\r
- diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- }\r
- break;\r
- case ID_COMPARETWO:\r
- {\r
- GitRev r1 = revSelected;\r
- GitRev r2 = revSelected2;\r
- if (GetSelectedCount() > 2)\r
- {\r
- r1 = revHighest;\r
- r2 = revLowest;\r
- }\r
- //user clicked on the menu item "compare revisions"\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, m_hWnd, true);\r
- diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(CTGitPath(pathURL), r2, CTGitPath(pathURL), r1);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), r2, CTGitPath(pathURL), r1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- }\r
- break;\r
- case ID_COMPAREWITHPREVIOUS:\r
- {\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, m_hWnd, true);\r
- diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- }\r
- break;\r
- case ID_BLAMECOMPARE:\r
- {\r
- //user clicked on the menu item "compare with working copy"\r
- //now first get the revision which is selected\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, this->m_hWnd, true);\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);\r
- }\r
- break;\r
- case ID_BLAMETWO:\r
- {\r
- //user clicked on the menu item "compare and blame revisions"\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, this->m_hWnd, true);\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);\r
- }\r
- break;\r
- case ID_BLAMEWITHPREVIOUS:\r
- {\r
- //user clicked on the menu item "Compare and Blame with previous revision"\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, this->m_hWnd, true);\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);\r
- }\r
- else\r
- CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);\r
- }\r
- break;\r
- case ID_SAVEAS:\r
- {\r
- //now first get the revision which is selected\r
- CString revFilename;\r
- if (m_hasWC)\r
- {\r
- CString strWinPath = m_path.GetWinPathString();\r
- int rfind = strWinPath.ReverseFind('.');\r
- if (rfind > 0)\r
- revFilename.Format(_T("%s-%ld%s"), (LPCTSTR)strWinPath.Left(rfind), (LONG)revSelected, (LPCTSTR)strWinPath.Mid(rfind));\r
- else\r
- revFilename.Format(_T("%s-%ld"), (LPCTSTR)strWinPath, revSelected);\r
- }\r
- if (CAppUtils::FileOpenSave(revFilename, NULL, IDS_LOG_POPUP_SAVE, IDS_COMMONFILEFILTER, false, m_hWnd))\r
- {\r
- CTGitPath tempfile;\r
- tempfile.SetFromWin(revFilename);\r
- CProgressDlg progDlg;\r
- progDlg.SetTitle(IDS_APPNAME);\r
- progDlg.SetAnimation(IDR_DOWNLOAD);\r
- CString sInfoLine;\r
- sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());\r
- progDlg.SetLine(1, sInfoLine, true);\r
- SetAndClearProgressInfo(&progDlg);\r
- progDlg.ShowModeless(m_hWnd);\r
- if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))\r
- {\r
- // try again with another peg revision\r
- if (!Cat(m_path, revSelected, revSelected, tempfile))\r
- {\r
- progDlg.Stop();\r
- SetAndClearProgressInfo((HWND)NULL);\r
- CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
- EnableOKButton();\r
- break;\r
- }\r
- }\r
- progDlg.Stop();\r
- SetAndClearProgressInfo((HWND)NULL);\r
- }\r
- }\r
- break;\r
- case ID_OPENWITH:\r
- bOpenWith = true;\r
- case ID_OPEN:\r
- {\r
- CProgressDlg progDlg;\r
- progDlg.SetTitle(IDS_APPNAME);\r
- progDlg.SetAnimation(IDR_DOWNLOAD);\r
- CString sInfoLine;\r
- sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());\r
- progDlg.SetLine(1, sInfoLine, true);\r
- SetAndClearProgressInfo(&progDlg);\r
- progDlg.ShowModeless(m_hWnd);\r
- CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);\r
- bool bSuccess = true;\r
- if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))\r
- {\r
- bSuccess = false;\r
- // try again, but with the selected revision as the peg revision\r
- if (!Cat(m_path, revSelected, revSelected, tempfile))\r
- {\r
- progDlg.Stop();\r
- SetAndClearProgressInfo((HWND)NULL);\r
- CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
- EnableOKButton();\r
- break;\r
- }\r
- bSuccess = true;\r
- }\r
- if (bSuccess)\r
- {\r
- progDlg.Stop();\r
- SetAndClearProgressInfo((HWND)NULL);\r
- SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);\r
- int ret = 0;\r
- if (!bOpenWith)\r
- ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);\r
- if ((ret <= HINSTANCE_ERROR)||bOpenWith)\r
- {\r
- CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");\r
- cmd += tempfile.GetWinPathString() + _T(" ");\r
- CAppUtils::LaunchApplication(cmd, NULL, false);\r
- }\r
- }\r
- }\r
- break;\r
- case ID_BLAME:\r
- {\r
- CBlameDlg dlg;\r
- dlg.EndRev = revSelected;\r
- if (dlg.DoModal() == IDOK)\r
- {\r
- CBlame blame;\r
- CString tempfile;\r
- CString logfile;\r
- tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);\r
- if (!tempfile.IsEmpty())\r
- {\r
- if (dlg.m_bTextView)\r
- {\r
- //open the default text editor for the result file\r
- CAppUtils::StartTextViewer(tempfile);\r
- }\r
- else\r
- {\r
- CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");\r
- if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))\r
- {\r
- break;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
- }\r
- }\r
- }\r
- break;\r
- case ID_UPDATE:\r
- {\r
- CString sCmd;\r
- CString url = _T("tgit:")+pathURL;\r
- sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),\r
- (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),\r
- (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);\r
- CAppUtils::LaunchApplication(sCmd, NULL, false);\r
- }\r
- break;\r
- case ID_FINDENTRY:\r
- {\r
- m_nSearchIndex = GetSelectionMark();\r
- if (m_nSearchIndex < 0)\r
- m_nSearchIndex = 0;\r
- if (m_pFindDialog)\r
- {\r
- break;\r
- }\r
- else\r
- {\r
- m_pFindDialog = new CFindReplaceDialog();\r
- m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this); \r
- }\r
- }\r
- break;\r
- case ID_REPOBROWSE:\r
- {\r
- CString sCmd;\r
- sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),\r
- (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),\r
- (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());\r
-\r
- CAppUtils::LaunchApplication(sCmd, NULL, false);\r
- }\r
- break;\r
- case ID_EDITLOG:\r
- {\r
- EditLogMessage(selIndex);\r
- }\r
- break;\r
- case ID_EDITAUTHOR:\r
- {\r
- EditAuthor(selEntries);\r
- }\r
- break;\r
- case ID_REVPROPS:\r
- {\r
- CEditPropertiesDlg dlg;\r
- dlg.SetProjectProperties(&m_ProjectProperties);\r
- CTGitPathList escapedlist;\r
- dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));\r
- dlg.SetRevision(revSelected);\r
- dlg.RevProps(true);\r
- dlg.DoModal();\r
- }\r
- break;\r
- case ID_COPYCLIPBOARD:\r
- {\r
- CopySelectionToClipBoard();\r
- }\r
- break;\r
- case ID_EXPORT:\r
- {\r
- CString sCmd;\r
- sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),\r
- (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),\r
- (LPCTSTR)pathURL, (LONG)revSelected);\r
- CAppUtils::LaunchApplication(sCmd, NULL, false);\r
- }\r
- break;\r
- case ID_CHECKOUT:\r
- {\r
- CString sCmd;\r
- CString url = _T("tgit:")+pathURL;\r
- sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),\r
- (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),\r
- (LPCTSTR)url, (LONG)revSelected);\r
- CAppUtils::LaunchApplication(sCmd, NULL, false);\r
- }\r
- break;\r
- case ID_VIEWREV:\r
- {\r
- CString url = m_ProjectProperties.sWebViewerRev;\r
- url = GetAbsoluteUrlFromRelativeUrl(url);\r
- url.Replace(_T("%REVISION%"), revSelected.ToString());\r
- if (!url.IsEmpty())\r
- ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT); \r
- }\r
- break;\r
- case ID_VIEWPATHREV:\r
- {\r
- CString relurl = pathURL;\r
- CString sRoot = GetRepositoryRoot(CTGitPath(relurl));\r
- relurl = relurl.Mid(sRoot.GetLength());\r
- CString url = m_ProjectProperties.sWebViewerPathRev;\r
- url = GetAbsoluteUrlFromRelativeUrl(url);\r
- url.Replace(_T("%REVISION%"), revSelected.ToString());\r
- url.Replace(_T("%PATH%"), relurl);\r
- if (!url.IsEmpty())\r
- ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT); \r
- }\r
- break;\r
-#endif\r
- default:\r
- break;\r
- } // switch (cmd)\r
- theApp.DoWaitCursor(-1);\r
-// EnableOKButton();\r
- } // if (popup.CreatePopupMenu())\r
-\r
-}\r
-\r
-bool CGitLogList::IsSelectionContinuous()\r
-{\r
- if ( GetSelectedCount()==1 )\r
- {\r
- // if only one revision is selected, the selection is of course\r
- // continuous\r
- return true;\r
- }\r
-\r
- POSITION pos = GetFirstSelectedItemPosition();\r
- bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());\r
- if (bContinuous)\r
- {\r
- int itemindex = GetNextSelectedItem(pos);\r
- while (pos)\r
- {\r
- int nextindex = GetNextSelectedItem(pos);\r
- if (nextindex - itemindex > 1)\r
- {\r
- bContinuous = false;\r
- break;\r
- }\r
- itemindex = nextindex;\r
- }\r
- }\r
- return bContinuous;\r
-}\r
-\r
-void CGitLogList::CopySelectionToClipBoard()\r
-{\r
-#if 0\r
- CString sClipdata;\r
- POSITION pos = GetFirstSelectedItemPosition();\r
- if (pos != NULL)\r
- {\r
- CString sRev;\r
- sRev.LoadString(IDS_LOG_REVISION);\r
- CString sAuthor;\r
- sAuthor.LoadString(IDS_LOG_AUTHOR);\r
- CString sDate;\r
- sDate.LoadString(IDS_LOG_DATE);\r
- CString sMessage;\r
- sMessage.LoadString(IDS_LOG_MESSAGE);\r
- while (pos)\r
- {\r
- CString sLogCopyText;\r
- CString sPaths;\r
- PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));\r
- LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;\r
- for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)\r
- {\r
- LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);\r
- sPaths += cpath->GetAction() + _T(" : ") + cpath->sPath;\r
- if (cpath->sCopyFromPath.IsEmpty())\r
- sPaths += _T("\r\n");\r
- else\r
- {\r
- CString sCopyFrom;\r
- sCopyFrom.Format(_T(" (%s: %s, %s, %ld)\r\n"), CString(MAKEINTRESOURCE(IDS_LOG_COPYFROM)), \r
- (LPCTSTR)cpath->sCopyFromPath, \r
- (LPCTSTR)CString(MAKEINTRESOURCE(IDS_LOG_REVISION)), \r
- (LPCTSTR)cpath->lCopyFromRev);\r
- sPaths += sCopyFrom;\r
- }\r
- }\r
- sPaths.Trim();\r
- sLogCopyText.Format(_T("%s: %d\r\n%s: %s\r\n%s: %s\r\n%s:\r\n%s\r\n----\r\n%s\r\n\r\n"),\r
- (LPCTSTR)sRev, pLogEntry->Rev,\r
- (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->sAuthor,\r
- (LPCTSTR)sDate, (LPCTSTR)pLogEntry->sDate,\r
- (LPCTSTR)sMessage, (LPCTSTR)pLogEntry->sMessage,\r
- (LPCTSTR)sPaths);\r
- sClipdata += sLogCopyText;\r
- }\r
- CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());\r
- }\r
-#endif\r
-}\r
-\r
-void CGitLogList::DiffSelectedRevWithPrevious()\r
-{\r
-#if 0\r
- if (m_bThreadRunning)\r
- return;\r
- UpdateLogInfoLabel();\r
- int selIndex = m_LogList.GetSelectionMark();\r
- if (selIndex < 0)\r
- return;\r
- int selCount = m_LogList.GetSelectedCount();\r
- if (selCount != 1)\r
- return;\r
-\r
- // Find selected entry in the log list\r
- POSITION pos = m_LogList.GetFirstSelectedItemPosition();\r
- PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));\r
- long rev1 = pLogEntry->Rev;\r
- long rev2 = rev1-1;\r
- CTGitPath path = m_path;\r
-\r
- // See how many files under the relative root were changed in selected revision\r
- int nChanged = 0;\r
- LogChangedPath * changed = NULL;\r
- for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)\r
- {\r
- LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);\r
- if (cpath && cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)\r
- {\r
- ++nChanged;\r
- changed = cpath;\r
- }\r
- }\r
-\r
- if (m_path.IsDirectory() && nChanged == 1) \r
- {\r
- // We're looking at the log for a directory and only one file under dir was changed in the revision\r
- // Do diff on that file instead of whole directory\r
- path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));\r
- } \r
-\r
- m_bCancelled = FALSE;\r
- DialogEnableWindow(IDOK, FALSE);\r
- SetPromptApp(&theApp);\r
- theApp.DoWaitCursor(1);\r
-\r
- if (PromptShown())\r
- {\r
- GitDiff diff(this, m_hWnd, true);\r
- diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- diff.SetHEADPeg(m_LogRevision);\r
- diff.ShowCompare(path, rev2, path, rev1);\r
- }\r
- else\r
- {\r
- CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));\r
- }\r
-\r
- theApp.DoWaitCursor(-1);\r
- EnableOKButton();\r
-#endif\r
-}\r
-\r
-void CGitLogList::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)\r
-{\r
- LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);\r
- *pResult = -1;\r
- \r
- if (pFindInfo->lvfi.flags & LVFI_PARAM)\r
- return; \r
- if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))\r
- return;\r
- if (pFindInfo->lvfi.psz == 0)\r
- return;\r
-#if 0\r
- CString sCmp = pFindInfo->lvfi.psz;\r
- CString sRev; \r
- for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)\r
- {\r
- GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));\r
- sRev.Format(_T("%ld"), pLogEntry->Rev);\r
- if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
- {\r
- if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
- {\r
- *pResult = i;\r
- return;\r
- }\r
- }\r
- else\r
- {\r
- if (sCmp.Compare(sRev)==0)\r
- {\r
- *pResult = i;\r
- return;\r
- }\r
- }\r
- }\r
- if (pFindInfo->lvfi.flags & LVFI_WRAP)\r
- {\r
- for (int i=0; i<pFindInfo->iStart; ++i)\r
- {\r
- PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));\r
- sRev.Format(_T("%ld"), pLogEntry->Rev);\r
- if (pFindInfo->lvfi.flags & LVFI_PARTIAL)\r
- {\r
- if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)\r
- {\r
- *pResult = i;\r
- return;\r
- }\r
- }\r
- else\r
- {\r
- if (sCmp.Compare(sRev)==0)\r
- {\r
- *pResult = i;\r
- return;\r
- }\r
- }\r
- }\r
- }\r
-#endif\r
- *pResult = -1;\r
-}\r
-\r
-int CGitLogList::FillGitLog()\r
-{\r
- ClearText();\r
-\r
- this->m_logEntries.ClearAll();\r
- this->m_logEntries.ParserFromLog();\r
- SetItemCountEx(this->m_logEntries.size());\r
-\r
- this->m_arShownList.RemoveAll();\r
-\r
- for(int i=0;i<m_logEntries.size();i++)\r
- this->m_arShownList.Add(&m_logEntries[i]);\r
-\r
- return 0;\r
-}\r
-\r
-BOOL CGitLogList::PreTranslateMessage(MSG* pMsg)\r
-{\r
- // Skip Ctrl-C when copying text out of the log message or search filter\r
- BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );\r
- if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')\r
- {\r
- //if (GetFocus()==GetDlgItem(IDC_LOGLIST))\r
- {\r
- if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
- {\r
- DiffSelectedRevWithPrevious();\r
- return TRUE;\r
- }\r
- }\r
-#if 0\r
- if (GetFocus()==GetDlgItem(IDC_LOGMSG))\r
- {\r
- DiffSelectedFile();\r
- return TRUE;\r
- }\r
-#endif\r
- }\r
-\r
-#if 0\r
- if (m_hAccel && !bSkipAccelerator)\r
- {\r
- int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
- if (ret)\r
- return TRUE;\r
- }\r
- \r
-#endif\r
- //m_tooltips.RelayEvent(pMsg);\r
- return __super::PreTranslateMessage(pMsg);\r
-}\r
-\r
-void CGitLogList::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)\r
-{\r
- // a double click on an entry in the revision list has happened\r
- *pResult = 0;\r
-\r
- if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))\r
- DiffSelectedRevWithPrevious();\r
-}\r
-\r
-int CGitLogList::FetchLogAsync(CALLBACK_PROCESS *proc,void * data)\r
-{\r
- m_ProcCallBack=proc;\r
- m_ProcData=data;\r
-\r
- InterlockedExchange(&m_bThreadRunning, TRUE);\r
- InterlockedExchange(&m_bNoDispUpdates, TRUE);\r
- if (AfxBeginThread(LogThreadEntry, this)==NULL)\r
- {\r
- InterlockedExchange(&m_bThreadRunning, FALSE);\r
- InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
- CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
- return -1;\r
- }\r
- return 0;\r
-}\r
-\r
-//this is the thread function which calls the subversion function\r
-UINT CGitLogList::LogThreadEntry(LPVOID pVoid)\r
-{\r
- return ((CGitLogList*)pVoid)->LogThread();\r
-}\r
-\r
-\r
-UINT CGitLogList::LogThread()\r
-{\r
-\r
- if(m_ProcCallBack)\r
- m_ProcCallBack(m_ProcData,GITLOG_START);\r
-\r
- InterlockedExchange(&m_bThreadRunning, TRUE);\r
-\r
- //does the user force the cache to refresh (shift or control key down)?\r
- bool refresh = (GetKeyState (VK_CONTROL) < 0) \r
- || (GetKeyState (VK_SHIFT) < 0);\r
-\r
- //disable the "Get All" button while we're receiving\r
- //log messages.\r
-\r
- CString temp;\r
- temp.LoadString(IDS_PROGRESSWAIT);\r
- ShowText(temp, true);\r
-\r
-// git_revnum_t r = -1;\r
- \r
- // get the repository root url, because the changed-files-list has the\r
- // paths shown there relative to the repository root.\r
-// CTGitPath rootpath;\r
-// BOOL succeeded = GetRootAndHead(m_path, rootpath, r);\r
-\r
-// m_sRepositoryRoot = rootpath.GetGitPathString();\r
-// m_sURL = m_path.GetGitPathString();\r
-\r
- // we need the UUID to unambigously identify the log cache\r
-// if (logCachePool.IsEnabled())\r
-// m_sUUID = logCachePool.GetRepositoryInfo().GetRepositoryUUID (rootpath);\r
-\r
- // if the log dialog is started from a working copy, we need to turn that\r
- // local path into an url here\r
-// if (succeeded)\r
-// {\r
-// if (!m_path.IsUrl())\r
-// {\r
-// m_sURL = GetURLFromPath(m_path);\r
-\r
- // The URL is escaped because Git::logReceiver\r
- // returns the path in a native format\r
-// m_sURL = CPathUtils::PathUnescape(m_sURL);\r
- // }\r
-// m_sRelativeRoot = m_sURL.Mid(CPathUtils::PathUnescape(m_sRepositoryRoot).GetLength());\r
-// m_sSelfRelativeURL = m_sRelativeRoot;\r
- // }\r
-#if 0\r
- if (succeeded && !m_mergePath.IsEmpty() && m_mergedRevs.empty())\r
- {\r
- // in case we got a merge path set, retrieve the merge info\r
- // of that path and check whether one of the merge URLs\r
- // match the URL we show the log for.\r
- GitPool localpool(pool);\r
- git_error_clear(Err);\r
- apr_hash_t * mergeinfo = NULL;\r
- if (git_client_mergeinfo_get_merged (&mergeinfo, m_mergePath.GetGitApiPath(localpool), GitRev(GitRev::REV_WC), m_pctx, localpool) == NULL)\r
- {\r
- // now check the relative paths\r
- apr_hash_index_t *hi;\r
- const void *key;\r
- void *val;\r
-\r
- if (mergeinfo)\r
- {\r
- for (hi = apr_hash_first(localpool, mergeinfo); hi; hi = apr_hash_next(hi))\r
- {\r
- apr_hash_this(hi, &key, NULL, &val);\r
- if (m_sURL.Compare(CUnicodeUtils::GetUnicode((char*)key)) == 0)\r
- {\r
- apr_array_header_t * arr = (apr_array_header_t*)val;\r
- if (val)\r
- {\r
- for (long i=0; i<arr->nelts; ++i)\r
- {\r
- git_merge_range_t * pRange = APR_ARRAY_IDX(arr, i, git_merge_range_t*);\r
- if (pRange)\r
- {\r
- for (git_revnum_t r=pRange->start+1; r<=pRange->end; ++r)\r
- {\r
- m_mergedRevs.insert(r);\r
- }\r
- }\r
- }\r
- }\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- m_LogProgress.SetPos(1);\r
- if (m_startrev == GitRev::REV_HEAD)\r
- {\r
- m_startrev = r;\r
- }\r
- if (m_endrev == GitRev::REV_HEAD)\r
- {\r
- m_endrev = r;\r
- }\r
-\r
- if (m_limit != 0)\r
- {\r
- m_limitcounter = m_limit;\r
- m_LogProgress.SetRange32(0, m_limit);\r
- }\r
- else\r
- m_LogProgress.SetRange32(m_endrev, m_startrev);\r
- \r
- if (!m_pegrev.IsValid())\r
- m_pegrev = m_startrev;\r
- size_t startcount = m_logEntries.size();\r
- m_lowestRev = -1;\r
- m_bStrictStopped = false;\r
-\r
- if (succeeded)\r
- {\r
- succeeded = ReceiveLog (CTGitPathList(m_path), m_pegrev, m_startrev, m_endrev, m_limit, m_bStrict, m_bIncludeMerges, refresh);\r
- if ((!succeeded)&&(!m_path.IsUrl()))\r
- {\r
- // try again with REV_WC as the start revision, just in case the path doesn't\r
- // exist anymore in HEAD\r
- succeeded = ReceiveLog(CTGitPathList(m_path), GitRev(), GitRev::REV_WC, m_endrev, m_limit, m_bStrict, m_bIncludeMerges, refresh);\r
- }\r
- }\r
- m_LogList.ClearText();\r
- if (!succeeded)\r
- {\r
- m_LogList.ShowText(GetLastErrorMessage(), true);\r
- }\r
- else\r
- {\r
- if (!m_wcRev.IsValid())\r
- {\r
- // fetch the revision the wc path is on so we can mark it\r
- CTGitPath revWCPath = m_ProjectProperties.GetPropsPath();\r
- if (!m_path.IsUrl())\r
- revWCPath = m_path;\r
- if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RecursiveLogRev"), FALSE)))\r
- {\r
- git_revnum_t minrev, maxrev;\r
- bool switched, modified, sparse;\r
- GetWCRevisionStatus(revWCPath, true, minrev, maxrev, switched, modified, sparse);\r
- if (maxrev)\r
- m_wcRev = maxrev;\r
- }\r
- else\r
- {\r
- CTGitPath dummypath;\r
- GitStatus status;\r
- git_wc_status2_t * stat = status.GetFirstFileStatus(revWCPath, dummypath, false, git_depth_empty);\r
- if (stat && stat->entry && stat->entry->cmt_rev)\r
- m_wcRev = stat->entry->cmt_rev;\r
- if (stat && stat->entry && (stat->entry->kind == git_node_dir))\r
- m_wcRev = stat->entry->revision;\r
- }\r
- }\r
- }\r
- if (m_bStrict && (m_lowestRev>1) && ((m_limit>0) ? ((startcount + m_limit)>m_logEntries.size()) : (m_endrev<m_lowestRev)))\r
- m_bStrictStopped = true;\r
- m_LogList.SetItemCountEx(ShownCountWithStopped());\r
-\r
- m_timFrom = (__time64_t(m_tFrom));\r
- m_timTo = (__time64_t(m_tTo));\r
- m_DateFrom.SetRange(&m_timFrom, &m_timTo);\r
- m_DateTo.SetRange(&m_timFrom, &m_timTo);\r
- m_DateFrom.SetTime(&m_timFrom);\r
- m_DateTo.SetTime(&m_timTo);\r
-#endif\r
- //DialogEnableWindow(IDC_GETALL, TRUE);\r
- FillGitLog();\r
- \r
- InterlockedExchange(&m_bThreadRunning, FALSE);\r
-\r
- RedrawItems(0, m_arShownList.GetCount());\r
- SetRedraw(false);\r
- ResizeAllListCtrlCols();\r
- SetRedraw(true);\r
-\r
- if ( m_pStoreSelection )\r
- {\r
- // Deleting the instance will restore the\r
- // selection of the CLogDlg.\r
- delete m_pStoreSelection;\r
- m_pStoreSelection = NULL;\r
- }\r
- else\r
- {\r
- // If no selection has been set then this must be the first time\r
- // the revisions are shown. Let's preselect the topmost revision.\r
- if ( GetItemCount()>0 )\r
- {\r
- SetSelectionMark(0);\r
- SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);\r
- }\r
- }\r
-\r
- //RefreshCursor();\r
- // make sure the filter is applied (if any) now, after we refreshed/fetched\r
- // the log messages\r
-\r
- InterlockedExchange(&m_bNoDispUpdates, FALSE);\r
-\r
- if(m_ProcCallBack)\r
- m_ProcCallBack(m_ProcData,GITLOG_END);\r
-\r
- return 0;\r
-}
\ No newline at end of file
+// GitLogList.cpp : implementation file
+//
+/*
+ Description: qgit revision list view
+
+ Author: Marco Costalba (C) 2005-2007
+
+ Copyright: See COPYING file that comes with this distribution
+
+*/
+#include "stdafx.h"
+#include "TortoiseProc.h"
+#include "GitLogList.h"
+#include "GitRev.h"
+//#include "VssStyle.h"
+#include "IconMenu.h"
+// CGitLogList
+#include "cursor.h"
+#include "InputDlg.h"
+#include "PropDlg.h"
+#include "SVNProgressDlg.h"
+#include "ProgressDlg.h"
+//#include "RepositoryBrowser.h"
+//#include "CopyDlg.h"
+//#include "StatGraphDlg.h"
+#include "Logdlg.h"
+#include "MessageBox.h"
+#include "Registry.h"
+#include "AppUtils.h"
+#include "PathUtils.h"
+#include "StringUtils.h"
+#include "UnicodeUtils.h"
+#include "TempFile.h"
+//#include "GitInfo.h"
+//#include "GitDiff.h"
+#include "IconMenu.h"
+//#include "RevisionRangeDlg.h"
+//#include "BrowseFolder.h"
+//#include "BlameDlg.h"
+//#include "Blame.h"
+//#include "GitHelpers.h"
+#include "GitStatus.h"
+//#include "LogDlgHelper.h"
+//#include "CachedLogInfo.h"
+//#include "RepositoryInfo.h"
+//#include "EditPropertiesDlg.h"
+#include "FileDiffDlg.h"
+
+
+
+
+IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
+
+CGitLogList::CGitLogList():CHintListCtrl()
+ ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)
+ ,m_nSearchIndex(0)
+ ,m_bNoDispUpdates(FALSE)
+ , m_bThreadRunning(FALSE)
+ , m_bStrictStopped(false)
+ , m_pStoreSelection(NULL)
+{
+ // use the default GUI font, create a copy of it and
+ // change the copy to BOLD (leave the rest of the font
+ // the same)
+ HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ LOGFONT lf = {0};
+ GetObject(hFont, sizeof(LOGFONT), &lf);
+ lf.lfWeight = FW_BOLD;
+ m_boldFont = CreateFontIndirect(&lf);
+
+ m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+ m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+ m_hAddedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+ m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
+
+ g_Git.GetMapHashToFriendName(m_HashMap);
+}
+
+CGitLogList::~CGitLogList()
+{
+ InterlockedExchange(&m_bNoDispUpdates, TRUE);
+
+ DestroyIcon(m_hModifiedIcon);
+ DestroyIcon(m_hReplacedIcon);
+ DestroyIcon(m_hAddedIcon);
+ DestroyIcon(m_hDeletedIcon);
+ m_logEntries.ClearAll();
+
+ if (m_boldFont)
+ DeleteObject(m_boldFont);
+
+ if ( m_pStoreSelection )
+ {
+ delete m_pStoreSelection;
+ m_pStoreSelection = NULL;
+ }
+}
+
+
+BEGIN_MESSAGE_MAP(CGitLogList, CHintListCtrl)
+ ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)
+ ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)
+ ON_WM_CONTEXTMENU()
+ ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)
+ ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)
+ ON_WM_CREATE()
+END_MESSAGE_MAP()
+
+int CGitLogList:: OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ PreSubclassWindow();
+ return CHintListCtrl::OnCreate(lpCreateStruct);
+}
+
+void CGitLogList::PreSubclassWindow()
+{
+ SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);
+ // load the icons for the action columns
+ m_Theme.SetWindowTheme(GetSafeHwnd(), L"Explorer", NULL);
+ CHintListCtrl::PreSubclassWindow();
+}
+
+void CGitLogList::InsertGitColumn()
+{
+ CString temp;
+
+ int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
+
+ while (c>=0)
+ DeleteColumn(c--);
+ temp.LoadString(IDS_LOG_GRAPH);
+
+ InsertColumn(this->LOGLIST_GRAPH, temp);
+
+#if 0
+ // make the revision column right aligned
+ LVCOLUMN Column;
+ Column.mask = LVCF_FMT;
+ Column.fmt = LVCFMT_RIGHT;
+ SetColumn(0, &Column);
+#endif
+// CString log;
+// g_Git.GetLog(log);
+
+ temp.LoadString(IDS_LOG_ACTIONS);
+ InsertColumn(this->LOGLIST_ACTION, temp);
+
+ temp.LoadString(IDS_LOG_MESSAGE);
+ InsertColumn(this->LOGLIST_MESSAGE, temp);
+
+ temp.LoadString(IDS_LOG_AUTHOR);
+ InsertColumn(this->LOGLIST_AUTHOR, temp);
+
+ temp.LoadString(IDS_LOG_DATE);
+ InsertColumn(this->LOGLIST_DATE, temp);
+
+
+ if (m_bShowBugtraqColumn)
+ {
+// temp = m_ProjectProperties.sLabel;
+ if (temp.IsEmpty())
+ temp.LoadString(IDS_LOG_BUGIDS);
+ InsertColumn(this->LOGLIST_BUG, temp);
+
+ }
+
+ SetRedraw(false);
+ ResizeAllListCtrlCols();
+ SetRedraw(true);
+
+}
+
+void CGitLogList::ResizeAllListCtrlCols()
+{
+
+ const int nMinimumWidth = ICONITEMBORDER+16*4;
+ int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
+ int nItemCount = GetItemCount();
+ TCHAR textbuf[MAX_PATH];
+ CHeaderCtrl * pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
+ if (pHdrCtrl)
+ {
+ for (int col = 0; col <= maxcol; col++)
+ {
+ HDITEM hdi = {0};
+ hdi.mask = HDI_TEXT;
+ hdi.pszText = textbuf;
+ hdi.cchTextMax = sizeof(textbuf);
+ pHdrCtrl->GetItem(col, &hdi);
+ int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin
+ for (int index = 0; index<nItemCount; ++index)
+ {
+ // get the width of the string and add 14 pixels for the column separator and margins
+ int linewidth = GetStringWidth(GetItemText(index, col)) + 14;
+ if (index < m_arShownList.GetCount())
+ {
+ GitRev * pCurLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
+ if ((pCurLogEntry)&&(pCurLogEntry->m_CommitHash == m_wcRev.m_CommitHash))
+ {
+ // set the bold font and ask for the string width again
+ SendMessage(WM_SETFONT, (WPARAM)m_boldFont, NULL);
+ linewidth = GetStringWidth(GetItemText(index, col)) + 14;
+ // restore the system font
+ SendMessage(WM_SETFONT, NULL, NULL);
+ }
+ }
+ if (index == 0)
+ {
+ // add the image size
+ CImageList * pImgList = GetImageList(LVSIL_SMALL);
+ if ((pImgList)&&(pImgList->GetImageCount()))
+ {
+ IMAGEINFO imginfo;
+ pImgList->GetImageInfo(0, &imginfo);
+ linewidth += (imginfo.rcImage.right - imginfo.rcImage.left);
+ linewidth += 3; // 3 pixels between icon and text
+ }
+ }
+ if (cx < linewidth)
+ cx = linewidth;
+ }
+ // Adjust columns "Actions" containing icons
+ if (col == this->LOGLIST_ACTION)
+ {
+ if (cx < nMinimumWidth)
+ {
+ cx = nMinimumWidth;
+ }
+ }
+
+ if (col == this->LOGLIST_MESSAGE)
+ {
+ if (cx > LOGLIST_MESSAGE_MAX)
+ {
+ cx = LOGLIST_MESSAGE_MAX;
+ }
+
+ }
+ // keep the bug id column small
+ if ((col == 4)&&(m_bShowBugtraqColumn))
+ {
+ if (cx > (int)(DWORD)m_regMaxBugIDColWidth)
+ {
+ cx = (int)(DWORD)m_regMaxBugIDColWidth;
+ }
+ }
+
+ SetColumnWidth(col, cx);
+ }
+ }
+
+}
+BOOL CGitLogList::GetShortName(CString ref, CString &shortname,CString prefix)
+{
+ if(ref.Left(prefix.GetLength()) == prefix)
+ {
+ shortname = ref.Right(ref.GetLength()-prefix.GetLength());
+ return TRUE;
+ }
+ return FALSE;
+}
+void CGitLogList::FillBackGround(HDC hdc, int Index,CRect &rect)
+{
+// HBRUSH brush;
+ LVITEM rItem;
+ SecureZeroMemory(&rItem, sizeof(LVITEM));
+ rItem.mask = LVIF_STATE;
+ rItem.iItem = Index;
+ rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
+ GetItem(&rItem);
+
+ if (m_Theme.IsAppThemed() && m_bVista)
+ {
+ m_Theme.Open(m_hWnd, L"Explorer");
+ int state = LISS_NORMAL;
+ if (rItem.state & LVIS_SELECTED)
+ {
+ if (::GetFocus() == m_hWnd)
+ state |= LISS_SELECTED;
+ else
+ state |= LISS_SELECTEDNOTFOCUS;
+ }
+ else
+ {
+#if 0
+ if (pLogEntry->bCopiedSelf)
+ {
+ // unfortunately, the pLVCD->nmcd.uItemState does not contain valid
+ // information at this drawing stage. But we can check the whether the
+ // previous stage changed the background color of the item
+ if (pLVCD->clrTextBk == GetSysColor(COLOR_MENU))
+ {
+ HBRUSH brush;
+ brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
+ if (brush)
+ {
+ ::FillRect(pLVCD->nmcd.hdc, &rect, brush);
+ ::DeleteObject(brush);
+ }
+ }
+ }
+#endif
+ }
+
+ if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTDETAIL, state))
+ m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);
+
+ m_Theme.DrawBackground(hdc, LVP_LISTDETAIL, state, &rect, NULL);
+ }
+ else
+ {
+ HBRUSH brush;
+ if (rItem.state & LVIS_SELECTED)
+ {
+ if (::GetFocus() == m_hWnd)
+ brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
+ else
+ brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
+ }
+ else
+ {
+ //if (pLogEntry->bCopiedSelf)
+ // brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
+ //else
+ brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
+ }
+ if (brush == NULL)
+ return;
+
+ ::FillRect(hdc, &rect, brush);
+ ::DeleteObject(brush);
+
+ }
+}
+
+void CGitLogList::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)
+{
+ GitRev* data = (GitRev*)m_arShownList.GetAt(index);
+ CRect rt=rect;
+ LVITEM rItem;
+ SecureZeroMemory(&rItem, sizeof(LVITEM));
+ rItem.mask = LVIF_STATE;
+ rItem.iItem = index;
+ rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
+ GetItem(&rItem);
+
+ for(int i=0;i<m_HashMap[data->m_CommitHash].size();i++)
+ {
+ CString str;
+ str=m_HashMap[data->m_CommitHash][i];
+
+ CString shortname;
+ HBRUSH brush=0;
+ shortname=_T("");
+ if(GetShortName(str,shortname,_T("refs/heads/")))
+ {
+ brush = ::CreateSolidBrush(RGB(0xff, 0, 0));
+ }else if(GetShortName(str,shortname,_T("refs/remotes/")))
+ {
+ brush = ::CreateSolidBrush(RGB(0xff, 0xff, 0));
+ }
+ else if(GetShortName(str,shortname,_T("refs/tags/")))
+ {
+ brush = ::CreateSolidBrush(RGB(0, 0, 0xff));
+ }
+
+ if(!shortname.IsEmpty())
+ {
+ SIZE size;
+ memset(&size,0,sizeof(SIZE));
+ GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);
+
+ rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);
+ rt.right+=4;
+ ::FillRect(hdc, &rt, brush);
+ if (rItem.state & LVIS_SELECTED)
+ {
+ COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
+ ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);
+ ::SetTextColor(hdc,clrOld);
+ }else
+ {
+ ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);
+ }
+
+
+ ::MoveToEx(hdc,rt.left,rt.top,NULL);
+ ::LineTo(hdc,rt.right,rt.top);
+ ::LineTo(hdc,rt.right,rt.bottom);
+ ::LineTo(hdc,rt.left,rt.bottom);
+ ::LineTo(hdc,rt.left,rt.top);
+
+ rt.left=rt.right+3;
+ }
+ if(brush)
+ ::DeleteObject(brush);
+ }
+ rt.right=rect.right;
+
+ if (rItem.state & LVIS_SELECTED)
+ {
+ COLORREF clrOld = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
+ ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);
+ ::SetTextColor(hdc,clrOld);
+ }else
+ {
+ ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);
+ }
+
+}
+
+void CGitLogList::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,
+ const COLORREF& col,int top
+ )
+{
+ int h = laneHeight / 2;
+ int m = (x1 + x2) / 2;
+ int r = (x2 - x1) / 3;
+ int d = 2 * r;
+
+ #define P_CENTER m , h+top
+ #define P_0 x2, h+top
+ #define P_90 m , 0+top
+ #define P_180 x1, h+top
+ #define P_270 m , 2 * h+top
+ #define R_CENTER m - r, h - r+top, m - r+d, h - r+top+d
+
+ //static QPen myPen(Qt::black, 2); // fast path here
+ CPen pen;
+ pen.CreatePen(PS_SOLID,2,col);
+ //myPen.setColor(col);
+ HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);
+
+ //p->setPen(myPen);
+
+ // vertical line
+ switch (type) {
+ case Lanes::ACTIVE:
+ case Lanes::NOT_ACTIVE:
+ case Lanes::MERGE_FORK:
+ case Lanes::MERGE_FORK_R:
+ case Lanes::MERGE_FORK_L:
+ case Lanes::JOIN:
+ case Lanes::JOIN_R:
+ case Lanes::JOIN_L:
+ DrawLine(hdc,P_90,P_270);
+ //p->drawLine(P_90, P_270);
+ break;
+ case Lanes::HEAD:
+ case Lanes::HEAD_R:
+ case Lanes::HEAD_L:
+ case Lanes::BRANCH:
+ DrawLine(hdc,P_CENTER,P_270);
+ //p->drawLine(P_CENTER, P_270);
+ break;
+ case Lanes::TAIL:
+ case Lanes::TAIL_R:
+ case Lanes::TAIL_L:
+ case Lanes::INITIAL:
+ case Lanes::BOUNDARY:
+ case Lanes::BOUNDARY_C:
+ case Lanes::BOUNDARY_R:
+ case Lanes::BOUNDARY_L:
+ DrawLine(hdc,P_90, P_CENTER);
+ //p->drawLine(P_90, P_CENTER);
+ break;
+ default:
+ break;
+ }
+
+ // horizontal line
+ switch (type) {
+ case Lanes::MERGE_FORK:
+ case Lanes::JOIN:
+ case Lanes::HEAD:
+ case Lanes::TAIL:
+ case Lanes::CROSS:
+ case Lanes::CROSS_EMPTY:
+ case Lanes::BOUNDARY_C:
+ DrawLine(hdc,P_180,P_0);
+ //p->drawLine(P_180, P_0);
+ break;
+ case Lanes::MERGE_FORK_R:
+ case Lanes::JOIN_R:
+ case Lanes::HEAD_R:
+ case Lanes::TAIL_R:
+ case Lanes::BOUNDARY_R:
+ DrawLine(hdc,P_180,P_CENTER);
+ //p->drawLine(P_180, P_CENTER);
+ break;
+ case Lanes::MERGE_FORK_L:
+ case Lanes::JOIN_L:
+ case Lanes::HEAD_L:
+ case Lanes::TAIL_L:
+ case Lanes::BOUNDARY_L:
+ DrawLine(hdc,P_CENTER,P_0);
+ //p->drawLine(P_CENTER, P_0);
+ break;
+ default:
+ break;
+ }
+
+ CBrush brush;
+ brush.CreateSolidBrush(col);
+ HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);
+ // center symbol, e.g. rect or ellipse
+ switch (type) {
+ case Lanes::ACTIVE:
+ case Lanes::INITIAL:
+ case Lanes::BRANCH:
+
+ //p->setPen(Qt::NoPen);
+ //p->setBrush(col);
+ ::Ellipse(hdc, R_CENTER);
+ //p->drawEllipse(R_CENTER);
+ break;
+ case Lanes::MERGE_FORK:
+ case Lanes::MERGE_FORK_R:
+ case Lanes::MERGE_FORK_L:
+ //p->setPen(Qt::NoPen);
+ //p->setBrush(col);
+ //p->drawRect(R_CENTER);
+ Rectangle(hdc,R_CENTER);
+ break;
+ case Lanes::UNAPPLIED:
+ // Red minus sign
+ //p->setPen(Qt::NoPen);
+ //p->setBrush(Qt::red);
+ //p->drawRect(m - r, h - 1, d, 2);
+ ::Rectangle(hdc,m-r,h-1,d,2);
+ break;
+ case Lanes::APPLIED:
+ // Green plus sign
+ //p->setPen(Qt::NoPen);
+ //p->setBrush(DARK_GREEN);
+ //p->drawRect(m - r, h - 1, d, 2);
+ //p->drawRect(m - 1, h - r, 2, d);
+ ::Rectangle(hdc,m-r,h-1,d,2);
+ ::Rectangle(hdc,m-1,h-r,2,d);
+ break;
+ case Lanes::BOUNDARY:
+ //p->setBrush(back);
+ //p->drawEllipse(R_CENTER);
+ ::Ellipse(hdc, R_CENTER);
+ break;
+ case Lanes::BOUNDARY_C:
+ case Lanes::BOUNDARY_R:
+ case Lanes::BOUNDARY_L:
+ //p->setBrush(back);
+ //p->drawRect(R_CENTER);
+ ::Rectangle(hdc,R_CENTER);
+ break;
+ default:
+ break;
+ }
+
+ ::SelectObject(hdc,oldpen);
+ ::SelectObject(hdc,oldbrush);
+ #undef P_CENTER
+ #undef P_0
+ #undef P_90
+ #undef P_180
+ #undef P_270
+ #undef R_CENTER
+}
+
+void CGitLogList::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)
+{
+ //todo unfinished
+ return;
+ GitRev* data = (GitRev*)m_arShownList.GetAt(index);
+ CRect rt=rect;
+ LVITEM rItem;
+ SecureZeroMemory(&rItem, sizeof(LVITEM));
+ rItem.mask = LVIF_STATE;
+ rItem.iItem = index;
+ rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
+ GetItem(&rItem);
+
+ static const COLORREF colors[Lanes::COLORS_NUM] = { RGB(0,0,0), RGB(0xFF,0,0), RGB(0,0x1F,0),
+ RGB(0,0,0xFF), RGB(128,128,128), RGB(128,128,0),
+ RGB(0,128,128), RGB(128,0,128) };
+
+
+// p->translate(QPoint(opt.rect.left(), opt.rect.top()));
+
+
+
+ if (data->m_Lanes.size() == 0)
+ m_logEntries.setLane(data->m_CommitHash);
+
+ std::vector<int>& lanes=data->m_Lanes;
+ UINT laneNum = lanes.size();
+ UINT mergeLane = 0;
+ for (UINT i = 0; i < laneNum; i++)
+ if (Lanes::isMerge(lanes[i])) {
+ mergeLane = i;
+ break;
+ }
+
+ int x1 = 0, x2 = 0;
+ int maxWidth = rect.Width();
+ int lw = 3 * rect.Height() / 4; //laneWidth()
+ for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {
+
+ x1 = x2;
+ x2 += lw;
+
+ int ln = lanes[i];
+ if (ln == Lanes::EMPTY)
+ continue;
+
+ UINT col = ( Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)
+ || ln ==Lanes:: CROSS_EMPTY) ? mergeLane : i;
+
+ if (ln == Lanes::CROSS) {
+ paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, colors[col % Lanes::COLORS_NUM],rect.top);
+ paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, colors[mergeLane % Lanes::COLORS_NUM],rect.top);
+ } else
+ paintGraphLane(hdc, rect.Height(),ln, x1, x2, colors[col % Lanes::COLORS_NUM],rect.top);
+ }
+
+ TRACE(_T("index %d %d\r\n"),index,data->m_Lanes.size());
+}
+
+void CGitLogList::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)
+{
+
+ NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
+ // Take the default processing unless we set this to something else below.
+ *pResult = CDRF_DODEFAULT;
+
+ if (m_bNoDispUpdates)
+ return;
+
+ switch (pLVCD->nmcd.dwDrawStage)
+ {
+ case CDDS_PREPAINT:
+ {
+ *pResult = CDRF_NOTIFYITEMDRAW;
+ return;
+ }
+ break;
+ case CDDS_ITEMPREPAINT:
+ {
+ // This is the prepaint stage for an item. Here's where we set the
+ // item's text color.
+
+ // Tell Windows to send draw notifications for each subitem.
+ *pResult = CDRF_NOTIFYSUBITEMDRAW;
+
+ COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
+
+ if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
+ {
+ GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
+ if (data)
+ {
+#if 0
+ if (data->bCopiedSelf)
+ {
+ // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)
+ if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))
+ pLVCD->clrTextBk = GetSysColor(COLOR_MENU);
+ }
+
+ if (data->bCopies)
+ crText = m_Colors.GetColor(CColors::Modified);
+#endif
+// if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))
+// crText = GetSysColor(COLOR_GRAYTEXT);
+// if (data->Rev == m_wcRev)
+// {
+// SelectObject(pLVCD->nmcd.hdc, m_boldFont);
+ // We changed the font, so we're returning CDRF_NEWFONT. This
+ // tells the control to recalculate the extent of the text.
+// *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
+// }
+ }
+ }
+ if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)
+ {
+ if (m_bStrictStopped)
+ crText = GetSysColor(COLOR_GRAYTEXT);
+ }
+ // Store the color back in the NMLVCUSTOMDRAW struct.
+ pLVCD->clrText = crText;
+ return;
+ }
+ break;
+ case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:
+ {
+ if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))
+ {
+ pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);
+ }
+
+ if (pLVCD->iSubItem == LOGLIST_GRAPH)
+ {
+ if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
+ {
+ CRect rect;
+ GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
+
+ FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
+ DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
+
+ *pResult = CDRF_SKIPDEFAULT;
+ return;
+
+ }
+ }
+
+ if (pLVCD->iSubItem == LOGLIST_MESSAGE)
+ {
+ if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
+ {
+ GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
+ if(m_HashMap[data->m_CommitHash].size()!=0)
+ {
+ CRect rect;
+ GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
+
+ FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
+ DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
+
+ *pResult = CDRF_SKIPDEFAULT;
+ return;
+
+ }
+ }
+ }
+
+ if (pLVCD->iSubItem == 1)
+ {
+ *pResult = CDRF_DODEFAULT;
+
+ if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)
+ return;
+
+ int nIcons = 0;
+ int iconwidth = ::GetSystemMetrics(SM_CXSMICON);
+ int iconheight = ::GetSystemMetrics(SM_CYSMICON);
+
+ GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));
+ CRect rect;
+ GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
+ // Get the selected state of the
+ // item being drawn.
+
+ // Fill the background
+ FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
+
+ // Draw the icon(s) into the compatible DC
+ if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)
+ ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
+ nIcons++;
+
+ if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_ADDED)
+ ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
+ nIcons++;
+
+ if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)
+ ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
+ nIcons++;
+
+ if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
+ ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
+ nIcons++;
+ *pResult = CDRF_SKIPDEFAULT;
+ return;
+ }
+ }
+ break;
+ }
+ *pResult = CDRF_DODEFAULT;
+
+}
+
+// CGitLogList message handlers
+
+void CGitLogList::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
+
+ // Create a pointer to the item
+ LV_ITEM* pItem = &(pDispInfo)->item;
+
+ // Do the list need text information?
+ if (!(pItem->mask & LVIF_TEXT))
+ return;
+
+ // By default, clear text buffer.
+ lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);
+
+ bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();
+
+ *pResult = 0;
+ if (m_bNoDispUpdates || m_bThreadRunning || bOutOfRange)
+ return;
+
+ // Which item number?
+ int itemid = pItem->iItem;
+ GitRev * pLogEntry = NULL;
+ if (itemid < m_arShownList.GetCount())
+ pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));
+
+ // Which column?
+ switch (pItem->iSubItem)
+ {
+ case this->LOGLIST_GRAPH: //Graphic
+ if (pLogEntry)
+ {
+ }
+ break;
+ case this->LOGLIST_ACTION: //action -- no text in the column
+ break;
+ case this->LOGLIST_MESSAGE: //Message
+ if (pLogEntry)
+ lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);
+ break;
+ case this->LOGLIST_AUTHOR: //Author
+ if (pLogEntry)
+ lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);
+ break;
+ case this->LOGLIST_DATE: //Date
+ if (pLogEntry)
+ lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorDate.Format(_T("%Y-%m-%d %H:%M")), pItem->cchTextMax);
+ break;
+
+ case 5:
+
+ break;
+ default:
+ ASSERT(false);
+ }
+}
+
+void CGitLogList::OnContextMenu(CWnd* pWnd, CPoint point)
+{
+
+ int selIndex = GetSelectionMark();
+ if (selIndex < 0)
+ return; // nothing selected, nothing to do with a context menu
+
+ // if the user selected the info text telling about not all revisions shown due to
+ // the "stop on copy/rename" option, we also don't show the context menu
+ if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))
+ return;
+
+ // if the context menu is invoked through the keyboard, we have to use
+ // a calculated position on where to anchor the menu on
+ if ((point.x == -1) && (point.y == -1))
+ {
+ CRect rect;
+ GetItemRect(selIndex, &rect, LVIR_LABEL);
+ ClientToScreen(&rect);
+ point = rect.CenterPoint();
+ }
+ m_nSearchIndex = selIndex;
+ m_bCancelled = FALSE;
+
+ // calculate some information the context menu commands can use
+// CString pathURL = GetURLFromPath(m_path);
+
+ POSITION pos = GetFirstSelectedItemPosition();
+ int indexNext = GetNextSelectedItem(pos);
+ if (indexNext < 0)
+ return;
+
+ GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
+#if 0
+ GitRev revSelected = pSelLogEntry->Rev;
+ GitRev revPrevious = git_revnum_t(revSelected)-1;
+ if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))
+ {
+ for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)
+ {
+ LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);
+ if (changedpath->lCopyFromRev)
+ revPrevious = changedpath->lCopyFromRev;
+ }
+ }
+ GitRev revSelected2;
+ if (pos)
+ {
+ PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
+ revSelected2 = pLogEntry->Rev;
+ }
+ bool bAllFromTheSameAuthor = true;
+ CString firstAuthor;
+ CLogDataVector selEntries;
+ GitRev revLowest, revHighest;
+ GitRevRangeArray revisionRanges;
+ {
+ POSITION pos = GetFirstSelectedItemPosition();
+ PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
+ revisionRanges.AddRevision(pLogEntry->Rev);
+ selEntries.push_back(pLogEntry);
+ firstAuthor = pLogEntry->sAuthor;
+ revLowest = pLogEntry->Rev;
+ revHighest = pLogEntry->Rev;
+ while (pos)
+ {
+ pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
+ revisionRanges.AddRevision(pLogEntry->Rev);
+ selEntries.push_back(pLogEntry);
+ if (firstAuthor.Compare(pLogEntry->sAuthor))
+ bAllFromTheSameAuthor = false;
+ revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);
+ revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);
+ }
+ }
+
+#endif
+
+ int FirstSelect=-1, LastSelect=-1;
+ pos = GetFirstSelectedItemPosition();
+ FirstSelect = GetNextSelectedItem(pos);
+ while(pos)
+ {
+ LastSelect = GetNextSelectedItem(pos);
+ }
+ //entry is selected, now show the popup menu
+ CIconMenu popup;
+ if (popup.CreatePopupMenu())
+ {
+ if (GetSelectedCount() == 1)
+ {
+#if 0
+ if (!m_path.IsDirectory())
+ {
+ if (m_hasWC)
+ {
+ popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
+ popup.AppendMenuIcon(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
+ }
+ popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
+ popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
+ popup.AppendMenu(MF_SEPARATOR, NULL);
+ popup.AppendMenuIcon(ID_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);
+ popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
+ popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
+ popup.AppendMenuIcon(ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);
+ popup.AppendMenu(MF_SEPARATOR, NULL);
+ }
+ else
+#endif
+ {
+ if (m_hasWC)
+ {
+ popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
+ // TODO:
+ // TortoiseMerge could be improved to take a /blame switch
+ // and then not 'cat' the files from a unified diff but
+ // blame then.
+ // But until that's implemented, the context menu entry for
+ // this feature is commented out.
+ //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
+ }
+ popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
+ popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
+ popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);
+ popup.AppendMenu(MF_SEPARATOR, NULL);
+ }
+
+// if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
+// {
+// popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);
+// }
+// if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
+// {
+// popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
+// }
+// if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
+// (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
+// {
+// popup.AppendMenu(MF_SEPARATOR, NULL);
+// }
+
+// popup.AppendMenuIcon(ID_REPOBROWSE, IDS_LOG_BROWSEREPO, IDI_REPOBROWSE);
+// popup.AppendMenuIcon(ID_COPY, IDS_LOG_POPUP_COPY);
+// if (m_hasWC)
+// popup.AppendMenuIcon(ID_UPDATE, IDS_LOG_POPUP_UPDATE, IDI_UPDATE);
+ if (m_hasWC)
+ popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
+ if (m_hasWC)
+ popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
+// if (m_hasWC)
+// popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREV, IDI_MERGE);
+ popup.AppendMenuIcon(ID_CHECKOUT, IDS_MENUCHECKOUT, IDI_CHECKOUT);
+ popup.AppendMenuIcon(ID_EXPORT, IDS_MENUEXPORT, IDI_EXPORT);
+ popup.AppendMenu(MF_SEPARATOR, NULL);
+ }
+ else if (GetSelectedCount() >= 2)
+ {
+ bool bAddSeparator = false;
+ if (IsSelectionContinuous() || (GetSelectedCount() == 2))
+ {
+ popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
+ }
+ if (GetSelectedCount() == 2)
+ {
+ popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);
+ popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
+ bAddSeparator = true;
+ }
+ if (m_hasWC)
+ {
+ popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
+// if (m_hasWC)
+// popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);
+ bAddSeparator = true;
+ }
+ if (bAddSeparator)
+ popup.AppendMenu(MF_SEPARATOR, NULL);
+ }
+#if 0
+// if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))
+// {
+// popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);
+// }
+// if (GetSelectedCount() == 1)
+// {
+// popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);
+// popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
+// popup.AppendMenu(MF_SEPARATOR, NULL);
+// }
+#endif
+ if (GetSelectedCount() != 0)
+ {
+ popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);
+ }
+ popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);
+
+ int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
+// DialogEnableWindow(IDOK, FALSE);
+// SetPromptApp(&theApp);
+ theApp.DoWaitCursor(1);
+ bool bOpenWith = false;
+
+ switch (cmd)
+ {
+ case ID_GNUDIFF1:
+ {
+ CString tempfile=GetTempFile();
+ CString cmd;
+ GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
+ cmd.Format(_T("git.cmd diff-tree -r -p --stat %s"),r1->m_CommitHash);
+ g_Git.RunLogFile(cmd,tempfile);
+ CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);
+ }
+ break;
+
+ case ID_GNUDIFF2:
+ {
+ CString tempfile=GetTempFile();
+ CString cmd;
+ GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
+ GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
+ cmd.Format(_T("git.cmd diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);
+ g_Git.RunLogFile(cmd,tempfile);
+ CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));
+
+ }
+ break;
+
+ case ID_COMPARETWO:
+ {
+ GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
+ GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
+ CFileDiffDlg dlg;
+ dlg.SetDiff(NULL,*r1,*r2);
+ dlg.DoModal();
+
+ }
+ break;
+
+#if 0
+ case ID_GNUDIFF1:
+ {
+ if (PromptShown())
+ {
+ GitDiff diff(this, this->m_hWnd, true);
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowUnifiedDiff(m_path, revPrevious, m_path, revSelected);
+ }
+ else
+ CAppUtils::StartShowUnifiedDiff(m_hWnd, m_path, revPrevious, m_path, revSelected, GitRev(), m_LogRevision);
+ }
+ break;
+
+ case ID_GNUDIFF2:
+ {
+ if (PromptShown())
+ {
+ GitDiff diff(this, this->m_hWnd, true);
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowUnifiedDiff(m_path, revSelected2, m_path, revSelected);
+ }
+ else
+ CAppUtils::StartShowUnifiedDiff(m_hWnd, m_path, revSelected2, m_path, revSelected, GitRev(), m_LogRevision);
+ }
+ break;
+ case ID_REVERTREV:
+ {
+ // we need an URL to complete this command, so error out if we can't get an URL
+ if (pathURL.IsEmpty())
+ {
+ CString strMessage;
+ strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
+ CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
+ TRACE(_T("could not retrieve the URL of the folder!\n"));
+ break; //exit
+ }
+ CString msg;
+ msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());
+ if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
+ {
+ CGitProgressDlg dlg;
+ dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
+ dlg.SetPathList(CTGitPathList(m_path));
+ dlg.SetUrl(pathURL);
+ dlg.SetSecondUrl(pathURL);
+ revisionRanges.AdjustForMerge(true);
+ dlg.SetRevisionRanges(revisionRanges);
+ dlg.SetPegRevision(m_LogRevision);
+ dlg.DoModal();
+ }
+ }
+ break;
+ case ID_MERGEREV:
+ {
+ // we need an URL to complete this command, so error out if we can't get an URL
+ if (pathURL.IsEmpty())
+ {
+ CString strMessage;
+ strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
+ CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
+ TRACE(_T("could not retrieve the URL of the folder!\n"));
+ break; //exit
+ }
+
+ CString path = m_path.GetWinPathString();
+ bool bGotSavePath = false;
+ if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))
+ {
+ bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());
+ }
+ else
+ {
+ CBrowseFolder folderBrowser;
+ folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));
+ bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);
+ }
+ if (bGotSavePath)
+ {
+ CGitProgressDlg dlg;
+ dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
+ dlg.SetPathList(CTGitPathList(CTGitPath(path)));
+ dlg.SetUrl(pathURL);
+ dlg.SetSecondUrl(pathURL);
+ revisionRanges.AdjustForMerge(false);
+ dlg.SetRevisionRanges(revisionRanges);
+ dlg.SetPegRevision(m_LogRevision);
+ dlg.DoModal();
+ }
+ }
+ break;
+ case ID_REVERTTOREV:
+ {
+ // we need an URL to complete this command, so error out if we can't get an URL
+ if (pathURL.IsEmpty())
+ {
+ CString strMessage;
+ strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
+ CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
+ TRACE(_T("could not retrieve the URL of the folder!\n"));
+ break; //exit
+ }
+
+ CString msg;
+ msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());
+ if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
+ {
+ CGitProgressDlg dlg;
+ dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
+ dlg.SetPathList(CTGitPathList(m_path));
+ dlg.SetUrl(pathURL);
+ dlg.SetSecondUrl(pathURL);
+ GitRevRangeArray revarray;
+ revarray.AddRevRange(GitRev::REV_HEAD, revSelected);
+ dlg.SetRevisionRanges(revarray);
+ dlg.SetPegRevision(m_LogRevision);
+ dlg.DoModal();
+ }
+ }
+ break;
+ case ID_COPY:
+ {
+ // we need an URL to complete this command, so error out if we can't get an URL
+ if (pathURL.IsEmpty())
+ {
+ CString strMessage;
+ strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
+ CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
+ TRACE(_T("could not retrieve the URL of the folder!\n"));
+ break; //exit
+ }
+
+ CCopyDlg dlg;
+ dlg.m_URL = pathURL;
+ dlg.m_path = m_path;
+ dlg.m_CopyRev = revSelected;
+ if (dlg.DoModal() == IDOK)
+ {
+ // should we show a progress dialog here? Copies are done really fast
+ // and without much network traffic.
+ if (!Copy(CTGitPathList(CTGitPath(pathURL)), CTGitPath(dlg.m_URL), dlg.m_CopyRev, dlg.m_CopyRev, dlg.m_sLogMessage))
+ CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
+ else
+ CMessageBox::Show(this->m_hWnd, IDS_LOG_COPY_SUCCESS, IDS_APPNAME, MB_ICONINFORMATION);
+ }
+ }
+ break;
+ case ID_COMPARE:
+ {
+ //user clicked on the menu item "compare with working copy"
+ if (PromptShown())
+ {
+ GitDiff diff(this, m_hWnd, true);
+ diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ }
+ break;
+ case ID_COMPARETWO:
+ {
+ GitRev r1 = revSelected;
+ GitRev r2 = revSelected2;
+ if (GetSelectedCount() > 2)
+ {
+ r1 = revHighest;
+ r2 = revLowest;
+ }
+ //user clicked on the menu item "compare revisions"
+ if (PromptShown())
+ {
+ GitDiff diff(this, m_hWnd, true);
+ diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(CTGitPath(pathURL), r2, CTGitPath(pathURL), r1);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), r2, CTGitPath(pathURL), r1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ }
+ break;
+ case ID_COMPAREWITHPREVIOUS:
+ {
+ if (PromptShown())
+ {
+ GitDiff diff(this, m_hWnd, true);
+ diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ }
+ break;
+ case ID_BLAMECOMPARE:
+ {
+ //user clicked on the menu item "compare with working copy"
+ //now first get the revision which is selected
+ if (PromptShown())
+ {
+ GitDiff diff(this, this->m_hWnd, true);
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
+ }
+ break;
+ case ID_BLAMETWO:
+ {
+ //user clicked on the menu item "compare and blame revisions"
+ if (PromptShown())
+ {
+ GitDiff diff(this, this->m_hWnd, true);
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
+ }
+ break;
+ case ID_BLAMEWITHPREVIOUS:
+ {
+ //user clicked on the menu item "Compare and Blame with previous revision"
+ if (PromptShown())
+ {
+ GitDiff diff(this, this->m_hWnd, true);
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
+ }
+ else
+ CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
+ }
+ break;
+ case ID_SAVEAS:
+ {
+ //now first get the revision which is selected
+ CString revFilename;
+ if (m_hasWC)
+ {
+ CString strWinPath = m_path.GetWinPathString();
+ int rfind = strWinPath.ReverseFind('.');
+ if (rfind > 0)
+ revFilename.Format(_T("%s-%ld%s"), (LPCTSTR)strWinPath.Left(rfind), (LONG)revSelected, (LPCTSTR)strWinPath.Mid(rfind));
+ else
+ revFilename.Format(_T("%s-%ld"), (LPCTSTR)strWinPath, revSelected);
+ }
+ if (CAppUtils::FileOpenSave(revFilename, NULL, IDS_LOG_POPUP_SAVE, IDS_COMMONFILEFILTER, false, m_hWnd))
+ {
+ CTGitPath tempfile;
+ tempfile.SetFromWin(revFilename);
+ CProgressDlg progDlg;
+ progDlg.SetTitle(IDS_APPNAME);
+ progDlg.SetAnimation(IDR_DOWNLOAD);
+ CString sInfoLine;
+ sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
+ progDlg.SetLine(1, sInfoLine, true);
+ SetAndClearProgressInfo(&progDlg);
+ progDlg.ShowModeless(m_hWnd);
+ if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
+ {
+ // try again with another peg revision
+ if (!Cat(m_path, revSelected, revSelected, tempfile))
+ {
+ progDlg.Stop();
+ SetAndClearProgressInfo((HWND)NULL);
+ CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
+ EnableOKButton();
+ break;
+ }
+ }
+ progDlg.Stop();
+ SetAndClearProgressInfo((HWND)NULL);
+ }
+ }
+ break;
+ case ID_OPENWITH:
+ bOpenWith = true;
+ case ID_OPEN:
+ {
+ CProgressDlg progDlg;
+ progDlg.SetTitle(IDS_APPNAME);
+ progDlg.SetAnimation(IDR_DOWNLOAD);
+ CString sInfoLine;
+ sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
+ progDlg.SetLine(1, sInfoLine, true);
+ SetAndClearProgressInfo(&progDlg);
+ progDlg.ShowModeless(m_hWnd);
+ CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
+ bool bSuccess = true;
+ if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
+ {
+ bSuccess = false;
+ // try again, but with the selected revision as the peg revision
+ if (!Cat(m_path, revSelected, revSelected, tempfile))
+ {
+ progDlg.Stop();
+ SetAndClearProgressInfo((HWND)NULL);
+ CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
+ EnableOKButton();
+ break;
+ }
+ bSuccess = true;
+ }
+ if (bSuccess)
+ {
+ progDlg.Stop();
+ SetAndClearProgressInfo((HWND)NULL);
+ SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
+ int ret = 0;
+ if (!bOpenWith)
+ ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
+ if ((ret <= HINSTANCE_ERROR)||bOpenWith)
+ {
+ CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
+ cmd += tempfile.GetWinPathString() + _T(" ");
+ CAppUtils::LaunchApplication(cmd, NULL, false);
+ }
+ }
+ }
+ break;
+ case ID_BLAME:
+ {
+ CBlameDlg dlg;
+ dlg.EndRev = revSelected;
+ if (dlg.DoModal() == IDOK)
+ {
+ CBlame blame;
+ CString tempfile;
+ CString logfile;
+ tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
+ if (!tempfile.IsEmpty())
+ {
+ if (dlg.m_bTextView)
+ {
+ //open the default text editor for the result file
+ CAppUtils::StartTextViewer(tempfile);
+ }
+ else
+ {
+ CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
+ if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
+ }
+ }
+ }
+ break;
+ case ID_UPDATE:
+ {
+ CString sCmd;
+ CString url = _T("tgit:")+pathURL;
+ sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
+ (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
+ CAppUtils::LaunchApplication(sCmd, NULL, false);
+ }
+ break;
+ case ID_FINDENTRY:
+ {
+ m_nSearchIndex = GetSelectionMark();
+ if (m_nSearchIndex < 0)
+ m_nSearchIndex = 0;
+ if (m_pFindDialog)
+ {
+ break;
+ }
+ else
+ {
+ m_pFindDialog = new CFindReplaceDialog();
+ m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);
+ }
+ }
+ break;
+ case ID_REPOBROWSE:
+ {
+ CString sCmd;
+ sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
+ (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
+
+ CAppUtils::LaunchApplication(sCmd, NULL, false);
+ }
+ break;
+ case ID_EDITLOG:
+ {
+ EditLogMessage(selIndex);
+ }
+ break;
+ case ID_EDITAUTHOR:
+ {
+ EditAuthor(selEntries);
+ }
+ break;
+ case ID_REVPROPS:
+ {
+ CEditPropertiesDlg dlg;
+ dlg.SetProjectProperties(&m_ProjectProperties);
+ CTGitPathList escapedlist;
+ dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
+ dlg.SetRevision(revSelected);
+ dlg.RevProps(true);
+ dlg.DoModal();
+ }
+ break;
+ case ID_COPYCLIPBOARD:
+ {
+ CopySelectionToClipBoard();
+ }
+ break;
+ case ID_EXPORT:
+ {
+ CString sCmd;
+ sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
+ (LPCTSTR)pathURL, (LONG)revSelected);
+ CAppUtils::LaunchApplication(sCmd, NULL, false);
+ }
+ break;
+ case ID_CHECKOUT:
+ {
+ CString sCmd;
+ CString url = _T("tgit:")+pathURL;
+ sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
+ (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
+ (LPCTSTR)url, (LONG)revSelected);
+ CAppUtils::LaunchApplication(sCmd, NULL, false);
+ }
+ break;
+ case ID_VIEWREV:
+ {
+ CString url = m_ProjectProperties.sWebViewerRev;
+ url = GetAbsoluteUrlFromRelativeUrl(url);
+ url.Replace(_T("%REVISION%"), revSelected.ToString());
+ if (!url.IsEmpty())
+ ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
+ }
+ break;
+ case ID_VIEWPATHREV:
+ {
+ CString relurl = pathURL;
+ CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
+ relurl = relurl.Mid(sRoot.GetLength());
+ CString url = m_ProjectProperties.sWebViewerPathRev;
+ url = GetAbsoluteUrlFromRelativeUrl(url);
+ url.Replace(_T("%REVISION%"), revSelected.ToString());
+ url.Replace(_T("%PATH%"), relurl);
+ if (!url.IsEmpty())
+ ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);
+ }
+ break;
+#endif
+ default:
+ break;
+ } // switch (cmd)
+ theApp.DoWaitCursor(-1);
+// EnableOKButton();
+ } // if (popup.CreatePopupMenu())
+
+}
+
+bool CGitLogList::IsSelectionContinuous()
+{
+ if ( GetSelectedCount()==1 )
+ {
+ // if only one revision is selected, the selection is of course
+ // continuous
+ return true;
+ }
+
+ POSITION pos = GetFirstSelectedItemPosition();
+ bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());
+ if (bContinuous)
+ {
+ int itemindex = GetNextSelectedItem(pos);
+ while (pos)
+ {
+ int nextindex = GetNextSelectedItem(pos);
+ if (nextindex - itemindex > 1)
+ {
+ bContinuous = false;
+ break;
+ }
+ itemindex = nextindex;
+ }
+ }
+ return bContinuous;
+}
+
+void CGitLogList::CopySelectionToClipBoard()
+{
+#if 0
+ CString sClipdata;
+ POSITION pos = GetFirstSelectedItemPosition();
+ if (pos != NULL)
+ {
+ CString sRev;
+ sRev.LoadString(IDS_LOG_REVISION);
+ CString sAuthor;
+ sAuthor.LoadString(IDS_LOG_AUTHOR);
+ CString sDate;
+ sDate.LoadString(IDS_LOG_DATE);
+ CString sMessage;
+ sMessage.LoadString(IDS_LOG_MESSAGE);
+ while (pos)
+ {
+ CString sLogCopyText;
+ CString sPaths;
+ PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
+ LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
+ for (INT_PTR cpPathIndex = 0; cpPathIndex<cpatharray->GetCount(); ++cpPathIndex)
+ {
+ LogChangedPath * cpath = cpatharray->GetAt(cpPathIndex);
+ sPaths += cpath->GetAction() + _T(" : ") + cpath->sPath;
+ if (cpath->sCopyFromPath.IsEmpty())
+ sPaths += _T("\r\n");
+ else
+ {
+ CString sCopyFrom;
+ sCopyFrom.Format(_T(" (%s: %s, %s, %ld)\r\n"), CString(MAKEINTRESOURCE(IDS_LOG_COPYFROM)),
+ (LPCTSTR)cpath->sCopyFromPath,
+ (LPCTSTR)CString(MAKEINTRESOURCE(IDS_LOG_REVISION)),
+ (LPCTSTR)cpath->lCopyFromRev);
+ sPaths += sCopyFrom;
+ }
+ }
+ sPaths.Trim();
+ sLogCopyText.Format(_T("%s: %d\r\n%s: %s\r\n%s: %s\r\n%s:\r\n%s\r\n----\r\n%s\r\n\r\n"),
+ (LPCTSTR)sRev, pLogEntry->Rev,
+ (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->sAuthor,
+ (LPCTSTR)sDate, (LPCTSTR)pLogEntry->sDate,
+ (LPCTSTR)sMessage, (LPCTSTR)pLogEntry->sMessage,
+ (LPCTSTR)sPaths);
+ sClipdata += sLogCopyText;
+ }
+ CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
+ }
+#endif
+}
+
+void CGitLogList::DiffSelectedRevWithPrevious()
+{
+#if 0
+ if (m_bThreadRunning)
+ return;
+ UpdateLogInfoLabel();
+ int selIndex = m_LogList.GetSelectionMark();
+ if (selIndex < 0)
+ return;
+ int selCount = m_LogList.GetSelectedCount();
+ if (selCount != 1)
+ return;
+
+ // Find selected entry in the log list
+ POSITION pos = m_LogList.GetFirstSelectedItemPosition();
+ PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
+ long rev1 = pLogEntry->Rev;
+ long rev2 = rev1-1;
+ CTGitPath path = m_path;
+
+ // See how many files under the relative root were changed in selected revision
+ int nChanged = 0;
+ LogChangedPath * changed = NULL;
+ for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)
+ {
+ LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);
+ if (cpath && cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
+ {
+ ++nChanged;
+ changed = cpath;
+ }
+ }
+
+ if (m_path.IsDirectory() && nChanged == 1)
+ {
+ // We're looking at the log for a directory and only one file under dir was changed in the revision
+ // Do diff on that file instead of whole directory
+ path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));
+ }
+
+ m_bCancelled = FALSE;
+ DialogEnableWindow(IDOK, FALSE);
+ SetPromptApp(&theApp);
+ theApp.DoWaitCursor(1);
+
+ if (PromptShown())
+ {
+ GitDiff diff(this, m_hWnd, true);
+ diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ diff.SetHEADPeg(m_LogRevision);
+ diff.ShowCompare(path, rev2, path, rev1);
+ }
+ else
+ {
+ CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
+ }
+
+ theApp.DoWaitCursor(-1);
+ EnableOKButton();
+#endif
+}
+
+void CGitLogList::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)
+{
+ LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
+ *pResult = -1;
+
+ if (pFindInfo->lvfi.flags & LVFI_PARAM)
+ return;
+ if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))
+ return;
+ if (pFindInfo->lvfi.psz == 0)
+ return;
+#if 0
+ CString sCmp = pFindInfo->lvfi.psz;
+ CString sRev;
+ for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)
+ {
+ GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
+ sRev.Format(_T("%ld"), pLogEntry->Rev);
+ if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
+ {
+ if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
+ {
+ *pResult = i;
+ return;
+ }
+ }
+ else
+ {
+ if (sCmp.Compare(sRev)==0)
+ {
+ *pResult = i;
+ return;
+ }
+ }
+ }
+ if (pFindInfo->lvfi.flags & LVFI_WRAP)
+ {
+ for (int i=0; i<pFindInfo->iStart; ++i)
+ {
+ PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
+ sRev.Format(_T("%ld"), pLogEntry->Rev);
+ if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
+ {
+ if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
+ {
+ *pResult = i;
+ return;
+ }
+ }
+ else
+ {
+ if (sCmp.Compare(sRev)==0)
+ {
+ *pResult = i;
+ return;
+ }
+ }
+ }
+ }
+#endif
+ *pResult = -1;
+}
+
+int CGitLogList::FillGitLog()
+{
+ ClearText();
+
+ this->m_logEntries.ClearAll();
+ this->m_logEntries.ParserFromLog();
+ SetItemCountEx(this->m_logEntries.size());
+
+ this->m_arShownList.RemoveAll();
+
+ for(int i=0;i<m_logEntries.size();i++)
+ this->m_arShownList.Add(&m_logEntries[i]);
+
+ return 0;
+}
+
+BOOL CGitLogList::PreTranslateMessage(MSG* pMsg)
+{
+ // Skip Ctrl-C when copying text out of the log message or search filter
+ BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );
+ if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
+ {
+ //if (GetFocus()==GetDlgItem(IDC_LOGLIST))
+ {
+ if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
+ {
+ DiffSelectedRevWithPrevious();
+ return TRUE;
+ }
+ }
+#if 0
+ if (GetFocus()==GetDlgItem(IDC_LOGMSG))
+ {
+ DiffSelectedFile();
+ return TRUE;
+ }
+#endif
+ }
+
+#if 0
+ if (m_hAccel && !bSkipAccelerator)
+ {
+ int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
+ if (ret)
+ return TRUE;
+ }
+
+#endif
+ //m_tooltips.RelayEvent(pMsg);
+ return __super::PreTranslateMessage(pMsg);
+}
+
+void CGitLogList::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
+{
+ // a double click on an entry in the revision list has happened
+ *pResult = 0;
+
+ if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
+ DiffSelectedRevWithPrevious();
+}
+
+int CGitLogList::FetchLogAsync(CALLBACK_PROCESS *proc,void * data)
+{
+ m_ProcCallBack=proc;
+ m_ProcData=data;
+
+ InterlockedExchange(&m_bThreadRunning, TRUE);
+ InterlockedExchange(&m_bNoDispUpdates, TRUE);
+ if (AfxBeginThread(LogThreadEntry, this)==NULL)
+ {
+ InterlockedExchange(&m_bThreadRunning, FALSE);
+ InterlockedExchange(&m_bNoDispUpdates, FALSE);
+ CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
+ return -1;
+ }
+ return 0;
+}
+
+//this is the thread function which calls the subversion function
+UINT CGitLogList::LogThreadEntry(LPVOID pVoid)
+{
+ return ((CGitLogList*)pVoid)->LogThread();
+}
+
+
+UINT CGitLogList::LogThread()
+{
+
+ if(m_ProcCallBack)
+ m_ProcCallBack(m_ProcData,GITLOG_START);
+
+ InterlockedExchange(&m_bThreadRunning, TRUE);
+
+ //does the user force the cache to refresh (shift or control key down)?
+ bool refresh = (GetKeyState (VK_CONTROL) < 0)
+ || (GetKeyState (VK_SHIFT) < 0);
+
+ //disable the "Get All" button while we're receiving
+ //log messages.
+
+ CString temp;
+ temp.LoadString(IDS_PROGRESSWAIT);
+ ShowText(temp, true);
+
+// git_revnum_t r = -1;
+
+ // get the repository root url, because the changed-files-list has the
+ // paths shown there relative to the repository root.
+// CTGitPath rootpath;
+// BOOL succeeded = GetRootAndHead(m_path, rootpath, r);
+
+// m_sRepositoryRoot = rootpath.GetGitPathString();
+// m_sURL = m_path.GetGitPathString();
+
+ // we need the UUID to unambigously identify the log cache
+// if (logCachePool.IsEnabled())
+// m_sUUID = logCachePool.GetRepositoryInfo().GetRepositoryUUID (rootpath);
+
+ // if the log dialog is started from a working copy, we need to turn that
+ // local path into an url here
+// if (succeeded)
+// {
+// if (!m_path.IsUrl())
+// {
+// m_sURL = GetURLFromPath(m_path);
+
+ // The URL is escaped because Git::logReceiver
+ // returns the path in a native format
+// m_sURL = CPathUtils::PathUnescape(m_sURL);
+ // }
+// m_sRelativeRoot = m_sURL.Mid(CPathUtils::PathUnescape(m_sRepositoryRoot).GetLength());
+// m_sSelfRelativeURL = m_sRelativeRoot;
+ // }
+#if 0
+ if (succeeded && !m_mergePath.IsEmpty() && m_mergedRevs.empty())
+ {
+ // in case we got a merge path set, retrieve the merge info
+ // of that path and check whether one of the merge URLs
+ // match the URL we show the log for.
+ GitPool localpool(pool);
+ git_error_clear(Err);
+ apr_hash_t * mergeinfo = NULL;
+ if (git_client_mergeinfo_get_merged (&mergeinfo, m_mergePath.GetGitApiPath(localpool), GitRev(GitRev::REV_WC), m_pctx, localpool) == NULL)
+ {
+ // now check the relative paths
+ apr_hash_index_t *hi;
+ const void *key;
+ void *val;
+
+ if (mergeinfo)
+ {
+ for (hi = apr_hash_first(localpool, mergeinfo); hi; hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &key, NULL, &val);
+ if (m_sURL.Compare(CUnicodeUtils::GetUnicode((char*)key)) == 0)
+ {
+ apr_array_header_t * arr = (apr_array_header_t*)val;
+ if (val)
+ {
+ for (long i=0; i<arr->nelts; ++i)
+ {
+ git_merge_range_t * pRange = APR_ARRAY_IDX(arr, i, git_merge_range_t*);
+ if (pRange)
+ {
+ for (git_revnum_t r=pRange->start+1; r<=pRange->end; ++r)
+ {
+ m_mergedRevs.insert(r);
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ m_LogProgress.SetPos(1);
+ if (m_startrev == GitRev::REV_HEAD)
+ {
+ m_startrev = r;
+ }
+ if (m_endrev == GitRev::REV_HEAD)
+ {
+ m_endrev = r;
+ }
+
+ if (m_limit != 0)
+ {
+ m_limitcounter = m_limit;
+ m_LogProgress.SetRange32(0, m_limit);
+ }
+ else
+ m_LogProgress.SetRange32(m_endrev, m_startrev);
+
+ if (!m_pegrev.IsValid())
+ m_pegrev = m_startrev;
+ size_t startcount = m_logEntries.size();
+ m_lowestRev = -1;
+ m_bStrictStopped = false;
+
+ if (succeeded)
+ {
+ succeeded = ReceiveLog (CTGitPathList(m_path), m_pegrev, m_startrev, m_endrev, m_limit, m_bStrict, m_bIncludeMerges, refresh);
+ if ((!succeeded)&&(!m_path.IsUrl()))
+ {
+ // try again with REV_WC as the start revision, just in case the path doesn't
+ // exist anymore in HEAD
+ succeeded = ReceiveLog(CTGitPathList(m_path), GitRev(), GitRev::REV_WC, m_endrev, m_limit, m_bStrict, m_bIncludeMerges, refresh);
+ }
+ }
+ m_LogList.ClearText();
+ if (!succeeded)
+ {
+ m_LogList.ShowText(GetLastErrorMessage(), true);
+ }
+ else
+ {
+ if (!m_wcRev.IsValid())
+ {
+ // fetch the revision the wc path is on so we can mark it
+ CTGitPath revWCPath = m_ProjectProperties.GetPropsPath();
+ if (!m_path.IsUrl())
+ revWCPath = m_path;
+ if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RecursiveLogRev"), FALSE)))
+ {
+ git_revnum_t minrev, maxrev;
+ bool switched, modified, sparse;
+ GetWCRevisionStatus(revWCPath, true, minrev, maxrev, switched, modified, sparse);
+ if (maxrev)
+ m_wcRev = maxrev;
+ }
+ else
+ {
+ CTGitPath dummypath;
+ GitStatus status;
+ git_wc_status2_t * stat = status.GetFirstFileStatus(revWCPath, dummypath, false, git_depth_empty);
+ if (stat && stat->entry && stat->entry->cmt_rev)
+ m_wcRev = stat->entry->cmt_rev;
+ if (stat && stat->entry && (stat->entry->kind == git_node_dir))
+ m_wcRev = stat->entry->revision;
+ }
+ }
+ }
+ if (m_bStrict && (m_lowestRev>1) && ((m_limit>0) ? ((startcount + m_limit)>m_logEntries.size()) : (m_endrev<m_lowestRev)))
+ m_bStrictStopped = true;
+ m_LogList.SetItemCountEx(ShownCountWithStopped());
+
+ m_timFrom = (__time64_t(m_tFrom));
+ m_timTo = (__time64_t(m_tTo));
+ m_DateFrom.SetRange(&m_timFrom, &m_timTo);
+ m_DateTo.SetRange(&m_timFrom, &m_timTo);
+ m_DateFrom.SetTime(&m_timFrom);
+ m_DateTo.SetTime(&m_timTo);
+#endif
+ //DialogEnableWindow(IDC_GETALL, TRUE);
+ FillGitLog();
+
+ InterlockedExchange(&m_bThreadRunning, FALSE);
+
+ RedrawItems(0, m_arShownList.GetCount());
+ SetRedraw(false);
+ ResizeAllListCtrlCols();
+ SetRedraw(true);
+
+ if ( m_pStoreSelection )
+ {
+ // Deleting the instance will restore the
+ // selection of the CLogDlg.
+ delete m_pStoreSelection;
+ m_pStoreSelection = NULL;
+ }
+ else
+ {
+ // If no selection has been set then this must be the first time
+ // the revisions are shown. Let's preselect the topmost revision.
+ if ( GetItemCount()>0 )
+ {
+ SetSelectionMark(0);
+ SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
+ }
+ }
+
+ //RefreshCursor();
+ // make sure the filter is applied (if any) now, after we refreshed/fetched
+ // the log messages
+
+ InterlockedExchange(&m_bNoDispUpdates, FALSE);
+
+ if(m_ProcCallBack)
+ m_ProcCallBack(m_ProcData,GITLOG_END);
+
+ return 0;
+}
+