1 /////////////////////////////////////////////////////////////////////////////
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or (at
6 // your option) any later version.
8 // This program is distributed in the hope that it will be useful, but
9 // WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
20 * @brief Implementation of Patch creation dialog
25 #include "PatchTool.h"
28 #include "CompareOptions.h"
29 #include "FileOrFolderSelect.h"
30 #include "Environment.h"
38 /////////////////////////////////////////////////////////////////////////////
42 * @brief Constructor, initializes members.
44 CPatchDlg::CPatchDlg(CWnd* pParent /*=NULL*/)
45 : CTrDialog(CPatchDlg::IDD, pParent)
46 , m_caseSensitive(FALSE)
48 , m_ignoreEOLDifference(FALSE)
49 , m_whitespaceCompare(0)
51 , m_openToEditor(FALSE)
52 , m_includeCmdLine(FALSE)
53 , m_outputStyle(OUTPUT_NORMAL)
59 * @brief Map dialog controls and class member variables.
61 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
63 CTrDialog::DoDataExchange(pDX);
64 //{{AFX_DATA_MAP(CPatchDlg)
65 DDX_Control(pDX, IDC_DIFF_STYLE, m_comboStyle);
66 DDX_Control(pDX, IDC_DIFF_CONTEXT, m_comboContext);
67 DDX_Check(pDX, IDC_DIFF_CASESENSITIVE, m_caseSensitive);
68 DDX_Check(pDX, IDC_DIFF_WHITESPACE_IGNOREBLANKS, m_ignoreBlanks);
69 DDX_Radio(pDX, IDC_DIFF_WHITESPACE_COMPARE, m_whitespaceCompare);
70 DDX_Check(pDX, IDC_DIFF_IGNOREEOL, m_ignoreEOLDifference);
71 DDX_Check(pDX, IDC_DIFF_APPENDFILE, m_appendFile);
72 DDX_Control(pDX, IDC_DIFF_FILE1, m_ctlFile1);
73 DDX_Control(pDX, IDC_DIFF_FILE2, m_ctlFile2);
74 DDX_Control(pDX, IDC_DIFF_FILERESULT, m_ctlResult);
75 DDX_CBStringExact(pDX, IDC_DIFF_FILE1, m_file1);
76 DDX_CBStringExact(pDX, IDC_DIFF_FILE2, m_file2);
77 DDX_CBStringExact(pDX, IDC_DIFF_FILERESULT, m_fileResult);
78 DDX_Check(pDX, IDC_DIFF_OPENTOEDITOR, m_openToEditor);
79 DDX_Check(pDX, IDC_DIFF_INCLCMDLINE, m_includeCmdLine);
84 BEGIN_MESSAGE_MAP(CPatchDlg, CTrDialog)
85 //{{AFX_MSG_MAP(CPatchDlg)
86 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE1, OnDiffBrowseFile1)
87 ON_BN_CLICKED(IDC_DIFF_BROWSE_FILE2, OnDiffBrowseFile2)
88 ON_BN_CLICKED(IDC_DIFF_BROWSE_RESULT, OnDiffBrowseResult)
89 ON_BN_CLICKED(IDC_DIFF_DEFAULTS, OnDefaultSettings)
90 ON_CBN_SELCHANGE(IDC_DIFF_FILE1, OnSelchangeFile1Combo)
91 ON_CBN_SELCHANGE(IDC_DIFF_FILE2, OnSelchangeFile2Combo)
92 ON_CBN_SELCHANGE(IDC_DIFF_FILERESULT, OnSelchangeResultCombo)
93 ON_CBN_SELCHANGE(IDC_DIFF_STYLE, OnSelchangeDiffStyle)
94 ON_BN_CLICKED(IDC_DIFF_SWAPFILES, OnDiffSwapFiles)
98 /////////////////////////////////////////////////////////////////////////////
99 // CPatchDlg message handlers
102 * @brief Called when dialog is closed with OK.
103 * Check options and filenames given and close the dialog.
105 void CPatchDlg::OnOK()
109 // There are two different cases: single files or
110 // multiple files. Multiple files are selected from DirView.
111 // Only if single files selected, filenames are checked here.
112 // Filenames read from Dirview must be valid ones.
113 size_t selectCount = m_fileList.size();
114 if (selectCount == 1)
116 bool file1Ok = (paths::DoesPathExist(m_file1) == paths::IS_EXISTING_FILE);
117 bool file2Ok = (paths::DoesPathExist(m_file2) == paths::IS_EXISTING_FILE);
119 if (!file1Ok || !file2Ok)
122 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
125 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
130 // Check that result (patch) file is absolute path
131 if (!paths::IsPathAbsolute(m_fileResult))
133 if (m_fileResult.length() == 0)
135 TCHAR szTempFile[MAX_PATH];
136 ::GetTempFileName(env::GetTemporaryPath().c_str(), _T("pat"), 0, szTempFile);
137 m_fileResult = szTempFile;
138 m_ctlResult.SetWindowText(m_fileResult.c_str());
139 DeleteFile(m_fileResult.c_str());
141 if (paths::IsPathAbsolute(m_fileResult) == FALSE)
143 String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
145 AfxMessageBox(msg.c_str(), MB_ICONSTOP);
146 m_ctlResult.SetFocus();
151 bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
153 // Result file already exists and append not selected
154 if (fileExists && !m_appendFile)
156 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
157 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
158 IDS_DIFF_FILEOVERWRITE) != IDYES)
163 // else it's OK to write new file
165 switch (m_comboStyle.GetCurSel())
167 case 1: m_outputStyle = (enum output_style)OUTPUT_CONTEXT; break;
168 case 2: m_outputStyle = (enum output_style)OUTPUT_UNIFIED; break;
169 case 3: m_outputStyle = (enum output_style)OUTPUT_HTML; break;
170 default: m_outputStyle = (enum output_style)OUTPUT_NORMAL; break;
173 int contextSel = m_comboContext.GetCurSel();
174 if (contextSel != CB_ERR)
177 m_comboContext.GetLBText(contextSel, PopString(contextText));
178 m_contextLines = std::stoi(contextText);
185 // Save combobox history
186 m_ctlResult.SaveState(_T("Files\\DiffFileResult"));
187 m_comboContext.SaveState(_T("PatchCreator\\DiffContext"));
188 // Don't save filenames if multiple file selected (as editbox reads
189 // [X files selected])
190 if (selectCount <= 1)
192 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
193 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
200 * @brief Initialise dialog data.
202 * There are two cases for filename editboxes:
203 * - if one file was added to list then we show that filename
204 * - if multiple files were added we show text [X files selected]
206 BOOL CPatchDlg::OnInitDialog()
208 CTrDialog::OnInitDialog();
210 // Load combobox history
211 m_ctlFile1.LoadState(_T("Files\\DiffFile1"));
212 m_ctlFile2.LoadState(_T("Files\\DiffFile2"));
213 m_comboContext.LoadState(_T("PatchCreator\\DiffContext"));
214 m_ctlResult.LoadState(_T("Files\\DiffFileResult"));
216 size_t count = m_fileList.size();
218 // If one file added, show filenames on dialog
221 const PATCHFILES& files = m_fileList.front();
222 m_file1 = files.lfile;
223 m_ctlFile1.SetWindowText(files.lfile.c_str());
224 m_file2 = files.rfile;
225 m_ctlFile2.SetWindowText(files.rfile.c_str());
227 else if (count > 1) // Multiple files added, show number of files
229 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count)).c_str();
233 // Add patch styles to combobox
234 m_comboStyle.AddString(_("Normal").c_str());
235 m_comboStyle.AddString(_("Context").c_str());
236 m_comboStyle.AddString(_("Unified").c_str());
237 m_comboStyle.AddString(_("HTML").c_str());
239 m_outputStyle = OUTPUT_NORMAL;
240 m_comboStyle.SetCurSel(0);
242 // Add default context line counts to combobox if its empty
243 if (m_comboContext.GetCount() == 0)
245 m_comboContext.AddString(_T("0"));
246 m_comboContext.AddString(_T("1"));
247 m_comboContext.AddString(_T("3"));
248 m_comboContext.AddString(_T("5"));
249 m_comboContext.AddString(_T("7"));
254 return TRUE; // return TRUE unless you set the focus to a control
255 // EXCEPTION: OCX Property Pages should return FALSE
259 * @brief Select the left file.
261 void CPatchDlg::OnDiffBrowseFile1()
267 if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Open"), _T(""), TRUE))
270 m_ctlFile1.SetWindowText(s.c_str());
275 * @brief Select the right file.
277 void CPatchDlg::OnDiffBrowseFile2()
283 if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Open"), _T(""), TRUE))
285 ChangeFile(s, false);
286 m_ctlFile2.SetWindowText(s.c_str());
291 * @brief Changes original file to patch.
292 * This function sets new file for left/right file to create patch from.
293 * @param [in] sFile New file for patch creation.
294 * @param [in] bLeft If true left file is changed, otherwise right file.
296 void CPatchDlg::ChangeFile(const String &sFile, bool bLeft)
299 size_t count = GetItemCount();
329 * @brief Select the patch file.
331 void CPatchDlg::OnDiffBrowseResult()
336 folder = m_fileResult;
337 if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Save As"), _T(""), FALSE))
340 m_ctlResult.SetWindowText(s.c_str());
345 * @brief Called when File1 combo selection is changed.
347 void CPatchDlg::OnSelchangeFile1Combo()
349 int sel = m_ctlFile1.GetCurSel();
352 m_ctlFile1.GetLBText(sel, PopString(m_file1));
353 m_ctlFile1.SetWindowText(m_file1.c_str());
354 ChangeFile(m_file1, true);
359 * @brief Called when File2 combo selection is changed.
361 void CPatchDlg::OnSelchangeFile2Combo()
363 int sel = m_ctlFile2.GetCurSel();
366 m_ctlFile2.GetLBText(sel, PopString(m_file2));
367 m_ctlFile2.SetWindowText(m_file2.c_str());
368 ChangeFile(m_file2, false);
373 * @brief Called when Result combo selection is changed.
375 void CPatchDlg::OnSelchangeResultCombo()
377 int sel = m_ctlResult.GetCurSel();
380 m_ctlResult.GetLBText(sel, PopString(m_fileResult));
381 m_ctlResult.SetWindowText(m_fileResult.c_str());
386 * @brief Called when diff style dropdown selection is changed.
387 * Called when diff style dropdown selection is changed.
388 * If the new selection is context patch or unified patch format then
389 * enable context lines selection control. Otherwise context lines selection
392 void CPatchDlg::OnSelchangeDiffStyle()
394 int selection = m_comboStyle.GetCurSel();
396 // Only context and unified formats allow context lines
397 if (selection != OUTPUT_NORMAL)
399 m_comboContext.EnableWindow(TRUE);
400 // 3 lines is default context for Difftools too
401 m_comboContext.SetCurSel(2);
406 m_comboContext.SetCurSel(0);
407 m_comboContext.EnableWindow(FALSE);
412 * @brief Swap filenames on file1 and file2.
414 void CPatchDlg::OnDiffSwapFiles()
418 m_ctlFile1.GetWindowText(PopString(m_file1));
419 m_ctlFile2.GetWindowText(PopString(m_file2));
421 m_ctlFile1.SetWindowText(m_file2.c_str());
422 m_ctlFile2.SetWindowText(m_file1.c_str());
429 * @brief Add patch item to internal list.
430 * @param [in] pf Patch item to add.
432 void CPatchDlg::AddItem(const PATCHFILES& pf)
434 m_fileList.push_back(pf);
438 * @brief Returns amount of patch items in the internal list.
439 * @return Count of patch items in the list.
441 size_t CPatchDlg::GetItemCount()
443 return m_fileList.size();
447 * @brief Return item in the internal list at given position
448 * @param [in] position Zero-based index of item to get
449 * @return PATCHFILES from given position.
451 const PATCHFILES& CPatchDlg::GetItemAt(size_t position)
453 return m_fileList.at(position);
457 * @brief Empties internal item list.
459 void CPatchDlg::ClearItems()
465 * @brief Updates patch dialog settings from member variables.
467 void CPatchDlg::UpdateSettings()
471 switch (m_outputStyle)
473 case DIFF_OUTPUT_NORMAL:
474 m_comboStyle.SelectString(-1, _("Normal").c_str());
476 case DIFF_OUTPUT_CONTEXT:
477 m_comboStyle.SelectString(-1, _("Context").c_str());
479 case DIFF_OUTPUT_UNIFIED:
480 m_comboStyle.SelectString(-1, _("Unified").c_str());
482 case DIFF_OUTPUT_HTML:
483 m_comboStyle.SelectString(-1, _("HTML").c_str());
487 m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
489 if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
490 m_comboContext.EnableWindow(TRUE);
492 m_comboContext.EnableWindow(FALSE);
496 * @brief Loads patch dialog settings from registry.
498 void CPatchDlg::LoadSettings()
500 int patchStyle = AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("PatchStyle"), 0);
501 if ((patchStyle < DIFF_OUTPUT_NORMAL || patchStyle > DIFF_OUTPUT_UNIFIED) && patchStyle != DIFF_OUTPUT_HTML)
502 patchStyle = DIFF_OUTPUT_NORMAL;
503 m_outputStyle = (enum output_style) patchStyle;
505 m_contextLines = AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("ContextLines"), 0);
506 if (m_contextLines < 0 || m_contextLines > 50)
509 m_caseSensitive = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("CaseSensitive"), true);
510 m_ignoreEOLDifference = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("EOLSensitive"), true);
511 m_ignoreBlanks = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("IgnoreBlankLines"), false);
513 m_whitespaceCompare = AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("Whitespace"), WHITESPACE_COMPARE_ALL);
514 if (m_whitespaceCompare < WHITESPACE_COMPARE_ALL ||
515 m_whitespaceCompare > WHITESPACE_IGNORE_ALL)
517 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
520 m_openToEditor = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("OpenToEditor"), false);
521 m_includeCmdLine = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("IncludeCmdLine"), false);
527 * @brief Saves patch dialog settings to registry.
529 void CPatchDlg::SaveSettings()
531 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("PatchStyle"), m_outputStyle);
532 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("ContextLines"), m_contextLines);
533 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("CaseSensitive"), m_caseSensitive);
534 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("EOLSensitive"), m_ignoreEOLDifference);
535 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("IgnoreBlankLines"), m_ignoreBlanks);
536 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("Whitespace"), m_whitespaceCompare);
537 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("OpenToEditor"), m_openToEditor);
538 AfxGetApp()->WriteProfileInt(_T("PatchCreator"), _T("IncludeCmdLine"), m_includeCmdLine);
542 * @brief Resets patch dialog settings to defaults.
544 void CPatchDlg::OnDefaultSettings()
546 m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
548 m_caseSensitive = TRUE;
549 m_ignoreEOLDifference = FALSE;
550 m_ignoreBlanks = FALSE;
551 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
552 m_openToEditor = FALSE;
553 m_includeCmdLine = FALSE;
561 void CPatchDlg::Swap()
563 std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
564 std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
565 while (iter != iterEnd)
567 (*iter).swap_sides();