OSDN Git Service

Add Version Graph Tree Code but there are some problem
authorFrank Li <lznuaa@gmail.com>
Sun, 28 Dec 2008 03:45:14 +0000 (11:45 +0800)
committerFrank Li <lznuaa@gmail.com>
Sun, 28 Dec 2008 03:45:14 +0000 (11:45 +0800)
src/Git/Git.cpp
src/Git/GitRev.h
src/TortoiseProc/GitLogList.cpp
src/TortoiseProc/GitLogList.h
src/TortoiseProc/LogDataVector.cpp [new file with mode: 0644]
src/TortoiseProc/LogDlg.cpp
src/TortoiseProc/LogDlgHelper.h
src/TortoiseProc/TortoiseProc.vcproj
src/TortoiseProc/TortoiseProc.vcproj.FSL.B20596.user
src/TortoiseProc/lanes.cpp [new file with mode: 0644]
src/TortoiseProc/lanes.h [new file with mode: 0644]

index 4e28fb1..08f2f27 100644 (file)
@@ -145,7 +145,7 @@ int CGit::GetLog(CString& logOut)
        cmd += log;\r
        log.Format(_T("#<%c>%%n"),LOG_REV_COMMIT_FILE);\r
        cmd += log;\r
-       cmd += CString(_T("\" HEAD~4..HEAD"));\r
+       cmd += CString(_T("\" HEAD~40..HEAD"));\r
        Run(cmd,&logOut);\r
        return 0;\r
 }\r
index 718e584..64dda9a 100644 (file)
@@ -50,4 +50,8 @@ public:
        int     m_Action;\r
        int ParserFromLog(CString &log);\r
        CTime ConverFromString(CString input);\r
+       inline int ParentsCount(){return m_ParentHash.size();}\r
+       \r
+       //Show version tree Graphic\r
+       std::vector<int> m_Lanes;\r
 };\r
index 38bea67..b96d666 100644 (file)
-// 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;
+}
+
index 83247a3..637b1e7 100644 (file)
@@ -17,6 +17,7 @@
 #include "Tooltip.h"\r
 #include "HintListCtrl.h"\r
 #include "GitLogList.h"\r
+#include "lanes.h"\r
 \r
 #include <regex>\r
 // CGitLogList\r
@@ -133,7 +134,12 @@ protected:
        UINT LogThread();\r
        void FillBackGround(HDC hdc, int Index,CRect &rect);\r
        void DrawTagBranch(HDC,CRect &rect,INT_PTR index);\r
+       void DrawGraph(HDC,CRect &rect,INT_PTR index);\r
+\r
        BOOL GetShortName(CString ref, CString &shortname,CString prefix);\r
+       void paintGraphLane(HDC hdc,int laneHeight, int type, int x1, int x2,\r
+                                      const COLORREF& col,int top) ; \r
+       void DrawLine(HDC hdc, int x1, int y1, int x2, int y2){::MoveToEx(hdc,x1,y1,NULL);::LineTo(hdc,x2,y2);}\r
        \r
        CXPTheme                        m_Theme;\r
        BOOL                            m_bVista;\r
