OSDN Git Service

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