OSDN Git Service

resource.h: Add IDS_PLUGIN_DESCRIPTION*
[winmerge-jp/winmerge-jp.git] / Src / PatchDlg.cpp
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** 
3  * @file  PatchDlg.cpp
4  *
5  * @brief Implementation of Patch creation dialog
6  */
7
8 #include "StdAfx.h"
9 #include "PatchDlg.h"
10 #include "PatchTool.h"
11 #include "diff.h"
12 #include "paths.h"
13 #include "CompareOptions.h"
14 #include "FileOrFolderSelect.h"
15 #include "Environment.h"
16 #include "OptionsDef.h"
17 #include "OptionsMgr.h"
18
19 #ifdef _DEBUG
20 #define new DEBUG_NEW
21 #endif
22
23 using std::swap;
24
25 /////////////////////////////////////////////////////////////////////////////
26 // CPatchDlg dialog
27
28 /** 
29  * @brief Constructor, initializes members.
30  */
31 CPatchDlg::CPatchDlg(CWnd* pParent /*= nullptr*/)
32         : CTrDialog(CPatchDlg::IDD, pParent)
33         , m_ignoreCase(false)
34         , m_ignoreBlanks(false)
35         , m_ignoreEOLDifference(false)
36         , m_whitespaceCompare(0)
37         , m_appendFile(false)
38         , m_openToEditor(false)
39         , m_includeCmdLine(false)
40         , m_outputStyle(OUTPUT_NORMAL)
41         , m_contextLines(0)
42         , m_diffAlgorithm(DIFF_ALGORITHM_DEFAULT)
43         , m_indentHeuristic(true)
44 {
45 }
46
47 /**
48  * @brief Map dialog controls and class member variables.
49  */
50 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
51 {
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);
69         //}}AFX_DATA_MAP
70 }
71
72
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)
85         //}}AFX_MSG_MAP
86 END_MESSAGE_MAP()
87
88 /////////////////////////////////////////////////////////////////////////////
89 // CPatchDlg message handlers
90
91 /** 
92  * @brief Called when dialog is closed with OK.
93  * Check options and filenames given and close the dialog.
94  */
95 void CPatchDlg::OnOK()
96 {
97         UpdateData(TRUE);
98
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)
105         {
106                 PATCHFILES tFiles;
107                 tFiles.lfile = m_file1;
108                 tFiles.rfile = m_file2;
109                 AddItem(tFiles);
110                 selectCount = 1;
111         }
112         if (selectCount == 1)
113         {
114                 bool file1Ok = (paths::DoesPathExist(m_file1) != paths::DOES_NOT_EXIST);
115                 bool file2Ok = (paths::DoesPathExist(m_file2) != paths::DOES_NOT_EXIST);
116
117                 if (!file1Ok || !file2Ok)
118                 {
119                         if (!file1Ok)
120                                 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
121
122                         if (!file2Ok)
123                                 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
124                         return;
125                 }
126
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;
135         }
136
137         // Check that result (patch) file is absolute path
138         if (!paths::IsPathAbsolute(m_fileResult))
139         {
140                 if (m_fileResult.length() == 0)
141                 {
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());
147                 }
148                 if (!paths::IsPathAbsolute(m_fileResult))
149                 {
150                         String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
151                                 m_fileResult);
152                         AfxMessageBox(msg.c_str(), MB_ICONSTOP);
153                         m_ctlResult.SetFocus();
154                         return;
155                 }
156         }
157         
158         bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
159
160         // Result file already exists and append not selected
161         if (fileExists && !m_appendFile)
162         {
163                 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
164                                 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
165                                 IDS_DIFF_FILEOVERWRITE) != IDYES)
166                 {
167                         return;
168                 }
169         }
170         // else it's OK to write new file
171
172         switch (m_comboStyle.GetCurSel())
173         {
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;
178         }
179
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);
183
184         SaveSettings();
185
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)
192         {
193                 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
194                 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
195         }
196         
197         CTrDialog::OnOK();
198 }
199
200 /** 
201  * @brief Initialise dialog data.
202  *
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]
206  */
207 BOOL CPatchDlg::OnInitDialog()
208 {
209         CTrDialog::OnInitDialog();
210
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"));
216
217         size_t count = m_fileList.size();
218
219         // If one file added, show filenames on dialog
220         if (count == 1)
221         {
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());
227         }
228         else if (count > 1)     // Multiple files added, show number of files
229         {
230                 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count));
231         }
232         UpdateData(FALSE);
233
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());
239
240         m_outputStyle = OUTPUT_NORMAL;
241         m_comboStyle.SetCurSel(0);
242
243         // Add default context line counts to combobox if its empty
244         if (m_comboContext.GetCount() == 0)
245         {
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"));
251         }
252         
253         LoadSettings();
254
255         return TRUE;  // return TRUE unless you set the focus to a control
256                       // EXCEPTION: OCX Property Pages should return FALSE
257 }
258
259 /** 
260  * @brief Select the left file.
261  */
262 void CPatchDlg::OnDiffBrowseFile1()
263 {
264         String s;
265         String folder;
266
267         folder = m_file1;
268         if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
269         {
270                 m_ctlFile1.SetWindowText(s.c_str());
271                 if (m_fileList.size() > 1)
272                 {
273                         m_ctlFile2.SetWindowText(_T(""));
274                         ClearItems();
275                 }
276         }
277 }
278
279 /** 
280  * @brief Select the right file.
281  */
282 void CPatchDlg::OnDiffBrowseFile2()
283 {
284         String s;
285         String folder;
286
287         folder = m_file2;
288         if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
289         {
290                 m_ctlFile2.SetWindowText(s.c_str());
291                 if (m_fileList.size() > 1)
292                 {
293                         m_ctlFile1.SetWindowText(_T(""));
294                         ClearItems();
295                 }
296         }
297 }
298
299 /** 
300  * @brief Select the patch file.
301  */
302 void CPatchDlg::OnDiffBrowseResult()
303 {
304         String s;
305         String folder;
306
307         folder = m_fileResult;
308         if (SelectFile(GetSafeHwnd(), s, false, folder.c_str()))
309                 m_ctlResult.SetWindowText(s.c_str());
310 }
311
312 /** 
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
317  * is disabled.
318  */
319 void CPatchDlg::OnSelchangeDiffStyle()
320 {
321         int selection = m_comboStyle.GetCurSel();
322
323         // Only context and unified formats allow context lines
324         if (selection != OUTPUT_NORMAL)
325         {
326                 m_comboContext.EnableWindow(TRUE);
327                 // 3 lines is default context for Difftools too
328                 m_comboContext.SetCurSel(2);
329         }
330         else
331         {
332                 m_contextLines = 0;
333                 m_comboContext.SetCurSel(0);
334                 m_comboContext.EnableWindow(FALSE);
335         }
336 }
337
338 /** 
339  * @brief Swap filenames on file1 and file2.
340  */
341 void CPatchDlg::OnDiffSwapFiles()
342 {
343         CString cstrFile1 = m_file1.c_str();
344         CString cstrFile2 = m_file2.c_str();
345         m_ctlFile1.GetWindowText(cstrFile1);
346         m_ctlFile2.GetWindowText(cstrFile2);
347
348         m_ctlFile1.SetWindowText(cstrFile2);
349         m_ctlFile2.SetWindowText(cstrFile1);
350
351         //  swapped files
352         Swap();
353 }
354
355 /** 
356  * @brief Updates patch dialog settings from member variables.
357  */
358 void CPatchDlg::UpdateSettings()
359 {
360         UpdateData(FALSE);
361
362         switch (m_outputStyle)
363         {
364         case DIFF_OUTPUT_NORMAL:
365                 m_comboStyle.SelectString(-1, _("Normal").c_str());
366                 break;
367         case DIFF_OUTPUT_CONTEXT:
368                 m_comboStyle.SelectString(-1, _("Context").c_str());
369                 break;
370         case DIFF_OUTPUT_UNIFIED:
371                 m_comboStyle.SelectString(-1, _("Unified").c_str());
372                 break;
373         case DIFF_OUTPUT_HTML:
374                 m_comboStyle.SelectString(-1, _("HTML").c_str());
375                 break;
376         }
377
378         m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
379
380         if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
381                 m_comboContext.EnableWindow(TRUE);
382         else
383                 m_comboContext.EnableWindow(FALSE);
384 }
385
386 /** 
387  * @brief Loads patch dialog settings from registry.
388  */
389 void CPatchDlg::LoadSettings()
390 {
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;
395         
396         m_contextLines = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_CONTEXT_LINES);
397         if (m_contextLines < 0 || m_contextLines > 50)
398                 m_contextLines = 0;
399
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);
403         
404         m_whitespaceCompare = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_WHITESPACE);
405         if (m_whitespaceCompare < WHITESPACE_COMPARE_ALL ||
406                 m_whitespaceCompare > WHITESPACE_IGNORE_ALL)
407         {
408                 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
409         }
410         
411         m_openToEditor = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_OPEN_TO_EDITOR);
412         m_includeCmdLine = GetOptionsMgr()->GetBool(OPT_PATCHCREATOR_INCLUDE_CMD_LINE);
413
414         UpdateSettings();
415 }
416
417 /** 
418  * @brief Saves patch dialog settings to registry.
419  */
420 void CPatchDlg::SaveSettings()
421 {
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);
431 }
432
433 /** 
434  * @brief Resets patch dialog settings to defaults.
435  */
436 void CPatchDlg::OnDefaultSettings()
437 {
438         m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
439         m_contextLines = 0;
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;
446
447         UpdateSettings();
448 }
449
450 void CPatchDlg::OnSelchangeFile1()
451 {
452         if (m_fileList.size() > 1)
453         {
454                 m_ctlFile2.SetWindowText(_T(""));
455                 ClearItems();
456         }
457 }
458
459 void CPatchDlg::OnSelchangeFile2()
460 {
461         if (m_fileList.size() > 1)
462         {
463                 m_ctlFile1.SetWindowText(_T(""));
464                 ClearItems();
465         }
466 }
467
468 void CPatchDlg::OnEditchangeFile1()
469 {
470         if (m_fileList.size() > 1)
471         {
472                 m_ctlFile2.SetWindowText(_T(""));
473                 ClearItems();
474         }
475 }
476
477 void CPatchDlg::OnEditchangeFile2()
478 {
479         if (m_fileList.size() > 1)
480         {
481                 m_ctlFile1.SetWindowText(_T(""));
482                 ClearItems();
483         }
484 }
485
486
487 /**
488  * @brief Swap sides.
489  */
490 void CPatchDlg::Swap()
491 {
492         std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
493         std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
494         while (iter != iterEnd)
495         {
496                 (*iter).swap_sides();
497                 ++iter;
498         }
499 }