OSDN Git Service

Possibility to store settings in INI file (#248) (#750)
authorgrzesie <63963799+grzesie@users.noreply.github.com>
Thu, 29 Apr 2021 22:50:21 +0000 (00:50 +0200)
committerGitHub <noreply@github.com>
Thu, 29 Apr 2021 22:50:21 +0000 (07:50 +0900)
* Possibility to store settings in INI file (#248)

* changing name to CIniOptionsMgr

changing name to CIniOptionsMgr

Src/Common/CIniOptionsMgr.cpp [new file with mode: 0644]
Src/Common/CIniOptionsMgr.h [new file with mode: 0644]
Src/Merge.cpp
Src/Merge.h
Src/Merge.vcxproj
Src/Merge.vcxproj.filters
Src/OptionsInit.cpp

diff --git a/Src/Common/CIniOptionsMgr.cpp b/Src/Common/CIniOptionsMgr.cpp
new file mode 100644 (file)
index 0000000..948349e
--- /dev/null
@@ -0,0 +1,353 @@
+/**
+ * @file CIniOptionsMgr.cpp
+ *
+ * @brief Implementation of Ini file Options management class.
+ *
+ */
+
+#include "pch.h"
+#include "CIniOptionsMgr.h"
+#include "OptionsMgr.h"
+#include <Windows.h>
+#include <codecvt>
+#include <filesystem>
+#include <string>
+#include <fstream>
+
+using std::filesystem::current_path;
+
+LPCWSTR CIniOptionsMgr::lpFilePath = NULL;
+
+LPCWSTR lpAppName = TEXT("WinMerge");
+LPCWSTR lpFileName = TEXT("\\winmerge.ini");
+
+CIniOptionsMgr::CIniOptionsMgr()
+{
+       InitializeCriticalSection(&m_cs);
+}
+
+CIniOptionsMgr::~CIniOptionsMgr()
+{
+       DeleteCriticalSection(&m_cs);
+       delete[] CIniOptionsMgr::lpFilePath;
+}
+
+/**
+ * @brief Checks wheter INI file exists.
+ * @return TRUE if INI file exist,
+ *   FALSE otherwise.
+ */
+bool CIniOptionsMgr::CheckIfIniFileExist()
+{
+       std::ifstream f(GetFilePath());
+       return f.good();
+}
+
+/**
+ * @brief Get path to INI file.
+ * @return path to INI file
+ */
+LPCWSTR CIniOptionsMgr::GetFilePath()
+{
+       if (CIniOptionsMgr::lpFilePath == NULL)
+       {
+               // create path
+               std::filesystem::path p = current_path();
+               p += lpFileName;
+
+               // change type
+               std::wstring str = p.wstring();
+               size_t length = str.length() + 1;
+               wchar_t* strCp = new wchar_t[length];
+               wcscpy_s(strCp, length, str.c_str());
+
+               // set path
+               CIniOptionsMgr::lpFilePath = strCp;
+       }
+
+       return CIniOptionsMgr::lpFilePath;
+}
+
+int CIniOptionsMgr::InitOption(const String& name, const varprop::VariantValue& defaultValue)
+{
+       // Check type & bail if null
+       int valType = defaultValue.GetType();
+       if (valType == varprop::VT_NULL)
+               return COption::OPT_ERR;
+
+       // If we're not loading & saving options, bail
+       if (!m_serializing)
+               return AddOption(name, defaultValue);
+
+       EnterCriticalSection(&m_cs);
+
+       // check if value exist
+       String textValue = ReadValueFromFile(name);
+       bool found = textValue.size() != 0;
+
+       // Actually save value into our in-memory options table
+       int retVal = AddOption(name, defaultValue);
+
+       // Update registry if successfully saved to in-memory table
+       if (retVal == COption::OPT_OK)
+       {
+               if (found)
+               {
+                       varprop::VariantValue value(defaultValue);
+                       retVal = ParseValue(name, textValue, value);
+                       if (retVal == COption::OPT_OK)
+                       {
+                               retVal = Set(name, value);
+                       }
+               }
+       }
+
+       LeaveCriticalSection(&m_cs);
+       return retVal;
+}
+
+int CIniOptionsMgr::InitOption(const String& name, const String& defaultValue)
+{
+       varprop::VariantValue defValue;
+       defValue.SetString(defaultValue);
+       return InitOption(name, defValue);
+}
+
+int CIniOptionsMgr::InitOption(const String& name, const TCHAR* defaultValue)
+{
+       return InitOption(name, String(defaultValue));
+}
+
+int CIniOptionsMgr::InitOption(const String& name, int defaultValue, bool serializable)
+{
+       varprop::VariantValue defValue;
+       int retVal = COption::OPT_OK;
+
+       defValue.SetInt(defaultValue);
+       if (serializable)
+               retVal = InitOption(name, defValue);
+       else
+               AddOption(name, defValue);
+       return retVal;
+}
+
+int CIniOptionsMgr::InitOption(const String& name, bool defaultValue)
+{
+       varprop::VariantValue defValue;
+       defValue.SetBool(defaultValue);
+       return InitOption(name, defValue);
+}
+
+int CIniOptionsMgr::SaveOption(const String& name)
+{
+       if (!m_serializing) return COption::OPT_OK;
+
+       varprop::VariantValue value;
+       int retVal = COption::OPT_OK;
+
+       value = Get(name);
+       int valType = value.GetType();
+       if (valType == varprop::VT_NULL)
+               retVal = COption::OPT_NOTFOUND;
+
+       if (retVal == COption::OPT_OK)
+       {
+               if (valType == varprop::VT_STRING)
+               {
+                       String strVal = value.GetString();
+                       LPCWSTR text = strVal.c_str();
+                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+               }
+               else if (valType == varprop::VT_INT)
+               {
+                       DWORD dwordVal = value.GetInt();
+                       String strVal = strutils::to_str(dwordVal);
+                       LPCWSTR text = strVal.c_str();
+                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+               }
+               else if (valType == varprop::VT_BOOL)
+               {
+                       DWORD dwordVal = value.GetBool() ? 1 : 0;
+                       String strVal = strutils::to_str(dwordVal);
+                       LPCWSTR text = strVal.c_str();
+                       WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
+               }
+               else
+               {
+                       retVal = COption::OPT_UNKNOWN_TYPE;
+               }
+       }
+       return retVal;
+}
+
+/**
+ * @brief Set new value for option and save option to file
+ */
+int CIniOptionsMgr::SaveOption(const String& name, const varprop::VariantValue& value)
+{
+       int retVal = Set(name, value);
+       if (retVal == COption::OPT_OK)
+               retVal = SaveOption(name);
+       return retVal;
+}
+
+/**
+ * @brief Set new string value for option and save option to file
+ */
+int CIniOptionsMgr::SaveOption(const String& name, const String& value)
+{
+       varprop::VariantValue val;
+       val.SetString(value);
+       int retVal = Set(name, val);
+       if (retVal == COption::OPT_OK)
+               retVal = SaveOption(name);
+       return retVal;
+}
+
+/**
+ * @brief Set new string value for option and save option to file
+ */
+int CIniOptionsMgr::SaveOption(const String& name, const TCHAR* value)
+{
+       return SaveOption(name, String(value));
+}
+
+int CIniOptionsMgr::SaveOption(const String& name, int value)
+{
+       varprop::VariantValue val;
+       val.SetInt(value);
+       int retVal = Set(name, val);
+       if (retVal == COption::OPT_OK)
+               retVal = SaveOption(name);
+       return retVal;
+}
+
+int CIniOptionsMgr::SaveOption(const String& name, bool value)
+{
+       varprop::VariantValue val;
+       val.SetBool(value);
+       int retVal = Set(name, val);
+       if (retVal == COption::OPT_OK)
+               retVal = SaveOption(name);
+       return retVal;
+}
+
+int CIniOptionsMgr::RemoveOption(const String& name)
+{
+       int retVal = COption::OPT_OK;
+
+       String strPath;
+       String strValueName;
+
+       SplitName(name, strPath, strValueName);
+
+       if (!strValueName.empty())
+       {
+               retVal = COptionsMgr::RemoveOption(name);
+       }
+       else
+       {
+               for (auto it = m_optionsMap.begin(); it != m_optionsMap.end(); )
+               {
+                       if (it->first.find(strPath) == 0)
+                               it = m_optionsMap.erase(it);
+                       else
+                               ++it;
+               }
+               retVal = COption::OPT_OK;
+       }
+
+       EnterCriticalSection(&m_cs);
+
+       WritePrivateProfileString(lpAppName, name.c_str(), NULL, GetFilePath());
+
+       LeaveCriticalSection(&m_cs);
+
+       return retVal;
+}
+
+int CIniOptionsMgr::ExportOptions(const String& filename, const bool bHexColor) const
+{
+       if (std::filesystem::copy_file(CIniOptionsMgr::GetFilePath(), filename))
+       {
+               return COption::OPT_OK;
+       }
+       else
+       {
+               return COption::OPT_ERR;
+       }
+}
+
+int CIniOptionsMgr::ImportOptions(const String& filename)
+{
+       if (std::filesystem::copy_file(filename, CIniOptionsMgr::GetFilePath()))
+       {
+               return COption::OPT_OK;
+       }
+       else
+       {
+               return COption::OPT_ERR;
+       }
+}
+
+String CIniOptionsMgr::ReadValueFromFile(const String& name)
+{
+       const int size = 100;
+       LPWSTR buffor = new TCHAR[size];
+       DWORD result = GetPrivateProfileString(lpAppName, name.c_str(), NULL, buffor, size, GetFilePath());
+       return buffor;
+}
+
+int CIniOptionsMgr::ParseValue(const String& strName, String& textValue, varprop::VariantValue& value)
+{
+       int valType = value.GetType();
+       int retVal = COption::OPT_OK;
+
+       if (valType == varprop::VT_STRING)
+       {
+               value.SetString(textValue);
+               retVal = Set(strName, value);
+       }
+       else if (valType == varprop::VT_INT)
+       {
+               value.SetInt(std::stoi(textValue));
+               retVal = Set(strName, value);
+       }
+       else if (valType == varprop::VT_BOOL)
+       {
+               value.SetBool(textValue[0] == '1' ? true : false);
+               retVal = Set(strName, value);
+       }
+       else
+               retVal = COption::OPT_WRONG_TYPE;
+
+       return retVal;
+}
+
+/**
+ * @brief Split option name to path (in registry) and
+ * valuename (in registry).
+ *
+ * Option names are given as "full path", e.g. "Settings/AutomaticRescan".
+ * This function splits that to path "Settings/" and valuename
+ * "AutomaticRescan".
+ * @param [in] strName Option name
+ * @param [out] srPath Path (key) in registry
+ * @param [out] strValue Value in registry
+ */
+void CIniOptionsMgr::SplitName(const String& strName, String& strPath,
+       String& strValue) const
+{
+       size_t pos = strName.rfind('/');
+       if (pos != String::npos)
+       {
+               size_t len = strName.length();
+               strValue = strName.substr(pos + 1, len - pos - 1); //Right(len - pos - 1);
+               strPath = strName.substr(0, pos);  //Left(pos);
+       }
+       else
+       {
+               strValue = strName;
+               strPath.erase();
+       }
+}
\ No newline at end of file
diff --git a/Src/Common/CIniOptionsMgr.h b/Src/Common/CIniOptionsMgr.h
new file mode 100644 (file)
index 0000000..a4ba5dc
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * @file CIniOptionsMgr.h
+ *
+ * @brief Implementation of Ini file Options management class.
+ *
+ */
+#pragma once
+
+#include <Windows.h>
+#include "OptionsMgr.h"
+
+class COptionsMgr;
+
+/**
+ * @brief Ini-based implementation of OptionsMgr interface (q.v.).
+ */
+class CIniOptionsMgr : public COptionsMgr
+{
+public:
+       CIniOptionsMgr();
+       virtual ~CIniOptionsMgr();
+
+       static bool CheckIfIniFileExist();
+
+       static LPCWSTR GetFilePath();
+
+       virtual int InitOption(const String& name, const varprop::VariantValue& defaultValue) override;
+       virtual int InitOption(const String& name, const String& defaultValue) override;
+       virtual int InitOption(const String& name, const TCHAR* defaultValue) override;
+       virtual int InitOption(const String& name, int defaultValue, bool serializable = true) override;
+       virtual int InitOption(const String& name, bool defaultValue) override;
+
+       virtual int SaveOption(const String& name) override;
+       virtual int SaveOption(const String& name, const varprop::VariantValue& value) override;
+       virtual int SaveOption(const String& name, const String& value) override;
+       virtual int SaveOption(const String& name, const TCHAR* value) override;
+       virtual int SaveOption(const String& name, int value) override;
+       virtual int SaveOption(const String& name, bool value) override;
+
+       virtual int RemoveOption(const String& name) override;
+
+       virtual void SetSerializing(bool serializing = true) override { m_serializing = serializing; }
+
+       virtual int ExportOptions(const String& filename, const bool bHexColor = false) const override;
+       virtual int ImportOptions(const String& filename) override;
+
+private:
+       CRITICAL_SECTION m_cs;
+       bool m_serializing;
+       static LPCWSTR lpFilePath;
+
+       String ReadValueFromFile(const String& name);
+       int ParseValue(const String& strName, String& textValue, varprop::VariantValue& value);
+
+       void SplitName(const String& strName, String& strPath, String& strValue) const;
+};
\ No newline at end of file
index 0419419..6d3397c 100644 (file)
@@ -4,7 +4,7 @@
 //    Author: Dean Grimm
 //    SPDX-License-Identifier: GPL-2.0-or-later
 /////////////////////////////////////////////////////////////////////////////
-/** 
+/**
  * @file  Merge.cpp
  *
  * @brief Defines the class behaviors for the application.
@@ -20,6 +20,7 @@
 #include "OptionsMgr.h"
 #include "OptionsInit.h"
 #include "RegOptionsMgr.h"
+#include "CIniOptionsMgr.h"
 #include "OpenDoc.h"
 #include "OpenFrm.h"
 #include "OpenView.h"
@@ -99,7 +100,7 @@ CMergeApp::CMergeApp() :
 , m_mainThreadScripts(nullptr)
 , m_nLastCompareResult(0)
 , m_bNonInteractive(false)
-, m_pOptions(new CRegOptionsMgr())
+, m_pOptions(CreateOptionManager())
 , m_pGlobalFileFilter(new FileFilterHelper())
 , m_nActiveOperations(0)
 , m_pLangDlg(new CLanguageSelect())
@@ -115,6 +116,23 @@ CMergeApp::CMergeApp() :
        // Place all significant initialization in InitInstance
 }
 
+/**
+ * @brief Chose which options manager should be initialized.
+ * @return IniOptionsMgr if initial config file exists,
+ *   CRegOptionsMgr otherwise.
+ */
+COptionsMgr *CreateOptionManager()
+{
+       if (CIniOptionsMgr::CheckIfIniFileExist())
+       {
+               return new CIniOptionsMgr();
+       }
+       else
+       {
+               return new CRegOptionsMgr();
+       }
+}
+
 CMergeApp::~CMergeApp()
 {
        strdiff::Close();
@@ -228,7 +246,7 @@ BOOL CMergeApp::InitInstance()
        // Create exclusion mutex name
        TCHAR szDesktopName[MAX_PATH] = _T("Win9xDesktop");
        DWORD dwLengthNeeded;
-       GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME, 
+       GetUserObjectInformation(GetThreadDesktop(GetCurrentThreadId()), UOI_NAME,
                szDesktopName, sizeof(szDesktopName), &dwLengthNeeded);
        TCHAR szMutexName[MAX_PATH + 40];
        // Combine window class name and desktop name to form a unique mutex name.
@@ -465,7 +483,7 @@ void CMergeApp::OnAppAbout()
  * good place to do cleanups.
  * @return Application's exit value (returned from WinMain()).
  */
-int CMergeApp::ExitInstance() 
+int CMergeApp::ExitInstance()
 {
        charsets_cleanup();
 
@@ -521,7 +539,7 @@ int CMergeApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
        // Create the message box dialog.
        CMessageBoxDialog dlgMessage(pParentWnd, lpszPrompt, _T(""), nType | MB_RIGHT_ALIGN,
                nIDPrompt);
-       
+
        if (m_pMainWnd->IsIconic())
                m_pMainWnd->ShowWindow(SW_RESTORE);
 
@@ -545,7 +563,7 @@ bool CMergeApp::IsReallyIdle() const
     return idle;
 }
 
-BOOL CMergeApp::OnIdle(LONG lCount) 
+BOOL CMergeApp::OnIdle(LONG lCount)
 {
        if (CWinApp::OnIdle(lCount))
                return TRUE;
@@ -560,7 +578,10 @@ BOOL CMergeApp::OnIdle(LONG lCount)
        if (m_bNonInteractive && IsReallyIdle())
                m_pMainWnd->PostMessage(WM_CLOSE, 0, 0);
 
-       static_cast<CRegOptionsMgr *>(GetOptionsMgr())->CloseKeys();
+       if (typeid(*GetOptionsMgr()) == typeid(CRegOptionsMgr))
+       {
+               static_cast<CRegOptionsMgr*>(GetOptionsMgr())->CloseKeys();
+       }
 
        return FALSE;
 }
@@ -711,7 +732,7 @@ bool CMergeApp::ParseArgsAndDoOpen(MergeCmdLineInfo& cmdInfo, CMainFrame* pMainF
                        {
                                DWORD dwFlags[3] = {cmdInfo.m_dwLeftFlags, cmdInfo.m_dwRightFlags, FFILEOPEN_NONE};
                                bCompared = pMainFrame->DoFileOpen(&cmdInfo.m_Files,
-                                       dwFlags, strDesc, cmdInfo.m_sReportFile, cmdInfo.m_bRecurse, nullptr, 
+                                       dwFlags, strDesc, cmdInfo.m_sReportFile, cmdInfo.m_bRecurse, nullptr,
                                        cmdInfo.m_sPreDiffer, infoUnpacker.get());
                        }
                }
@@ -881,7 +902,7 @@ bool CMergeApp::CreateBackup(bool bFolder, const String& pszPath)
                String path;
                String filename;
                String ext;
-       
+
                paths::SplitFilename(paths::GetLongPath(pszPath), &path, &filename, &ext);
 
                // Determine backup folder
@@ -944,7 +965,7 @@ bool CMergeApp::CreateBackup(bool bFolder, const String& pszPath)
                {
                        success = !!CopyFileW(TFile(pszPath).wpath().c_str(), TFile(bakPath).wpath().c_str(), FALSE);
                }
-               
+
                if (!success)
                {
                        String msg = strutils::format_string1(
@@ -1002,7 +1023,7 @@ int CMergeApp::HandleReadonlySave(String& strSavePath, bool bMultiFile,
        if (bFileExists && bFileRO)
        {
                UINT userChoice = 0;
-               
+
                // Don't ask again if its already asked
                if (bApplyToAll)
                        userChoice = IDYES;
@@ -1039,7 +1060,7 @@ int CMergeApp::HandleReadonlySave(String& strSavePath, bool bMultiFile,
                        CFile::SetStatus(strSavePath.c_str(), status);
                        nRetVal = IDYES;
                        break;
-               
+
                // Save to new filename (single) /skip this item (multiple)
                case IDNO:
                        if (!bMultiFile)
@@ -1119,7 +1140,7 @@ bool CMergeApp::SaveProjectFile(const String& sProject, const ProjectFile &proje
        return true;
 }
 
-/** 
+/**
  * @brief Read project and perform comparison specified
  * @param [in] sProject Full path to project file.
  * @return `true` if loading project file and starting compare succeeded.
@@ -1129,7 +1150,7 @@ bool CMergeApp::LoadAndOpenProjectFile(const String& sProject, const String& sRe
        ProjectFile project;
        if (!LoadProjectFile(sProject, project))
                return false;
-       
+
        bool rtn = true;
        for (auto& projItem : project.Items())
        {
@@ -1254,7 +1275,7 @@ std::wstring CMergeApp::LoadDialogCaption(LPCTSTR lpDialogTemplateID) const
  */
 void CMergeApp::AddToRecentProjectsMRU(LPCTSTR sPathName)
 {
-       // sPathName will be added to the top of the MRU list. 
+       // sPathName will be added to the top of the MRU list.
        // If sPathName already exists in the MRU list, it will be moved to the top
        if (m_pRecentFileList != nullptr)    {
                m_pRecentFileList->Add(sPathName);
index a598b2d..dd987cc 100644 (file)
@@ -3,7 +3,7 @@
 //    Copyright (C) 1997  Dean P. Grimm
 //    SPDX-License-Identifier: GPL-2.0-or-later
 /////////////////////////////////////////////////////////////////////////////
-/** 
+/**
  * @file  Merge.h
  *
  * @brief main header file for the MERGE application
@@ -45,7 +45,7 @@ class CCrystalTextMarkers;
 
 enum { IDLE_TIMER = 9754 };
 
-/** 
+/**
  * @brief WinMerge application class
  */
 class CMergeApp : public CWinApp
@@ -73,7 +73,7 @@ public:
        void TranslateMenu(HMENU) const;
        void TranslateDialog(HWND) const;
        String LoadString(UINT) const;
-       bool TranslateString(const std::string&, String&) const; 
+       bool TranslateString(const std::string&, String&) const;
        std::wstring LoadDialogCaption(LPCTSTR) const;
 
        CMergeApp();
@@ -174,12 +174,13 @@ private:
 
 extern CMergeApp theApp;
 
-/** 
+/**
  * @brief Set flag so that application will broadcast notification at next
  * idle time (via WM_TIMER id=IDLE_TIMER)
  */
 inline void CMergeApp::SetNeedIdleTimer()
 {
-       m_bNeedIdleTimer = true; 
+       m_bNeedIdleTimer = true;
 }
 
+COptionsMgr* CreateOptionManager();
index e91dbcd..d7c62cf 100644 (file)
     </ClCompile>\r
     <ClCompile Include="AboutDlg.cpp">\r
     </ClCompile>\r
+    <ClCompile Include="Common\CIniOptionsMgr.cpp">\r
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r
+      <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)2.pch</PrecompiledHeaderOutputFile>\r
+    </ClCompile>\r
     <ClCompile Include="Common\Shell.cpp">\r
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)2.pch</PrecompiledHeaderOutputFile>\r
     <ClInclude Include="..\Version.h" />\r
     <ClInclude Include="7zCommon.h" />\r
     <ClInclude Include="AboutDlg.h" />\r
+    <ClInclude Include="Common\CIniOptionsMgr.h" />\r
     <ClInclude Include="Common\Shell.h" />\r
     <ClInclude Include="Concurrent.h" />\r
     <ClInclude Include="Common\BCMenu.h" />\r
index f337f44..4e4b401 100644 (file)
     <ClCompile Include="Common\Shell.cpp">\r
       <Filter>Common\Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="Common\CIniOptionsMgr.cpp">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="charsets.h">\r
     <ClInclude Include="Common\Shell.h">\r
       <Filter>Common\Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="Common\CIniOptionsMgr.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="res\binarydiff.ico">\r
index e66992c..d908f92 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "pch.h"
 #include <vector>
+#include <typeinfo>
 #include "OptionsDef.h"
 #include "OptionsMgr.h"
 #include "RegOptionsMgr.h"
@@ -38,7 +39,10 @@ namespace Options
  */
 void Init(COptionsMgr *pOptions)
 {
-       static_cast<CRegOptionsMgr *>(pOptions)->SetRegRootKey(_T("Thingamahoochie\\WinMerge\\"));
+       if (typeid(*pOptions) == typeid(CRegOptionsMgr))
+       {
+               static_cast<CRegOptionsMgr*>(pOptions)->SetRegRootKey(_T("Thingamahoochie\\WinMerge\\"));
+       }
 
        LANGID LangId = GetUserDefaultLangID();
        pOptions->InitOption(OPT_SELECTED_LANGUAGE, static_cast<int>(LangId));