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)
33 , m_copyToClipboard(false)
35 , m_openToEditor(false)
36 , m_includeCmdLine(false)
37 , m_outputStyle(OUTPUT_NORMAL)
43 * @brief Map dialog controls and class member variables.
45 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
47 CTrDialog::DoDataExchange(pDX);
48 //{{AFX_DATA_MAP(CPatchDlg)
49 DDX_Control(pDX, IDC_DIFF_STYLE, m_comboStyle);
50 DDX_Control(pDX, IDC_DIFF_CONTEXT, m_comboContext);
51 DDX_Check(pDX, IDC_DIFF_COPYCLIPBOARD, m_copyToClipboard);
52 DDX_Check(pDX, IDC_DIFF_APPENDFILE, m_appendFile);
53 DDX_Control(pDX, IDC_DIFF_FILE1, m_ctlFile1);
54 DDX_Control(pDX, IDC_DIFF_FILE2, m_ctlFile2);
55 DDX_Control(pDX, IDC_DIFF_FILERESULT, m_ctlResult);
56 DDX_CBStringExact(pDX, IDC_DIFF_FILE1, m_file1);
57 DDX_CBStringExact(pDX, IDC_DIFF_FILE2, m_file2);
58 DDX_CBStringExact(pDX, IDC_DIFF_FILERESULT, m_fileResult);
59 DDX_Check(pDX, IDC_DIFF_OPENTOEDITOR, m_openToEditor);
60 DDX_Check(pDX, IDC_DIFF_INCLCMDLINE, m_includeCmdLine);
65 BEGIN_MESSAGE_MAP(CPatchDlg, CTrDialog)
66 //{{AFX_MSG_MAP(CPatchDlg)
67 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE1, OnDiffBrowseFile1)
68 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE2, OnDiffBrowseFile2)
69 ON_BN_CLICKED(IDC_DIFF_BROWSE_RESULT, OnDiffBrowseResult)
70 ON_BN_CLICKED(IDC_DIFF_DEFAULTS, OnDefaultSettings)
71 ON_CBN_SELCHANGE(IDC_DIFF_STYLE, OnSelchangeDiffStyle)
72 ON_BN_CLICKED(IDC_DIFF_SWAPFILES, OnDiffSwapFiles)
73 ON_CBN_SELCHANGE(IDC_DIFF_FILE1, OnSelchangeFile1)
74 ON_CBN_SELCHANGE(IDC_DIFF_FILE2, OnSelchangeFile2)
75 ON_CBN_EDITCHANGE(IDC_DIFF_FILE1, OnEditchangeFile1)
76 ON_CBN_EDITCHANGE(IDC_DIFF_FILE2, OnEditchangeFile2)
80 /////////////////////////////////////////////////////////////////////////////
81 // CPatchDlg message handlers
84 * @brief Called when dialog is closed with OK.
85 * Check options and filenames given and close the dialog.
87 void CPatchDlg::OnOK()
91 // There are two different cases: single files or
92 // multiple files. Multiple files are selected from DirView.
93 // Only if single files selected, filenames are checked here.
94 // Filenames read from Dirview must be valid ones.
95 size_t selectCount = m_fileList.size();
99 tFiles.lfile = m_file1;
100 tFiles.rfile = m_file2;
104 if (selectCount == 1)
106 bool file1Ok = (paths::DoesPathExist(m_file1) != paths::DOES_NOT_EXIST);
107 bool file2Ok = (paths::DoesPathExist(m_file2) != paths::DOES_NOT_EXIST);
109 if (!file1Ok || !file2Ok)
112 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
115 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
119 PATCHFILES tFiles = m_fileList[0];
120 if (tFiles.lfile != m_file1 && !tFiles.pathLeft.empty())
121 tFiles.pathLeft.clear();
122 if (tFiles.rfile != m_file2 && !tFiles.pathRight.empty())
123 tFiles.pathRight.clear();
124 tFiles.lfile = m_file1;
125 tFiles.rfile = m_file2;
126 m_fileList[0] = tFiles;
129 // Check that result (patch) file is absolute path
130 if (!paths::IsPathAbsolute(m_fileResult))
132 if (m_fileResult.length() == 0)
134 TCHAR szTempFile[MAX_PATH];
135 ::GetTempFileName(env::GetTemporaryPath().c_str(), _T("pat"), 0, szTempFile);
136 m_fileResult = szTempFile;
137 m_ctlResult.SetWindowText(m_fileResult.c_str());
138 DeleteFile(m_fileResult.c_str());
140 if (!paths::IsPathAbsolute(m_fileResult))
142 String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
144 AfxMessageBox(msg.c_str(), MB_ICONSTOP);
145 m_ctlResult.SetFocus();
150 bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
152 // Result file already exists and append not selected
153 if (fileExists && !m_appendFile)
155 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
156 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
157 IDS_DIFF_FILEOVERWRITE) != IDYES)
162 // else it's OK to write new file
164 switch (m_comboStyle.GetCurSel())
166 case 1: m_outputStyle = (enum output_style)OUTPUT_CONTEXT; break;
167 case 2: m_outputStyle = (enum output_style)OUTPUT_UNIFIED; break;
168 case 3: m_outputStyle = (enum output_style)OUTPUT_HTML; break;
169 default: m_outputStyle = (enum output_style)OUTPUT_NORMAL; break;
172 m_contextLines = GetDlgItemInt(IDC_DIFF_CONTEXT);
176 // Save combobox history
177 m_ctlResult.SaveState(_T("Files\\DiffFileResult"));
178 m_comboContext.SaveState(_T("PatchCreator\\DiffContext"));
179 // Don't save filenames if multiple file selected (as editbox reads
180 // [X files selected])
181 if (selectCount <= 1)
183 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
184 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
191 * @brief Initialise dialog data.
193 * There are two cases for filename editboxes:
194 * - if one file was added to list then we show that filename
195 * - if multiple files were added we show text [X files selected]
197 BOOL CPatchDlg::OnInitDialog()
199 CTrDialog::OnInitDialog();
201 // Load combobox history
202 m_ctlFile1.LoadState(_T("Files\\DiffFile1"));
203 m_ctlFile2.LoadState(_T("Files\\DiffFile2"));
204 m_comboContext.LoadState(_T("PatchCreator\\DiffContext"));
205 m_ctlResult.LoadState(_T("Files\\DiffFileResult"));
207 size_t count = m_fileList.size();
209 // If one file added, show filenames on dialog
212 const PATCHFILES& tFiles = m_fileList.front();
213 m_file1 = tFiles.lfile;
214 m_ctlFile1.SetWindowText(tFiles.lfile.c_str());
215 m_file2 = tFiles.rfile;
216 m_ctlFile2.SetWindowText(tFiles.rfile.c_str());
218 else if (count > 1) // Multiple files added, show number of files
220 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count));
224 // Add patch styles to combobox
225 m_comboStyle.AddString(_("Normal").c_str());
226 m_comboStyle.AddString(_("Context").c_str());
227 m_comboStyle.AddString(_("Unified").c_str());
228 m_comboStyle.AddString(_("HTML").c_str());
230 m_outputStyle = OUTPUT_NORMAL;
231 m_comboStyle.SetCurSel(0);
233 // Add default context line counts to combobox if its empty
234 if (m_comboContext.GetCount() == 0)
236 m_comboContext.AddString(_T("0"));
237 m_comboContext.AddString(_T("1"));
238 m_comboContext.AddString(_T("3"));
239 m_comboContext.AddString(_T("5"));
240 m_comboContext.AddString(_T("7"));
245 return TRUE; // return TRUE unless you set the focus to a control
246 // EXCEPTION: OCX Property Pages should return FALSE
250 * @brief Select the left file.
252 void CPatchDlg::OnDiffBrowseFile1()
258 if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
260 m_ctlFile1.SetWindowText(s.c_str());
261 if (m_fileList.size() > 1)
263 m_ctlFile2.SetWindowText(_T(""));
270 * @brief Select the right file.
272 void CPatchDlg::OnDiffBrowseFile2()
278 if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
280 m_ctlFile2.SetWindowText(s.c_str());
281 if (m_fileList.size() > 1)
283 m_ctlFile1.SetWindowText(_T(""));
290 * @brief Select the patch file.
292 void CPatchDlg::OnDiffBrowseResult()
297 folder = m_fileResult;
298 if (SelectFile(GetSafeHwnd(), s, false, folder.c_str()))
299 m_ctlResult.SetWindowText(s.c_str());
303 * @brief Called when diff style dropdown selection is changed.
304 * Called when diff style dropdown selection is changed.
305 * If the new selection is context patch or unified patch format then
306 * enable context lines selection control. Otherwise context lines selection
309 void CPatchDlg::OnSelchangeDiffStyle()
311 int selection = m_comboStyle.GetCurSel();
313 // Only context and unified formats allow context lines
314 if (selection != OUTPUT_NORMAL)
316 m_comboContext.EnableWindow(TRUE);
317 // 3 lines is default context for Difftools too
318 m_comboContext.SetCurSel(2);
323 m_comboContext.SetCurSel(0);
324 m_comboContext.EnableWindow(FALSE);
329 * @brief Swap filenames on file1 and file2.
331 void CPatchDlg::OnDiffSwapFiles()
333 CString cstrFile1 = m_file1.c_str();
334 CString cstrFile2 = m_file2.c_str();
335 m_ctlFile1.GetWindowText(cstrFile1);
336 m_ctlFile2.GetWindowText(cstrFile2);
338 m_ctlFile1.SetWindowText(cstrFile2);
339 m_ctlFile2.SetWindowText(cstrFile1);
346 * @brief Updates patch dialog settings from member variables.
348 void CPatchDlg::UpdateSettings()
352 switch (m_outputStyle)
354 case DIFF_OUTPUT_NORMAL:
355 m_comboStyle.SelectString(-1, _("Normal").c_str());
357 case DIFF_OUTPUT_CONTEXT:
358 m_comboStyle.SelectString(-1, _("Context").c_str());
360 case DIFF_OUTPUT_UNIFIED:
361 m_comboStyle.SelectString(-1, _("Unified").c_str());
363 case DIFF_OUTPUT_HTML:
364 m_comboStyle.SelectString(-1, _("HTML").c_str());
368 m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
370 if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
371 m_comboContext.EnableWindow(TRUE);
373 m_comboContext.EnableWindow(FALSE);
377 * @brief Loads patch dialog settings from registry.
379 void CPatchDlg::LoadSettings()
381 int patchStyle = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_PATCH_STYLE);
382 if ((patchStyle < DIFF_OUTPUT_NORMAL || patchStyle > DIFF_OUTPUT_UNIFIED) && patchStyle != DIFF_OUTPUT_HTML)
383 patchStyle = DIFF_OUTPUT_NORMAL;
384 m_outputStyle = (enum output_style) patchStyle;
386 m_contextLines = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_CONTEXT_LINES);
387 if (m_contextLines < 0 || m_contextLines > 50)
390 m_openToEditor = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_OPEN_TO_EDITOR);
391 m_includeCmdLine = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_INCLUDE_CMD_LINE);
392 m_copyToClipboard = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_COPY_TO_CLIPBOARD);
398 * @brief Saves patch dialog settings to registry.
400 void CPatchDlg::SaveSettings()
402 COptionsMgr *pOptions = GetOptionsMgr();
403 pOptions->SaveOption(OPT_PATCHCREATOR_PATCH_STYLE, m_outputStyle);
404 pOptions->SaveOption(OPT_PATCHCREATOR_CONTEXT_LINES, m_contextLines);
405 pOptions->SaveOption(OPT_PATCHCREATOR_OPEN_TO_EDITOR, m_openToEditor);
406 pOptions->SaveOption(OPT_PATCHCREATOR_INCLUDE_CMD_LINE, m_includeCmdLine);
407 pOptions->SaveOption(OPT_PATCHCREATOR_COPY_TO_CLIPBOARD, m_copyToClipboard);
411 * @brief Resets patch dialog settings to defaults.
413 void CPatchDlg::OnDefaultSettings()
415 m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
417 m_openToEditor = false;
418 m_includeCmdLine = false;
419 m_copyToClipboard = false;
424 void CPatchDlg::OnSelchangeFile1()
426 if (m_fileList.size() > 1)
428 m_ctlFile2.SetWindowText(_T(""));
433 void CPatchDlg::OnSelchangeFile2()
435 if (m_fileList.size() > 1)
437 m_ctlFile1.SetWindowText(_T(""));
442 void CPatchDlg::OnEditchangeFile1()
444 if (m_fileList.size() > 1)
446 m_ctlFile2.SetWindowText(_T(""));
451 void CPatchDlg::OnEditchangeFile2()
453 if (m_fileList.size() > 1)
455 m_ctlFile1.SetWindowText(_T(""));
464 void CPatchDlg::Swap()
466 std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
467 std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
468 while (iter != iterEnd)
470 (*iter).swap_sides();