OSDN Git Service

Use new method to update log So the user don't wait for log full log
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / GitLogList.cpp
1 // GitLogList.cpp : implementation file
2 //
3 /*
4         Description: qgit revision list view
5
6         Author: Marco Costalba (C) 2005-2007
7
8         Copyright: See COPYING file that comes with this distribution
9
10 */
11 #include "stdafx.h"
12 #include "TortoiseProc.h"
13 #include "GitLogList.h"
14 #include "GitRev.h"
15 //#include "VssStyle.h"
16 #include "IconMenu.h"
17 // CGitLogList
18 #include "cursor.h"
19 #include "InputDlg.h"
20 #include "PropDlg.h"
21 #include "SVNProgressDlg.h"
22 #include "ProgressDlg.h"
23 //#include "RepositoryBrowser.h"
24 //#include "CopyDlg.h"
25 //#include "StatGraphDlg.h"
26 #include "Logdlg.h"
27 #include "MessageBox.h"
28 #include "Registry.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "UnicodeUtils.h"
33 #include "TempFile.h"
34 //#include "GitInfo.h"
35 //#include "GitDiff.h"
36 #include "IconMenu.h"
37 //#include "RevisionRangeDlg.h"
38 //#include "BrowseFolder.h"
39 //#include "BlameDlg.h"
40 //#include "Blame.h"
41 //#include "GitHelpers.h"
42 #include "GitStatus.h"
43 //#include "LogDlgHelper.h"
44 //#include "CachedLogInfo.h"
45 //#include "RepositoryInfo.h"
46 //#include "EditPropertiesDlg.h"
47 #include "FileDiffDlg.h"
48
49
50
51
52 IMPLEMENT_DYNAMIC(CGitLogList, CHintListCtrl)
53
54 CGitLogList::CGitLogList():CHintListCtrl()
55         ,m_regMaxBugIDColWidth(_T("Software\\TortoiseGit\\MaxBugIDColWidth"), 200)
56         ,m_nSearchIndex(0)
57         ,m_bNoDispUpdates(FALSE)
58         , m_bThreadRunning(FALSE)
59         , m_bStrictStopped(false)
60         , m_pStoreSelection(NULL)
61 {
62         // use the default GUI font, create a copy of it and
63         // change the copy to BOLD (leave the rest of the font
64         // the same)
65         HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
66         LOGFONT lf = {0};
67         GetObject(hFont, sizeof(LOGFONT), &lf);
68         lf.lfWeight = FW_BOLD;
69         m_boldFont = CreateFontIndirect(&lf);
70
71         m_wcRev.m_CommitHash=GIT_REV_ZERO;
72         m_wcRev.m_Subject=_T("Working Copy");
73
74         m_hModifiedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONMODIFIED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
75         m_hReplacedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONREPLACED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
76         m_hAddedIcon    =  (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONADDED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
77         m_hDeletedIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ACTIONDELETED), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
78
79         g_Git.GetMapHashToFriendName(m_HashMap);
80 }
81
82 CGitLogList::~CGitLogList()
83 {
84         InterlockedExchange(&m_bNoDispUpdates, TRUE);
85
86         DestroyIcon(m_hModifiedIcon);
87         DestroyIcon(m_hReplacedIcon);
88         DestroyIcon(m_hAddedIcon);
89         DestroyIcon(m_hDeletedIcon);
90         m_logEntries.ClearAll();
91
92         if (m_boldFont)
93                 DeleteObject(m_boldFont);
94
95         if ( m_pStoreSelection )
96         {
97                 delete m_pStoreSelection;
98                 m_pStoreSelection = NULL;
99         }
100 }
101
102
103 BEGIN_MESSAGE_MAP(CGitLogList, CHintListCtrl)
104         ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawLoglist)
105         ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoLoglist)
106         ON_WM_CONTEXTMENU()
107         ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkLoglist)
108         ON_NOTIFY_REFLECT(LVN_ODFINDITEM,OnLvnOdfinditemLoglist)
109         ON_WM_CREATE()
110 END_MESSAGE_MAP()
111
112 int CGitLogList:: OnCreate(LPCREATESTRUCT lpCreateStruct)
113 {
114         PreSubclassWindow();
115         return CHintListCtrl::OnCreate(lpCreateStruct);
116 }
117
118 void CGitLogList::PreSubclassWindow()
119 {
120         SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_SUBITEMIMAGES);
121         // load the icons for the action columns
122         m_Theme.SetWindowTheme(GetSafeHwnd(), L"Explorer", NULL);
123         CHintListCtrl::PreSubclassWindow();
124 }
125
126 void CGitLogList::InsertGitColumn()
127 {
128         CString temp;
129
130         int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
131         
132         while (c>=0)
133                 DeleteColumn(c--);
134         temp.LoadString(IDS_LOG_GRAPH);
135
136         InsertColumn(this->LOGLIST_GRAPH, temp);
137         
138 #if 0   
139         // make the revision column right aligned
140         LVCOLUMN Column;
141         Column.mask = LVCF_FMT;
142         Column.fmt = LVCFMT_RIGHT;
143         SetColumn(0, &Column); 
144 #endif  
145 //      CString log;
146 //      g_Git.GetLog(log);
147
148         temp.LoadString(IDS_LOG_ACTIONS);
149         InsertColumn(this->LOGLIST_ACTION, temp);
150         
151         temp.LoadString(IDS_LOG_MESSAGE);
152         InsertColumn(this->LOGLIST_MESSAGE, temp);
153         
154         temp.LoadString(IDS_LOG_AUTHOR);
155         InsertColumn(this->LOGLIST_AUTHOR, temp);
156         
157         temp.LoadString(IDS_LOG_DATE);
158         InsertColumn(this->LOGLIST_DATE, temp);
159         
160
161         if (m_bShowBugtraqColumn)
162         {
163 //              temp = m_ProjectProperties.sLabel;
164                 if (temp.IsEmpty())
165                         temp.LoadString(IDS_LOG_BUGIDS);
166                 InsertColumn(this->LOGLIST_BUG, temp);
167
168         }
169         
170         SetRedraw(false);
171         ResizeAllListCtrlCols();
172         SetRedraw(true);
173
174 }
175
176 void CGitLogList::ResizeAllListCtrlCols()
177 {
178
179         const int nMinimumWidth = ICONITEMBORDER+16*4;
180         int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
181         int nItemCount = GetItemCount();
182         TCHAR textbuf[MAX_PATH];
183         CHeaderCtrl * pHdrCtrl = (CHeaderCtrl*)(GetDlgItem(0));
184         if (pHdrCtrl)
185         {
186                 for (int col = 0; col <= maxcol; col++)
187                 {
188                         HDITEM hdi = {0};
189                         hdi.mask = HDI_TEXT;
190                         hdi.pszText = textbuf;
191                         hdi.cchTextMax = sizeof(textbuf);
192                         pHdrCtrl->GetItem(col, &hdi);
193                         int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin
194                         for (int index = 0; index<nItemCount; ++index)
195                         {
196                                 // get the width of the string and add 14 pixels for the column separator and margins
197                                 int linewidth = GetStringWidth(GetItemText(index, col)) + 14;
198                                 if (index < m_arShownList.GetCount())
199                                 {
200                                         GitRev * pCurLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(index));
201                                         if ((pCurLogEntry)&&(pCurLogEntry->m_CommitHash == m_wcRev.m_CommitHash))
202                                         {
203                                                 // set the bold font and ask for the string width again
204                                                 SendMessage(WM_SETFONT, (WPARAM)m_boldFont, NULL);
205                                                 linewidth = GetStringWidth(GetItemText(index, col)) + 14;
206                                                 // restore the system font
207                                                 SendMessage(WM_SETFONT, NULL, NULL);
208                                         }
209                                 }
210                                 if (index == 0)
211                                 {
212                                         // add the image size
213                                         CImageList * pImgList = GetImageList(LVSIL_SMALL);
214                                         if ((pImgList)&&(pImgList->GetImageCount()))
215                                         {
216                                                 IMAGEINFO imginfo;
217                                                 pImgList->GetImageInfo(0, &imginfo);
218                                                 linewidth += (imginfo.rcImage.right - imginfo.rcImage.left);
219                                                 linewidth += 3; // 3 pixels between icon and text
220                                         }
221                                 }
222                                 if (cx < linewidth)
223                                         cx = linewidth;
224                         }
225                         // Adjust columns "Actions" containing icons
226                         if (col == this->LOGLIST_ACTION)
227                         {
228                                 if (cx < nMinimumWidth)
229                                 {
230                                         cx = nMinimumWidth;
231                                 }
232                         }
233                         
234                         if (col == this->LOGLIST_MESSAGE)
235                         {
236                                 if (cx > LOGLIST_MESSAGE_MAX)
237                                 {
238                                         cx = LOGLIST_MESSAGE_MAX;
239                                 }
240                                 if (cx < LOGLIST_MESSAGE_MIN)
241                                 {
242                                         cx = LOGLIST_MESSAGE_MIN;
243                                 }
244
245                         }
246                         // keep the bug id column small
247                         if ((col == 4)&&(m_bShowBugtraqColumn))
248                         {
249                                 if (cx > (int)(DWORD)m_regMaxBugIDColWidth)
250                                 {
251                                         cx = (int)(DWORD)m_regMaxBugIDColWidth;
252                                 }
253                         }
254
255                         SetColumnWidth(col, cx);
256                 }
257         }
258
259 }
260 BOOL CGitLogList::GetShortName(CString ref, CString &shortname,CString prefix)
261 {
262         if(ref.Left(prefix.GetLength()) ==  prefix)
263         {
264                 shortname = ref.Right(ref.GetLength()-prefix.GetLength());
265                 return TRUE;
266         }
267         return FALSE;
268 }
269 void CGitLogList::FillBackGround(HDC hdc, int Index,CRect &rect)
270 {       
271 //      HBRUSH brush;
272         LVITEM   rItem;
273         SecureZeroMemory(&rItem, sizeof(LVITEM));
274         rItem.mask  = LVIF_STATE;
275         rItem.iItem = Index;
276         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
277         GetItem(&rItem);
278
279         if (m_Theme.IsAppThemed() && m_bVista)
280         {
281                 m_Theme.Open(m_hWnd, L"Explorer");
282                 int state = LISS_NORMAL;
283                 if (rItem.state & LVIS_SELECTED)
284                 {
285                         if (::GetFocus() == m_hWnd)
286                                 state |= LISS_SELECTED;
287                         else
288                                 state |= LISS_SELECTEDNOTFOCUS;
289                 }
290                 else
291                 {
292 #if 0
293                         if (pLogEntry->bCopiedSelf)
294                         {
295                                 // unfortunately, the pLVCD->nmcd.uItemState does not contain valid
296                                 // information at this drawing stage. But we can check the whether the
297                                 // previous stage changed the background color of the item
298                                 if (pLVCD->clrTextBk == GetSysColor(COLOR_MENU))
299                                 {
300                                         HBRUSH brush;
301                                         brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
302                                         if (brush)
303                                         {
304                                                 ::FillRect(pLVCD->nmcd.hdc, &rect, brush);
305                                                 ::DeleteObject(brush);
306                                         }
307                                 }
308                         }
309 #endif
310                 }
311
312                 if (m_Theme.IsBackgroundPartiallyTransparent(LVP_LISTDETAIL, state))
313                         m_Theme.DrawParentBackground(m_hWnd, hdc, &rect);
314
315                         m_Theme.DrawBackground(hdc, LVP_LISTDETAIL, state, &rect, NULL);
316         }
317         else
318         {
319                 HBRUSH brush;
320                 if (rItem.state & LVIS_SELECTED)
321                 {
322                         if (::GetFocus() == m_hWnd)
323                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
324                         else
325                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
326                 }
327                 else
328                 {
329                         //if (pLogEntry->bCopiedSelf)
330                         //      brush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
331                         //else
332                                 brush = ::CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
333                 }
334                 if (brush == NULL)
335                         return;
336
337                 ::FillRect(hdc, &rect, brush);
338                 ::DeleteObject(brush);
339                 
340         }
341 }
342
343 void CGitLogList::DrawTagBranch(HDC hdc,CRect &rect,INT_PTR index)
344 {
345         GitRev* data = (GitRev*)m_arShownList.GetAt(index);
346         CRect rt=rect;
347         LVITEM   rItem;
348         SecureZeroMemory(&rItem, sizeof(LVITEM));
349         rItem.mask  = LVIF_STATE;
350         rItem.iItem = index;
351         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
352         GetItem(&rItem);
353
354         for(int i=0;i<m_HashMap[data->m_CommitHash].size();i++)
355         {
356                 CString str;
357                 str=m_HashMap[data->m_CommitHash][i];
358                 
359                 CString shortname;
360                 HBRUSH brush=0;
361                 shortname=_T("");
362                 if(GetShortName(str,shortname,_T("refs/heads/")))
363                 {
364                         brush = ::CreateSolidBrush(RGB(0xff, 0, 0));
365                 }else if(GetShortName(str,shortname,_T("refs/remotes/")))
366                 {
367                         brush = ::CreateSolidBrush(RGB(0xff, 0xff, 0));
368                 }
369                 else if(GetShortName(str,shortname,_T("refs/tags/")))
370                 {
371                         brush = ::CreateSolidBrush(RGB(0, 0, 0xff));
372                 }
373
374                 if(!shortname.IsEmpty())
375                 {
376                         SIZE size;
377                         memset(&size,0,sizeof(SIZE));
378                         GetTextExtentPoint32(hdc, shortname,shortname.GetLength(),&size);
379                 
380                         rt.SetRect(rt.left,rt.top,rt.left+size.cx,rt.bottom);
381                         rt.right+=4;
382                         ::FillRect(hdc, &rt, brush);
383                         if (rItem.state & LVIS_SELECTED)
384                         {
385                                 COLORREF   clrOld   = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));   
386                                 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);
387                                 ::SetTextColor(hdc,clrOld);   
388                         }else
389                         {
390                                 ::DrawText(hdc,shortname,shortname.GetLength(),&rt,DT_CENTER);
391                         }
392
393                         
394                         ::MoveToEx(hdc,rt.left,rt.top,NULL);
395                         ::LineTo(hdc,rt.right,rt.top);
396                         ::LineTo(hdc,rt.right,rt.bottom);
397                         ::LineTo(hdc,rt.left,rt.bottom);
398                         ::LineTo(hdc,rt.left,rt.top);
399                                 
400                         rt.left=rt.right+3;
401                 }
402                 if(brush)
403                         ::DeleteObject(brush);
404         }               
405         rt.right=rect.right;
406
407         if (rItem.state & LVIS_SELECTED)
408         {
409                 COLORREF   clrOld   = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));   
410                 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);
411                 ::SetTextColor(hdc,clrOld);   
412         }else
413         {
414                 ::DrawText(hdc,data->m_Subject,data->m_Subject.GetLength(),&rt,DT_LEFT);
415         }
416         
417 }
418
419 void CGitLogList::paintGraphLane(HDC hdc, int laneHeight,int type, int x1, int x2,
420                                       const COLORREF& col,int top
421                                                                           )  
422 {
423         int h = laneHeight / 2;
424         int m = (x1 + x2) / 2;
425         int r = (x2 - x1) / 3;
426         int d =  2 * r;
427
428         #define P_CENTER m , h+top
429         #define P_0      x2, h+top
430         #define P_90     m , 0+top
431         #define P_180    x1, h+top
432         #define P_270    m , 2 * h+top
433         #define R_CENTER m - r, h - r+top, m - r+d, h - r+top+d
434
435         //static QPen myPen(Qt::black, 2); // fast path here
436         CPen pen;
437         pen.CreatePen(PS_SOLID,2,col);
438         //myPen.setColor(col);
439         HPEN oldpen=(HPEN)::SelectObject(hdc,(HPEN)pen);
440
441         //p->setPen(myPen);
442
443         // vertical line
444         switch (type) {
445         case Lanes::ACTIVE:
446         case Lanes::NOT_ACTIVE:
447         case Lanes::MERGE_FORK:
448         case Lanes::MERGE_FORK_R:
449         case Lanes::MERGE_FORK_L:
450         case Lanes::JOIN:
451         case Lanes::JOIN_R:
452         case Lanes::JOIN_L:
453                 DrawLine(hdc,P_90,P_270);
454                 //p->drawLine(P_90, P_270);
455                 break;
456         case Lanes::HEAD:
457         case Lanes::HEAD_R:
458         case Lanes::HEAD_L:
459         case Lanes::BRANCH:
460                 DrawLine(hdc,P_CENTER,P_270);
461                 //p->drawLine(P_CENTER, P_270);
462                 break;
463         case Lanes::TAIL:
464         case Lanes::TAIL_R:
465         case Lanes::TAIL_L:
466         case Lanes::INITIAL:
467         case Lanes::BOUNDARY:
468         case Lanes::BOUNDARY_C:
469         case Lanes::BOUNDARY_R:
470         case Lanes::BOUNDARY_L:
471                 DrawLine(hdc,P_90, P_CENTER);
472                 //p->drawLine(P_90, P_CENTER);
473                 break;
474         default:
475                 break;
476         }
477
478         // horizontal line
479         switch (type) {
480         case Lanes::MERGE_FORK:
481         case Lanes::JOIN:
482         case Lanes::HEAD:
483         case Lanes::TAIL:
484         case Lanes::CROSS:
485         case Lanes::CROSS_EMPTY:
486         case Lanes::BOUNDARY_C:
487                 DrawLine(hdc,P_180,P_0);
488                 //p->drawLine(P_180, P_0);
489                 break;
490         case Lanes::MERGE_FORK_R:
491         case Lanes::JOIN_R:
492         case Lanes::HEAD_R:
493         case Lanes::TAIL_R:
494         case Lanes::BOUNDARY_R:
495                 DrawLine(hdc,P_180,P_CENTER);
496                 //p->drawLine(P_180, P_CENTER);
497                 break;
498         case Lanes::MERGE_FORK_L:
499         case Lanes::JOIN_L:
500         case Lanes::HEAD_L:
501         case Lanes::TAIL_L:
502         case Lanes::BOUNDARY_L:
503                 DrawLine(hdc,P_CENTER,P_0);
504                 //p->drawLine(P_CENTER, P_0);
505                 break;
506         default:
507                 break;
508         }
509
510         CBrush brush;
511         brush.CreateSolidBrush(col);
512         HBRUSH oldbrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)brush);
513         // center symbol, e.g. rect or ellipse
514         switch (type) {
515         case Lanes::ACTIVE:
516         case Lanes::INITIAL:
517         case Lanes::BRANCH:
518
519                 //p->setPen(Qt::NoPen);
520                 //p->setBrush(col);
521                 ::Ellipse(hdc, R_CENTER);
522                 //p->drawEllipse(R_CENTER);
523                 break;
524         case Lanes::MERGE_FORK:
525         case Lanes::MERGE_FORK_R:
526         case Lanes::MERGE_FORK_L:
527                 //p->setPen(Qt::NoPen);
528                 //p->setBrush(col);
529                 //p->drawRect(R_CENTER);
530                 Rectangle(hdc,R_CENTER);
531                 break;
532         case Lanes::UNAPPLIED:
533                 // Red minus sign
534                 //p->setPen(Qt::NoPen);
535                 //p->setBrush(Qt::red);
536                 //p->drawRect(m - r, h - 1, d, 2);
537                 ::Rectangle(hdc,m-r,h-1,d,2);
538                 break;
539         case Lanes::APPLIED:
540                 // Green plus sign
541                 //p->setPen(Qt::NoPen);
542                 //p->setBrush(DARK_GREEN);
543                 //p->drawRect(m - r, h - 1, d, 2);
544                 //p->drawRect(m - 1, h - r, 2, d);
545                 ::Rectangle(hdc,m-r,h-1,d,2);
546                 ::Rectangle(hdc,m-1,h-r,2,d);
547                 break;
548         case Lanes::BOUNDARY:
549                 //p->setBrush(back);
550                 //p->drawEllipse(R_CENTER);
551                 ::Ellipse(hdc, R_CENTER);
552                 break;
553         case Lanes::BOUNDARY_C:
554         case Lanes::BOUNDARY_R:
555         case Lanes::BOUNDARY_L:
556                 //p->setBrush(back);
557                 //p->drawRect(R_CENTER);
558                 ::Rectangle(hdc,R_CENTER);
559                 break;
560         default:
561                 break;
562         }
563
564         ::SelectObject(hdc,oldpen);
565         ::SelectObject(hdc,oldbrush);
566         #undef P_CENTER
567         #undef P_0
568         #undef P_90
569         #undef P_180
570         #undef P_270
571         #undef R_CENTER
572 }
573
574 void CGitLogList::DrawGraph(HDC hdc,CRect &rect,INT_PTR index)
575 {
576         //todo unfinished
577         return;
578         GitRev* data = (GitRev*)m_arShownList.GetAt(index);
579         CRect rt=rect;
580         LVITEM   rItem;
581         SecureZeroMemory(&rItem, sizeof(LVITEM));
582         rItem.mask  = LVIF_STATE;
583         rItem.iItem = index;
584         rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
585         GetItem(&rItem);
586
587         static const COLORREF colors[Lanes::COLORS_NUM] = { RGB(0,0,0), RGB(0xFF,0,0), RGB(0,0x1F,0),
588                                                    RGB(0,0,0xFF), RGB(128,128,128), RGB(128,128,0),
589                                                    RGB(0,128,128), RGB(128,0,128) };
590
591
592 //      p->translate(QPoint(opt.rect.left(), opt.rect.top()));
593
594
595
596         if (data->m_Lanes.size() == 0)
597                 m_logEntries.setLane(data->m_CommitHash);
598
599         std::vector<int>& lanes=data->m_Lanes;
600         UINT laneNum = lanes.size();
601         UINT mergeLane = 0;
602         for (UINT i = 0; i < laneNum; i++)
603                 if (Lanes::isMerge(lanes[i])) {
604                         mergeLane = i;
605                         break;
606                 }
607
608         int x1 = 0, x2 = 0;
609         int maxWidth = rect.Width();
610         int lw = 3 * rect.Height() / 4; //laneWidth() 
611         for (UINT i = 0; i < laneNum && x2 < maxWidth; i++) {
612
613                 x1 = x2;
614                 x2 += lw;
615
616                 int ln = lanes[i];
617                 if (ln == Lanes::EMPTY)
618                         continue;
619
620                 UINT col = (  Lanes:: isHead(ln) ||Lanes:: isTail(ln) || Lanes::isJoin(ln)
621                             || ln ==Lanes:: CROSS_EMPTY) ? mergeLane : i;
622
623                 if (ln == Lanes::CROSS) {
624                         paintGraphLane(hdc, rect.Height(),Lanes::NOT_ACTIVE, x1, x2, colors[col % Lanes::COLORS_NUM],rect.top);
625                         paintGraphLane(hdc, rect.Height(),Lanes::CROSS, x1, x2, colors[mergeLane % Lanes::COLORS_NUM],rect.top);
626                 } else
627                         paintGraphLane(hdc, rect.Height(),ln, x1, x2, colors[col % Lanes::COLORS_NUM],rect.top);
628         }
629
630         TRACE(_T("index %d %d\r\n"),index,data->m_Lanes.size());
631 }
632
633 void CGitLogList::OnNMCustomdrawLoglist(NMHDR *pNMHDR, LRESULT *pResult)
634 {
635
636         NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
637         // Take the default processing unless we set this to something else below.
638         *pResult = CDRF_DODEFAULT;
639
640         if (m_bNoDispUpdates)
641                 return;
642
643
644
645         switch (pLVCD->nmcd.dwDrawStage)
646         {
647         case CDDS_PREPAINT:
648                 {
649                         *pResult = CDRF_NOTIFYITEMDRAW;
650                         return;
651                 }
652                 break;
653         case CDDS_ITEMPREPAINT:
654                 {
655                         // This is the prepaint stage for an item. Here's where we set the
656                         // item's text color. 
657                         
658                         // Tell Windows to send draw notifications for each subitem.
659                         *pResult = CDRF_NOTIFYSUBITEMDRAW;
660
661                         COLORREF crText = GetSysColor(COLOR_WINDOWTEXT);
662
663                         if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
664                         {
665                                 GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
666                                 if (data)
667                                 {
668 #if 0
669                                         if (data->bCopiedSelf)
670                                         {
671                                                 // only change the background color if the item is not 'hot' (on vista with m_Themes enabled)
672                                                 if (!m_Theme.IsAppm_Themed() || !m_bVista || ((pLVCD->nmcd.uItemState & CDIS_HOT)==0))
673                                                         pLVCD->clrTextBk = GetSysColor(COLOR_MENU);
674                                         }
675
676                                         if (data->bCopies)
677                                                 crText = m_Colors.GetColor(CColors::Modified);
678 #endif
679 //                                      if ((data->childStackDepth)||(m_mergedRevs.find(data->Rev) != m_mergedRevs.end()))
680 //                                              crText = GetSysColor(COLOR_GRAYTEXT);
681 //                                      if (data->Rev == m_wcRev)
682 //                                      {
683 //                                              SelectObject(pLVCD->nmcd.hdc, m_boldFont);
684                                                 // We changed the font, so we're returning CDRF_NEWFONT. This
685                                                 // tells the control to recalculate the extent of the text.
686 //                                              *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
687 //                                      }
688                                 }
689                         }
690                         if (m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec)
691                         {
692                                 if (m_bStrictStopped)
693                                         crText = GetSysColor(COLOR_GRAYTEXT);
694                         }
695                         // Store the color back in the NMLVCUSTOMDRAW struct.
696                         pLVCD->clrText = crText;
697                         return;
698                 }
699                 break;
700         case CDDS_ITEMPREPAINT|CDDS_ITEM|CDDS_SUBITEM:
701                 {
702                         if ((m_bStrictStopped)&&(m_arShownList.GetCount() == (INT_PTR)pLVCD->nmcd.dwItemSpec))
703                         {
704                                 pLVCD->nmcd.uItemState &= ~(CDIS_SELECTED|CDIS_FOCUS);
705                         }
706
707                         if (pLVCD->iSubItem == LOGLIST_GRAPH)
708                         {
709                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
710                                 {
711                                         CRect rect;
712                                         GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
713                                         
714                                         FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
715                                         DrawGraph(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
716
717                                         *pResult = CDRF_SKIPDEFAULT;
718                                         return;
719                                 
720                                 }
721                         }
722
723                         if (pLVCD->iSubItem == LOGLIST_MESSAGE)
724                         {
725                                 if (m_arShownList.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
726                                 {
727                                         GitRev* data = (GitRev*)m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec);
728                                         if(!data->m_IsFull)
729                                         {
730                                                 if(data->SafeFetchFullInfo(&g_Git))
731                                                         this->Invalidate();
732                                                 TRACE(_T("Update ... %d\r\n"),pLVCD->nmcd.dwItemSpec);
733                                         }
734
735                                         if(m_HashMap[data->m_CommitHash].size()!=0)
736                                         {
737                                                 CRect rect;
738
739                                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
740                                         
741                                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
742                                                 DrawTagBranch(pLVCD->nmcd.hdc,rect,pLVCD->nmcd.dwItemSpec);
743
744                                                 *pResult = CDRF_SKIPDEFAULT;
745                                                 return;
746
747                                         }
748                                 }
749                         }
750                         
751                         if (pLVCD->iSubItem == 1)
752                         {
753                                 *pResult = CDRF_DODEFAULT;
754
755                                 if (m_arShownList.GetCount() <= (INT_PTR)pLVCD->nmcd.dwItemSpec)
756                                         return;
757
758                                 int             nIcons = 0;
759                                 int             iconwidth = ::GetSystemMetrics(SM_CXSMICON);
760                                 int             iconheight = ::GetSystemMetrics(SM_CYSMICON);
761
762                                 GitRev* pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(pLVCD->nmcd.dwItemSpec));
763                                 CRect rect;
764                                 GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rect);
765                                 // Get the selected state of the
766                                 // item being drawn.                                                    
767
768                                 // Fill the background
769                                 FillBackGround(pLVCD->nmcd.hdc, (INT_PTR)pLVCD->nmcd.dwItemSpec,rect);
770                                 
771                                 // Draw the icon(s) into the compatible DC
772                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_MODIFIED)
773                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left + ICONITEMBORDER, rect.top, m_hModifiedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
774                                 nIcons++;
775
776                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_ADDED)
777                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hAddedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
778                                 nIcons++;
779
780                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_DELETED)
781                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hDeletedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
782                                 nIcons++;
783
784                                 if (pLogEntry->m_Action & CTGitPath::LOGACTIONS_REPLACED)
785                                         ::DrawIconEx(pLVCD->nmcd.hdc, rect.left+nIcons*iconwidth + ICONITEMBORDER, rect.top, m_hReplacedIcon, iconwidth, iconheight, 0, NULL, DI_NORMAL);
786                                 nIcons++;
787                                 *pResult = CDRF_SKIPDEFAULT;
788                                 return;
789                         }
790                 }
791                 break;
792         }
793         *pResult = CDRF_DODEFAULT;
794
795 }
796
797 // CGitLogList message handlers
798
799 void CGitLogList::OnLvnGetdispinfoLoglist(NMHDR *pNMHDR, LRESULT *pResult)
800 {
801         NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
802
803         // Create a pointer to the item
804         LV_ITEM* pItem = &(pDispInfo)->item;
805
806         // Do the list need text information?
807         if (!(pItem->mask & LVIF_TEXT))
808                 return;
809
810         // By default, clear text buffer.
811         lstrcpyn(pItem->pszText, _T(""), pItem->cchTextMax);
812
813         bool bOutOfRange = pItem->iItem >= ShownCountWithStopped();
814         
815         *pResult = 0;
816         if (m_bNoDispUpdates || m_bThreadRunning || bOutOfRange)
817                 return;
818
819         // Which item number?
820         int itemid = pItem->iItem;
821         GitRev * pLogEntry = NULL;
822         if (itemid < m_arShownList.GetCount())
823                 pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(pItem->iItem));
824
825             
826         // Which column?
827         switch (pItem->iSubItem)
828         {
829         case this->LOGLIST_GRAPH:       //Graphic
830                 if (pLogEntry)
831                 {
832                 }
833                 break;
834         case this->LOGLIST_ACTION: //action -- no text in the column
835                 break;
836         case this->LOGLIST_MESSAGE: //Message
837                 if (pLogEntry)
838                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_Subject, pItem->cchTextMax);
839                 break;
840         case this->LOGLIST_AUTHOR: //Author
841                 if (pLogEntry)
842                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorName, pItem->cchTextMax);
843                 break;
844         case this->LOGLIST_DATE: //Date
845                 if (pLogEntry)
846                         lstrcpyn(pItem->pszText, (LPCTSTR)pLogEntry->m_AuthorDate.Format(_T("%Y-%m-%d %H:%M")), pItem->cchTextMax);
847                 break;
848                 
849         case 5:
850
851                 break;
852         default:
853                 ASSERT(false);
854         }
855 }
856
857 void CGitLogList::OnContextMenu(CWnd* pWnd, CPoint point)
858 {
859
860         int selIndex = GetSelectionMark();
861         if (selIndex < 0)
862                 return; // nothing selected, nothing to do with a context menu
863
864         // if the user selected the info text telling about not all revisions shown due to
865         // the "stop on copy/rename" option, we also don't show the context menu
866         if ((m_bStrictStopped)&&(selIndex == m_arShownList.GetCount()))
867                 return;
868
869         // if the context menu is invoked through the keyboard, we have to use
870         // a calculated position on where to anchor the menu on
871         if ((point.x == -1) && (point.y == -1))
872         {
873                 CRect rect;
874                 GetItemRect(selIndex, &rect, LVIR_LABEL);
875                 ClientToScreen(&rect);
876                 point = rect.CenterPoint();
877         }
878         m_nSearchIndex = selIndex;
879         m_bCancelled = FALSE;
880
881         // calculate some information the context menu commands can use
882 //      CString pathURL = GetURLFromPath(m_path);
883
884         POSITION pos = GetFirstSelectedItemPosition();
885         int indexNext = GetNextSelectedItem(pos);
886         if (indexNext < 0)
887                 return;
888
889         GitRev* pSelLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(indexNext));
890 #if 0
891         GitRev revSelected = pSelLogEntry->Rev;
892         GitRev revPrevious = git_revnum_t(revSelected)-1;
893         if ((pSelLogEntry->pArChangedPaths)&&(pSelLogEntry->pArChangedPaths->GetCount() <= 2))
894         {
895                 for (int i=0; i<pSelLogEntry->pArChangedPaths->GetCount(); ++i)
896                 {
897                         LogChangedPath * changedpath = (LogChangedPath *)pSelLogEntry->pArChangedPaths->GetAt(i);
898                         if (changedpath->lCopyFromRev)
899                                 revPrevious = changedpath->lCopyFromRev;
900                 }
901         }
902         GitRev revSelected2;
903         if (pos)
904         {
905                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
906                 revSelected2 = pLogEntry->Rev;
907         }
908         bool bAllFromTheSameAuthor = true;
909         CString firstAuthor;
910         CLogDataVector selEntries;
911         GitRev revLowest, revHighest;
912         GitRevRangeArray revisionRanges;
913         {
914                 POSITION pos = GetFirstSelectedItemPosition();
915                 PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
916                 revisionRanges.AddRevision(pLogEntry->Rev);
917                 selEntries.push_back(pLogEntry);
918                 firstAuthor = pLogEntry->sAuthor;
919                 revLowest = pLogEntry->Rev;
920                 revHighest = pLogEntry->Rev;
921                 while (pos)
922                 {
923                         pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
924                         revisionRanges.AddRevision(pLogEntry->Rev);
925                         selEntries.push_back(pLogEntry);
926                         if (firstAuthor.Compare(pLogEntry->sAuthor))
927                                 bAllFromTheSameAuthor = false;
928                         revLowest = (git_revnum_t(pLogEntry->Rev) > git_revnum_t(revLowest) ? revLowest : pLogEntry->Rev);
929                         revHighest = (git_revnum_t(pLogEntry->Rev) < git_revnum_t(revHighest) ? revHighest : pLogEntry->Rev);
930                 }
931         }
932
933 #endif
934
935         int FirstSelect=-1, LastSelect=-1;
936         pos = GetFirstSelectedItemPosition();
937         FirstSelect = GetNextSelectedItem(pos);
938         while(pos)
939         {
940                 LastSelect = GetNextSelectedItem(pos);
941         }
942         //entry is selected, now show the popup menu
943         CIconMenu popup;
944         if (popup.CreatePopupMenu())
945         {
946                 if (GetSelectedCount() == 1)
947                 {
948 #if 0
949                         if (!m_path.IsDirectory())
950                         {
951                                 if (m_hasWC)
952                                 {
953                                         popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
954                                         popup.AppendMenuIcon(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
955                                 }
956                                 popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
957                                 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
958                                 popup.AppendMenu(MF_SEPARATOR, NULL);
959                                 popup.AppendMenuIcon(ID_SAVEAS, IDS_LOG_POPUP_SAVE, IDI_SAVEAS);
960                                 popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
961                                 popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
962                                 popup.AppendMenuIcon(ID_BLAME, IDS_LOG_POPUP_BLAME, IDI_BLAME);
963                                 popup.AppendMenu(MF_SEPARATOR, NULL);
964                         }
965                         else
966 #endif 
967                         {
968                                 if (m_hasWC)
969                                 {
970                                         popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
971                                         // TODO:
972                                         // TortoiseMerge could be improved to take a /blame switch
973                                         // and then not 'cat' the files from a unified diff but
974                                         // blame then.
975                                         // But until that's implemented, the context menu entry for
976                                         // this feature is commented out.
977                                         //popup.AppendMenu(ID_BLAMECOMPARE, IDS_LOG_POPUP_BLAMECOMPARE, IDI_BLAME);
978                                 }
979                                 popup.AppendMenuIcon(ID_GNUDIFF1, IDS_LOG_POPUP_GNUDIFF_CH, IDI_DIFF);
980                                 popup.AppendMenuIcon(ID_COMPAREWITHPREVIOUS, IDS_LOG_POPUP_COMPAREWITHPREVIOUS, IDI_DIFF);
981                                 //popup.AppendMenuIcon(ID_BLAMEWITHPREVIOUS, IDS_LOG_POPUP_BLAMEWITHPREVIOUS, IDI_BLAME);
982                                 popup.AppendMenu(MF_SEPARATOR, NULL);
983                         }
984
985 //                      if (!m_ProjectProperties.sWebViewerRev.IsEmpty())
986 //                      {
987 //                              popup.AppendMenuIcon(ID_VIEWREV, IDS_LOG_POPUP_VIEWREV);
988 //                      }
989 //                      if (!m_ProjectProperties.sWebViewerPathRev.IsEmpty())
990 //                      {
991 //                              popup.AppendMenuIcon(ID_VIEWPATHREV, IDS_LOG_POPUP_VIEWPATHREV);
992 //                      }
993 //                      if ((!m_ProjectProperties.sWebViewerPathRev.IsEmpty())||
994 //                              (!m_ProjectProperties.sWebViewerRev.IsEmpty()))
995 //                      {
996 //                              popup.AppendMenu(MF_SEPARATOR, NULL);
997 //                      }
998
999                         //if (m_hasWC)
1000                         //      popup.AppendMenuIcon(ID_REVERTTOREV, IDS_LOG_POPUP_REVERTTOREV, IDI_REVERT);
1001                         //if (m_hasWC)
1002                         //      popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREV, IDI_REVERT);
1003                         //if (m_hasWC)
1004                         //      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREV, IDI_MERGE);
1005                         
1006                         popup.AppendMenuIcon(ID_SWITCHTOREV, _T("Switch/Checkout to this") , IDI_SWITCH);
1007                         popup.AppendMenuIcon(ID_CREATE_BRANCH, _T("Create Branch at this version") , IDI_COPY);
1008                         popup.AppendMenuIcon(ID_CREATE_TAG, _T("Create Tag at this version"), IDI_COPY);
1009                         popup.AppendMenuIcon(ID_CHERRY_PICK, _T("Cherry Pick this version"), IDI_EXPORT);
1010                         popup.AppendMenuIcon(ID_EXPORT, _T("Export this version"), IDI_EXPORT);
1011                         
1012
1013                         popup.AppendMenu(MF_SEPARATOR, NULL);
1014                 }
1015                 else if (GetSelectedCount() >= 2)
1016                 {
1017                         bool bAddSeparator = false;
1018                         if (IsSelectionContinuous() || (GetSelectedCount() == 2))
1019                         {
1020                                 popup.AppendMenuIcon(ID_COMPARETWO, IDS_LOG_POPUP_COMPARETWO, IDI_DIFF);
1021                         }
1022                         if (GetSelectedCount() == 2)
1023                         {
1024                                 //popup.AppendMenuIcon(ID_BLAMETWO, IDS_LOG_POPUP_BLAMEREVS, IDI_BLAME);
1025                                 popup.AppendMenuIcon(ID_GNUDIFF2, IDS_LOG_POPUP_GNUDIFF, IDI_DIFF);
1026                                 bAddSeparator = true;
1027                         }
1028                         if (m_hasWC)
1029                         {
1030                                 //popup.AppendMenuIcon(ID_REVERTREV, IDS_LOG_POPUP_REVERTREVS, IDI_REVERT);
1031 //                              if (m_hasWC)
1032 //                                      popup.AppendMenuIcon(ID_MERGEREV, IDS_LOG_POPUP_MERGEREVS, IDI_MERGE);
1033                                 bAddSeparator = true;
1034                         }
1035                         if (bAddSeparator)
1036                                 popup.AppendMenu(MF_SEPARATOR, NULL);
1037                 }
1038 #if 0
1039 //              if ((selEntries.size() > 0)&&(bAllFromTheSameAuthor))
1040 //              {
1041 //                      popup.AppendMenuIcon(ID_EDITAUTHOR, IDS_LOG_POPUP_EDITAUTHOR);
1042 //              }
1043 //              if (GetSelectedCount() == 1)
1044 //              {
1045 //                      popup.AppendMenuIcon(ID_EDITLOG, IDS_LOG_POPUP_EDITLOG);
1046 //                      popup.AppendMenuIcon(ID_REVPROPS, IDS_REPOBROWSE_SHOWREVPROP, IDI_PROPERTIES); // "Show Revision Properties"
1047 //                      popup.AppendMenu(MF_SEPARATOR, NULL);
1048 //              }
1049 #endif
1050
1051                 
1052                 if (GetSelectedCount() == 1)
1053                 {
1054                         popup.AppendMenuIcon(ID_COPYHASH, _T("Copy Commit Hash"));
1055                 }
1056                 if (GetSelectedCount() != 0)
1057                 {
1058                         popup.AppendMenuIcon(ID_COPYCLIPBOARD, IDS_LOG_POPUP_COPYTOCLIPBOARD);
1059                 }
1060                 popup.AppendMenuIcon(ID_FINDENTRY, IDS_LOG_POPUP_FIND);
1061
1062                 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1063 //              DialogEnableWindow(IDOK, FALSE);
1064 //              SetPromptApp(&theApp);
1065                 theApp.DoWaitCursor(1);
1066                 bool bOpenWith = false;
1067
1068                 switch (cmd)
1069                 {
1070                         case ID_GNUDIFF1:
1071                         {
1072                                 CString tempfile=GetTempFile();
1073                                 CString cmd;
1074                                 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
1075                                 cmd.Format(_T("git.exe diff-tree -r -p --stat %s"),r1->m_CommitHash);
1076                                 g_Git.RunLogFile(cmd,tempfile);
1077                                 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r1->m_Subject);
1078                         }
1079                         break;
1080
1081                         case ID_GNUDIFF2:
1082                         {
1083                                 CString tempfile=GetTempFile();
1084                                 CString cmd;
1085                                 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
1086                                 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
1087                                 cmd.Format(_T("git.exe diff-tree -r -p --stat %s %s"),r1->m_CommitHash,r2->m_CommitHash);
1088                                 g_Git.RunLogFile(cmd,tempfile);
1089                                 CAppUtils::StartUnifiedDiffViewer(tempfile,r1->m_CommitHash.Left(6)+_T(":")+r2->m_CommitHash.Left(6));
1090
1091                         }
1092                         break;
1093
1094                 case ID_COMPARETWO:
1095                         {
1096                                 GitRev * r1 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(FirstSelect));
1097                                 GitRev * r2 = reinterpret_cast<GitRev*>(m_arShownList.GetAt(LastSelect));
1098                                 CFileDiffDlg dlg;
1099                                 dlg.SetDiff(NULL,*r1,*r2);
1100                                 dlg.DoModal();
1101                                 
1102                         }
1103                         break;
1104                 
1105
1106                 case ID_COMPARE:
1107                         {
1108                                 GitRev * r1 = &m_wcRev;
1109                                 GitRev * r2 = pSelLogEntry;
1110                                 CFileDiffDlg dlg;
1111                                 dlg.SetDiff(NULL,*r1,*r2);
1112                                 dlg.DoModal();
1113
1114                                 //user clicked on the menu item "compare with working copy"
1115                                 //if (PromptShown())
1116                                 //{
1117                                 //      GitDiff diff(this, m_hWnd, true);
1118                                 //      diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1119                                 //      diff.SetHEADPeg(m_LogRevision);
1120                                 //      diff.ShowCompare(m_path, GitRev::REV_WC, m_path, revSelected);
1121                                 //}
1122                                 //else
1123                                 //      CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_WC, m_path, revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1124                         }
1125                         break;
1126
1127                 case ID_COMPAREWITHPREVIOUS:
1128                         {
1129
1130                                 CFileDiffDlg dlg;
1131                                 
1132                                 if(pSelLogEntry->m_ParentHash.size()>0)
1133                                 //if(m_logEntries.m_HashMap[pSelLogEntry->m_ParentHash[0]]>=0)
1134                                 {
1135                                         dlg.SetDiff(NULL,pSelLogEntry->m_CommitHash,pSelLogEntry->m_ParentHash[0]);
1136                                         dlg.DoModal();
1137                                 }else
1138                                 {
1139                                         CMessageBox::Show(NULL,_T("No previous version"),_T("TortoiseGit"),MB_OK);      
1140                                 }
1141                                 //if (PromptShown())
1142                                 //{
1143                                 //      GitDiff diff(this, m_hWnd, true);
1144                                 //      diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1145                                 //      diff.SetHEADPeg(m_LogRevision);
1146                                 //      diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected);
1147                                 //}
1148                                 //else
1149                                 //      CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1150                         }
1151                         break;
1152                 case ID_COPYCLIPBOARD:
1153                         {
1154                                 CopySelectionToClipBoard();
1155                         }
1156                         break;
1157                 case ID_COPYHASH:
1158                         {
1159                                 CopySelectionToClipBoard(TRUE);
1160                         }
1161                         break;
1162                 case ID_EXPORT:
1163                         CAppUtils::Export(&pSelLogEntry->m_CommitHash);
1164                         break;
1165                 case ID_CREATE_BRANCH:
1166                         CAppUtils::CreateBranchTag(FALSE,&pSelLogEntry->m_CommitHash);
1167                         m_HashMap.clear();
1168                         g_Git.GetMapHashToFriendName(m_HashMap);
1169                         Invalidate();                   
1170                         break;
1171                 case ID_CREATE_TAG:
1172                         CAppUtils::CreateBranchTag(TRUE,&pSelLogEntry->m_CommitHash);
1173                         m_HashMap.clear();
1174                         g_Git.GetMapHashToFriendName(m_HashMap);
1175                         Invalidate();
1176                         break;
1177                 case ID_SWITCHTOREV:
1178                         CAppUtils::Switch(&pSelLogEntry->m_CommitHash);
1179                         break;
1180
1181                 default:
1182                         //CMessageBox::Show(NULL,_T("Have not implemented"),_T("TortoiseGit"),MB_OK);
1183                         break;
1184 #if 0
1185         
1186                 case ID_REVERTREV:
1187                         {
1188                                 // we need an URL to complete this command, so error out if we can't get an URL
1189                                 if (pathURL.IsEmpty())
1190                                 {
1191                                         CString strMessage;
1192                                         strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
1193                                         CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
1194                                         TRACE(_T("could not retrieve the URL of the folder!\n"));
1195                                         break;          //exit
1196                                 }
1197                                 CString msg;
1198                                 msg.Format(IDS_LOG_REVERT_CONFIRM, m_path.GetWinPath());
1199                                 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
1200                                 {
1201                                         CGitProgressDlg dlg;
1202                                         dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
1203                                         dlg.SetPathList(CTGitPathList(m_path));
1204                                         dlg.SetUrl(pathURL);
1205                                         dlg.SetSecondUrl(pathURL);
1206                                         revisionRanges.AdjustForMerge(true);
1207                                         dlg.SetRevisionRanges(revisionRanges);
1208                                         dlg.SetPegRevision(m_LogRevision);
1209                                         dlg.DoModal();
1210                                 }
1211                         }
1212                         break;
1213                 case ID_MERGEREV:
1214                         {
1215                                 // we need an URL to complete this command, so error out if we can't get an URL
1216                                 if (pathURL.IsEmpty())
1217                                 {
1218                                         CString strMessage;
1219                                         strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
1220                                         CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
1221                                         TRACE(_T("could not retrieve the URL of the folder!\n"));
1222                                         break;          //exit
1223                                 }
1224
1225                                 CString path = m_path.GetWinPathString();
1226                                 bool bGotSavePath = false;
1227                                 if ((GetSelectedCount() == 1)&&(!m_path.IsDirectory()))
1228                                 {
1229                                         bGotSavePath = CAppUtils::FileOpenSave(path, NULL, IDS_LOG_MERGETO, IDS_COMMONFILEFILTER, true, GetSafeHwnd());
1230                                 }
1231                                 else
1232                                 {
1233                                         CBrowseFolder folderBrowser;
1234                                         folderBrowser.SetInfo(CString(MAKEINTRESOURCE(IDS_LOG_MERGETO)));
1235                                         bGotSavePath = (folderBrowser.Show(GetSafeHwnd(), path, path) == CBrowseFolder::OK);
1236                                 }
1237                                 if (bGotSavePath)
1238                                 {
1239                                         CGitProgressDlg dlg;
1240                                         dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
1241                                         dlg.SetPathList(CTGitPathList(CTGitPath(path)));
1242                                         dlg.SetUrl(pathURL);
1243                                         dlg.SetSecondUrl(pathURL);
1244                                         revisionRanges.AdjustForMerge(false);
1245                                         dlg.SetRevisionRanges(revisionRanges);
1246                                         dlg.SetPegRevision(m_LogRevision);
1247                                         dlg.DoModal();
1248                                 }
1249                         }
1250                         break;
1251                 case ID_REVERTTOREV:
1252                         {
1253                                 // we need an URL to complete this command, so error out if we can't get an URL
1254                                 if (pathURL.IsEmpty())
1255                                 {
1256                                         CString strMessage;
1257                                         strMessage.Format(IDS_ERR_NOURLOFFILE, (LPCTSTR)(m_path.GetUIPathString()));
1258                                         CMessageBox::Show(this->m_hWnd, strMessage, _T("TortoiseGit"), MB_ICONERROR);
1259                                         TRACE(_T("could not retrieve the URL of the folder!\n"));
1260                                         break;          //exit
1261                                 }
1262
1263                                 CString msg;
1264                                 msg.Format(IDS_LOG_REVERTTOREV_CONFIRM, m_path.GetWinPath());
1265                                 if (CMessageBox::Show(this->m_hWnd, msg, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) == IDYES)
1266                                 {
1267                                         CGitProgressDlg dlg;
1268                                         dlg.SetCommand(CGitProgressDlg::GitProgress_Merge);
1269                                         dlg.SetPathList(CTGitPathList(m_path));
1270                                         dlg.SetUrl(pathURL);
1271                                         dlg.SetSecondUrl(pathURL);
1272                                         GitRevRangeArray revarray;
1273                                         revarray.AddRevRange(GitRev::REV_HEAD, revSelected);
1274                                         dlg.SetRevisionRanges(revarray);
1275                                         dlg.SetPegRevision(m_LogRevision);
1276                                         dlg.DoModal();
1277                                 }
1278                         }
1279                         break;
1280         
1281
1282         
1283                 case ID_BLAMECOMPARE:
1284                         {
1285                                 //user clicked on the menu item "compare with working copy"
1286                                 //now first get the revision which is selected
1287                                 if (PromptShown())
1288                                 {
1289                                         GitDiff diff(this, this->m_hWnd, true);
1290                                         diff.SetHEADPeg(m_LogRevision);
1291                                         diff.ShowCompare(m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), false, true);
1292                                 }
1293                                 else
1294                                         CAppUtils::StartShowCompare(m_hWnd, m_path, GitRev::REV_BASE, m_path, revSelected, GitRev(), m_LogRevision, false, false, true);
1295                         }
1296                         break;
1297                 case ID_BLAMETWO:
1298                         {
1299                                 //user clicked on the menu item "compare and blame revisions"
1300                                 if (PromptShown())
1301                                 {
1302                                         GitDiff diff(this, this->m_hWnd, true);
1303                                         diff.SetHEADPeg(m_LogRevision);
1304                                         diff.ShowCompare(CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), false, true);
1305                                 }
1306                                 else
1307                                         CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revSelected2, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
1308                         }
1309                         break;
1310                 case ID_BLAMEWITHPREVIOUS:
1311                         {
1312                                 //user clicked on the menu item "Compare and Blame with previous revision"
1313                                 if (PromptShown())
1314                                 {
1315                                         GitDiff diff(this, this->m_hWnd, true);
1316                                         diff.SetHEADPeg(m_LogRevision);
1317                                         diff.ShowCompare(CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), false, true);
1318                                 }
1319                                 else
1320                                         CAppUtils::StartShowCompare(m_hWnd, CTGitPath(pathURL), revPrevious, CTGitPath(pathURL), revSelected, GitRev(), m_LogRevision, false, false, true);
1321                         }
1322                         break;
1323                 
1324                 case ID_OPENWITH:
1325                         bOpenWith = true;
1326                 case ID_OPEN:
1327                         {
1328                                 CProgressDlg progDlg;
1329                                 progDlg.SetTitle(IDS_APPNAME);
1330                                 progDlg.SetAnimation(IDR_DOWNLOAD);
1331                                 CString sInfoLine;
1332                                 sInfoLine.Format(IDS_PROGRESSGETFILEREVISION, m_path.GetWinPath(), (LPCTSTR)revSelected.ToString());
1333                                 progDlg.SetLine(1, sInfoLine, true);
1334                                 SetAndClearProgressInfo(&progDlg);
1335                                 progDlg.ShowModeless(m_hWnd);
1336                                 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, m_path, revSelected);
1337                                 bool bSuccess = true;
1338                                 if (!Cat(m_path, GitRev(GitRev::REV_HEAD), revSelected, tempfile))
1339                                 {
1340                                         bSuccess = false;
1341                                         // try again, but with the selected revision as the peg revision
1342                                         if (!Cat(m_path, revSelected, revSelected, tempfile))
1343                                         {
1344                                                 progDlg.Stop();
1345                                                 SetAndClearProgressInfo((HWND)NULL);
1346                                                 CMessageBox::Show(this->m_hWnd, GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1347                                                 EnableOKButton();
1348                                                 break;
1349                                         }
1350                                         bSuccess = true;
1351                                 }
1352                                 if (bSuccess)
1353                                 {
1354                                         progDlg.Stop();
1355                                         SetAndClearProgressInfo((HWND)NULL);
1356                                         SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1357                                         int ret = 0;
1358                                         if (!bOpenWith)
1359                                                 ret = (int)ShellExecute(this->m_hWnd, NULL, tempfile.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
1360                                         if ((ret <= HINSTANCE_ERROR)||bOpenWith)
1361                                         {
1362                                                 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1363                                                 cmd += tempfile.GetWinPathString() + _T(" ");
1364                                                 CAppUtils::LaunchApplication(cmd, NULL, false);
1365                                         }
1366                                 }
1367                         }
1368                         break;
1369                 case ID_BLAME:
1370                         {
1371                                 CBlameDlg dlg;
1372                                 dlg.EndRev = revSelected;
1373                                 if (dlg.DoModal() == IDOK)
1374                                 {
1375                                         CBlame blame;
1376                                         CString tempfile;
1377                                         CString logfile;
1378                                         tempfile = blame.BlameToTempFile(m_path, dlg.StartRev, dlg.EndRev, dlg.EndRev, logfile, _T(""), dlg.m_bIncludeMerge, TRUE, TRUE);
1379                                         if (!tempfile.IsEmpty())
1380                                         {
1381                                                 if (dlg.m_bTextView)
1382                                                 {
1383                                                         //open the default text editor for the result file
1384                                                         CAppUtils::StartTextViewer(tempfile);
1385                                                 }
1386                                                 else
1387                                                 {
1388                                                         CString sParams = _T("/path:\"") + m_path.GetGitPathString() + _T("\" ");
1389                                                         if(!CAppUtils::LaunchTortoiseBlame(tempfile, logfile, CPathUtils::GetFileNameFromPath(m_path.GetFileOrDirectoryName()),sParams))
1390                                                         {
1391                                                                 break;
1392                                                         }
1393                                                 }
1394                                         }
1395                                         else
1396                                         {
1397                                                 CMessageBox::Show(this->m_hWnd, blame.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1398                                         }
1399                                 }
1400                         }
1401                         break;
1402                 case ID_UPDATE:
1403                         {
1404                                 CString sCmd;
1405                                 CString url = _T("tgit:")+pathURL;
1406                                 sCmd.Format(_T("%s /command:update /path:\"%s\" /rev:%ld"),
1407                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
1408                                         (LPCTSTR)m_path.GetWinPath(), (LONG)revSelected);
1409                                 CAppUtils::LaunchApplication(sCmd, NULL, false);
1410                         }
1411                         break;
1412                 case ID_FINDENTRY:
1413                         {
1414                                 m_nSearchIndex = GetSelectionMark();
1415                                 if (m_nSearchIndex < 0)
1416                                         m_nSearchIndex = 0;
1417                                 if (m_pFindDialog)
1418                                 {
1419                                         break;
1420                                 }
1421                                 else
1422                                 {
1423                                         m_pFindDialog = new CFindReplaceDialog();
1424                                         m_pFindDialog->Create(TRUE, NULL, NULL, FR_HIDEUPDOWN | FR_HIDEWHOLEWORD, this);                                                                        
1425                                 }
1426                         }
1427                         break;
1428                 case ID_REPOBROWSE:
1429                         {
1430                                 CString sCmd;
1431                                 sCmd.Format(_T("%s /command:repobrowser /path:\"%s\" /rev:%s"),
1432                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
1433                                         (LPCTSTR)pathURL, (LPCTSTR)revSelected.ToString());
1434
1435                                 CAppUtils::LaunchApplication(sCmd, NULL, false);
1436                         }
1437                         break;
1438                 case ID_EDITLOG:
1439                         {
1440                                 EditLogMessage(selIndex);
1441                         }
1442                         break;
1443                 case ID_EDITAUTHOR:
1444                         {
1445                                 EditAuthor(selEntries);
1446                         }
1447                         break;
1448                 case ID_REVPROPS:
1449                         {
1450                                 CEditPropertiesDlg dlg;
1451                                 dlg.SetProjectProperties(&m_ProjectProperties);
1452                                 CTGitPathList escapedlist;
1453                                 dlg.SetPathList(CTGitPathList(CTGitPath(pathURL)));
1454                                 dlg.SetRevision(revSelected);
1455                                 dlg.RevProps(true);
1456                                 dlg.DoModal();
1457                         }
1458                         break;
1459                 
1460                 case ID_EXPORT:
1461                         {
1462                                 CString sCmd;
1463                                 sCmd.Format(_T("%s /command:export /path:\"%s\" /revision:%ld"),
1464                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
1465                                         (LPCTSTR)pathURL, (LONG)revSelected);
1466                                 CAppUtils::LaunchApplication(sCmd, NULL, false);
1467                         }
1468                         break;
1469                 case ID_CHECKOUT:
1470                         {
1471                                 CString sCmd;
1472                                 CString url = _T("tgit:")+pathURL;
1473                                 sCmd.Format(_T("%s /command:checkout /url:\"%s\" /revision:%ld"),
1474                                         (LPCTSTR)(CPathUtils::GetAppDirectory()+_T("TortoiseProc.exe")),
1475                                         (LPCTSTR)url, (LONG)revSelected);
1476                                 CAppUtils::LaunchApplication(sCmd, NULL, false);
1477                         }
1478                         break;
1479                 case ID_VIEWREV:
1480                         {
1481                                 CString url = m_ProjectProperties.sWebViewerRev;
1482                                 url = GetAbsoluteUrlFromRelativeUrl(url);
1483                                 url.Replace(_T("%REVISION%"), revSelected.ToString());
1484                                 if (!url.IsEmpty())
1485                                         ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);                                        
1486                         }
1487                         break;
1488                 case ID_VIEWPATHREV:
1489                         {
1490                                 CString relurl = pathURL;
1491                                 CString sRoot = GetRepositoryRoot(CTGitPath(relurl));
1492                                 relurl = relurl.Mid(sRoot.GetLength());
1493                                 CString url = m_ProjectProperties.sWebViewerPathRev;
1494                                 url = GetAbsoluteUrlFromRelativeUrl(url);
1495                                 url.Replace(_T("%REVISION%"), revSelected.ToString());
1496                                 url.Replace(_T("%PATH%"), relurl);
1497                                 if (!url.IsEmpty())
1498                                         ShellExecute(this->m_hWnd, _T("open"), url, NULL, NULL, SW_SHOWDEFAULT);                                        
1499                         }
1500                         break;
1501 #endif
1502                 
1503                 } // switch (cmd)
1504                 theApp.DoWaitCursor(-1);
1505 //              EnableOKButton();
1506         } // if (popup.CreatePopupMenu())
1507
1508 }
1509
1510 bool CGitLogList::IsSelectionContinuous()
1511 {
1512         if ( GetSelectedCount()==1 )
1513         {
1514                 // if only one revision is selected, the selection is of course
1515                 // continuous
1516                 return true;
1517         }
1518
1519         POSITION pos = GetFirstSelectedItemPosition();
1520         bool bContinuous = (m_arShownList.GetCount() == (INT_PTR)m_logEntries.size());
1521         if (bContinuous)
1522         {
1523                 int itemindex = GetNextSelectedItem(pos);
1524                 while (pos)
1525                 {
1526                         int nextindex = GetNextSelectedItem(pos);
1527                         if (nextindex - itemindex > 1)
1528                         {
1529                                 bContinuous = false;
1530                                 break;
1531                         }
1532                         itemindex = nextindex;
1533                 }
1534         }
1535         return bContinuous;
1536 }
1537
1538 void CGitLogList::CopySelectionToClipBoard(bool HashOnly)
1539 {
1540
1541         CString sClipdata;
1542         POSITION pos = GetFirstSelectedItemPosition();
1543         if (pos != NULL)
1544         {
1545                 CString sRev;
1546                 sRev.LoadString(IDS_LOG_REVISION);
1547                 CString sAuthor;
1548                 sAuthor.LoadString(IDS_LOG_AUTHOR);
1549                 CString sDate;
1550                 sDate.LoadString(IDS_LOG_DATE);
1551                 CString sMessage;
1552                 sMessage.LoadString(IDS_LOG_MESSAGE);
1553                 while (pos)
1554                 {
1555                         CString sLogCopyText;
1556                         CString sPaths;
1557                         GitRev * pLogEntry = reinterpret_cast<GitRev *>(m_arShownList.GetAt(GetNextSelectedItem(pos)));
1558
1559                         if(!HashOnly)
1560                         {
1561                                 //pLogEntry->m_Files
1562                                 //LogChangedPathArray * cpatharray = pLogEntry->pArChangedPaths;
1563                         
1564                                 for (int cpPathIndex = 0; cpPathIndex<pLogEntry->m_Files.GetCount(); ++cpPathIndex)
1565                                 {
1566                                         sPaths += ((CTGitPath&)pLogEntry->m_Files[cpPathIndex]).GetActionName() + _T(" : ") + pLogEntry->m_Files[cpPathIndex].GetGitPathString();
1567                                         sPaths += _T("\r\n");
1568                                 }
1569                                 sPaths.Trim();
1570                                 sLogCopyText.Format(_T("%s: %s\r\n%s: %s\r\n%s: %s\r\n%s:\r\n%s\r\n----\r\n%s\r\n\r\n"),
1571                                         (LPCTSTR)sRev, pLogEntry->m_CommitHash,
1572                                         (LPCTSTR)sAuthor, (LPCTSTR)pLogEntry->m_AuthorName,
1573                                         (LPCTSTR)sDate, (LPCTSTR)pLogEntry->m_AuthorDate.Format(_T("%Y-%m-%d %H:%M")),
1574                                         (LPCTSTR)sMessage, pLogEntry->m_Subject+_T("\r\n")+pLogEntry->m_Body,
1575                                         (LPCTSTR)sPaths);
1576                                 sClipdata +=  sLogCopyText;
1577                         }else
1578                         {
1579                                 sClipdata += pLogEntry->m_CommitHash;
1580                                 break;
1581                         }
1582
1583                 }
1584                 CStringUtils::WriteAsciiStringToClipboard(sClipdata, GetSafeHwnd());
1585         }
1586
1587 }
1588
1589 void CGitLogList::DiffSelectedRevWithPrevious()
1590 {
1591 #if 0
1592         if (m_bThreadRunning)
1593                 return;
1594         UpdateLogInfoLabel();
1595         int selIndex = m_LogList.GetSelectionMark();
1596         if (selIndex < 0)
1597                 return;
1598         int selCount = m_LogList.GetSelectedCount();
1599         if (selCount != 1)
1600                 return;
1601
1602         // Find selected entry in the log list
1603         POSITION pos = m_LogList.GetFirstSelectedItemPosition();
1604         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(m_LogList.GetNextSelectedItem(pos)));
1605         long rev1 = pLogEntry->Rev;
1606         long rev2 = rev1-1;
1607         CTGitPath path = m_path;
1608
1609         // See how many files under the relative root were changed in selected revision
1610         int nChanged = 0;
1611         LogChangedPath * changed = NULL;
1612         for (INT_PTR c = 0; c < pLogEntry->pArChangedPaths->GetCount(); ++c)
1613         {
1614                 LogChangedPath * cpath = pLogEntry->pArChangedPaths->GetAt(c);
1615                 if (cpath  &&  cpath -> sPath.Left(m_sRelativeRoot.GetLength()).Compare(m_sRelativeRoot)==0)
1616                 {
1617                         ++nChanged;
1618                         changed = cpath;
1619                 }
1620         }
1621
1622         if (m_path.IsDirectory() && nChanged == 1) 
1623         {
1624                 // We're looking at the log for a directory and only one file under dir was changed in the revision
1625                 // Do diff on that file instead of whole directory
1626                 path.AppendPathString(changed->sPath.Mid(m_sRelativeRoot.GetLength()));
1627         } 
1628
1629         m_bCancelled = FALSE;
1630         DialogEnableWindow(IDOK, FALSE);
1631         SetPromptApp(&theApp);
1632         theApp.DoWaitCursor(1);
1633
1634         if (PromptShown())
1635         {
1636                 GitDiff diff(this, m_hWnd, true);
1637                 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1638                 diff.SetHEADPeg(m_LogRevision);
1639                 diff.ShowCompare(path, rev2, path, rev1);
1640         }
1641         else
1642         {
1643                 CAppUtils::StartShowCompare(m_hWnd, path, rev2, path, rev1, GitRev(), m_LogRevision, !!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1644         }
1645
1646         theApp.DoWaitCursor(-1);
1647         EnableOKButton();
1648 #endif
1649 }
1650
1651 void CGitLogList::OnLvnOdfinditemLoglist(NMHDR *pNMHDR, LRESULT *pResult)
1652 {
1653         LPNMLVFINDITEM pFindInfo = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
1654         *pResult = -1;
1655         
1656         if (pFindInfo->lvfi.flags & LVFI_PARAM)
1657                 return; 
1658         if ((pFindInfo->iStart < 0)||(pFindInfo->iStart >= m_arShownList.GetCount()))
1659                 return;
1660         if (pFindInfo->lvfi.psz == 0)
1661                 return;
1662 #if 0
1663         CString sCmp = pFindInfo->lvfi.psz;
1664         CString sRev;   
1665         for (int i=pFindInfo->iStart; i<m_arShownList.GetCount(); ++i)
1666         {
1667                 GitRev * pLogEntry = reinterpret_cast<GitRev*>(m_arShownList.GetAt(i));
1668                 sRev.Format(_T("%ld"), pLogEntry->Rev);
1669                 if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
1670                 {
1671                         if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
1672                         {
1673                                 *pResult = i;
1674                                 return;
1675                         }
1676                 }
1677                 else
1678                 {
1679                         if (sCmp.Compare(sRev)==0)
1680                         {
1681                                 *pResult = i;
1682                                 return;
1683                         }
1684                 }
1685         }
1686         if (pFindInfo->lvfi.flags & LVFI_WRAP)
1687         {
1688                 for (int i=0; i<pFindInfo->iStart; ++i)
1689                 {
1690                         PLOGENTRYDATA pLogEntry = reinterpret_cast<PLOGENTRYDATA>(m_arShownList.GetAt(i));
1691                         sRev.Format(_T("%ld"), pLogEntry->Rev);
1692                         if (pFindInfo->lvfi.flags & LVFI_PARTIAL)
1693                         {
1694                                 if (sCmp.Compare(sRev.Left(sCmp.GetLength()))==0)
1695                                 {
1696                                         *pResult = i;
1697                                         return;
1698                                 }
1699                         }
1700                         else
1701                         {
1702                                 if (sCmp.Compare(sRev)==0)
1703                                 {
1704                                         *pResult = i;
1705                                         return;
1706                                 }
1707                         }
1708                 }
1709         }
1710 #endif
1711         *pResult = -1;
1712 }
1713
1714 int CGitLogList::FillGitShortLog()
1715 {
1716         ClearText();
1717
1718         this->m_logEntries.ClearAll();
1719         this->m_logEntries.ParserShortLog();
1720
1721         //this->m_logEntries.ParserFromLog();
1722         SetItemCountEx(this->m_logEntries.size());
1723
1724         this->m_arShownList.RemoveAll();
1725
1726         for(int i=0;i<m_logEntries.size();i++)
1727                 this->m_arShownList.Add(&m_logEntries[i]);
1728
1729         return 0;
1730 }
1731
1732 BOOL CGitLogList::PreTranslateMessage(MSG* pMsg)
1733 {
1734         // Skip Ctrl-C when copying text out of the log message or search filter
1735         BOOL bSkipAccelerator = ( pMsg->message == WM_KEYDOWN && pMsg->wParam=='C' && (GetFocus()==GetDlgItem(IDC_MSGVIEW) || GetFocus()==GetDlgItem(IDC_SEARCHEDIT) ) && GetKeyState(VK_CONTROL)&0x8000 );
1736         if (pMsg->message == WM_KEYDOWN && pMsg->wParam=='\r')
1737         {
1738                 //if (GetFocus()==GetDlgItem(IDC_LOGLIST))
1739                 {
1740                         if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1741                         {
1742                                 DiffSelectedRevWithPrevious();
1743                                 return TRUE;
1744                         }
1745                 }
1746 #if 0
1747                 if (GetFocus()==GetDlgItem(IDC_LOGMSG))
1748                 {
1749                         DiffSelectedFile();
1750                         return TRUE;
1751                 }
1752 #endif
1753         }
1754
1755 #if 0
1756         if (m_hAccel && !bSkipAccelerator)
1757         {
1758                 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);
1759                 if (ret)
1760                         return TRUE;
1761         }
1762         
1763 #endif
1764         //m_tooltips.RelayEvent(pMsg);
1765         return __super::PreTranslateMessage(pMsg);
1766 }
1767
1768 void CGitLogList::OnNMDblclkLoglist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
1769 {
1770         // a double click on an entry in the revision list has happened
1771         *pResult = 0;
1772
1773   if (CRegDWORD(_T("Software\\TortoiseGit\\DiffByDoubleClickInLog"), FALSE))
1774           DiffSelectedRevWithPrevious();
1775 }
1776
1777 int CGitLogList::FetchLogAsync(CALLBACK_PROCESS *proc,void * data)
1778 {
1779         m_ProcCallBack=proc;
1780         m_ProcData=data;
1781
1782         InterlockedExchange(&m_bThreadRunning, TRUE);
1783         InterlockedExchange(&m_bNoDispUpdates, TRUE);
1784         if (AfxBeginThread(LogThreadEntry, this)==NULL)
1785         {
1786                 InterlockedExchange(&m_bThreadRunning, FALSE);
1787                 InterlockedExchange(&m_bNoDispUpdates, FALSE);
1788                 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
1789                 return -1;
1790         }
1791         return 0;
1792 }
1793
1794 //this is the thread function which calls the subversion function
1795 UINT CGitLogList::LogThreadEntry(LPVOID pVoid)
1796 {
1797         return ((CGitLogList*)pVoid)->LogThread();
1798 }
1799
1800
1801 UINT CGitLogList::LogThread()
1802 {
1803
1804         if(m_ProcCallBack)
1805                 m_ProcCallBack(m_ProcData,GITLOG_START);
1806
1807         InterlockedExchange(&m_bThreadRunning, TRUE);
1808
1809     //does the user force the cache to refresh (shift or control key down)?
1810     bool refresh =    (GetKeyState (VK_CONTROL) < 0) 
1811                    || (GetKeyState (VK_SHIFT) < 0);
1812
1813         //disable the "Get All" button while we're receiving
1814         //log messages.
1815
1816         CString temp;
1817         temp.LoadString(IDS_PROGRESSWAIT);
1818         ShowText(temp, true);
1819
1820         FillGitShortLog();
1821         
1822         InterlockedExchange(&m_bThreadRunning, FALSE);
1823
1824         RedrawItems(0, m_arShownList.GetCount());
1825         SetRedraw(false);
1826         ResizeAllListCtrlCols();
1827         SetRedraw(true);
1828
1829         if ( m_pStoreSelection )
1830         {
1831                 // Deleting the instance will restore the
1832                 // selection of the CLogDlg.
1833                 delete m_pStoreSelection;
1834                 m_pStoreSelection = NULL;
1835         }
1836         else
1837         {
1838                 // If no selection has been set then this must be the first time
1839                 // the revisions are shown. Let's preselect the topmost revision.
1840                 if ( GetItemCount()>0 )
1841                 {
1842                         SetSelectionMark(0);
1843                         SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
1844                 }
1845         }
1846         InterlockedExchange(&m_bNoDispUpdates, FALSE);
1847
1848         int index=0;
1849         int updated=0;
1850         int percent=0;
1851         while(1)
1852         {
1853                 for(int i=0;i<m_logEntries.size();i++)
1854                 {
1855                         if(!m_logEntries.FetchFullInfo(i))
1856                         {
1857                                 updated++;
1858                         }
1859                         
1860                         percent=updated*98/m_logEntries.size() + GITLOG_START+1;
1861                         if(percent == GITLOG_END)
1862                                 percent == GITLOG_END -1;
1863                         
1864                         if(m_ProcCallBack)
1865                                 m_ProcCallBack(m_ProcData,percent);
1866                 }
1867                 if(updated==m_logEntries.size())
1868                         break;
1869         }
1870
1871         //RefreshCursor();
1872         // make sure the filter is applied (if any) now, after we refreshed/fetched
1873         // the log messages
1874
1875         
1876
1877         if(m_ProcCallBack)
1878                 m_ProcCallBack(m_ProcData,GITLOG_END);
1879
1880         return 0;
1881 }
1882