Fixed issue #4126: Capitalize the first letter in the Push dialog
[TortoiseGit.git] / src / TortoiseProc / PatchViewDlg.cpp
blobfc105d11262ebcc876e5c6b261ac5026c4352f2f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2017, 2019-2023 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // PatchViewDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "MessageBox.h"
25 #include "PatchViewDlg.h"
26 #include "CommonAppUtils.h"
27 #include "StringUtils.h"
28 #include "DPIAware.h"
29 #include "StagingOperations.h"
30 #include "Git.h"
31 #include "EnableStagingTypes.h"
33 #pragma comment(lib, "Dwmapi.lib")
35 // CPatchViewDlg dialog
37 UINT CPatchViewDlg::WM_PARTIALSTAGINGREFRESHPATCHVIEW = RegisterWindowMessage(L"TORTOISEGIT_COMMIT_PARTIALSTAGINGREFRESHPATCHVIEW"); // same string in CommitDlg.cpp!!!
39 IMPLEMENT_DYNAMIC(CPatchViewDlg, CStandAloneDialog)
41 #define SEARCHBARHEIGHT 30
43 CPatchViewDlg::CPatchViewDlg(CWnd* pParent /*=nullptr*/)
44 : CStandAloneDialog(CPatchViewDlg::IDD, pParent)
48 CPatchViewDlg::~CPatchViewDlg()
52 void CPatchViewDlg::DoDataExchange(CDataExchange* pDX)
54 CStandAloneDialog::DoDataExchange(pDX);
55 DDX_Control(pDX, IDC_PATCH, m_ctrlPatchView);
58 BEGIN_MESSAGE_MAP(CPatchViewDlg, CStandAloneDialog)
59 ON_WM_SIZE()
60 ON_WM_MOVING()
61 ON_WM_CLOSE()
62 ON_WM_DESTROY()
63 ON_COMMAND(IDM_SHOWFINDBAR, OnShowFindBar)
64 ON_COMMAND(IDM_FINDEXIT, OnEscape)
65 ON_COMMAND(IDM_FINDNEXT, OnFindNext)
66 ON_COMMAND(IDM_FINDPREV, OnFindPrev)
67 ON_COMMAND(ID_STAGING_STAGESELECTEDLINES, OnStageLines)
68 ON_COMMAND(ID_STAGING_STAGESELECTEDHUNKS, OnStageHunks)
69 ON_COMMAND(ID_UNSTAGING_UNSTAGESELECTEDLINES, OnUnstageLines)
70 ON_COMMAND(ID_UNSTAGING_UNSTAGESELECTEDHUNKS, OnUnstageHunks)
71 ON_REGISTERED_MESSAGE(CFindBar::WM_FINDEXIT, OnFindExitMessage)
72 ON_REGISTERED_MESSAGE(CFindBar::WM_FINDNEXT, OnFindNextMessage)
73 ON_REGISTERED_MESSAGE(CFindBar::WM_FINDPREV, OnFindPrevMessage)
74 ON_REGISTERED_MESSAGE(CFindBar::WM_FINDRESET, OnFindResetMessage)
75 END_MESSAGE_MAP()
77 // CPatchViewDlg message handlers
79 BOOL CPatchViewDlg::OnInitDialog()
81 CStandAloneDialog::OnInitDialog();
83 auto hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_PATCH));
84 SetIcon(hIcon, TRUE);
85 SetIcon(hIcon, FALSE);
87 m_ctrlPatchView.Init(-1);
89 m_ctrlPatchView.SetUDiffStyle();
91 m_ctrlPatchView.Call(SCI_SETSCROLLWIDTH, 1);
92 m_ctrlPatchView.Call(SCI_SETSCROLLWIDTHTRACKING, TRUE);
94 m_ctrlPatchView.RegisterContextMenuHandler(this);
96 m_hAccel = LoadAccelerators(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_ACC_PATCHVIEW));
98 m_FindBar.Create(IDD_FINDBAR, this);
100 return TRUE; // return TRUE unless you set the focus to a control
101 // EXCEPTION: OCX Property Pages should return FALSE
104 // This is intended to be called by the commit window when staging support is enabled there and
105 // we were invoked becaused the user clicked on "Partial Staging>>" or "Partial Unstaging>>".
106 // We assume the commit window will later on pass us the output of "git diff" (for staging) or
107 // "git diff --cached" (for unstaging), of one file only.
108 // If we were invoked from somewhere else (e.g. the File Diff Dialog), this will never be called
109 // and all staging menu items will stay disabled.
110 void CPatchViewDlg::EnableStaging(EnableStagingTypes enableStagingType)
112 EnableMenuItem(GetMenu()->GetSafeHmenu(), ID_STAGING_STAGESELECTEDHUNKS, enableStagingType == EnableStagingTypes::Staging ? MF_ENABLED : MF_DISABLED);
113 EnableMenuItem(GetMenu()->GetSafeHmenu(), ID_STAGING_STAGESELECTEDLINES, enableStagingType == EnableStagingTypes::Staging ? MF_ENABLED : MF_DISABLED);
114 EnableMenuItem(GetMenu()->GetSafeHmenu(), ID_UNSTAGING_UNSTAGESELECTEDHUNKS, enableStagingType == EnableStagingTypes::Unstaging ? MF_ENABLED : MF_DISABLED);
115 EnableMenuItem(GetMenu()->GetSafeHmenu(), ID_UNSTAGING_UNSTAGESELECTEDLINES, enableStagingType == EnableStagingTypes::Unstaging ? MF_ENABLED : MF_DISABLED);
117 switch (enableStagingType)
119 case EnableStagingTypes::None:
120 SetWindowText(CString(MAKEINTRESOURCE(IDS_VIEWPATCH)));
121 break;
122 case EnableStagingTypes::Staging:
123 SetWindowText(CString(MAKEINTRESOURCE(IDS_VIEWPATCH_INDEX_WORKTREE)));
124 break;
125 case EnableStagingTypes::Unstaging:
126 SetWindowText(CString(MAKEINTRESOURCE(IDS_VIEWPATCH_HEAD_INDEX)));
127 break;
130 m_nEnableStagingType = enableStagingType; // This will be used to determine which context menu items to show
133 void CPatchViewDlg::SetText(const CString& text)
135 m_ctrlPatchView.SetText(text);
136 if (!text.IsEmpty())
138 m_ctrlPatchView.Call(SCI_GOTOPOS, 0);
139 CRect rect;
140 m_ctrlPatchView.GetClientRect(rect);
144 void CPatchViewDlg::ClearView()
146 SetText(CString());
149 static int GetBorderAjustment(HWND parentHWND, const RECT& parentRect)
151 CRect recta{ 0, 0, 0, 0 };
152 if (SUCCEEDED(::DwmGetWindowAttribute(parentHWND, DWMWA_EXTENDED_FRAME_BOUNDS, &recta, sizeof(recta))))
153 return 2 * (recta.left - parentRect.left) + 1;
155 return 0;
158 void CPatchViewDlg::OnSize(UINT nType, int cx, int cy)
160 CStandAloneDialog::OnSize(nType, cx, cy);
162 if (this->IsWindowVisible())
164 CRect rect;
165 GetClientRect(rect);
166 GetDlgItem(IDC_PATCH)->MoveWindow(0, 0, cx, cy);
168 if (m_bShowFindBar)
170 ::SetWindowPos(m_ctrlPatchView.GetSafeHwnd(), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top - CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT), SWP_SHOWWINDOW);
171 ::SetWindowPos(m_FindBar.GetSafeHwnd(), HWND_TOP, rect.left, rect.bottom - CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT + 2), rect.right - rect.left, CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT), SWP_SHOWWINDOW);
173 else
175 ::SetWindowPos(m_ctrlPatchView.GetSafeHwnd(), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);
176 m_FindBar.ShowWindow(SW_HIDE);
181 void CPatchViewDlg::OnMoving(UINT fwSide, LPRECT pRect)
183 #define STICKYSIZE 5
184 RECT parentRect;
185 m_ParentDlg->GetPatchViewParentWnd()->GetWindowRect(&parentRect);
187 const int adjust = GetBorderAjustment(m_ParentDlg->GetPatchViewParentWnd()->GetSafeHwnd(), parentRect);
188 if (abs(parentRect.right - pRect->left - adjust) < STICKYSIZE)
190 const int width = pRect->right - pRect->left;
191 pRect->left = parentRect.right - adjust;
192 pRect->right = pRect->left + width;
194 CStandAloneDialog::OnMoving(fwSide, pRect);
197 void CPatchViewDlg::ParentOnMoving(HWND parentHWND, LPRECT pRect)
199 if (!::IsWindow(m_hWnd))
200 return;
202 if (!::IsWindow(parentHWND))
203 return;
205 RECT patchrect;
206 GetWindowRect(&patchrect);
208 RECT parentRect;
209 ::GetWindowRect(parentHWND, &parentRect);
211 const int adjust = GetBorderAjustment(parentHWND, parentRect);
212 if (patchrect.left == parentRect.right - adjust)
213 SetWindowPos(nullptr, patchrect.left - (parentRect.left - pRect->left), patchrect.top - (parentRect.top - pRect->top), 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
216 void CPatchViewDlg::ParentOnSizing(HWND parentHWND, LPRECT pRect)
218 if (!::IsWindow(m_hWnd))
219 return;
221 if (!::IsWindow(parentHWND))
222 return;
224 RECT patchrect;
225 GetWindowRect(&patchrect);
227 RECT parentRect;
228 ::GetWindowRect(parentHWND, &parentRect);
230 const int adjust = GetBorderAjustment(parentHWND, parentRect);
231 if (patchrect.left != parentRect.right - adjust)
232 return;
234 if (patchrect.bottom == parentRect.bottom)
235 patchrect.bottom -= (parentRect.bottom - pRect->bottom);
236 if (patchrect.top == parentRect.top)
237 patchrect.top -= parentRect.top - pRect->top;
239 SetWindowPos(nullptr, patchrect.left - (parentRect.right - pRect->right), patchrect.top - (parentRect.top - pRect->top), patchrect.right - patchrect.left, patchrect.bottom - patchrect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
242 void CPatchViewDlg::ShowAndAlignToParent()
244 CRect rect;
245 m_ParentDlg->GetPatchViewParentWnd()->GetWindowRect(&rect);
246 int adjust = GetBorderAjustment(m_ParentDlg->GetPatchViewParentWnd()->GetSafeHwnd(), rect);
247 rect.left = rect.right - adjust;
248 rect.right = rect.left;
249 int xPos = CDPIAware::Instance().ScaleX(GetSafeHwnd(), static_cast<DWORD>(CRegDWORD(L"Software\\TortoiseGit\\TortoiseProc\\ResizableState\\PatchDlgWidth")));
250 if (xPos)
251 rect.right += xPos;
252 else
253 rect.right += rect.Width();
255 WINDOWPLACEMENT wp;
256 m_ParentDlg->GetPatchViewParentWnd()->GetWindowPlacement(&wp);
257 if (wp.showCmd != SW_MAXIMIZE)
258 SetWindowPos(nullptr, rect.left, rect.top, rect.Width(), rect.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
259 else if (auto monitor = MonitorFromRect(rect, MONITOR_DEFAULTTONULL); !monitor)
261 CRect pos;
262 GetWindowRect(&pos);
263 SetWindowPos(nullptr, 0, 0, xPos ? xPos : pos.Width(), pos.Height(), SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
265 else
267 MONITORINFO monitorinfo;
268 monitorinfo.cbSize = sizeof(MONITORINFO);
269 GetMonitorInfo(monitor, &monitorinfo);
270 SetWindowPos(nullptr, monitorinfo.rcWork.left, monitorinfo.rcWork.top, min(rect.Width(), static_cast<int>(monitorinfo.rcWork.right - monitorinfo.rcWork.left)), min(rect.Height(), static_cast<int>(monitorinfo.rcWork.bottom - monitorinfo.rcWork.top)), SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
274 void CPatchViewDlg::OnClose()
276 CStandAloneDialog::OnClose();
277 m_ParentDlg->TogglePatchView();
280 BOOL CPatchViewDlg::PreTranslateMessage(MSG* pMsg)
282 if (m_hAccel)
284 if (TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
285 return TRUE;
287 return __super::PreTranslateMessage(pMsg);
290 void CPatchViewDlg::OnEscape()
292 if (::IsWindowVisible(m_FindBar))
294 OnFindExit();
295 return;
297 SendMessage(WM_CLOSE);
300 void CPatchViewDlg::OnShowFindBar()
302 m_bShowFindBar = true;
303 RECT rect;
304 GetClientRect(&rect);
305 ::SetWindowPos(m_ctrlPatchView.GetSafeHwnd(), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top - CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT), SWP_SHOWWINDOW);
306 ::SetWindowPos(m_FindBar, HWND_TOP, rect.left, rect.bottom - CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT + 2), rect.right - rect.left, CDPIAware::Instance().ScaleX(GetSafeHwnd(), SEARCHBARHEIGHT), SWP_SHOWWINDOW);
307 if (auto selstart = static_cast<Sci_Position>(m_ctrlPatchView.Call(SCI_GETSELECTIONSTART)), selend = static_cast<Sci_Position>(m_ctrlPatchView.Call(SCI_GETSELECTIONEND)); selstart != selend && selend - selstart < INT_MAX)
309 CStringA selText;
310 Sci_TextRangeFull range = { selstart, selend, selText.GetBuffer(SafeSizeToInt(selend - selstart)) };
311 selText.ReleaseBufferSetLength(SafeSizeToInt(m_ctrlPatchView.Call(SCI_GETTEXTRANGEFULL, 0, reinterpret_cast<LPARAM>(&range))));
312 if (!selText.IsEmpty())
313 m_FindBar.SetFindText(m_ctrlPatchView.StringFromControl(selText));
315 m_FindBar.SetFocusTextBox();
318 void CPatchViewDlg::OnFindNext()
320 if (m_FindBar.GetFindText().IsEmpty())
322 OnShowFindBar();
323 return;
325 DoSearch(false);
328 void CPatchViewDlg::DoSearch(bool reverse)
330 Sci_Position lastcursor = m_ctrlPatchView.Call(SCI_GETSELECTIONEND);
331 if (!reverse)
332 m_ctrlPatchView.Call(SCI_SETTARGETRANGE, lastcursor, m_ctrlPatchView.Call(SCI_GETLENGTH));
333 else
335 lastcursor = m_ctrlPatchView.Call(SCI_GETSELECTIONSTART);
336 m_ctrlPatchView.Call(SCI_SETTARGETRANGE, lastcursor, 0);
339 auto searchText = CUnicodeUtils::GetUTF8(m_FindBar.GetFindText());
340 m_ctrlPatchView.Call(SCI_SETSEARCHFLAGS, m_FindBar.IsMatchCase() ? SCFIND_MATCHCASE : SCFIND_NONE);
341 if (auto pos = m_ctrlPatchView.Call(SCI_SEARCHINTARGET, searchText.GetLength(), reinterpret_cast<LPARAM>(static_cast<LPCSTR>(searchText))); pos >= 0)
343 m_ctrlPatchView.Call(SCI_SETSELECTION, pos, pos + searchText.GetLength());
344 m_ctrlPatchView.Call(SCI_SCROLLCARET);
346 else
348 m_ctrlPatchView.Call(SCI_SETSELECTION, lastcursor, lastcursor);
349 FlashWindowEx(FLASHW_ALL, 3, 100);
353 void CPatchViewDlg::OnFindPrev()
355 if (m_FindBar.GetFindText().IsEmpty())
357 OnShowFindBar();
358 return;
360 DoSearch(true);
363 void CPatchViewDlg::OnFindExit()
365 if (!::IsWindowVisible(m_FindBar))
366 return;
368 RECT rect;
369 GetClientRect(&rect);
370 m_bShowFindBar = false;
371 m_FindBar.ShowWindow(SW_HIDE);
372 ::SetWindowPos(m_ctrlPatchView.GetSafeHwnd(), HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);
373 m_ctrlPatchView.SetFocus();
376 void CPatchViewDlg::OnFindReset()
378 m_ctrlPatchView.Call(SCI_SETSELECTIONSTART, 0);
379 m_ctrlPatchView.Call(SCI_SETSELECTIONEND, 0);
382 LRESULT CPatchViewDlg::OnFindNextMessage(WPARAM, LPARAM)
384 OnFindNext();
385 return 0;
388 LRESULT CPatchViewDlg::OnFindPrevMessage(WPARAM, LPARAM)
390 OnFindPrev();
391 return 0;
394 LRESULT CPatchViewDlg::OnFindExitMessage(WPARAM, LPARAM)
396 OnFindExit();
397 return 0;
400 LRESULT CPatchViewDlg::OnFindResetMessage(WPARAM, LPARAM)
402 OnFindReset();
403 return 0;
406 void CPatchViewDlg::OnStageHunks()
408 StageOrUnstageSelectedLinesOrHunks(StagingType::StageHunks);
411 void CPatchViewDlg::OnStageLines()
413 StageOrUnstageSelectedLinesOrHunks(StagingType::StageLines);
416 void CPatchViewDlg::OnUnstageHunks()
418 StageOrUnstageSelectedLinesOrHunks(StagingType::UnstageHunks);
421 void CPatchViewDlg::OnUnstageLines()
423 StageOrUnstageSelectedLinesOrHunks(StagingType::UnstageLines);
426 int CPatchViewDlg::GetFirstLineNumberSelected()
428 auto selstart = m_ctrlPatchView.Call(SCI_GETSELECTIONSTART);
429 return static_cast<int>(m_ctrlPatchView.Call(SCI_LINEFROMPOSITION, selstart));
432 int CPatchViewDlg::GetLastLineNumberSelected()
434 auto selend = m_ctrlPatchView.Call(SCI_GETSELECTIONEND);
435 return static_cast<int>(m_ctrlPatchView.Call(SCI_LINEFROMPOSITION, selend));
438 void CPatchViewDlg::StageOrUnstageSelectedLinesOrHunks(StagingType stagingType)
440 auto documentLength = static_cast<Sci_Position>(m_ctrlPatchView.Call(SCI_GETLENGTH));
441 auto wholePatchBuf = std::make_unique<char[]>(documentLength + 1);
442 m_ctrlPatchView.Call(SCI_GETTEXT, documentLength + 1, reinterpret_cast<LPARAM>(wholePatchBuf.get()));
444 int lineCount = static_cast<int>(m_ctrlPatchView.Call(SCI_GETLINECOUNT));
446 CDiffLinesForStaging lines(wholePatchBuf.get(), lineCount, GetFirstLineNumberSelected(), GetLastLineNumberSelected());
447 auto op = StagingOperations(&lines);
448 std::string strPatch;
449 if (stagingType == StagingType::StageLines || stagingType == StagingType::UnstageLines)
450 strPatch = op.CreatePatchBufferToStageOrUnstageSelectedLines(stagingType);
451 else if (stagingType == StagingType::StageHunks || stagingType == StagingType::UnstageHunks)
452 strPatch = op.CreatePatchBufferToStageOrUnstageSelectedHunks();
453 else
454 return; // this should never happen
455 if (strPatch.empty())
457 CMessageBox::Show(GetSafeHwnd(), IDS_ERROR_PARTIALSTAGING, IDS_APPNAME, MB_OK | MB_ICONERROR);
458 return;
461 CTGitPath::StagingStatus newStatus; // this will be sent to the commit dialog so that it can update the file checkbox/status
462 if (strcmp(wholePatchBuf.get(), strPatch.c_str()) == 0)
464 if (stagingType == StagingType::StageLines || stagingType == StagingType::StageHunks)
465 newStatus = CTGitPath::StagingStatus::TotallyStaged;
466 else // stagingType == StagingType::UnstageLines || stagingType == StagingType::UnstageHunks
467 newStatus = CTGitPath::StagingStatus::TotallyUnstaged;
469 else // if the patch to be applied is different than the whole diff, the file is still partially staged or became partially staged
470 newStatus = CTGitPath::StagingStatus::PartiallyStaged;
472 CString tempPatch = StagingOperations::WritePatchBufferToTemporaryFile(strPatch);
473 if (tempPatch.IsEmpty())
474 return;
476 CString out;
477 int ret;
478 if (stagingType == StagingType::StageHunks || stagingType == StagingType::StageLines)
479 ret = g_Git.ApplyPatchToIndex(tempPatch, &out);
480 else //if (stagingType == StagingType::UnstageHunks || stagingType == StagingType::UnstageLines)
481 ret = g_Git.ApplyPatchToIndexReverse(tempPatch, &out);
482 if (ret != 0)
484 MessageBox(out, L"TortoiseGit", MB_OK | MB_ICONERROR);
485 return;
488 // Tell the commit window we partially staged a file and ask it to update ourselves with the updated diff
489 m_ParentDlg->GetPatchViewParentWnd()->SendMessage(WM_PARTIALSTAGINGREFRESHPATCHVIEW, static_cast<WPARAM>(newStatus));
492 void CPatchViewDlg::OnDestroy()
494 __super::OnDestroy();
495 CRect rect;
496 GetWindowRect(&rect);
497 CRegStdDWORD(L"Software\\TortoiseGit\\TortoiseProc\\ResizableState\\PatchDlgWidth") = CDPIAware::Instance().UnscaleX(GetSafeHwnd(), rect.Width());
498 m_ctrlPatchView.ClearContextMenuHandlers();
501 // CSciEditContextMenuInterface
502 void CPatchViewDlg::InsertMenuItems(CMenu& mPopup, int& nCmd)
504 CString sMenuItemText;
505 sMenuItemText.LoadString(IDS_REPOBROWSE_SAVEAS);
506 m_nPopupSave = nCmd++;
507 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nPopupSave, sMenuItemText);
508 // We need to reset those to 0:
509 m_nStageHunks = 0;
510 m_nStageLines = 0;
511 m_nUnstageHunks = 0;
512 m_nUnstageLines = 0;
513 if (m_nEnableStagingType == EnableStagingTypes::Staging || m_nEnableStagingType == EnableStagingTypes::Unstaging)
514 mPopup.AppendMenu(MF_SEPARATOR);
515 if (m_nEnableStagingType == EnableStagingTypes::Staging)
517 sMenuItemText.LoadString(IDS_PROC_STAGE_SELECTED_HUNKS);
518 m_nStageHunks = nCmd++;
519 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nStageHunks, sMenuItemText);
521 sMenuItemText.LoadString(IDS_PROC_STAGE_SELECTED_LINES);
522 m_nStageLines = nCmd++;
523 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nStageLines, sMenuItemText);
525 if (m_nEnableStagingType == EnableStagingTypes::Unstaging)
527 sMenuItemText.LoadString(IDS_PROC_UNSTAGE_SELECTED_HUNKS);
528 m_nUnstageHunks = nCmd++;
529 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nUnstageHunks, sMenuItemText);
531 sMenuItemText.LoadString(IDS_PROC_UNSTAGE_SELECTED_LINES);
532 m_nUnstageLines = nCmd++;
533 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nUnstageLines, sMenuItemText);
537 bool CPatchViewDlg::HandleMenuItemClick(int cmd, CSciEdit*)
539 if (cmd == m_nPopupSave)
541 CString filename;
542 if (CCommonAppUtils::FileOpenSave(filename, nullptr, 0, IDS_PATCHFILEFILTER, false, GetSafeHwnd(), L"diff"))
543 CStringUtils::WriteStringToTextFile(filename, m_ctrlPatchView.GetText());
544 return true;
546 else if (cmd == m_nStageHunks)
548 OnStageHunks();
549 return true;
551 else if (cmd == m_nStageLines)
553 OnStageLines();
554 return true;
556 else if (cmd == m_nUnstageHunks)
558 OnUnstageHunks();
559 return true;
561 else if (cmd == m_nUnstageLines)
563 OnUnstageLines();
564 return true;
566 return false;