OSDN Git Service

Fix untranslated strings
[winmerge-jp/winmerge-jp.git] / Src / PatchDlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
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.
7 //    
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.
12 //
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 /////////////////////////////////////////////////////////////////////////////
17 /** 
18  * @file  PatchDlg.cpp
19  *
20  * @brief Implementation of Patch creation dialog
21  */
22
23 #include "StdAfx.h"
24 #include "PatchDlg.h"
25 #include "PatchTool.h"
26 #include "diff.h"
27 #include "paths.h"
28 #include "CompareOptions.h"
29 #include "FileOrFolderSelect.h"
30 #include "Environment.h"
31
32 #ifdef _DEBUG
33 #define new DEBUG_NEW
34 #endif
35
36 using std::swap;
37
38 /////////////////////////////////////////////////////////////////////////////
39 // CPatchDlg dialog
40
41 /** 
42  * @brief Constructor, initializes members.
43  */
44 CPatchDlg::CPatchDlg(CWnd* pParent /*=NULL*/)
45         : CTrDialog(CPatchDlg::IDD, pParent)
46         , m_caseSensitive(FALSE)
47         , m_ignoreBlanks(0)
48         , m_ignoreEOLDifference(FALSE)
49         , m_whitespaceCompare(0)
50         , m_appendFile(FALSE)
51         , m_openToEditor(FALSE)
52         , m_includeCmdLine(FALSE)
53         , m_outputStyle(OUTPUT_NORMAL)
54         , m_contextLines(0)
55 {
56 }
57
58 /**
59  * @brief Map dialog controls and class member variables.
60  */
61 void CPatchDlg::DoDataExchange(CDataExchange* pDX)
62 {
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);
80         //}}AFX_DATA_MAP
81 }
82
83
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)
95         //}}AFX_MSG_MAP
96 END_MESSAGE_MAP()
97
98 /////////////////////////////////////////////////////////////////////////////
99 // CPatchDlg message handlers
100
101 /** 
102  * @brief Called when dialog is closed with OK.
103  * Check options and filenames given and close the dialog.
104  */
105 void CPatchDlg::OnOK()
106 {
107         UpdateData(TRUE);
108
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)
115         {
116                 bool file1Ok = (paths::DoesPathExist(m_file1) == paths::IS_EXISTING_FILE);
117                 bool file2Ok = (paths::DoesPathExist(m_file2) == paths::IS_EXISTING_FILE);
118
119                 if (!file1Ok || !file2Ok)
120                 {
121                         if (!file1Ok)
122                                 LangMessageBox(IDS_DIFF_ITEM1NOTFOUND, MB_ICONSTOP);
123
124                         if (!file2Ok)
125                                 LangMessageBox(IDS_DIFF_ITEM2NOTFOUND, MB_ICONSTOP);
126                         return;
127                 }
128         }
129
130         // Check that result (patch) file is absolute path
131         if (!paths::IsPathAbsolute(m_fileResult))
132         {
133                 if (m_fileResult.length() == 0)
134                 {
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());
140                 }
141                 if (paths::IsPathAbsolute(m_fileResult) == FALSE)
142                 {
143                         String msg = strutils::format_string1(_("The specified output path is not an absolute path: %1"),
144                                 m_fileResult);
145                         AfxMessageBox(msg.c_str(), MB_ICONSTOP);
146                         m_ctlResult.SetFocus();
147                         return;
148                 }
149         }
150         
151         bool fileExists = (paths::DoesPathExist(m_fileResult) == paths::IS_EXISTING_FILE);
152
153         // Result file already exists and append not selected
154         if (fileExists && !m_appendFile)
155         {
156                 if (LangMessageBox(IDS_DIFF_FILEOVERWRITE,
157                                 MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN,
158                                 IDS_DIFF_FILEOVERWRITE) != IDYES)
159                 {
160                         return;
161                 }
162         }
163         // else it's OK to write new file
164
165         switch (m_comboStyle.GetCurSel())
166         {
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;
171         }
172
173         int contextSel = m_comboContext.GetCurSel();
174         if (contextSel != CB_ERR)
175         {
176                 String contextText;
177                 m_comboContext.GetLBText(contextSel, PopString(contextText));
178                 m_contextLines = std::stoi(contextText);
179         }
180         else
181                 m_contextLines = 0;
182
183         SaveSettings();
184
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)
191         {
192                 m_ctlFile1.SaveState(_T("Files\\DiffFile1"));
193                 m_ctlFile2.SaveState(_T("Files\\DiffFile2"));
194         }
195         
196         CTrDialog::OnOK();
197 }
198
199 /** 
200  * @brief Initialise dialog data.
201  *
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]
205  */
206 BOOL CPatchDlg::OnInitDialog()
207 {
208         CTrDialog::OnInitDialog();
209
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"));
215
216         size_t count = m_fileList.size();
217
218         // If one file added, show filenames on dialog
219         if (count == 1)
220         {
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());
226         }
227         else if (count > 1)     // Multiple files added, show number of files
228         {
229                 m_file1 = m_file2 = strutils::format_string1(_("[%1 files selected]"), strutils::to_str(count)).c_str();
230         }
231         UpdateData(FALSE);
232
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());
238
239         m_outputStyle = OUTPUT_NORMAL;
240         m_comboStyle.SetCurSel(0);
241
242         // Add default context line counts to combobox if its empty
243         if (m_comboContext.GetCount() == 0)
244         {
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"));
250         }
251         
252         LoadSettings();
253
254         return TRUE;  // return TRUE unless you set the focus to a control
255                       // EXCEPTION: OCX Property Pages should return FALSE
256 }
257
258 /** 
259  * @brief Select the left file.
260  */
261 void CPatchDlg::OnDiffBrowseFile1()
262 {
263         String s;
264         String folder;
265
266         folder = m_file1;
267         if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Open"), _T(""), TRUE))
268         {
269                 ChangeFile(s, true);
270                 m_ctlFile1.SetWindowText(s.c_str());
271         }
272 }
273
274 /** 
275  * @brief Select the right file.
276  */
277 void CPatchDlg::OnDiffBrowseFile2()
278 {
279         String s;
280         String folder;
281
282         folder = m_file2;
283         if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Open"), _T(""), TRUE))
284         {
285                 ChangeFile(s, false);
286                 m_ctlFile2.SetWindowText(s.c_str());
287         }
288 }
289
290 /** 
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.
295  */
296 void CPatchDlg::ChangeFile(const String &sFile, bool bLeft)
297 {
298         PATCHFILES pf;
299         size_t count = GetItemCount();
300
301         if (count == 1)
302         {
303                 pf = GetItemAt(0);
304         }
305         else if (count > 1)
306         {
307                 if (bLeft)
308                         m_file1.clear();
309                 else
310                         m_file2.clear();
311         }
312         ClearItems();
313
314         // Change file
315         if (bLeft)
316         {
317                 pf.lfile = sFile;
318                 m_file1 = sFile;
319         }
320         else
321         {
322                 pf.rfile = sFile;
323                 m_file2 = sFile;
324         }
325         AddItem(pf);
326 }
327
328 /** 
329  * @brief Select the patch file.
330  */
331 void CPatchDlg::OnDiffBrowseResult()
332 {
333         String s;
334         String folder;
335
336         folder = m_fileResult;
337         if (SelectFile(GetSafeHwnd(), s, folder.c_str(), _("Save As"), _T(""), FALSE))
338         {
339                 m_fileResult = s;
340                 m_ctlResult.SetWindowText(s.c_str());
341         }
342 }
343
344 /** 
345  * @brief Called when File1 combo selection is changed.
346  */
347 void CPatchDlg::OnSelchangeFile1Combo() 
348 {
349         int sel = m_ctlFile1.GetCurSel();
350         if (sel != CB_ERR)
351         {
352                 m_ctlFile1.GetLBText(sel, PopString(m_file1));
353                 m_ctlFile1.SetWindowText(m_file1.c_str());
354                 ChangeFile(m_file1, true);
355         }
356 }
357
358 /** 
359  * @brief Called when File2 combo selection is changed.
360  */
361 void CPatchDlg::OnSelchangeFile2Combo() 
362 {
363         int sel = m_ctlFile2.GetCurSel();
364         if (sel != CB_ERR)
365         {
366                 m_ctlFile2.GetLBText(sel, PopString(m_file2));
367                 m_ctlFile2.SetWindowText(m_file2.c_str());
368                 ChangeFile(m_file2, false);
369         }
370 }
371
372 /** 
373  * @brief Called when Result combo selection is changed.
374  */
375 void CPatchDlg::OnSelchangeResultCombo() 
376 {
377         int sel = m_ctlResult.GetCurSel();
378         if (sel != CB_ERR)
379         {
380                 m_ctlResult.GetLBText(sel, PopString(m_fileResult));
381                 m_ctlResult.SetWindowText(m_fileResult.c_str());
382         }
383 }
384
385 /** 
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
390  * is disabled.
391  */
392 void CPatchDlg::OnSelchangeDiffStyle()
393 {
394         int selection = m_comboStyle.GetCurSel();
395
396         // Only context and unified formats allow context lines
397         if (selection != OUTPUT_NORMAL)
398         {
399                 m_comboContext.EnableWindow(TRUE);
400                 // 3 lines is default context for Difftools too
401                 m_comboContext.SetCurSel(2);
402         }
403         else
404         {
405                 m_contextLines = 0;
406                 m_comboContext.SetCurSel(0);
407                 m_comboContext.EnableWindow(FALSE);
408         }
409 }
410
411 /** 
412  * @brief Swap filenames on file1 and file2.
413  */
414 void CPatchDlg::OnDiffSwapFiles()
415 {
416         PATCHFILES files;
417
418         m_ctlFile1.GetWindowText(PopString(m_file1));
419         m_ctlFile2.GetWindowText(PopString(m_file2));
420
421         m_ctlFile1.SetWindowText(m_file2.c_str());
422         m_ctlFile2.SetWindowText(m_file1.c_str());
423
424         //  swapped files
425         Swap();
426 }
427
428 /** 
429  * @brief Add patch item to internal list.
430  * @param [in] pf Patch item to add.
431  */
432 void CPatchDlg::AddItem(const PATCHFILES& pf)
433 {
434         m_fileList.push_back(pf);
435 }
436
437 /** 
438  * @brief Returns amount of patch items in the internal list.
439  * @return Count of patch items in the list.
440  */
441 size_t CPatchDlg::GetItemCount()
442 {
443         return m_fileList.size();
444 }
445
446 /** 
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.
450  */
451 const PATCHFILES& CPatchDlg::GetItemAt(size_t position)
452 {
453         return m_fileList.at(position);
454 }
455
456 /** 
457  * @brief Empties internal item list.
458  */
459 void CPatchDlg::ClearItems()
460 {
461         m_fileList.clear();
462 }
463
464 /** 
465  * @brief Updates patch dialog settings from member variables.
466  */
467 void CPatchDlg::UpdateSettings()
468 {
469         UpdateData(FALSE);
470
471         switch (m_outputStyle)
472         {
473         case DIFF_OUTPUT_NORMAL:
474                 m_comboStyle.SelectString(-1, _("Normal").c_str());
475                 break;
476         case DIFF_OUTPUT_CONTEXT:
477                 m_comboStyle.SelectString(-1, _("Context").c_str());
478                 break;
479         case DIFF_OUTPUT_UNIFIED:
480                 m_comboStyle.SelectString(-1, _("Unified").c_str());
481                 break;
482         case DIFF_OUTPUT_HTML:
483                 m_comboStyle.SelectString(-1, _("HTML").c_str());
484                 break;
485         }
486
487         m_comboContext.SelectString(-1, strutils::to_str(m_contextLines).c_str());
488
489         if (m_outputStyle == OUTPUT_CONTEXT || m_outputStyle == OUTPUT_UNIFIED || m_outputStyle == OUTPUT_HTML)
490                 m_comboContext.EnableWindow(TRUE);
491         else
492                 m_comboContext.EnableWindow(FALSE);
493 }
494
495 /** 
496  * @brief Loads patch dialog settings from registry.
497  */
498 void CPatchDlg::LoadSettings()
499 {
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;
504         
505         m_contextLines = AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("ContextLines"), 0);
506         if (m_contextLines < 0 || m_contextLines > 50)
507                 m_contextLines = 0;
508
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);
512         
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)
516         {
517                 m_whitespaceCompare = WHITESPACE_COMPARE_ALL;
518         }
519         
520         m_openToEditor = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("OpenToEditor"), false);
521         m_includeCmdLine = !!AfxGetApp()->GetProfileInt(_T("PatchCreator"), _T("IncludeCmdLine"), false);
522
523         UpdateSettings();
524 }
525
526 /** 
527  * @brief Saves patch dialog settings to registry.
528  */
529 void CPatchDlg::SaveSettings()
530 {
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);
539 }
540
541 /** 
542  * @brief Resets patch dialog settings to defaults.
543  */
544 void CPatchDlg::OnDefaultSettings()
545 {
546         m_outputStyle = (enum output_style) DIFF_OUTPUT_NORMAL;
547         m_contextLines = 0;
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;
554
555         UpdateSettings();
556 }
557
558 /**
559  * @brief Swap sides.
560  */
561 void CPatchDlg::Swap()
562 {
563         std::vector<PATCHFILES>::iterator iter = m_fileList.begin();
564         std::vector<PATCHFILES>::const_iterator iterEnd = m_fileList.end();
565         while (iter != iterEnd)
566         {
567                 (*iter).swap_sides();
568                 ++iter;
569         }
570 }