diff --git a/src/TortoiseProc/LogDataVector.cpp b/src/TortoiseProc/LogDataVector.cpp
new file mode 100644 (file)
index 0000000..1f9b340
--- /dev/null
@@ -0,0 +1,194 @@
+// TortoiseSVN - a Windows shell extension for easy version control
+
+// Copyright (C) 2007-2008 - TortoiseSVN
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+/*
+       Description: start-up repository opening and reading
+
+       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"
+
+//CLogDataVector Class
+int CLogDataVector::ParserFromLog()
+{
+       CString log;
+       GitRev rev;
+       g_Git.GetLog(log);
+
+       CString begin;
+       begin.Format(_T("#<%c>"),LOG_REV_ITEM_BEGIN);
+       
+       if(log.GetLength()==0)
+               return 0;
+       
+       int start=4;
+       int length;
+       int next =1;
+       while( next>0 )
+       {
+               next=log.Find(begin,start);
+               if(next >0 )
+                       length = next - start+4;
+               else
+                       length = log.GetLength()-start+4;
+
+               CString onelog =log;
+               onelog=log.Mid(start -4,length);
+               rev.ParserFromLog(onelog);
+               this->push_back(rev);
+               m_HashMap[rev.m_CommitHash]=&rev;
+               start = next +4;
+       }
+
+       return 0;
+}
+
+void CLogDataVector::setLane(CString& sha) 
+{
+       Lanes* l = &(this->m_Lanes);
+       int i = m_FirstFreeLane;
+       
+//     QVector<QByteArray> ba;
+//     const ShaString& ss = toPersistentSha(sha, ba);
+//     const ShaVect& shaVec(fh->revOrder);
+
+       for (int cnt = size(); i < cnt; ++i) {
+
+               GitRev* r = &(*this)[i]; 
+               CString &curSha=r->m_CommitHash;
+
+               if (r->m_Lanes.size() == 0)
+                       updateLanes(*r, *l, curSha);
+
+               if (curSha == sha)
+                       break;
+       }
+       m_FirstFreeLane = ++i;
+
+#if 0
+       Lanes* l = &(this->m_Lanes);
+       int i = m_FirstFreeLane;
+       
+       QVector<QByteArray> ba;
+       const ShaString& ss = toPersistentSha(sha, ba);
+       const ShaVect& shaVec(fh->revOrder);
+
+       for (uint cnt = shaVec.count(); i < cnt; ++i) {
+
+               const ShaString& curSha = shaVec[i];
+               Rev* r = m_HashMap[curSha]const_cast<Rev*>(revLookup(curSha, fh));
+               if (r->lanes.count() == 0)
+                       updateLanes(*r, *l, curSha);
+
+               if (curSha == ss)
+                       break;
+       }
+       fh->firstFreeLane = ++i;
+#endif
+}
+
+
+void CLogDataVector::updateLanes(GitRev& c, Lanes& lns, CString &sha) 
+{
+// we could get third argument from c.sha(), but we are in fast path here
+// and c.sha() involves a deep copy, so we accept a little redundancy
+
+       if (lns.isEmpty())
+               lns.init(sha);
+
+       bool isDiscontinuity;
+       bool isFork = lns.isFork(sha, isDiscontinuity);
+       bool isMerge = (c.ParentsCount() > 1);
+       bool isInitial = (c.ParentsCount() == 0);
+
+       if (isDiscontinuity)
+               lns.changeActiveLane(sha); // uses previous isBoundary state
+
+       lns.setBoundary(0/*c.isBoundary()*/); // update must be here
+
+       if (isFork)
+               lns.setFork(sha);
+       if (isMerge)
+               lns.setMerge(c.m_ParentHash);
+       //if (c.isApplied)
+       //      lns.setApplied();
+       if (isInitial)
+               lns.setInitial();
+
+       lns.getLanes(c.m_Lanes); // here lanes are snapshotted
+
+       CString nextSha = (isInitial) ? CString(_T("")) : QString(c.m_ParentHash[0]);
+
+       lns.nextParent(nextSha);
+
+       //if (c.isApplied)
+       //      lns.afterApplied();
+       if (isMerge)
+               lns.afterMerge();
+       if (isFork)
+               lns.afterFork();
+       if (lns.isBranch())
+               lns.afterBranch();
+
+//     QString tmp = "", tmp2;
+//     for (uint i = 0; i < c.lanes.count(); i++) {
+//             tmp2.setNum(c.lanes[i]);
+//             tmp.append(tmp2 + "-");
+//     }
+//     qDebug("%s %s",tmp.latin1(), c.sha.latin1());
+}
\ No newline at end of file
index 44a6218..88c1a17 100644 (file)
@@ -3907,35 +3907,3 @@ CString CLogDlg::GetAbsoluteUrlFromRelativeUrl(const CString& url)
 }\r
 \r
 \r
