Update editorconfig
[TortoiseGit.git] / src / TortoiseMerge / FilePatchesDlg.cpp
blob3e2818cc9dfb77bc6fa6fabccb3cd2bc4b2846b4
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2006, 2008, 2010-2012, 2015 - TortoiseSVN
4 // Copyright (C) 2012, 2016 - 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 "MessageBox.h"
28 #include "SysImageList.h"
30 IMPLEMENT_DYNAMIC(CFilePatchesDlg, CResizableStandAloneDialog)
31 CFilePatchesDlg::CFilePatchesDlg(CWnd* pParent /*=NULL*/)
32 : CResizableStandAloneDialog(CFilePatchesDlg::IDD, pParent)
33 , m_ShownIndex(-1)
34 , m_bMinimized(FALSE)
35 , m_pPatch(NULL)
36 , m_pCallBack(NULL)
37 , m_nWindowHeight(-1)
38 , m_pMainFrame(NULL)
39 , m_boldFont(NULL)
43 CFilePatchesDlg::~CFilePatchesDlg()
45 if (m_boldFont)
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);
63 Invalidate();
64 return TRUE;
67 return FALSE;
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;
77 return temp;
80 BOOL CFilePatchesDlg::OnInitDialog()
82 CResizableStandAloneDialog::OnInitDialog();
84 // hide the grip since it would overlap with the "path all" button
85 #if 0
86 HideGrip();
87 #endif
89 HFONT hFont = (HFONT)m_cFileList.SendMessage(WM_GETFONT);
90 LOGFONT lf = {0};
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);
99 return TRUE;
102 BOOL CFilePatchesDlg::Init(GitPatch * pPatch, CPatchFilesDlgCallBack * pCallBack, CString sPath, CWnd * pParent)
104 if ((pCallBack==NULL)||(pPatch==NULL))
106 m_cFileList.DeleteAllItems();
107 return FALSE;
109 m_arFileStates.RemoveAll();
110 m_pPatch = pPatch;
111 m_pCallBack = pCallBack;
112 m_sPath = sPath;
113 if (m_sPath.IsEmpty())
115 CString title(MAKEINTRESOURCE(IDS_DIFF_TITLE));
116 SetWindowText(title);
118 else
120 CRect rect;
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;
133 while (c>=0)
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));
144 int state;
145 if (m_sPath.IsEmpty())
146 state = 0;
147 else
149 state = m_pPatch->GetFailedHunks(i);
151 if (m_pPatch->GetHasConflict(i))
152 state = FPDLG_FILESTATE_CONFLICT;
153 if (state > 0)
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);
161 int mincol = 0;
162 int maxcol = ((CHeaderCtrl*)(m_cFileList.GetDlgItem(0)))->GetItemCount()-1;
163 int col;
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);
172 RECT parentrect;
173 pParent->GetWindowRect(&parentrect);
174 RECT windowrect;
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 windowrect.top = parentrect.top;
182 windowrect.bottom = windowrect.top + height;
183 auto hMonitor = MonitorFromRect(&windowrect, MONITOR_DEFAULTTONULL);
184 if (hMonitor)
185 SetWindowPos(nullptr, windowrect.left, windowrect.top, width, height, SWP_NOACTIVATE | SWP_NOZORDER);
187 m_nWindowHeight = windowrect.bottom - windowrect.top;
188 m_pMainFrame = pParent;
189 return TRUE;
192 BEGIN_MESSAGE_MAP(CFilePatchesDlg, CResizableStandAloneDialog)
193 ON_WM_SIZE()
194 ON_NOTIFY(LVN_GETINFOTIP, IDC_FILELIST, OnLvnGetInfoTipFilelist)
195 ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnNMDblclkFilelist)
196 ON_NOTIFY(NM_CUSTOMDRAW, IDC_FILELIST, OnNMCustomdrawFilelist)
197 ON_NOTIFY(NM_RCLICK, IDC_FILELIST, OnNMRclickFilelist)
198 ON_WM_NCLBUTTONDBLCLK()
199 ON_WM_MOVING()
200 ON_BN_CLICKED(IDC_PATCHSELECTEDBUTTON, &CFilePatchesDlg::OnBnClickedPatchselectedbutton)
201 ON_BN_CLICKED(IDC_PATCHALLBUTTON, &CFilePatchesDlg::OnBnClickedPatchallbutton)
202 ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILELIST, &CFilePatchesDlg::OnLvnItemchangedFilelist)
203 END_MESSAGE_MAP()
205 void CFilePatchesDlg::OnSize(UINT nType, int cx, int cy)
207 CResizableStandAloneDialog::OnSize(nType, cx, cy);
208 if (this->IsWindowVisible())
210 m_cFileList.SetColumnWidth(0, LVSCW_AUTOSIZE);
212 SetTitleWithPath(cx);
215 void CFilePatchesDlg::OnLvnGetInfoTipFilelist(NMHDR *pNMHDR, LRESULT *pResult)
217 LPNMLVGETINFOTIP pGetInfoTip = reinterpret_cast<LPNMLVGETINFOTIP>(pNMHDR);
219 if (m_arFileStates.GetCount() > pGetInfoTip->iItem)
221 CString temp;
222 if (m_arFileStates.GetAt(pGetInfoTip->iItem) == 0)
223 temp = GetFullPath(pGetInfoTip->iItem);
224 else
225 temp.Format(IDS_PATCH_ITEMTT, (LPCTSTR)GetFullPath(pGetInfoTip->iItem), m_arFileStates.GetAt(pGetInfoTip->iItem));
226 _tcsncpy_s(pGetInfoTip->pszText, pGetInfoTip->cchTextMax, temp, pGetInfoTip->cchTextMax - 1);
228 else
229 pGetInfoTip->pszText[0] = 0;
231 *pResult = 0;
234 void CFilePatchesDlg::OnNMDblclkFilelist(NMHDR *pNMHDR, LRESULT *pResult)
236 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
237 *pResult = 0;
238 if ((pNMLV->iItem < 0) || (pNMLV->iItem >= m_arFileStates.GetCount()))
239 return;
240 if (m_pCallBack==NULL)
241 return;
243 if (m_arFileStates.GetAt(pNMLV->iItem) == FPDLG_FILESTATE_ERROR)
245 MessageBox(m_pPatch->GetPatchRejects(pNMLV->iItem), NULL, MB_ICONERROR);
246 return;
249 if (m_sPath.IsEmpty())
251 m_pCallBack->DiffFiles(GetFullPath(pNMLV->iItem), _T(""),
252 _T(""), _T(""));
253 m_ShownIndex = pNMLV->iItem;
254 m_cFileList.Invalidate();
256 else
258 if (m_arFileStates.GetAt(pNMLV->iItem)!=FPDLG_FILESTATE_PATCHED)
260 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(pNMLV->iItem), m_pPatch->GetContentMods(pNMLV->iItem), m_pPatch->GetPropMods(pNMLV->iItem), _T(""));
261 m_ShownIndex = pNMLV->iItem;
262 m_cFileList.Invalidate();
267 void CFilePatchesDlg::OnNMCustomdrawFilelist(NMHDR *pNMHDR, LRESULT *pResult)
269 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
271 // Take the default processing unless we set this to something else below.
272 *pResult = CDRF_DODEFAULT;
274 // First thing - check the draw stage. If it's the control's prepaint
275 // stage, then tell Windows we want messages for every item.
277 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
279 *pResult = CDRF_NOTIFYITEMDRAW;
281 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
283 // This is the prepaint stage for an item. Here's where we set the
284 // item's text color. Our return value will tell Windows to draw the
285 // item itself, but it will use the new color we set here.
287 COLORREF crText = ::GetSysColor(COLOR_WINDOWTEXT);
289 if (m_arFileStates.GetCount() > (INT_PTR)pLVCD->nmcd.dwItemSpec)
291 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec) == FPDLG_FILESTATE_CONFLICT)
293 crText = RGB(255, 200, 100); // orange
295 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)==FPDLG_FILESTATE_ERROR)
297 crText = RGB(200, 0, 0);
299 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)>0)
301 crText = RGB(100, 0, 0);
303 if (m_arFileStates.GetAt(pLVCD->nmcd.dwItemSpec)==FPDLG_FILESTATE_PATCHED)
305 crText = ::GetSysColor(COLOR_GRAYTEXT);
307 // Store the color back in the NMLVCUSTOMDRAW struct.
308 pLVCD->clrText = crText;
309 if (m_ShownIndex == (int)pLVCD->nmcd.dwItemSpec)
311 SelectObject(pLVCD->nmcd.hdc, m_boldFont);
312 // We changed the font, so we're returning CDRF_NEWFONT. This
313 // tells the control to recalculate the extent of the text.
314 *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NEWFONT;
320 void CFilePatchesDlg::OnNMRclickFilelist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
322 *pResult = 0;
323 if (m_sPath.IsEmpty())
324 return;
325 CString temp;
326 CMenu popup;
327 POINT point;
328 DWORD ptW = GetMessagePos();
329 point.x = GET_X_LPARAM(ptW);
330 point.y = GET_Y_LPARAM(ptW);
331 if (!popup.CreatePopupMenu())
332 return;
334 UINT nFlags = MF_STRING | (m_cFileList.GetSelectedCount()==1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
335 temp.LoadString(IDS_PATCH_PREVIEW);
336 popup.AppendMenu(nFlags, ID_PATCHPREVIEW, temp);
337 popup.SetDefaultItem(ID_PATCHPREVIEW, FALSE);
339 temp.LoadString(IDS_PATCH_ALL);
340 popup.AppendMenu(MF_STRING | MF_ENABLED, ID_PATCHALL, temp);
342 nFlags = MF_STRING | (m_cFileList.GetSelectedCount() > 0 ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
343 temp.LoadString(IDS_PATCH_SELECTED);
344 popup.AppendMenu(nFlags, ID_PATCHSELECTED, temp);
346 // if the context menu is invoked through the keyboard, we have to use
347 // a calculated position on where to anchor the menu on
348 if ((point.x == -1) && (point.y == -1))
350 CRect rect;
351 GetWindowRect(&rect);
352 point = rect.CenterPoint();
355 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this);
356 switch (cmd)
358 case ID_PATCHPREVIEW:
359 if (m_pCallBack)
361 int nIndex = m_cFileList.GetSelectionMark();
362 if (m_arFileStates.GetAt(nIndex) == FPDLG_FILESTATE_ERROR)
364 MessageBox(m_pPatch->GetPatchRejects(nIndex), NULL, MB_ICONERROR);
366 else if ( m_arFileStates.GetAt(nIndex)!=FPDLG_FILESTATE_PATCHED)
368 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(nIndex), m_pPatch->GetContentMods(nIndex), m_pPatch->GetPropMods(nIndex), _T(""));
369 m_ShownIndex = nIndex;
370 m_cFileList.Invalidate();
373 break;
374 case ID_PATCHALL:
375 PatchAll();
376 break;
377 case ID_PATCHSELECTED:
378 PatchSelected();
379 break;
380 default:
381 break;
385 void CFilePatchesDlg::OnNcLButtonDblClk(UINT nHitTest, CPoint point)
387 if (!m_bMinimized)
389 RECT windowrect;
390 RECT clientrect;
391 GetWindowRect(&windowrect);
392 GetClientRect(&clientrect);
393 m_nWindowHeight = windowrect.bottom - windowrect.top;
394 MoveWindow(windowrect.left, windowrect.top,
395 windowrect.right - windowrect.left,
396 m_nWindowHeight - (clientrect.bottom - clientrect.top));
398 else
400 RECT windowrect;
401 GetWindowRect(&windowrect);
402 MoveWindow(windowrect.left, windowrect.top, windowrect.right - windowrect.left, m_nWindowHeight);
404 m_bMinimized = !m_bMinimized;
405 CResizableStandAloneDialog::OnNcLButtonDblClk(nHitTest, point);
408 void CFilePatchesDlg::OnMoving(UINT fwSide, LPRECT pRect)
410 RECT parentRect;
411 m_pMainFrame->GetWindowRect(&parentRect);
412 const int stickySize = 5;
413 if (abs(parentRect.left - pRect->right) < stickySize)
415 int width = pRect->right - pRect->left;
416 pRect->right = parentRect.left;
417 pRect->left = pRect->right - width;
419 CResizableStandAloneDialog::OnMoving(fwSide, pRect);
422 void CFilePatchesDlg::OnOK()
424 return;
427 void CFilePatchesDlg::SetTitleWithPath(int width)
429 CString title;
430 title.LoadString(IDS_PATCH_TITLE);
431 title += _T(" ") + m_sPath;
432 title = title.Left(MAX_PATH-1);
433 CDC * pDC = GetDC();
434 if (pDC)
436 PathCompactPath(pDC->GetSafeHdc(), title.GetBuffer(), width);
437 ReleaseDC(pDC);
438 title.ReleaseBuffer();
440 SetWindowText(title);
443 void CFilePatchesDlg::OnBnClickedPatchselectedbutton()
445 PatchSelected();
448 void CFilePatchesDlg::OnBnClickedPatchallbutton()
450 PatchAll();
453 void CFilePatchesDlg::PatchAll()
455 if (m_pCallBack)
457 CSysProgressDlg progDlg;
458 progDlg.SetTitle(IDR_MAINFRAME);
459 progDlg.SetShowProgressBar(true);
460 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_ALL)));
461 progDlg.ShowModeless(m_hWnd);
462 for (int i=0; i<m_arFileStates.GetCount() && !progDlg.HasUserCancelled(); i++)
464 if (m_arFileStates.GetAt(i) == FPDLG_FILESTATE_ERROR)
465 MessageBox(m_pPatch->GetPatchRejects(i), NULL, MB_ICONERROR);
466 else if (m_arFileStates.GetAt(i) != FPDLG_FILESTATE_PATCHED)
468 progDlg.SetLine(2, GetFullPath(i), true);
469 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(i), m_pPatch->GetContentMods(i), m_pPatch->GetPropMods(i), _T(""), TRUE);
470 m_ShownIndex = i;
471 m_cFileList.Invalidate();
473 progDlg.SetProgress64(i, m_arFileStates.GetCount());
475 progDlg.Stop();
479 void CFilePatchesDlg::PatchSelected()
481 if (m_pCallBack)
483 CSysProgressDlg progDlg;
484 progDlg.SetTitle(IDR_MAINFRAME);
485 progDlg.SetShowProgressBar(true);
486 progDlg.SetLine(1, CString(MAKEINTRESOURCE(IDS_PATCH_SELECTED)));
487 progDlg.ShowModeless(m_hWnd);
488 // The list cannot be sorted by user, so the order of the
489 // items in the list is identical to the order in the array
490 // m_arFileStates.
491 int selCount = m_cFileList.GetSelectedCount();
492 int count = 1;
493 POSITION pos = m_cFileList.GetFirstSelectedItemPosition();
494 int index;
495 while (((index = m_cFileList.GetNextSelectedItem(pos)) >= 0) && (!progDlg.HasUserCancelled()))
497 if (m_arFileStates.GetAt(index) == FPDLG_FILESTATE_ERROR)
498 MessageBox(m_pPatch->GetPatchRejects(index), NULL, MB_ICONERROR);
499 else if (m_arFileStates.GetAt(index) != FPDLG_FILESTATE_PATCHED)
501 progDlg.SetLine(2, GetFullPath(index), true);
502 m_pCallBack->PatchFile(m_pPatch->GetStrippedPath(index), m_pPatch->GetContentMods(index), m_pPatch->GetPropMods(index), _T(""), TRUE);
503 m_ShownIndex = index;
504 m_cFileList.Invalidate();
506 progDlg.SetProgress64(count++, selCount);
508 progDlg.Stop();
512 void CFilePatchesDlg::OnLvnItemchangedFilelist(NMHDR * /*pNMHDR*/, LRESULT *pResult)
514 DialogEnableWindow(IDC_PATCHSELECTEDBUTTON, m_cFileList.GetSelectedCount() > 0);
516 *pResult = 0;
519 void CFilePatchesDlg::SetStateText(int i, int state)
521 CString sState;
522 switch (state)
524 case FPDLG_FILESTATE_PATCHED:
525 sState.LoadString(IDS_STATE_PATCHED);
526 break;
527 case FPDLG_FILESTATE_ERROR:
528 sState.LoadString(IDS_STATE_ERROR);
529 break;
530 case 0:
531 // all is ok, not yet patched but no failed hunks
532 break;
533 default:
534 // there are failed hunks in the patch
535 sState.LoadString(IDS_STATE_CONFLICTS);
536 break;
538 m_cFileList.SetItemText(i, 1, sState);