OSDN Git Service

c75f116e95409405fb50a60e7df86be6d203b23f
[winmerge-jp/winmerge-jp.git] / Src / Common / PreferencesDlg.cpp
1 /** 
2  * @file PreferencesDlg.cpp
3  *
4  * @brief Implementation file for CPreferencesDlg
5  *
6  * @note This code originates from AbstractSpoon / TodoList
7  * (http://www.abstractspoon.com/) but is modified to use in
8  * WinMerge.
9  */
10
11 #include "StdAfx.h"
12 #include "PreferencesDlg.h"
13 #include "resource.h"
14 #include "UnicodeString.h"
15 #include "OptionsDef.h"
16 #include "OptionsMgr.h"
17 #include "SyntaxColors.h"
18 #include "Merge.h"
19 #include "paths.h"
20 #include "FileOrFolderSelect.h"
21 #include "OptionsSyntaxColors.h"
22
23 #ifdef _DEBUG
24 #define new DEBUG_NEW
25 #endif
26
27 /**
28  * @brief Location for file compare specific help to open.
29  */
30 static TCHAR OptionsHelpLocation[] = _T("::/htmlhelp/Configuration.html");
31
32 /////////////////////////////////////////////////////////////////////////////
33 // CPreferencesDlg dialog
34
35 const TCHAR PATHDELIM = '>';
36
37 CPreferencesDlg::CPreferencesDlg(COptionsMgr *regOptions, SyntaxColors *colors,
38                 UINT nMenuID /*= 0*/, CWnd* pParent /*= nullptr*/)   // standard constructor
39 : CTrDialog(IDD_PREFERENCES, pParent)
40 , m_pOptionsMgr(regOptions)
41 , m_pSyntaxColors(colors)
42 , m_pageGeneral(regOptions)
43 , m_pageCompare(regOptions)
44 , m_pageMessageBoxes(regOptions)
45 , m_pageColorSchemes(regOptions)
46 , m_pageMergeColors(regOptions)
47 , m_pageTextColors(regOptions, colors)
48 , m_pageSyntaxColors(regOptions, colors)
49 , m_pageMarkerColors(regOptions, colors)
50 , m_pageDirColors(regOptions)
51 , m_pageArchive(regOptions)
52 , m_pageCodepage(regOptions)
53 , m_pageEditor(regOptions)
54 , m_pageEditorSyntax(regOptions)
55 , m_pageProject(regOptions)
56 , m_pageSystem(regOptions)
57 , m_pageBackups(regOptions)
58 , m_pageShell(regOptions)
59 , m_pageCompareFolder(regOptions)
60 , m_pageCompareTable(regOptions)
61 , m_pageCompareBinary(regOptions)
62 , m_pageCompareImage(regOptions)
63 {
64         UNREFERENCED_PARAMETER(nMenuID);
65 }
66
67 CPreferencesDlg::~CPreferencesDlg()
68 {
69 }
70
71 void CPreferencesDlg::DoDataExchange(CDataExchange* pDX)
72 {
73         CDialog::DoDataExchange(pDX);
74         //{{AFX_DATA_MAP(CPreferencesDlg)
75         DDX_Control(pDX, IDC_TREEOPT_PAGES, m_tcPages);
76         //}}AFX_DATA_MAP
77 }
78
79 BEGIN_MESSAGE_MAP(CPreferencesDlg, CTrDialog)
80         //{{AFX_MSG_MAP(CPreferencesDlg)
81         ON_WM_SIZE()
82         ON_COMMAND(ID_HELP, OnHelpButton)
83         ON_BN_CLICKED(IDC_TREEOPT_HELP, OnHelpButton)
84         ON_NOTIFY(TVN_SELCHANGED, IDC_TREEOPT_PAGES, OnSelchangedPages)
85         ON_BN_CLICKED(IDC_TREEOPT_IMPORT, OnImportButton)
86         ON_BN_CLICKED(IDC_TREEOPT_EXPORT, OnExportButton)
87         ON_MESSAGE(WM_APP + IDC_COLOR_SCHEMES, OnColorSchemeChanged)
88         //}}AFX_MSG_MAP
89 END_MESSAGE_MAP()
90
91 /////////////////////////////////////////////////////////////////////////////
92 // CPreferencesDlg message handlers
93
94 BOOL CPreferencesDlg::OnInitDialog() 
95 {
96         CTrDialog::OnInitDialog();
97
98         m_tcPages.SetIndent(0);
99
100         // Second parameter is 'path', page's parent page(s) and caption.
101         // '>' is used as path separator.
102         // For example "General" creates top-level "General" page
103         // and "General>Colors" creates "Colors" sub-page for "General"
104         AddPage(&m_pageGeneral, IDS_OPTIONSPG_GENERAL);
105         AddPage(&m_pageCompare, IDS_OPTIONSPG_COMPARE, IDS_OPTIONSPG_GENCOMPARE);
106         AddPage(&m_pageCompareFolder, IDS_OPTIONSPG_COMPARE, IDS_OPTIONSPG_FOLDERCOMPARE);
107         AddPage(&m_pageCompareTable, IDS_OPTIONSPG_COMPARE, IDS_OPTIONSPG_TABLECOMPARE);
108         AddPage(&m_pageCompareBinary, IDS_OPTIONSPG_COMPARE, IDS_OPTIONSPG_BINARYCOMPARE);
109         AddPage(&m_pageCompareImage, IDS_OPTIONSPG_COMPARE, IDS_OPTIONSPG_IMAGECOMPARE);
110         AddPage(&m_pageMessageBoxes, IDS_OPTIONSPG_MESSAGEBOXES);
111         AddPage(&m_pageEditor, IDS_OPTIONSPG_EDITOR, IDS_OPTIONSPG_GENEDITOR);
112         AddPage(&m_pageEditorSyntax, IDS_OPTIONSPG_EDITOR, IDS_OPTIONSPG_EDITOR_SYNTAX);
113         AddPage(&m_pageColorSchemes, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_COLOR_SCHEMES);
114         AddPage(&m_pageMergeColors, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_MERGECOLORS);
115         AddPage(&m_pageSyntaxColors, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_SYNTAXCOLORS);
116         AddPage(&m_pageTextColors, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_TEXTCOLORS);
117         AddPage(&m_pageMarkerColors, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_MARKERCOLORS);
118         AddPage(&m_pageDirColors, IDS_OPTIONSPG_COLORS, IDS_OPTIONSPG_DIRCOLORS);
119         AddPage(&m_pageArchive, IDS_OPTIONSPG_ARCHIVE);
120         AddPage(&m_pageProject, IDS_OPTIONSPG_PROJECT);
121         AddPage(&m_pageSystem, IDS_OPTIONSPG_SYSTEM);
122         AddPage(&m_pageBackups, IDS_OPTIONSPG_BACKUPS);
123         AddPage(&m_pageCodepage, IDS_OPTIONSPG_CODEPAGE);
124         AddPage(&m_pageShell, IDS_OPTIONSPG_SHELL);
125
126         ReadOptions();
127         
128         CRect rPPHost;
129         GetDlgItem(IDC_TREEOPT_HOSTFRAME)->GetWindowRect(rPPHost);
130         ScreenToClient(rPPHost);
131
132         if (m_pphost.Create(rPPHost, this))
133                 SetActivePage(AfxGetApp()->GetProfileInt(_T("Settings"), _T("OptStartPage"), 0));
134  
135         // setup handler for resizing this dialog       
136         m_constraint.InitializeCurrentSize(this);
137         m_constraint.DisallowHeightGrowth();
138         m_constraint.SubclassWnd(); // install subclassing
139         m_constraint.LoadPosition(_T("ResizeableDialogs"), _T("OptionsDlg"), false); // persist size via registry
140         return TRUE;  // return TRUE unless you set the focus to a control
141                       // EXCEPTION: OCX Property Pages should return FALSE
142 }
143
144 void CPreferencesDlg::OnOK()
145 {
146         CDialog::OnOK();
147         m_pphost.OnOK();
148
149         SaveOptions();
150
151         AfxGetApp()->WriteProfileInt(_T("Settings"), _T("OptStartPage"), m_pphost.GetActiveIndex());
152 }
153
154 void CPreferencesDlg::OnSize(UINT nType, int cx, int cy)
155 {
156         CWnd::OnSize(nType, cx, cy);
157
158         if (CWnd *pPPHostWnd = GetDlgItem(IDC_TREEOPT_HOSTFRAME))
159         {
160                 CRect rPPHost;
161                 pPPHostWnd->GetWindowRect(rPPHost);
162                 ScreenToClient(rPPHost);
163                 m_pphost.MoveWindow(&rPPHost);
164         }
165 }
166         
167 void CPreferencesDlg::OnHelpButton() 
168 {
169         theApp.ShowHelp(OptionsHelpLocation);
170 }
171
172 void CPreferencesDlg::AddPage(CPropertyPage* pPage, UINT nResourceID)
173 {
174         String sPath = theApp.LoadString(nResourceID);
175         AddPage(pPage, sPath.c_str());
176 }
177
178 void CPreferencesDlg::AddPage(CPropertyPage* pPage, UINT nTopHeading, UINT nSubHeading)
179 {
180         String sPath = theApp.LoadString(nTopHeading);
181         sPath += _T(">");
182         sPath += theApp.LoadString(nSubHeading);
183         AddPage(pPage, sPath.c_str());
184 }
185
186 void CPreferencesDlg::AddPage(CPropertyPage* pPage, LPCTSTR szPath)
187 {
188         CString sPath(szPath);
189
190         if (m_pphost.AddPage(pPage))
191         {
192                 HTREEITEM htiParent = TVI_ROOT; // default
193                 int nFind = sPath.Find(PATHDELIM);
194
195                 while (nFind != -1)
196                 {
197                         CString sParent = sPath.Left(nFind);
198                         sPath = sPath.Mid(nFind + 1);
199
200                         // see if parent already exists
201                         HTREEITEM htiParentParent = htiParent;
202                         htiParent = m_tcPages.GetChildItem(htiParentParent);
203
204                         while (htiParent != nullptr)
205                         {
206                                 if (sParent.CompareNoCase(m_tcPages.GetItemText(htiParent)) == 0)
207                                         break;
208
209                                 htiParent = m_tcPages.GetNextItem(htiParent, TVGN_NEXT);
210                         }
211
212                         if (htiParent == nullptr)
213                                 htiParent = m_tcPages.InsertItem(sParent, htiParentParent);
214
215                         nFind = sPath.Find(PATHDELIM);
216                 }
217
218                 HTREEITEM hti = m_tcPages.InsertItem(sPath, htiParent); // whatever's left
219                 m_tcPages.EnsureVisible(hti);
220
221                 // map both ways
222                 m_tcPages.SetItemData(hti, static_cast<DWORD_PTR>(reinterpret_cast<uintptr_t>(pPage)));
223                 m_mapPP2HTI[(void*)pPage] = (void*)hti;
224         }
225 }
226
227 void CPreferencesDlg::OnSelchangedPages(NMHDR* pNMHDR, LRESULT* pResult) 
228 {
229         UNREFERENCED_PARAMETER(pNMHDR);
230         HTREEITEM htiSel = m_tcPages.GetSelectedItem();
231
232         while (m_tcPages.ItemHasChildren(htiSel))
233                 htiSel = m_tcPages.GetChildItem(htiSel);
234
235         CPropertyPage* pPage = (CPropertyPage*)m_tcPages.GetItemData(htiSel);
236         ASSERT (pPage != nullptr);
237
238         if (pPage != nullptr)
239         {
240                 m_pphost.SetActivePage(pPage, false);
241
242                 // update caption
243                 String sCaption = strutils::format_string1(_("Options (%1)"), (LPCTSTR)GetItemPath(htiSel));
244                 SetWindowText(sCaption.c_str());
245         }
246
247         m_tcPages.SetFocus();
248         
249         *pResult = 0;
250 }
251
252 void CPreferencesDlg::SetActivePage(int nPage)
253 {
254         m_pphost.SetActivePage(nPage, false);
255
256         // synchronize tree
257         CPropertyPage* pPage = m_pphost.GetActivePage();
258         HTREEITEM hti = nullptr;
259
260         if (m_mapPP2HTI.Lookup(pPage, (void*&)hti) && hti != nullptr)
261                 m_tcPages.SelectItem(hti);
262 }
263
264 CString CPreferencesDlg::GetItemPath(HTREEITEM hti)
265 {
266         CString sPath = m_tcPages.GetItemText(hti);
267
268         hti = m_tcPages.GetParentItem(hti);
269         while (hti != nullptr)
270         {
271                 sPath = m_tcPages.GetItemText(hti) + _T(" > ") + sPath;
272                 hti = m_tcPages.GetParentItem(hti);
273         }
274
275         return sPath;
276 }
277
278 /**
279  * @brief Read options from storage to UI controls.
280  * @param [in] bUpdate If `true` UpdateData() is called
281  */
282 void CPreferencesDlg::ReadOptions(bool bUpdate)
283 {
284         m_pageGeneral.ReadOptions();
285         m_pageColorSchemes.ReadOptions();
286         m_pageMergeColors.ReadOptions();
287         m_pageTextColors.ReadOptions();
288         m_pageSyntaxColors.ReadOptions();
289         m_pageMarkerColors.ReadOptions();
290         m_pageDirColors.ReadOptions();
291         m_pageSystem.ReadOptions();
292         m_pageCompare.ReadOptions();
293         m_pageCompareFolder.ReadOptions();
294         m_pageCompareTable.ReadOptions();
295         m_pageCompareBinary.ReadOptions();
296         m_pageCompareImage.ReadOptions();
297         m_pageMessageBoxes.ReadOptions();
298         m_pageEditor.ReadOptions();
299         m_pageEditorSyntax.ReadOptions();
300         m_pageCodepage.ReadOptions();
301         m_pageArchive.ReadOptions();
302         m_pageProject.ReadOptions();
303         m_pageBackups.ReadOptions();
304         m_pageShell.ReadOptions();
305
306         if (bUpdate)
307         {
308                 SafeUpdatePage(&m_pageGeneral, false);
309                 SafeUpdatePage(&m_pageColorSchemes, false);
310                 SafeUpdatePage(&m_pageMergeColors, false);
311                 SafeUpdatePage(&m_pageTextColors, false);
312                 SafeUpdatePage(&m_pageSyntaxColors, false);
313                 SafeUpdatePage(&m_pageMarkerColors, false);
314                 SafeUpdatePage(&m_pageDirColors, false);
315                 SafeUpdatePage(&m_pageSystem, false);
316                 SafeUpdatePage(&m_pageCompare, false);
317                 SafeUpdatePage(&m_pageCompareFolder, false);
318                 SafeUpdatePage(&m_pageCompareTable, false);
319                 SafeUpdatePage(&m_pageCompareBinary, false);
320                 SafeUpdatePage(&m_pageCompareImage, false);
321                 SafeUpdatePage(&m_pageMessageBoxes, false);
322                 SafeUpdatePage(&m_pageEditor, false);
323                 SafeUpdatePage(&m_pageEditorSyntax, false);
324                 SafeUpdatePage(&m_pageCodepage, false);
325                 SafeUpdatePage(&m_pageArchive, false);
326                 SafeUpdatePage(&m_pageProject, false);
327                 SafeUpdatePage(&m_pageBackups, false);
328                 SafeUpdatePage(&m_pageShell, false);
329         }
330 }
331
332 /**
333  * @brief Write options from UI to storage.
334  */
335 void CPreferencesDlg::SaveOptions()
336 {
337         m_pageGeneral.WriteOptions();
338         m_pageSystem.WriteOptions();
339         m_pageCompare.WriteOptions();
340         m_pageCompareFolder.WriteOptions();
341         m_pageCompareTable.WriteOptions();
342         m_pageCompareBinary.WriteOptions();
343         m_pageCompareImage.WriteOptions();
344         m_pageMessageBoxes.WriteOptions();
345         m_pageEditor.WriteOptions();
346         m_pageEditorSyntax.WriteOptions();
347         m_pageColorSchemes.WriteOptions();
348         m_pageMergeColors.WriteOptions();
349         m_pageTextColors.WriteOptions();
350         m_pageSyntaxColors.WriteOptions();
351         m_pageMarkerColors.WriteOptions();
352         m_pageDirColors.WriteOptions();
353         m_pageCodepage.WriteOptions();
354         m_pageArchive.WriteOptions();
355         m_pageProject.WriteOptions();
356         m_pageBackups.WriteOptions();
357         m_pageShell.WriteOptions();
358 }
359
360 /**
361  * @brief Imports options from file.
362  */
363 void CPreferencesDlg::OnImportButton()
364 {
365         String s;
366         if (SelectFile(GetSafeHwnd(), s, true, nullptr, _("Select file for import"), _("Options files (*.ini)|*.ini|All Files (*.*)|*.*||")))
367         {
368                 if (m_pOptionsMgr->ImportOptions(s) == COption::OPT_OK)
369                 {
370                         Options::SyntaxColors::Load(m_pOptionsMgr, m_pSyntaxColors);
371                         ReadOptions(true);
372                         LangMessageBox(IDS_OPT_IMPORT_DONE, MB_ICONINFORMATION);
373                 }
374                 else
375                         LangMessageBox(IDS_OPT_IMPORT_ERR, MB_ICONWARNING);
376         }
377 }
378
379 /**
380  * @brief Exports options to file.
381  */
382 void CPreferencesDlg::OnExportButton()
383 {
384         String settingsFile;
385         if (SelectFile(GetSafeHwnd(), settingsFile, false, nullptr, _("Select file for export"),
386                 _("Options files (*.ini)|*.ini|All Files (*.*)|*.*||")))
387         {
388                 // Add settings file extension if it is missing
389                 // So we allow 'filename.otherext' but add extension for 'filename'
390                 if (paths::FindExtension(settingsFile).empty())
391                         settingsFile += _T(".ini");
392
393                 // Save all new settings before exporting
394                 m_pphost.UpdatePagesData();
395                 SaveOptions();
396
397                 if (m_pOptionsMgr->ExportOptions(settingsFile, true) == COption::OPT_OK)
398                         LangMessageBox(IDS_OPT_EXPORT_DONE, MB_ICONINFORMATION);
399                 else
400                         LangMessageBox(IDS_OPT_EXPORT_ERR, MB_ICONWARNING);
401         }
402 }
403
404 LRESULT CPreferencesDlg::OnColorSchemeChanged(WPARAM wParams, LPARAM lParam)
405 {
406         Options::SyntaxColors::Load(m_pOptionsMgr, m_pSyntaxColors);
407         ReadOptions(true);
408         return 0;
409 }
410
411 /**
412  * @brief Do a safe UpdateData call for propertypage.
413  * This function does safe UpdateData call for given propertypage. As it is,
414  * all propertypages may not have been yet initialized properly, so we must
415  * have some care when calling updateData for them.
416  * @param [in] pPage Propertypage to update.
417  * @param bSaveAndValidate UpdateData direction parameter.
418  */
419 void CPreferencesDlg::SafeUpdatePage(CPropertyPage* pPage, bool bSaveAndValidate)
420 {
421         if (pPage->GetSafeHwnd() != nullptr)
422                 pPage->UpdateData(bSaveAndValidate);
423 }