1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * @brief Implementation of Patch creation dialog
10 #include "PatchTool.h"
13 #include "CompareOptions.h"
14 #include "FileOrFolderSelect.h"
15 #include "Environment.h"
16 #include "OptionsDef.h"
17 #include "OptionsMgr.h"
25 /////////////////////////////////////////////////////////////////////////////
29 * @brief Constructor, initializes members.
31 CPatchDlg::CPatchDlg(CWnd* pParent /*= nullptr*/)
32 : CTrDialog(CPatchDlg::IDD, pParent)
34 , m_ignoreBlanks(false)
35 , m_ignoreEOLDifference(false)
36 , m_whitespaceCompare(0)
38 , m_openToEditor(false)
39 , m_includeCmdLine(false)
40 , m_outputStyle(OUTPUT_NORMAL)
42 , m_diffAlgorithm(DIFF_ALGORITHM_DEFAULT)
43 , m_indentHeuristic(true)
48 * @brief Map dialog controls and class member variables.
50 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
52 CTrDialog::DoDataExchange(pDX);
53 //{{AFX_DATA_MAP(CPatchDlg)
54 DDX_Control(pDX, IDC_DIFF_STYLE, m_comboStyle);
55 DDX_Control(pDX, IDC_DIFF_CONTEXT, m_comboContext);
56 DDX_Check(pDX, IDC_DIFF_IGNORECASE, m_ignoreCase);
57 DDX_Check(pDX, IDC_DIFF_WHITESPACE_IGNOREBLANKS, m_ignoreBlanks);
58 DDX_Radio(pDX, IDC_DIFF_WHITESPACE_COMPARE, m_whitespaceCompare);
59 DDX_Check(pDX, IDC_DIFF_IGNOREEOL, m_ignoreEOLDifference);
60 DDX_Check(pDX, IDC_DIFF_APPENDFILE, m_appendFile);
61 DDX_Control(pDX, IDC_DIFF_FILE1, m_ctlFile1);
62 DDX_Control(pDX, IDC_DIFF_FILE2, m_ctlFile2);
63 DDX_Control(pDX, IDC_DIFF_FILERESULT, m_ctlResult);
64 DDX_CBStringExact(pDX, IDC_DIFF_FILE1, m_file1);
65 DDX_CBStringExact(pDX, IDC_DIFF_FILE2, m_file2);
66 DDX_CBStringExact(pDX, IDC_DIFF_FILERESULT, m_fileResult);
67 DDX_Check(pDX, IDC_DIFF_OPENTOEDITOR, m_openToEditor);
68 DDX_Check(pDX, IDC_DIFF_INCLCMDLINE, m_includeCmdLine);
73 BEGIN_MESSAGE_MAP(CPatchDlg, CTrDialog)
74 //{{AFX_MSG_MAP(CPatchDlg)
75 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE1, OnDiffBrowseFile1)
76 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE2, OnDiffBrowseFile2)
77 ON_BN_CLICKED(IDC_DIFF_BROWSE_RESULT, OnDiffBrowseResult)
78 ON_BN_CLICKED(IDC_DIFF_DEFAULTS, OnDefaultSettings)
79 ON_CBN_SELCHANGE(IDC_DIFF_STYLE, OnSelchangeDiffStyle)
80 ON_BN_CLICKED(IDC_DIFF_SWAPFILES, OnDiffSwapFiles)
81 ON_CBN_SELCHANGE(IDC_DIFF_FILE1, OnSelchangeFile1)
82 ON_CBN_SELCHANGE(IDC_DIFF_FILE2, OnSelchangeFile2)
83 ON_CBN_EDITCHANGE(IDC_DIFF_FILE1, OnEditchangeFile1)
84 ON_CBN_EDITCHANGE(IDC_DIFF_FILE2, OnEditchangeFile2)
88 /////////////////////////////////////////////////////////////////////////////
89 // CPatchDlg message handlers
92 * @brief Called when dialog is closed with OK.
93 * Check options and filenames given and close the dialog.
95 void CPatchDlg::OnOK()
99 // There are two different cases: single files or
100 // multiple files. Multiple files are selected from DirView.
101 // Only if single files selected, filenames are checked here.
102 // Filenames read from Dirview must be valid ones.
103 size_t selectCount = m_fileList.size();
104 if (selectCount == 0)
107 tFiles.lfile = m_file1;
108 tFiles.rfile = m_file2;
112 if (selectCount == 1)
114 bool file1Ok = (paths::DoesPathExist(m_file1) != paths::DOES_NOT_EXIST);
115 bool file2Ok = (paths::DoesPathExist(m_file2) != paths::DOES_NOT_EXIST);
117 if (!file1Ok || !file2Ok)
120 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
123 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
127 PATCHFILES tFiles = m_fileList[0];
128 if (tFiles.lfile != m_file1 && !tFiles.pathLeft.empty())
129 tFiles.pathLeft = _T("");
130 if (tFiles.rfile != m_file2 && !tFiles.pathRight.empty())
131 tFiles.pathRight = _T("");
132 tFiles.lfile = m_file1;
133 tFiles.rfile = m_file2;
134 m_fileList[0] = tFiles;
137 // Check that result (patch) file is absolute path
138 if (!paths::IsPathAbsolute(m_fileResult))
140 if (m_fileResult.length() == 0)
142 TCHAR szTempFile[MAX_PATH];
143 ::GetTempFileName(env::GetTemporaryPath().c_str(), _T("pat"), 0, szTempFile);
144 m_fileResult = szTempFile;
145 m_ctlResult.SetWindowText(m_fileResult.c_str());
146 DeleteFile(m_fileResult.c_str());
148 if (!paths::IsPathAbsolute(m_fileResult))
150 String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
152 AfxMessageBox(msg.c_str(), MB_ICONSTOP);
153 m_ctlResult.SetFocus();
158 bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
160 // Result file already exists and append not selected
161 if (fileExists && !m_appendFile)
163 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
164 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
165 IDS_DIFF_FILEOVERWRITE) != IDYES)
170 // else it's OK to write new file
172 switch (m_comboStyle.GetCurSel())
174 case 1: m_outputStyle = (enum output_style)OUTPUT_CONTEXT; break;
175 case 2: m_outputStyle = (enum output_style)OUTPUT_UNIFIED; break;
176 case 3: m_outputStyle = (enum output_style)OUTPUT_HTML; break;
177 default: m_outputStyle = (enum output_style)OUTPUT_NORMAL; break;
180 m_contextLines = GetDlgItemInt(IDC_DIFF_CONTEXT);
181 m_diffAlgorithm = static_cast<DiffAlgorithm>(GetOptionsMgr()->GetInt(OPT_CMP_DIFF_ALGORITHM));
182 m_indentHeuristic = GetOptionsMgr()->GetBool(OPT_CMP_INDENT_HEURISTIC);
186 // Save combobox history
187 m_ctlResult.SaveState(_T("Files\\DiffFileResult"));
188 m_comboContext.SaveState(_T("PatchCreator\\DiffContext"));
189 // Don't save filenames if multiple file selected (as editbox reads
190 // [X files selected])
191 if (selectCount <= 1)
193 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
194 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
201 * @brief Initialise dialog data.
203 * There are two cases for filename editboxes:
204 * - if one file was added to list then we show that filename
205 * - if multiple files were added we show text [X files selected]
207 BOOL CPatchDlg::OnInitDialog()
209 CTrDialog::OnInitDialog();
211 // Load combobox history
212 m_ctlFile1.LoadState(_T("Files\\DiffFile1"));
213 m_ctlFile2.LoadState(_T("Files\\DiffFile2"));
214 m_comboContext.LoadState(_T("PatchCreator\\DiffContext"));
215 m_ctlResult.LoadState(_T("Files\\DiffFileResult"));
217 size_t count = m_fileList.size();
219 // If one file added, show filenames on dialog
222 const PATCHFILES& tFiles = m_fileList.front();
223 m_file1 = tFiles.lfile;
224 m_ctlFile1.SetWindowText(tFiles.lfile.c_str());
225 m_file2 = tFiles.rfile;
226 m_ctlFile2.SetWindowText(tFiles.rfile.c_str());
228 else if (count > 1) // Multiple files added, show number of files
230 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count));
234 // Add patch styles to combobox
235 m_comboStyle.AddString(_("Normal").c_str());
236 m_comboStyle.AddString(_("Context").c_str());
237 m_comboStyle.AddString(_("Unified").c_str());
238 m_comboStyle.AddString(_("HTML").c_str());
240 m_outputStyle = OUTPUT_NORMAL;
241 m_comboStyle.SetCurSel(0);
243 // Add default context line counts to combobox if its empty
244 if (m_comboContext.GetCount() == 0)
246 m_comboContext.AddString(_T("0"));
247 m_comboContext.AddString(_T("1"));
248 m_comboContext.AddString(_T("3"));
249 m_comboContext.AddString(_T("5"));
250 m_comboContext.AddString(_T("7"));
255 return TRUE; // return TRUE unless you set the focus to a control
256 // EXCEPTION: OCX Property Pages should return FALSE
260 * @brief Select the left file.
262 void CPatchDlg::OnDiffBrowseFile1()
268 if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
270 m_ctlFile1.SetWindowText(s.c_str());
271 if (m_fileList.size() > 1)
273 m_ctlFile2.SetWindowText(_T(""));
280 * @brief Select the right file.
282 void CPatchDlg::OnDiffBrowseFile2()
288 if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
290 m_ctlFile2.SetWindowText(s.c_str());
291 if (m_fileList.size() > 1)
293 m_ctlFile1.SetWindowText(_T(""));
300 * @brief Select the patch file.
302 void CPatchDlg::OnDiffBrowseResult()
307 folder = m_fileResult;
308 if (SelectFile(GetSafeHwnd(), s, false, folder.c_str()))
309 m_ctlResult.SetWindowText(s.c_str());
313 * @brief Called when diff style dropdown selection is changed.
314 * Called when diff style dropdown selection is changed.
315 * If the new selection is context patch or unified patch format then
316 * enable context lines selection control. Otherwise context lines selection
319 void CPatchDlg::OnSelchangeDiffStyle()
321 int selection = m_comboStyle.GetCurSel();
323 // Only context and unified formats allow context lines
324 if (selection != OUTPUT_NORMAL)
326 m_comboContext.EnableWindow(TRUE);
327 // 3 lines is default context for Difftools too
328 m_comboContext.SetCurSel(2);
333 m_comboContext.SetCurSel(0);
334 m_comboContext.EnableWindow(FALSE);
339 * @brief Swap filenames on file1 and file2.
341 void CPatchDlg::OnDiffSwapFiles()
343 CString cstrFile1 = m_file1.c_str();
344 CString cstrFile2 = m_file2.c_str();
345 m_ctlFile1.GetWindowText(cstrFile1);
346 m_ctlFile2.GetWindowText(cstrFile2);
348 m_ctlFile1.SetWindowText(cstrFile2);
349 m_ctlFile2.SetWindowText(cstrFile1);
356 * @brief Updates patch dialog settings from member variables.
358 void CPatchDlg::UpdateSettings()
362 switch (m_outputStyle)
364 case DIFF_OUTPUT_NORMAL:
365 m_comboStyle.SelectString(-1, _("Normal").c_str());
367 case DIFF_OUTPUT_CONTEXT:
368 m_comboStyle.SelectString(-1, _("Context").c_str());
370 case DIFF_OUTPUT_UNIFIED:
371 m_comboStyle.SelectString(-1, _("Unified").c_str());
373 case DIFF_OUTPUT_HTML:
374 m_comboStyle.SelectString(-1, _("HTML").c_str());
378 m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
380 if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
381 m_comboContext.EnableWindow(TRUE);
383 m_comboContext.EnableWindow(FALSE);
387 * @brief Loads patch dialog settings from registry.
389 void CPatchDlg::LoadSettings()
391 int patchStyle = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_PATCH_STYLE);
392 if ((patchStyle < DIFF_OUTPUT_NORMAL || patchStyle > DIFF_OUTPUT_UNIFIED) && patchStyle != DIFF_OUTPUT_HTML)
393 patchStyle = DIFF_OUTPUT_NORMAL;
394 m_outputStyle = (enum output_style) patchStyle;
396 m_contextLines = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_CONTEXT_LINES);
397 if (m_contextLines < 0 || m_contextLines > 50)
400 m_ignoreCase = !GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_CASE_SENSITIVE);
401 m_ignoreEOLDifference = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_EOL_SENSITIVE);
402 m_ignoreBlanks = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_IGNORE_BLANK_LINES);
404 m_whitespaceCompare = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_WHITESPACE);
405 if (m_whitespaceCompare < WHITESPACE_COMPARE_ALL ||
406 m_whitespaceCompare > WHITESPACE_IGNORE_ALL)
408 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
411 m_openToEditor = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_OPEN_TO_EDITOR);
412 m_includeCmdLine = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_INCLUDE_CMD_LINE);
418 * @brief Saves patch dialog settings to registry.
420 void CPatchDlg::SaveSettings()
422 COptionsMgr *pOptions = GetOptionsMgr();
423 pOptions->SaveOption(OPT_PATCHCREATOR_PATCH_STYLE, m_outputStyle);
424 pOptions->SaveOption(OPT_PATCHCREATOR_CONTEXT_LINES, m_contextLines);
425 pOptions->SaveOption(OPT_PATCHCREATOR_CASE_SENSITIVE, !m_ignoreCase);
426 pOptions->SaveOption(OPT_PATCHCREATOR_EOL_SENSITIVE, m_ignoreEOLDifference);
427 pOptions->SaveOption(OPT_PATCHCREATOR_IGNORE_BLANK_LINES, m_ignoreBlanks);
428 pOptions->SaveOption(OPT_PATCHCREATOR_WHITESPACE, m_whitespaceCompare);
429 pOptions->SaveOption(OPT_PATCHCREATOR_OPEN_TO_EDITOR, m_openToEditor);
430 pOptions->SaveOption(OPT_PATCHCREATOR_INCLUDE_CMD_LINE, m_includeCmdLine);
434 * @brief Resets patch dialog settings to defaults.
436 void CPatchDlg::OnDefaultSettings()
438 m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
440 m_ignoreCase = false;
441 m_ignoreEOLDifference = false;
442 m_ignoreBlanks = false;
443 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
444 m_openToEditor = false;
445 m_includeCmdLine = false;
450 void CPatchDlg::OnSelchangeFile1()
452 if (m_fileList.size() > 1)
454 m_ctlFile2.SetWindowText(_T(""));
459 void CPatchDlg::OnSelchangeFile2()
461 if (m_fileList.size() > 1)
463 m_ctlFile1.SetWindowText(_T(""));
468 void CPatchDlg::OnEditchangeFile1()
470 if (m_fileList.size() > 1)
472 m_ctlFile2.SetWindowText(_T(""));
477 void CPatchDlg::OnEditchangeFile2()
479 if (m_fileList.size() > 1)
481 m_ctlFile1.SetWindowText(_T(""));
490 void CPatchDlg::Swap()
492 std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
493 std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
494 while (iter != iterEnd)
496 (*iter).swap_sides();