-int CLogDataVector::ParserFromLog()\r
-{\r
-       CString log;\r
-       GitRev rev;\r
-       g_Git.GetLog(log);\r
-\r
-       CString begin;\r
-       begin.Format(_T("#<%c>"),LOG_REV_ITEM_BEGIN);\r
-       \r
-       if(log.GetLength()==0)\r
-               return 0;\r
-       \r
-       int start=4;\r
-       int length;\r
-       int next =1;\r
-       while( next>0 )\r
-       {\r
-               next=log.Find(begin,start);\r
-               if(next >0 )\r
-                       length = next - start+4;\r
-               else\r
-                       length = log.GetLength()-start+4;\r
-\r
-               CString onelog =log;\r
-               onelog=log.Mid(start -4,length);\r
-               rev.ParserFromLog(onelog);\r
-               this->push_back(rev);\r
-               start = next +4;\r
-       }\r
-\r
-       return 0;\r
-}\r
index 8192495..53c4d9d 100644 (file)
@@ -21,6 +21,7 @@
 #include "GitRev.h"\r
 #include "GitStatus.h"\r
 #include "ILogReceiver.h"\r
+#include "lanes.h"\r
 \r
 class CLogDlg;\r
 \r
@@ -29,6 +30,8 @@ class CLogDlg;
  * Instances of CStoreSelection save the selection of the CLogDlg. When the instance\r
  * is deleted the destructor restores the selection.\r
  */\r
