Doc: Fix broken link
[TortoiseGit.git] / src / TortoiseMerge / FilePatchesDlg.cpp
blob879ea40ee33d218ab63603d9ba8b971eb71aa00c
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006, 2008, 2010-2012, 2015 - TortoiseSVN
4 // Copyright (C) 2012, 2016-2017 - 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.
20 #include "stdafx.h"
21 #include "TortoiseMerge.h"
22 #include "FilePatchesDlg.h"
23 #include "GitPatch.h"
24 #include "AppUtils.h"
25 #include "PathUtils.h"
26 #include "SysProgressDlg.h"
27 #include "SysImageList.h"
29 IMPLEMENT_DYNAMIC(CFilePatchesDlg, CResizableStandAloneDialog)
30 CFilePatchesDlg::CFilePatchesDlg(CWnd* pParent /*=nullptr*/)
31 : CResizableStandAloneDialog(CFilePatchesDlg::IDD, pParent)
32 , m_ShownIndex(-1)
33 , m_bMinimized(FALSE)
34 , m_pPatch(nullptr)
35 , m_pCallBack(nullptr)
36 , m_nWindowHeight(-1)
37 , m_pMainFrame(nullptr)
38 , m_boldFont(nullptr)
42 CFilePatchesDlg::~CFilePatchesDlg()
44 if (m_boldFont)
45 DeleteObject(m_boldFont);
48 void CFilePatchesDlg::DoDataExchange(CDataExchange* pDX)
50 CResizableStandAloneDialog::DoDataExchange(pDX);
51 DDX_Control(pDX, IDC_FILELIST, m_cFileList);
54 BOOL CFilePatchesDlg::SetFileStatusAsPatched(CString sPath)
56 for (int i=0; i<m_arFileStates.GetCount(); i++)
58 if (sPath.CompareNoCase(GetFullPath(i))==0)
60 m_arFileStates.SetAt(i, (DWORD)FPDLG_FILESTATE_PATCHED);
61 SetStateText(i, FPDLG_FILESTATE_PATCHED);
62 Invalidate();
63 return TRUE;
66 return FALSE;
69 CString CFilePatchesDlg::GetFullPath(int nIndex)
71 CString temp = m_pPatch->GetStrippedPath(nIndex);
72 temp.Replace('/', '\\');
73 //temp = temp.Mid(temp.Find('\\')+1);
74 if (PathIsRelative(temp))
75 temp = m_sPath + temp;
76 return temp;
79 BOOL CFilePatchesDlg::OnInitDialog()
81 CResizableStandAloneDialog::OnInitDialog();
83 // hide the grip since it would overlap with the "path all" button
84 #if 0
85 HideGrip();
86 #endif
88 HFONT hFont = (HFONT)m_cFileList.SendMessage(WM_GETFONT);
89 LOGFONT lf = {0};
90 GetObject(hFont, sizeof(LOGFONT), &lf);
91 lf.lfWeight = FW_BOLD;
92 m_boldFont = CreateFontIndirect(&lf);
94 AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);
95 AddAnchor(IDC_PATCHSELECTEDBUTTON, BOTTOM_LEFT, BOTTOM_RIGHT);
96 AddAnchor(IDC_PATCHALLBUTTON, BOTTOM_LEFT, BOTTOM_RIGHT);
98 return TRUE;
101 BOOL CFilePatchesDlg::Init(GitPatch * pPatch, CPatchFilesDlgCallBack * pCallBack, CString sPath, CWnd * pParent)
103 if (!pCallBack || !pPatch)
105 m_cFileList.DeleteAllItems();
106 return FALSE;
108 m_arFileStates.RemoveAll();
109 m_pPatch = pPatch;
110 m_pCallBack = pCallBack;
111 m_sPath = sPath;
112 if (m_sPath.IsEmpty())
114 CString title(MAKEINTRESOURCE(IDS_DIFF_TITLE));
115 SetWindowText(title);
117 else
119 CRect rect;
120 GetClientRect(&rect);
121 SetTitleWithPath(rect.Width());
122 m_sPath.TrimRight(L'\\');
123 m_sPath += L'\\';
126 SetWindowTheme(m_cFileList.GetSafeHwnd(), L"Explorer", nullptr);
127 m_cFileList.SetExtendedStyle(LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
128 m_cFileList.DeleteAllItems();
129 int c = m_cFileList.GetHeaderCtrl()->GetItemCount() - 1;
130 while (c>=0)
131 m_cFileList.DeleteColumn(c--);
132 m_cFileList.InsertColumn(0, CString(MAKEINTRESOURCE(IDS_PATH)));
133 m_cFileList.InsertColumn(1, CString(MAKEINTRESOURCE(IDS_STATE)));
135 m_cFileList.SetRedraw(false);
137 for(int i=0; i<m_pPatch->GetNumberOfFiles(); i++)
139 CString sFile = CPathUtils::GetFileNameFromPath(m_pPatch->GetStrippedPath(i));
141 int state;
142 if (m_sPath.IsEmpty())
143 state = 0;
144 else
146 state = m_pPatch->GetFailedHunks(i);
148 if (m_pPatch->GetHasConflict(i))
149 state = FPDLG_FILESTATE_CONFLICT;
150 if (state > 0)
151 state = FPDLG_FILESTATE_ERROR;
152 m_arFileStates.Add(state);
153 CString sFileName = GetFullPath(i);
154 sFileName = CPathUtils::GetFileNameFromPath(sFileName);
155 m_cFileList.InsertItem(i, sFile, SYS_IMAGE_LIST().GetFileIconIndex(sFileName));
156 SetStateText(i, state);
158 int mincol = 0;
159 int maxcol = m_cFileList.GetHeaderCtrl()->GetItemCount() - 1;
160 int col;
161 for (col = mincol; col <= maxcol; col++)
163 m_cFileList.SetColumnWidth(col,LVSCW_AUTOSIZE_USEHEADER);
166 m_cFileList.SetImageList(&SYS_IMAGE_LIST(), LVSIL_SMALL);
167 m_cFileList.SetRedraw(true);
169 RECT parentrect;
170 pParent->GetWindowRect(&parentrect);
171 RECT windowrect;
172 GetWindowRect(&windowrect);
174 int width = windowrect.right - windowrect.left;
175 int height = windowrect.bottom - windowrect.top;
176 windowrect.right = parentrect.left;
177 windowrect.left = windowrect.right - width;
178 windowrect.top = parentrect.top;
179 windowrect.bottom = windowrect.top + height;
180 auto hMonitor = MonitorFromRect(&windowrect, MONITOR_DEFAULTTONULL);
181 if (hMonitor)
182 SetWindowPos(nullptr, windowrect.left, windowrect.top, width, height, SWP_NOACTIVATE | SWP_NOZORDER);
184 m_nWindowHeight = windowrect.bottom - windowrect.top;
185 m_pMainFrame = pParent;
186 return TRUE;
189 BEGIN_MESSAGE_MAP(CFilePatchesDlg, CResizableStandAloneDialog)
190 ON_WM_SIZE()
191 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
192 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
193 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
194 ON_NOTIFY(NM_RCLICK, IDC_FILELIST, OnNMRclickFilelist)
195 ON_WM_NCLBUTTONDBLCLK()
196 ON_WM_MOVING()
197 ON_BN_CLICKED(IDC_PATCHSELECTEDBUTTON, &CFilePatchesDlg::OnBnClickedPatchselectedbutton)
198 ON_BN_CLICKED(IDC_PATCHALLBUTTON, &CFilePatchesDlg::OnBnClickedPatchallbutton)
199 ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILELIST, &CFilePatchesDlg::OnLvnItemchangedFilelist)
200 END_MESSAGE_MAP()
202 void CFilePatchesDlg::OnSize(UINT nType, int cx, int cy)
204 CResizableStandAloneDialog::OnSize(nType, cx, cy);
205 if (this->IsWindowVisible())
207 m_cFileList.SetColumnWidth(0, LVSCW_AUTOSIZE);
209 SetTitleWithPath(cx);
212 void CFilePatchesDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
214 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
216 if (m_arFileStates.GetCount() > pGetInfoTip->iItem)
218 CString temp;
219 if (m_arFileStates.GetAt(pGetInfoTip->iItem) == 0)
220 temp = GetFullPath(pGetInfoTip->iItem);
221 else
222 temp.Format(IDS_PATCH_ITEMTT, (LPCTSTR)GetFullPath(pGetInfoTip->iItem));
223 wcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, temp, pGetInfoTip->cchTextMax - 1);
225 else
226 pGetInfoTip->pszText[0] = 0;
228 *pResult = 0;
231 void CFilePatchesDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
233 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
234 *pResult = 0;
235 if ((pNMLV->iItem < 0) || (pNMLV->iItem >= m_arFileStates.GetCount()))
236 return;
237 if (!m_pCallBack)
238 return;
240 if (m_arFileStates.GetAt(pNMLV->iItem) == FPDLG_FILESTATE_ERROR)
242 MessageBox(m_pPatch->GetPatchRejects(pNMLV->iItem), nullptr, MB_ICONERROR);
243 return;
246 if (m_sPath.IsEmpty())
248 m_pCallBack->DiffFiles(GetFullPath(pNMLV->iItem), L"", L"", L"");
249 m_ShownIndex = pNMLV->iItem;
250 m_cFileList.Invalidate();
252 else
254 if (m_arFileStates.GetAt(pNMLV->iItem)!=FPDLG_FILESTATE_PATCHED)
256 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(pNMLV->iItem), m_pPatch->GetContentMods(pNMLV->iItem), m_pPatch->GetPropMods(pNMLV->iItem), L"");
257 m_ShownIndex = pNMLV->iItem;
258 m_cFileList.Invalidate();
263 void CFilePatchesDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
265 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
267 // Take the default processing unless we set this to something else below.
268 *pResult = CDRF_DODEFAULT;
270 // First thing - check the draw stage. If it's the control's prepaint
271 // stage, then tell Windows we want messages for every item.
273 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
275 *pResult = CDRF_NOTIFYITEMDRAW;
277 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
279 // This is the prepaint stage for an item. Here's where we set the
280 // item's text color. Our return value will tell Windows to draw the
281 // item itself, but it will use the new color we set here.
283 COLORREF crText = ::GetSysColor(COLOR_WINDOWTEXT);
285 if (m_arFileStates.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
287 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec) == FPDLG_FILESTATE_CONFLICT)
289 crText = RGB(255, 200, 100); // orange
291 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)==FPDLG_FILESTATE_ERROR)
293 crText = RGB(200, 0, 0);
295 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)>0)
297 crText = RGB(100, 0, 0);
299 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)==FPDLG_FILESTATE_PATCHED)
301 crText = ::GetSysColor(COLOR_GRAYTEXT);
303 // Store the color back in the NMLVCUSTOMDRAW struct.
304 pLVCD->clrText = crText;
305 if (m_ShownIndex == (int)pLVCD->nmcd.dwItemSpec)
307 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
308 // We changed the font, so we're returning CDRF_NEWFONT. This
309 // tells the control to recalculate the extent of the text.
310 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
316 void CFilePatchesDlg::OnNMRclickFilelist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
318 *pResult = 0;
319 if (m_sPath.IsEmpty())
320 return;
321 CString temp;
322 CMenu popup;
323 POINT point;
324 DWORD ptW = GetMessagePos();
325 point.x = GET_X_LPARAM(ptW);
326 point.y = GET_Y_LPARAM(ptW);
327 if (!popup.CreatePopupMenu())
328 return;
330 UINT nFlags = MF_STRING | (m_cFileList.GetSelectedCount()==1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
331 temp.LoadString(IDS_PATCH_PREVIEW);
332 popup.AppendMenu(nFlags, ID_PATCHPREVIEW, temp);
333 popup.SetDefaultItem(ID_PATCHPREVIEW, FALSE);
335 temp.LoadString(IDS_PATCH_ALL);
336 popup.AppendMenu(MF_STRING | MF_ENABLED, ID_PATCHALL, temp);
338 nFlags = MF_STRING | (m_cFileList.GetSelectedCount() > 0 ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
339 temp.LoadString(IDS_PATCH_SELECTED);
340 popup.AppendMenu(nFlags, ID_PATCHSELECTED, temp);
342 // if the context menu is invoked through the keyboard, we have to use
343 // a calculated position on where to anchor the menu on
344 if ((point.x == -1) && (point.y == -1))
346 CRect rect;
347 GetWindowRect(&rect);
348 point = rect.CenterPoint();
351 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
352 switch (cmd)
354 case ID_PATCHPREVIEW:
355 if (m_pCallBack)
357 int nIndex = m_cFileList.GetSelectionMark();
358 if (m_arFileStates.GetAt(nIndex) == FPDLG_FILESTATE_ERROR)
360 MessageBox(m_pPatch->GetPatchRejects(nIndex), nullptr, MB_ICONERROR);
362 else if ( m_arFileStates.GetAt(nIndex)!=FPDLG_FILESTATE_PATCHED)
364 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(nIndex), m_pPatch->GetContentMods(nIndex), m_pPatch->GetPropMods(nIndex), L"");
365 m_ShownIndex = nIndex;
366 m_cFileList.Invalidate();
369 break;
370 case ID_PATCHALL:
371 PatchAll();
372 break;
373 case ID_PATCHSELECTED:
374 PatchSelected();
375 break;
376 default:
377 break;
381 void CFilePatchesDlg::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
383 if (!m_bMinimized)
385 RECT windowrect;
386 RECT clientrect;
387 GetWindowRect(&windowrect);
388 GetClientRect(&clientrect);
389 m_nWindowHeight = windowrect.bottom - windowrect.top;
390 MoveWindow(windowrect.left, windowrect.top,
391 windowrect.right - windowrect.left,
392 m_nWindowHeight - (clientrect.bottom - clientrect.top));
394 else
396 RECT windowrect;
397 GetWindowRect(&windowrect);
398 MoveWindow(windowrect.left, windowrect.top, windowrect.right - windowrect.left, m_nWindowHeight);
400 m_bMinimized = !m_bMinimized;
401 CResizableStandAloneDialog::OnNcLButtonDblClk(nHitTest, point);
404 void CFilePatchesDlg::OnMoving(UINT fwSide, LPRECT pRect)
406 RECT parentRect;
407 m_pMainFrame->GetWindowRect(&parentRect);
408 const int stickySize = 5;
409 if (abs(parentRect.left - pRect->right) < stickySize)
411 int width = pRect->right - pRect->left;
412 pRect->right = parentRect.left;
413 pRect->left = pRect->right - width;
415 CResizableStandAloneDialog::OnMoving(fwSide, pRect);
418 void CFilePatchesDlg::OnOK()
420 return;
423 void CFilePatchesDlg::SetTitleWithPath(int width)
425 CString title;
426 title.LoadString(IDS_PATCH_TITLE);
427 title += L" " + m_sPath;
428 title = title.Left(MAX_PATH-1);
429 CDC * pDC = GetDC();
430 if (pDC)
432 PathCompactPath(pDC->GetSafeHdc(), title.GetBuffer(), width);
433 ReleaseDC(pDC);
434 title.ReleaseBuffer();
436 SetWindowText(title);
439 void CFilePatchesDlg::OnBnClickedPatchselectedbutton()
441 PatchSelected();
444 void CFilePatchesDlg::OnBnClickedPatchallbutton()
446 PatchAll();
449 void CFilePatchesDlg::PatchAll()
451 if (m_pCallBack)
453 CSysProgressDlg progDlg;
454 progDlg.SetTitle(IDR_MAINFRAME);
455 progDlg.SetShowProgressBar(true);
456 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_ALL)));
457 progDlg.ShowModeless(m_hWnd);
458 for (int i=0; i<m_arFileStates.GetCount() && !progDlg.HasUserCancelled(); i++)
460 if (m_arFileStates.GetAt(i) == FPDLG_FILESTATE_ERROR)
461 MessageBox(m_pPatch->GetPatchRejects(i), nullptr, MB_ICONERROR);
462 else if (m_arFileStates.GetAt(i) != FPDLG_FILESTATE_PATCHED)
464 progDlg.SetLine(2, GetFullPath(i), true);
465 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(i), m_pPatch->GetContentMods(i), m_pPatch->GetPropMods(i), L"", TRUE);
466 m_ShownIndex = i;
467 m_cFileList.Invalidate();
469 progDlg.SetProgress64(i, m_arFileStates.GetCount());
471 progDlg.Stop();
475 void CFilePatchesDlg::PatchSelected()
477 if (m_pCallBack)
479 CSysProgressDlg progDlg;
480 progDlg.SetTitle(IDR_MAINFRAME);
481 progDlg.SetShowProgressBar(true);
482 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_SELECTED)));
483 progDlg.ShowModeless(m_hWnd);
484 // The list cannot be sorted by user, so the order of the
485 // items in the list is identical to the order in the array
486 // m_arFileStates.
487 int selCount = m_cFileList.GetSelectedCount();
488 int count = 1;
489 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
490 int index;
491 while (((index = m_cFileList.GetNextSelectedItem(pos)) >= 0) && (!progDlg.HasUserCancelled()))
493 if (m_arFileStates.GetAt(index) == FPDLG_FILESTATE_ERROR)
494 MessageBox(m_pPatch->GetPatchRejects(index), nullptr, MB_ICONERROR);
495 else if (m_arFileStates.GetAt(index) != FPDLG_FILESTATE_PATCHED)
497 progDlg.SetLine(2, GetFullPath(index), true);
498 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(index), m_pPatch->GetContentMods(index), m_pPatch->GetPropMods(index), L"", TRUE);
499 m_ShownIndex = index;
500 m_cFileList.Invalidate();
502 progDlg.SetProgress64(count++, selCount);
504 progDlg.Stop();
508 void CFilePatchesDlg::OnLvnItemchangedFilelist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
510 DialogEnableWindow(IDC_PATCHSELECTEDBUTTON, m_cFileList.GetSelectedCount() > 0);
512 *pResult = 0;
515 void CFilePatchesDlg::SetStateText(int i, int state)
517 CString sState;
518 switch (state)
520 case FPDLG_FILESTATE_PATCHED:
521 sState.LoadString(IDS_STATE_PATCHED);
522 break;
523 case FPDLG_FILESTATE_ERROR:
524 sState.LoadString(IDS_STATE_ERROR);
525 break;
526 case 0:
527 // all is ok, not yet patched but no failed hunks
528 break;
529 default:
530 // there are failed hunks in the patch
531 sState.LoadString(IDS_STATE_CONFLICTS);
532 break;
534 m_cFileList.SetItemText(i, 1, sState);