OSDN Git Service

Update ChangeLog&ReleaseNotes (3)
[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_copyToClipboard(false)
34         , m_appendFile(false)
35         , m_openToEditor(false)
36         , m_includeCmdLine(false)
37         , m_outputStyle(OUTPUT_NORMAL)
38         , m_contextLines(0)
39 {
40 }
41
42 /**
43  * @brief Map dialog controls and class member variables.
44  */
45 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
46 {
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);
61         //}}AFX_DATA_MAP
62 }
63
64
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)
77         //}}AFX_MSG_MAP
78 END_MESSAGE_MAP()
79
80 /////////////////////////////////////////////////////////////////////////////
81 // CPatchDlg message handlers
82
83 /** 
84  * @brief Called when dialog is closed with OK.
85  * Check options and filenames given and close the dialog.
86  */
87 void CPatchDlg::OnOK()
88 {
89         UpdateData(TRUE);
90
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();
96         if (selectCount == 0)
97         {
98                 PATCHFILES tFiles;
99                 tFiles.lfile = m_file1;
100                 tFiles.rfile = m_file2;
101                 AddItem(tFiles);
102                 selectCount = 1;
103         }
104         if (selectCount == 1)
105         {
106                 bool file1Ok = (paths::DoesPathExist(m_file1) != paths::DOES_NOT_EXIST);
107                 bool file2Ok = (paths::DoesPathExist(m_file2) != paths::DOES_NOT_EXIST);
108
109                 if (!file1Ok || !file2Ok)
110                 {
111                         if (!file1Ok)
112                                 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
113
114                         if (!file2Ok)
115                                 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
116                         return;
117                 }
118
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;
127         }
128
129         // Check that result (patch) file is absolute path
130         if (!paths::IsPathAbsolute(m_fileResult))
131         {
132                 if (m_fileResult.length() == 0)
133                 {
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());
139                 }
140                 if (!paths::IsPathAbsolute(m_fileResult))
141                 {
142                         String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
143                                 m_fileResult);
144                         AfxMessageBox(msg.c_str(), MB_ICONSTOP);
145                         m_ctlResult.SetFocus();
146                         return;
147                 }
148         }
149         
150         bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
151
152         // Result file already exists and append not selected
153         if (fileExists && !m_appendFile)
154         {
155                 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
156                                 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
157                                 IDS_DIFF_FILEOVERWRITE) != IDYES)
158                 {
159                         return;
160                 }
161         }
162         // else it's OK to write new file
163
164         switch (m_comboStyle.GetCurSel())
165         {
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;
170         }
171
172         m_contextLines = GetDlgItemInt(IDC_DIFF_CONTEXT);
173
174         SaveSettings();
175
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)
182         {
183                 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
184                 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
185         }
186         
187         CTrDialog::OnOK();
188 }
189
190 /** 
191  * @brief Initialise dialog data.
192  *
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]
196  */
197 BOOL CPatchDlg::OnInitDialog()
198 {
199         CTrDialog::OnInitDialog();
200
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"));
206
207         size_t count = m_fileList.size();
208
209         // If one file added, show filenames on dialog
210         if (count == 1)
211         {
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());
217         }
218         else if (count > 1)     // Multiple files added, show number of files
219         {
220                 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count));
221         }
222         UpdateData(FALSE);
223
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());
229
230         m_outputStyle = OUTPUT_NORMAL;
231         m_comboStyle.SetCurSel(0);
232
233         // Add default context line counts to combobox if its empty
234         if (m_comboContext.GetCount() == 0)
235         {
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"));
241         }
242         
243         LoadSettings();
244
245         return TRUE;  // return TRUE unless you set the focus to a control
246                       // EXCEPTION: OCX Property Pages should return FALSE
247 }
248
249 /** 
250  * @brief Select the left file.
251  */
252 void CPatchDlg::OnDiffBrowseFile1()
253 {
254         String s;
255         String folder;
256
257         folder = m_file1;
258         if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
259         {
260                 m_ctlFile1.SetWindowText(s.c_str());
261                 if (m_fileList.size() > 1)
262                 {
263                         m_ctlFile2.SetWindowText(_T(""));
264                         ClearItems();
265                 }
266         }
267 }
268
269 /** 
270  * @brief Select the right file.
271  */
272 void CPatchDlg::OnDiffBrowseFile2()
273 {
274         String s;
275         String folder;
276
277         folder = m_file2;
278         if (SelectFileOrFolder(GetSafeHwnd(), s, folder.c_str()))
279         {
280                 m_ctlFile2.SetWindowText(s.c_str());
281                 if (m_fileList.size() > 1)
282                 {
283                         m_ctlFile1.SetWindowText(_T(""));
284                         ClearItems();
285                 }
286         }
287 }
288
289 /** 
290  * @brief Select the patch file.
291  */
292 void CPatchDlg::OnDiffBrowseResult()
293 {
294         String s;
295         String folder;
296
297         folder = m_fileResult;
298         if (SelectFile(GetSafeHwnd(), s, false, folder.c_str()))
299                 m_ctlResult.SetWindowText(s.c_str());
300 }
301
302 /** 
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
307  * is disabled.
308  */
309 void CPatchDlg::OnSelchangeDiffStyle()
310 {
311         int selection = m_comboStyle.GetCurSel();
312
313         // Only context and unified formats allow context lines
314         if (selection != OUTPUT_NORMAL)
315         {
316                 m_comboContext.EnableWindow(TRUE);
317                 // 3 lines is default context for Difftools too
318                 m_comboContext.SetCurSel(2);
319         }
320         else
321         {
322                 m_contextLines = 0;
323                 m_comboContext.SetCurSel(0);
324                 m_comboContext.EnableWindow(FALSE);
325         }
326 }
327
328 /** 
329  * @brief Swap filenames on file1 and file2.
330  */
331 void CPatchDlg::OnDiffSwapFiles()
332 {
333         CString cstrFile1 = m_file1.c_str();
334         CString cstrFile2 = m_file2.c_str();
335         m_ctlFile1.GetWindowText(cstrFile1);
336         m_ctlFile2.GetWindowText(cstrFile2);
337
338         m_ctlFile1.SetWindowText(cstrFile2);
339         m_ctlFile2.SetWindowText(cstrFile1);
340
341         //  swapped files
342         Swap();
343 }
344
345 /** 
346  * @brief Updates patch dialog settings from member variables.
347  */
348 void CPatchDlg::UpdateSettings()
349 {
350         UpdateData(FALSE);
351
352         switch (m_outputStyle)
353         {
354         case DIFF_OUTPUT_NORMAL:
355                 m_comboStyle.SelectString(-1, _("Normal").c_str());
356                 break;
357         case DIFF_OUTPUT_CONTEXT:
358                 m_comboStyle.SelectString(-1, _("Context").c_str());
359                 break;
360         case DIFF_OUTPUT_UNIFIED:
361                 m_comboStyle.SelectString(-1, _("Unified").c_str());
362                 break;
363         case DIFF_OUTPUT_HTML:
364                 m_comboStyle.SelectString(-1, _("HTML").c_str());
365                 break;
366         }
367
368         m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
369
370         if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
371                 m_comboContext.EnableWindow(TRUE);
372         else
373                 m_comboContext.EnableWindow(FALSE);
374 }
375
376 /** 
377  * @brief Loads patch dialog settings from registry.
378  */
379 void CPatchDlg::LoadSettings()
380 {
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;
385         
386         m_contextLines = GetOptionsMgr()->GetInt(OPT_PATCHCREATOR_CONTEXT_LINES);
387         if (m_contextLines < 0 || m_contextLines > 50)
388                 m_contextLines = 0;
389
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);
393
394         UpdateSettings();
395 }
396
397 /** 
398  * @brief Saves patch dialog settings to registry.
399  */
400 void CPatchDlg::SaveSettings()
401 {
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);
408 }
409
410 /** 
411  * @brief Resets patch dialog settings to defaults.
412  */
413 void CPatchDlg::OnDefaultSettings()
414 {
415         m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
416         m_contextLines = 0;
417         m_openToEditor = false;
418         m_includeCmdLine = false;
419         m_copyToClipboard = false;
420
421         UpdateSettings();
422 }
423
424 void CPatchDlg::OnSelchangeFile1()
425 {
426         if (m_fileList.size() > 1)
427         {
428                 m_ctlFile2.SetWindowText(_T(""));
429                 ClearItems();
430         }
431 }
432
433 void CPatchDlg::OnSelchangeFile2()
434 {
435         if (m_fileList.size() > 1)
436         {
437                 m_ctlFile1.SetWindowText(_T(""));
438                 ClearItems();
439         }
440 }
441
442 void CPatchDlg::OnEditchangeFile1()
443 {
444         if (m_fileList.size() > 1)
445         {
446                 m_ctlFile2.SetWindowText(_T(""));
447                 ClearItems();
448         }
449 }
450
451 void CPatchDlg::OnEditchangeFile2()
452 {
453         if (m_fileList.size() > 1)
454         {
455                 m_ctlFile1.SetWindowText(_T(""));
456                 ClearItems();
457         }
458 }
459
460
461 /**
462  * @brief Swap sides.
463  */
464 void CPatchDlg::Swap()
465 {
466         std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
467         std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
468         while (iter != iterEnd)
469         {
470                 (*iter).swap_sides();
471                 ++iter;
472         }
473 }