+typedef std::map<CString, GitRev*> MAP_HASH_REV;\r
+\r
 class CStoreSelection\r
 {\r
 public:\r
@@ -71,8 +74,19 @@ class CLogDataVector :       public std::vector<GitRev>
 {\r
 public:\r
        /// De-allocates log items.\r
+       CLogDataVector()\r
+       {\r
+               m_FirstFreeLane=0;\r
+       }\r
        void ClearAll();\r
        int  ParserFromLog();\r
+       Lanes m_Lanes;\r
+       int      m_FirstFreeLane;\r
+       MAP_HASH_REV m_HashMap;\r
+       void updateLanes(GitRev& c, Lanes& lns, CString &sha) ;\r
+       void setLane(CString& sha) ;\r
+\r
+\r
 #if 0\r
        /// Ascending date sorting.\r
        struct AscDateSort\r
index 6d0fc77..4aa809c 100644 (file)
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\explorer.ico"\r
+                               RelativePath=".\explorer.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\explorer.ico"\r
+                               RelativePath="..\Resources\explorer.ico"\r
                                >\r
                        </File>\r
                        <File\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\newfolder.ico"\r
+                               RelativePath=".\newfolder.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\newfolder.ico"\r
+                               RelativePath="..\Resources\newfolder.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\open.ico"\r
+                               RelativePath=".\open.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\open.ico"\r
+                               RelativePath="..\Resources\open.ico"\r
                                >\r
                        </File>\r
                        <File\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\save.ico"\r
+                               RelativePath=".\save.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\save.ico"\r
+                               RelativePath="..\Resources\save.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\saveas.ico"\r
+                               RelativePath=".\saveas.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\saveas.ico"\r
+                               RelativePath="..\Resources\saveas.ico"\r
                                >\r
                        </File>\r
                        <File\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath="..\Resources\up.ico"\r
+                               RelativePath=".\up.ico"\r
                                >\r
                        </File>\r
                        <File\r
-                               RelativePath=".\up.ico"\r
+                               RelativePath="..\Resources\up.ico"\r
                                >\r
                        </File>\r
                        <File\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\lanes.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath=".\lanes.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\Commands\LogCommand.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\LogDataVector.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\LogDlg.cpp"\r
                                        >\r
                                </File>\r
index 883a87d..8990368 100644 (file)
@@ -11,7 +11,7 @@
                        <DebugSettings\r
                                Command="$(TargetPath)"\r
                                WorkingDirectory="D:\Profiles\b20596\tortoisegit"\r
-                               CommandArguments="/command:log /path:&quot;D:\\Profiles\\b20596\\buildtest\\TortoiseGit&quot;"\r
+                               CommandArguments="/command:log /path:&quot;D:\\Profiles\\b20596\\buildtest\\tortoisegit&quot;"\r
                                Attach="false"\r
                                DebuggerType="3"\r
                                Remote="1"\r
diff --git a/src/TortoiseProc/lanes.cpp b/src/TortoiseProc/lanes.cpp
new file mode 100644 (file)
index 0000000..07f3cc7
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+       Description: history graph computation
+
+       Author: Marco Costalba (C) 2005-2007
+
+       Copyright: See COPYING file that comes with this distribution
+
+*/
+#include "stdafx.h"
+#include "lanes.h"
+
+#define IS_NODE(x) (x == NODE || x == NODE_R || x == NODE_L)
+
+
+void Lanes::init(const QString& expectedSha) {
+
+       clear();
+       activeLane = 0;
+       setBoundary(false);
+       add(BRANCH, expectedSha, activeLane);
+}
+
+void Lanes::clear() {
+
+       typeVec.clear();
+       nextShaVec.clear();
+}
+
+void Lanes::setBoundary(bool b) {
+// changes the state so must be called as first one
+
+       NODE   = b ? BOUNDARY_C : MERGE_FORK;
+       NODE_R = b ? BOUNDARY_R : MERGE_FORK_R;
+       NODE_L = b ? BOUNDARY_L : MERGE_FORK_L;
+       boundary = b;
+
+       if (boundary)
+               typeVec[activeLane] = BOUNDARY;
+}
+
+bool Lanes::isFork(const QString& sha, bool& isDiscontinuity) {
+
+       int pos = findNextSha(sha, 0);
+       isDiscontinuity = (activeLane != pos);
+       if (pos == -1) // new branch case
+               return false;
+
+       return (findNextSha(sha, pos + 1) != -1);
+/*
+       int cnt = 0;
+       while (pos != -1) {
+               cnt++;
+               pos = findNextSha(sha, pos + 1);
+//             if (isDiscontinuity)
+//                     isDiscontinuity = (activeLane != pos);
+       }
+       return (cnt > 1);
+*/
+}
+
+void Lanes::setFork(const QString& sha) {
+
+       int rangeStart, rangeEnd, idx;
+       rangeStart = rangeEnd = idx = findNextSha(sha, 0);
+
+       while (idx != -1) {
+               rangeEnd = idx;
+               typeVec[idx] = TAIL;
+               idx = findNextSha(sha, idx + 1);
+       }
+       typeVec[activeLane] = NODE;
+
+       int& startT = typeVec[rangeStart];
+       int& endT = typeVec[rangeEnd];
+
+       if (startT == NODE)
+               startT = NODE_L;
+
+       if (endT == NODE)
+               endT = NODE_R;
+
+       if (startT == TAIL)
+               startT = TAIL_L;
+
+       if (endT == TAIL)
+               endT = TAIL_R;
+
+       for (int i = rangeStart + 1; i < rangeEnd; i++) {
+
+               int& t = typeVec[i];
+
+               if (t == NOT_ACTIVE)
+                       t = CROSS;
+
+               else if (t == EMPTY)
+                       t = CROSS_EMPTY;
+       }
+}
+
+void Lanes::setMerge(const QStringList& parents) {
+// setFork() must be called before setMerge()
+
+       if (boundary)
+               return; // handle as a simple active line
+
+       int& t = typeVec[activeLane];
+       bool wasFork   = (t == NODE);
+       bool wasFork_L = (t == NODE_L);
+       bool wasFork_R = (t == NODE_R);
+       bool joinWasACross = false;
+
+       t = NODE;
+
+       int rangeStart = activeLane, rangeEnd = activeLane;
+
+       QStringList::const_iterator it=parents.begin();
+
+       for (++it; it != parents.end(); ++it) { // skip first parent
+
+               int idx = findNextSha(*it, 0);
+               if (idx != -1) {
+
+                       if (typeVec[idx] == CROSS)
+                               joinWasACross = true;
+
+                       typeVec[idx] = JOIN;
+
+                       if (idx > rangeEnd)
+                               rangeEnd = idx;
+
+                       if (idx < rangeStart)
+                               rangeStart = idx;
+               } else
+                       rangeEnd = add(HEAD, *it, rangeEnd + 1);
+       }
+       int& startT = typeVec[rangeStart];
+       int& endT = typeVec[rangeEnd];
+
+       if (startT == NODE && !wasFork && !wasFork_R)
+               startT = NODE_L;
+
+       if (endT == NODE && !wasFork && !wasFork_L)
+               endT = NODE_R;
+
+       if (startT == JOIN && !joinWasACross)
+               startT = JOIN_L;
+
+       if (endT == JOIN && !joinWasACross)
+               endT = JOIN_R;
+
+       if (startT == HEAD)
+               startT = HEAD_L;
+
+       if (endT == HEAD)
+               endT = HEAD_R;
+
+       for (int i = rangeStart + 1; i < rangeEnd; i++) {
+
+               int& t = typeVec[i];
+
+               if (t == NOT_ACTIVE)
+                       t = CROSS;
+
+               else if (t == EMPTY)
+                       t = CROSS_EMPTY;
+
+               else if (t == TAIL_R || t == TAIL_L)
+                       t = TAIL;
+       }
+}
+
+void Lanes::setInitial() {
+
+       int& t = typeVec[activeLane];
+       if (!IS_NODE(t) && t != APPLIED)
+               t = (boundary ? BOUNDARY : INITIAL);
+}
+
+void Lanes::setApplied() {
+
+       // applied patches are not merges, nor forks
+       typeVec[activeLane] = APPLIED; // TODO test with boundaries
+}
+
+void Lanes::changeActiveLane(const QString& sha) {
+
+       int& t = typeVec[activeLane];
+       if (t == INITIAL || isBoundary(t))
+               t = EMPTY;
+       else
+               t = NOT_ACTIVE;
+
+       int idx = findNextSha(sha, 0); // find first sha
+       if (idx != -1)
+               typeVec[idx] = ACTIVE; // called before setBoundary()
+       else
+               idx = add(BRANCH, sha, activeLane); // new branch
+
+       activeLane = idx;
+}
+
+void Lanes::afterMerge() {
+
+       if (boundary)
+               return; // will be reset by changeActiveLane()
+
+       for (int i = 0; i < typeVec.size(); i++) {
+
+               int& t = typeVec[i];
+
+               if (isHead(t) || isJoin(t) || t == CROSS)
+                       t = NOT_ACTIVE;
+
+               else if (t == CROSS_EMPTY)
+                       t = EMPTY;
+
+               else if (IS_NODE(t))
+                       t = ACTIVE;
+       }
+}
+
+void Lanes::afterFork() {
+
+       for (int i = 0; i < typeVec.size(); i++) {
+
+               int& t = typeVec[i];
+
+               if (t == CROSS)
+                       t = NOT_ACTIVE;
+
+               else if (isTail(t) || t == CROSS_EMPTY)
+                       t = EMPTY;
+
+               if (!boundary && IS_NODE(t))
+                       t = ACTIVE; // boundary will be reset by changeActiveLane()
+       }
+       while (*typeVec.end() == EMPTY) {
+               typeVec.pop_back();
+               nextShaVec.pop_back();
+       }
+}
+
+bool Lanes::isBranch() {
+
+       return (typeVec[activeLane] == BRANCH);
+}
+
+void Lanes::afterBranch() {
+
+       typeVec[activeLane] = ACTIVE; // TODO test with boundaries
+}
+
+void Lanes::afterApplied() {
+
+       typeVec[activeLane] = ACTIVE; // TODO test with boundaries
+}
+
+void Lanes::nextParent(const QString& sha) {
+
+       nextShaVec[activeLane] = (boundary ? QString(_T("")) : sha);
+}
+
+int Lanes::findNextSha(const QString& next, int pos) {
+
+       for (int i = pos; i < nextShaVec.size(); i++)
+               if (nextShaVec[i] == next)
+                       return i;
+       return -1;
+}
+
+int Lanes::findType(int type, int pos) {
+
+       for (int i = pos; i < typeVec.size(); i++)
+               if (typeVec[i] == type)
+                       return i;
+       return -1;
+}
+
+int Lanes::add(int type, const QString& next, int pos) {
+
+       // first check empty lanes starting from pos
+       if (pos < (int)typeVec.size()) {
+               pos = findType(EMPTY, pos);
+               if (pos != -1) {
+                       typeVec[pos] = type;
+                       nextShaVec[pos] = next;
+                       return pos;
+               }
+       }
+       // if all lanes are occupied add a new lane
+       typeVec.push_back(type);
+       nextShaVec.push_back(next);
+       return typeVec.size() - 1;
+}
diff --git a/src/TortoiseProc/lanes.h b/src/TortoiseProc/lanes.h
new file mode 100644 (file)
index 0000000..68f06d4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+       Author: Marco Costalba (C) 2005-2007
+
+       Copyright: See COPYING file that comes with this distribution
+
+*/
+#ifndef LANES_H
+#define LANES_H
+
+//#include <QString>
+//#include <QVector>
+
+#define QString CString
+#define QVector vector
+using namespace std;
+
+typedef vector<CString> QStringList ;
+
+class Lanes {
+public:
+       // graph elements
+       enum LaneType {
+               EMPTY,
+               ACTIVE,
+               NOT_ACTIVE,
+               MERGE_FORK,
+               MERGE_FORK_R,
+               MERGE_FORK_L,
+               JOIN,
+               JOIN_R,
+               JOIN_L,
+               HEAD,
+               HEAD_R,
+               HEAD_L,
+               TAIL,
+               TAIL_R,
+               TAIL_L,
+               CROSS,
+               CROSS_EMPTY,
+               INITIAL,
+               BRANCH,
+               UNAPPLIED,
+               APPLIED,
+               BOUNDARY,
+               BOUNDARY_C, // corresponds to MERGE_FORK
+               BOUNDARY_R, // corresponds to MERGE_FORK_R
+               BOUNDARY_L, // corresponds to MERGE_FORK_L
+
+               LANE_TYPES_NUM,
+               COLORS_NUM=8
+       };
+
+       // graph helpers
+       static inline bool isHead(int x) { return (x == HEAD || x == HEAD_R || x == HEAD_L); }
+       static inline bool isTail(int x) { return (x == TAIL || x == TAIL_R || x == TAIL_L); }
+       static inline bool isJoin(int x) { return (x == JOIN || x == JOIN_R || x == JOIN_L); }
+       static inline bool isFreeLane(int x) { return (x == NOT_ACTIVE || x == CROSS || isJoin(x)); }
+       static inline bool isBoundary(int x) { return (x == BOUNDARY || x == BOUNDARY_C ||
+                                               x == BOUNDARY_R || x == BOUNDARY_L); }
+       static inline bool isMerge(int x) { return (x == MERGE_FORK || x == MERGE_FORK_R ||
+                                            x == MERGE_FORK_L || isBoundary(x)); }
+
+       Lanes() {} // init() will setup us later, when data is available
+       bool isEmpty() { return typeVec.empty(); }
+       void init(const QString& expectedSha);
+       void clear();
+       bool isFork(const QString& sha, bool& isDiscontinuity);
+       void setBoundary(bool isBoundary);
+       void setFork(const QString& sha);
+       void setMerge(const QStringList& parents);
+       void setInitial();
+       void setApplied();
+       void changeActiveLane(const QString& sha);
+       void afterMerge();
+       void afterFork();
+       bool isBranch();
+       void afterBranch();
+       void afterApplied();
+       void nextParent(const QString& sha);
+       void getLanes(QVector<int> &ln) { ln = typeVec; } // O(1) vector is implicitly shared
+
+private:
+       int findNextSha(const QString& next, int pos);
+       int findType(int type, int pos);
+       int add(int type, const QString& next, int pos);
+
+       int activeLane;
+       QVector<int> typeVec;
+       QVector<QString> nextShaVec;
+       bool boundary;
+       int NODE, NODE_L, NODE_R;
+};
+
+#endif