1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006, 2008, 2010-2012, 2015 - TortoiseSVN
4 // Copyright (C) 2012 - Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "TortoiseMerge.h"
22 #include "FilePatchesDlg.h"
25 #include "PathUtils.h"
26 #include "SysProgressDlg.h"
27 #include "MessageBox.h"
28 #include "SysImageList.h"
30 IMPLEMENT_DYNAMIC(CFilePatchesDlg
, CResizableStandAloneDialog
)
31 CFilePatchesDlg::CFilePatchesDlg(CWnd
* pParent
/*=NULL*/)
32 : CResizableStandAloneDialog(CFilePatchesDlg::IDD
, pParent
)
43 CFilePatchesDlg::~CFilePatchesDlg()
46 DeleteObject(m_boldFont
);
49 void CFilePatchesDlg::DoDataExchange(CDataExchange
* pDX
)
51 CResizableStandAloneDialog::DoDataExchange(pDX
);
52 DDX_Control(pDX
, IDC_FILELIST
, m_cFileList
);
55 BOOL
CFilePatchesDlg::SetFileStatusAsPatched(CString sPath
)
57 for (int i
=0; i
<m_arFileStates
.GetCount(); i
++)
59 if (sPath
.CompareNoCase(GetFullPath(i
))==0)
61 m_arFileStates
.SetAt(i
, (DWORD
)FPDLG_FILESTATE_PATCHED
);
62 SetStateText(i
, FPDLG_FILESTATE_PATCHED
);
70 CString
CFilePatchesDlg::GetFullPath(int nIndex
)
72 CString temp
= m_pPatch
->GetStrippedPath(nIndex
);
73 temp
.Replace('/', '\\');
74 //temp = temp.Mid(temp.Find('\\')+1);
75 if (PathIsRelative(temp
))
76 temp
= m_sPath
+ temp
;
80 BOOL
CFilePatchesDlg::OnInitDialog()
82 CResizableStandAloneDialog::OnInitDialog();
84 // hide the grip since it would overlap with the "path all" button
89 HFONT hFont
= (HFONT
)m_cFileList
.SendMessage(WM_GETFONT
);
91 GetObject(hFont
, sizeof(LOGFONT
), &lf
);
92 lf
.lfWeight
= FW_BOLD
;
93 m_boldFont
= CreateFontIndirect(&lf
);
95 AddAnchor(IDC_FILELIST
, TOP_LEFT
, BOTTOM_RIGHT
);
96 AddAnchor(IDC_PATCHSELECTEDBUTTON
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
97 AddAnchor(IDC_PATCHALLBUTTON
, BOTTOM_LEFT
, BOTTOM_RIGHT
);
102 BOOL
CFilePatchesDlg::Init(GitPatch
* pPatch
, CPatchFilesDlgCallBack
* pCallBack
, CString sPath
, CWnd
* pParent
)
104 if ((pCallBack
==NULL
)||(pPatch
==NULL
))
106 m_cFileList
.DeleteAllItems();
109 m_arFileStates
.RemoveAll();
111 m_pCallBack
= pCallBack
;
113 if (m_sPath
.IsEmpty())
115 CString
title(MAKEINTRESOURCE(IDS_DIFF_TITLE
));
116 SetWindowText(title
);
121 GetClientRect(&rect
);
122 SetTitleWithPath(rect
.Width());
123 if (m_sPath
.Right(1).Compare(_T("\\"))==0)
124 m_sPath
= m_sPath
.Left(m_sPath
.GetLength()-1);
126 m_sPath
= m_sPath
+ _T("\\");
129 SetWindowTheme(m_cFileList
.GetSafeHwnd(), L
"Explorer", NULL
);
130 m_cFileList
.SetExtendedStyle(LVS_EX_INFOTIP
| LVS_EX_FULLROWSELECT
| LVS_EX_DOUBLEBUFFER
);
131 m_cFileList
.DeleteAllItems();
132 int c
= ((CHeaderCtrl
*)(m_cFileList
.GetDlgItem(0)))->GetItemCount()-1;
134 m_cFileList
.DeleteColumn(c
--);
135 m_cFileList
.InsertColumn(0, CString(MAKEINTRESOURCE(IDS_PATH
)));
136 m_cFileList
.InsertColumn(1, CString(MAKEINTRESOURCE(IDS_STATE
)));
138 m_cFileList
.SetRedraw(false);
140 for(int i
=0; i
<m_pPatch
->GetNumberOfFiles(); i
++)
142 CString sFile
= CPathUtils::GetFileNameFromPath(m_pPatch
->GetStrippedPath(i
));
145 if (m_sPath
.IsEmpty())
149 state
= m_pPatch
->GetFailedHunks(i
);
151 if (m_pPatch
->GetHasConflict(i
))
152 state
= FPDLG_FILESTATE_CONFLICT
;
154 state
= FPDLG_FILESTATE_ERROR
;
155 m_arFileStates
.Add(state
);
156 CString sFileName
= GetFullPath(i
);
157 sFileName
= CPathUtils::GetFileNameFromPath(sFileName
);
158 m_cFileList
.InsertItem(i
, sFile
, SYS_IMAGE_LIST().GetFileIconIndex(sFileName
));
159 SetStateText(i
, state
);
162 int maxcol
= ((CHeaderCtrl
*)(m_cFileList
.GetDlgItem(0)))->GetItemCount()-1;
164 for (col
= mincol
; col
<= maxcol
; col
++)
166 m_cFileList
.SetColumnWidth(col
,LVSCW_AUTOSIZE_USEHEADER
);
169 m_cFileList
.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL
);
170 m_cFileList
.SetRedraw(true);
173 pParent
->GetWindowRect(&parentrect
);
175 GetWindowRect(&windowrect
);
177 int width
= windowrect
.right
- windowrect
.left
;
178 int height
= windowrect
.bottom
- windowrect
.top
;
179 windowrect
.right
= parentrect
.left
;
180 windowrect
.left
= windowrect
.right
- width
;
181 if (windowrect
.left
< 0)
184 windowrect
.right
= width
;
186 windowrect
.top
= parentrect
.top
;
187 windowrect
.bottom
= windowrect
.top
+ height
;
189 SetWindowPos(NULL
, windowrect
.left
, windowrect
.top
, width
, height
, SWP_NOACTIVATE
| SWP_NOZORDER
);
191 m_nWindowHeight
= windowrect
.bottom
- windowrect
.top
;
192 m_pMainFrame
= pParent
;
196 BEGIN_MESSAGE_MAP(CFilePatchesDlg
, CResizableStandAloneDialog
)
198 ON_NOTIFY(LVN_GETINFOTIP
, IDC_FILELIST
, OnLvnGetInfoTipFilelist
)
199 ON_NOTIFY(NM_DBLCLK
, IDC_FILELIST
, OnNMDblclkFilelist
)
200 ON_NOTIFY(NM_CUSTOMDRAW
, IDC_FILELIST
, OnNMCustomdrawFilelist
)
201 ON_NOTIFY(NM_RCLICK
, IDC_FILELIST
, OnNMRclickFilelist
)
202 ON_WM_NCLBUTTONDBLCLK()
204 ON_BN_CLICKED(IDC_PATCHSELECTEDBUTTON
, &CFilePatchesDlg::OnBnClickedPatchselectedbutton
)
205 ON_BN_CLICKED(IDC_PATCHALLBUTTON
, &CFilePatchesDlg::OnBnClickedPatchallbutton
)
206 ON_NOTIFY(LVN_ITEMCHANGED
, IDC_FILELIST
, &CFilePatchesDlg::OnLvnItemchangedFilelist
)
209 void CFilePatchesDlg::OnSize(UINT nType
, int cx
, int cy
)
211 CResizableStandAloneDialog::OnSize(nType
, cx
, cy
);
212 if (this->IsWindowVisible())
214 m_cFileList
.SetColumnWidth(0, LVSCW_AUTOSIZE
);
216 SetTitleWithPath(cx
);
219 void CFilePatchesDlg::OnLvnGetInfoTipFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
221 LPNMLVGETINFOTIP pGetInfoTip
= reinterpret_cast<LPNMLVGETINFOTIP
>(pNMHDR
);
223 if (m_arFileStates
.GetCount() > pGetInfoTip
->iItem
)
226 if (m_arFileStates
.GetAt(pGetInfoTip
->iItem
) == 0)
227 temp
= GetFullPath(pGetInfoTip
->iItem
);
229 temp
.Format(IDS_PATCH_ITEMTT
, (LPCTSTR
)GetFullPath(pGetInfoTip
->iItem
), m_arFileStates
.GetAt(pGetInfoTip
->iItem
));
230 _tcsncpy_s(pGetInfoTip
->pszText
, pGetInfoTip
->cchTextMax
, temp
, pGetInfoTip
->cchTextMax
- 1);
233 pGetInfoTip
->pszText
[0] = 0;
238 void CFilePatchesDlg::OnNMDblclkFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
240 LPNMLISTVIEW pNMLV
= reinterpret_cast<LPNMLISTVIEW
>(pNMHDR
);
242 if ((pNMLV
->iItem
< 0) || (pNMLV
->iItem
>= m_arFileStates
.GetCount()))
244 if (m_pCallBack
==NULL
)
247 if (m_arFileStates
.GetAt(pNMLV
->iItem
) == FPDLG_FILESTATE_ERROR
)
249 MessageBox(m_pPatch
->GetPatchRejects(pNMLV
->iItem
), NULL
, MB_ICONERROR
);
253 if (m_sPath
.IsEmpty())
255 m_pCallBack
->DiffFiles(GetFullPath(pNMLV
->iItem
), _T(""),
257 m_ShownIndex
= pNMLV
->iItem
;
258 m_cFileList
.Invalidate();
262 if (m_arFileStates
.GetAt(pNMLV
->iItem
)!=FPDLG_FILESTATE_PATCHED
)
264 m_pCallBack
->PatchFile(m_pPatch
->GetStrippedPath(pNMLV
->iItem
), m_pPatch
->GetContentMods(pNMLV
->iItem
), m_pPatch
->GetPropMods(pNMLV
->iItem
), _T(""));
265 m_ShownIndex
= pNMLV
->iItem
;
266 m_cFileList
.Invalidate();
271 void CFilePatchesDlg::OnNMCustomdrawFilelist(NMHDR
*pNMHDR
, LRESULT
*pResult
)
273 NMLVCUSTOMDRAW
* pLVCD
= reinterpret_cast<NMLVCUSTOMDRAW
*>( pNMHDR
);
275 // Take the default processing unless we set this to something else below.
276 *pResult
= CDRF_DODEFAULT
;
278 // First thing - check the draw stage. If it's the control's prepaint
279 // stage, then tell Windows we want messages for every item.
281 if ( CDDS_PREPAINT
== pLVCD
->nmcd
.dwDrawStage
)
283 *pResult
= CDRF_NOTIFYITEMDRAW
;
285 else if ( CDDS_ITEMPREPAINT
== pLVCD
->nmcd
.dwDrawStage
)
287 // This is the prepaint stage for an item. Here's where we set the
288 // item's text color. Our return value will tell Windows to draw the
289 // item itself, but it will use the new color we set here.
291 COLORREF crText
= ::GetSysColor(COLOR_WINDOWTEXT
);
293 if (m_arFileStates
.GetCount() > (INT_PTR
)pLVCD
->nmcd
.dwItemSpec
)
295 if (m_arFileStates
.GetAt(pLVCD
->nmcd
.dwItemSpec
) == FPDLG_FILESTATE_CONFLICT
)
297 crText
= RGB(255, 200, 100); // orange
299 if (m_arFileStates
.GetAt(pLVCD
->nmcd
.dwItemSpec
)==FPDLG_FILESTATE_ERROR
)
301 crText
= RGB(200, 0, 0);
303 if (m_arFileStates
.GetAt(pLVCD
->nmcd
.dwItemSpec
)>0)
305 crText
= RGB(100, 0, 0);
307 if (m_arFileStates
.GetAt(pLVCD
->nmcd
.dwItemSpec
)==FPDLG_FILESTATE_PATCHED
)
309 crText
= ::GetSysColor(COLOR_GRAYTEXT
);
311 // Store the color back in the NMLVCUSTOMDRAW struct.
312 pLVCD
->clrText
= crText
;
313 if (m_ShownIndex
== (int)pLVCD
->nmcd
.dwItemSpec
)
315 SelectObject(pLVCD
->nmcd
.hdc
, m_boldFont
);
316 // We changed the font, so we're returning CDRF_NEWFONT. This
317 // tells the control to recalculate the extent of the text.
318 *pResult
= CDRF_NOTIFYSUBITEMDRAW
| CDRF_NEWFONT
;
324 void CFilePatchesDlg::OnNMRclickFilelist(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
327 if (m_sPath
.IsEmpty())
332 DWORD ptW
= GetMessagePos();
333 point
.x
= GET_X_LPARAM(ptW
);
334 point
.y
= GET_Y_LPARAM(ptW
);
335 if (!popup
.CreatePopupMenu())
338 UINT nFlags
= MF_STRING
| (m_cFileList
.GetSelectedCount()==1 ? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
);
339 temp
.LoadString(IDS_PATCH_PREVIEW
);
340 popup
.AppendMenu(nFlags
, ID_PATCHPREVIEW
, temp
);
341 popup
.SetDefaultItem(ID_PATCHPREVIEW
, FALSE
);
343 temp
.LoadString(IDS_PATCH_ALL
);
344 popup
.AppendMenu(MF_STRING
| MF_ENABLED
, ID_PATCHALL
, temp
);
346 nFlags
= MF_STRING
| (m_cFileList
.GetSelectedCount() > 0 ? MF_ENABLED
: MF_DISABLED
| MF_GRAYED
);
347 temp
.LoadString(IDS_PATCH_SELECTED
);
348 popup
.AppendMenu(nFlags
, ID_PATCHSELECTED
, temp
);
350 // if the context menu is invoked through the keyboard, we have to use
351 // a calculated position on where to anchor the menu on
352 if ((point
.x
== -1) && (point
.y
== -1))
355 GetWindowRect(&rect
);
356 point
= rect
.CenterPoint();
359 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
| TPM_RIGHTBUTTON
, point
.x
, point
.y
, this, 0);
362 case ID_PATCHPREVIEW
:
365 int nIndex
= m_cFileList
.GetSelectionMark();
366 if (m_arFileStates
.GetAt(nIndex
) == FPDLG_FILESTATE_ERROR
)
368 MessageBox(m_pPatch
->GetPatchRejects(nIndex
), NULL
, MB_ICONERROR
);
370 else if ( m_arFileStates
.GetAt(nIndex
)!=FPDLG_FILESTATE_PATCHED
)
372 m_pCallBack
->PatchFile(m_pPatch
->GetStrippedPath(nIndex
), m_pPatch
->GetContentMods(nIndex
), m_pPatch
->GetPropMods(nIndex
), _T(""));
373 m_ShownIndex
= nIndex
;
374 m_cFileList
.Invalidate();
381 case ID_PATCHSELECTED
:
389 void CFilePatchesDlg::OnNcLButtonDblClk(UINT nHitTest
, CPoint point
)
395 GetWindowRect(&windowrect
);
396 GetClientRect(&clientrect
);
397 m_nWindowHeight
= windowrect
.bottom
- windowrect
.top
;
398 MoveWindow(windowrect
.left
, windowrect
.top
,
399 windowrect
.right
- windowrect
.left
,
400 m_nWindowHeight
- (clientrect
.bottom
- clientrect
.top
));
405 GetWindowRect(&windowrect
);
406 MoveWindow(windowrect
.left
, windowrect
.top
, windowrect
.right
- windowrect
.left
, m_nWindowHeight
);
408 m_bMinimized
= !m_bMinimized
;
409 CResizableStandAloneDialog::OnNcLButtonDblClk(nHitTest
, point
);
412 void CFilePatchesDlg::OnMoving(UINT fwSide
, LPRECT pRect
)
415 m_pMainFrame
->GetWindowRect(&parentRect
);
416 const int stickySize
= 5;
417 if (abs(parentRect
.left
- pRect
->right
) < stickySize
)
419 int width
= pRect
->right
- pRect
->left
;
420 pRect
->right
= parentRect
.left
;
421 pRect
->left
= pRect
->right
- width
;
423 CResizableStandAloneDialog::OnMoving(fwSide
, pRect
);
426 void CFilePatchesDlg::OnOK()
431 void CFilePatchesDlg::SetTitleWithPath(int width
)
434 title
.LoadString(IDS_PATCH_TITLE
);
435 title
+= _T(" ") + m_sPath
;
436 title
= title
.Left(MAX_PATH
-1);
440 PathCompactPath(pDC
->GetSafeHdc(), title
.GetBuffer(), width
);
442 title
.ReleaseBuffer();
444 SetWindowText(title
);
447 void CFilePatchesDlg::OnBnClickedPatchselectedbutton()
452 void CFilePatchesDlg::OnBnClickedPatchallbutton()
457 void CFilePatchesDlg::PatchAll()
461 CSysProgressDlg progDlg
;
462 progDlg
.SetTitle(IDR_MAINFRAME
);
463 progDlg
.SetShowProgressBar(true);
464 progDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_ALL
)));
465 progDlg
.ShowModeless(m_hWnd
);
466 for (int i
=0; i
<m_arFileStates
.GetCount() && !progDlg
.HasUserCancelled(); i
++)
468 if (m_arFileStates
.GetAt(i
) == FPDLG_FILESTATE_ERROR
)
469 MessageBox(m_pPatch
->GetPatchRejects(i
), NULL
, MB_ICONERROR
);
470 else if (m_arFileStates
.GetAt(i
) != FPDLG_FILESTATE_PATCHED
)
472 progDlg
.SetLine(2, GetFullPath(i
), true);
473 m_pCallBack
->PatchFile(m_pPatch
->GetStrippedPath(i
), m_pPatch
->GetContentMods(i
), m_pPatch
->GetPropMods(i
), _T(""), TRUE
);
475 m_cFileList
.Invalidate();
477 progDlg
.SetProgress64(i
, m_arFileStates
.GetCount());
483 void CFilePatchesDlg::PatchSelected()
487 CSysProgressDlg progDlg
;
488 progDlg
.SetTitle(IDR_MAINFRAME
);
489 progDlg
.SetShowProgressBar(true);
490 progDlg
.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_SELECTED
)));
491 progDlg
.ShowModeless(m_hWnd
);
492 // The list cannot be sorted by user, so the order of the
493 // items in the list is identical to the order in the array
495 int selCount
= m_cFileList
.GetSelectedCount();
497 POSITION pos
= m_cFileList
.GetFirstSelectedItemPosition();
499 while (((index
= m_cFileList
.GetNextSelectedItem(pos
)) >= 0) && (!progDlg
.HasUserCancelled()))
501 if (m_arFileStates
.GetAt(index
) == FPDLG_FILESTATE_ERROR
)
502 MessageBox(m_pPatch
->GetPatchRejects(index
), NULL
, MB_ICONERROR
);
503 else if (m_arFileStates
.GetAt(index
) != FPDLG_FILESTATE_PATCHED
)
505 progDlg
.SetLine(2, GetFullPath(index
), true);
506 m_pCallBack
->PatchFile(m_pPatch
->GetStrippedPath(index
), m_pPatch
->GetContentMods(index
), m_pPatch
->GetPropMods(index
), _T(""), TRUE
);
507 m_ShownIndex
= index
;
508 m_cFileList
.Invalidate();
510 progDlg
.SetProgress64(count
++, selCount
);
516 void CFilePatchesDlg::OnLvnItemchangedFilelist(NMHDR
* /*pNMHDR*/, LRESULT
*pResult
)
518 DialogEnableWindow(IDC_PATCHSELECTEDBUTTON
, m_cFileList
.GetSelectedCount() > 0);
523 void CFilePatchesDlg::SetStateText(int i
, int state
)
528 case FPDLG_FILESTATE_PATCHED
:
529 sState
.LoadString(IDS_STATE_PATCHED
);
531 case FPDLG_FILESTATE_ERROR
:
532 sState
.LoadString(IDS_STATE_ERROR
);
535 // all is ok, not yet patched but no failed hunks
538 // there are failed hunks in the patch
539 sState
.LoadString(IDS_STATE_CONFLICTS
);
542 m_cFileList
.SetItemText(i
, 1, sState
);