OSDN Git Service

Leave the class name as CIniOptionsMgr, but rename the filename to IniOptionsMgr.*
[winmerge-jp/winmerge-jp.git] / Src / Merge.cpp
index 5b6e7ad..bbda58a 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 "IniOptionsMgr.h"
 #include "OpenDoc.h"
 #include "OpenFrm.h"
 #include "OpenView.h"
@@ -35,9 +36,9 @@
 #include "DirView.h"
 #include "PropBackups.h"
 #include "FileOrFolderSelect.h"
-#include "paths.h"
 #include "FileFilterHelper.h"
 #include "LineFiltersList.h"
+#include "SubstitutionFiltersList.h"
 #include "SyntaxColors.h"
 #include "CCrystalTextMarkers.h"
 #include "OptionsSyntaxColors.h"
@@ -52,6 +53,7 @@
 #include "stringdiffs.h"
 #include "TFile.h"
 #include "paths.h"
+#include "Shell.h"
 #include "CompareStats.h"
 #include "TestMain.h"
 #include "charsets.h" // For shutdown cleanup
@@ -98,13 +100,14 @@ 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())
 , m_bEscShutdown(false)
-, m_bExitIfNoDiff(MergeCmdLineInfo::Disabled)
+, m_bExitIfNoDiff(MergeCmdLineInfo::ExitNoDiff::Disabled)
 , m_pLineFilters(new LineFiltersList())
+, m_pSubstitutionFiltersList(new SubstitutionFiltersList())
 , m_pSyntaxColors(new SyntaxColors())
 , m_pMarkers(new CCrystalTextMarkers())
 , m_bMergingMode(false)
@@ -113,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();
@@ -220,13 +240,13 @@ BOOL CMergeApp::InitInstance()
        // This is the name of the company of the original author (Dean Grimm)
        SetRegistryKey(_T("Thingamahoochie"));
 
-       bool bSingleInstance = cmdInfo.m_bSingleInstance.has_value() ?
-               *cmdInfo.m_bSingleInstance : GetOptionsMgr()->GetBool(OPT_SINGLE_INSTANCE);
+       int nSingleInstance = cmdInfo.m_nSingleInstance.has_value() ?
+               *cmdInfo.m_nSingleInstance : GetOptionsMgr()->GetInt(OPT_SINGLE_INSTANCE);
 
        // 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.
@@ -236,7 +256,7 @@ BOOL CMergeApp::InitInstance()
        HANDLE hMutex = CreateMutex(nullptr, FALSE, szMutexName);
        if (hMutex != nullptr)
                WaitForSingleObject(hMutex, INFINITE);
-       if (bSingleInstance && GetLastError() == ERROR_ALREADY_EXISTS)
+       if (nSingleInstance != 0 && GetLastError() == ERROR_ALREADY_EXISTS)
        {
                // Activate previous instance and send commandline to it
                HWND hWnd = FindWindow(CMainFrame::szClassName, nullptr);
@@ -251,6 +271,14 @@ BOOL CMergeApp::InitInstance()
                        {
                                ReleaseMutex(hMutex);
                                CloseHandle(hMutex);
+                               if (nSingleInstance > 1)
+                               {
+                                       DWORD dwProcessId = 0;
+                                       GetWindowThreadProcessId(hWnd, &dwProcessId);
+                                       HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, dwProcessId);
+                                       if (hProcess)
+                                               WaitForSingleObject(hProcess, INFINITE);
+                               }
                                return FALSE;
                        }
                }
@@ -307,6 +335,9 @@ BOOL CMergeApp::InitInstance()
                        m_pLineFilters->Import(oldFilter);
        }
 
+       if (m_pSubstitutionFiltersList != nullptr)
+               m_pSubstitutionFiltersList->Initialize(GetOptionsMgr());
+
        // Check if filter folder is set, and create it if not
        String pathMyFolders = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
        if (pathMyFolders.empty())
@@ -427,13 +458,20 @@ static void OpenContributersFile(int&)
        theApp.OpenFileToExternalEditor(paths::ConcatPath(env::GetProgPath(), ContributorsPath));
 }
 
+static void OpenUrl(int&)
+{
+       shell::Open(WinMergeURL);
+}
+
 // App command to run the dialog
 void CMergeApp::OnAppAbout()
 {
        CAboutDlg aboutDlg;
        aboutDlg.m_onclick_contributers += Poco::delegate(OpenContributersFile);
+       aboutDlg.m_onclick_url += Poco::delegate(OpenUrl);
        aboutDlg.DoModal();
        aboutDlg.m_onclick_contributers.clear();
+       aboutDlg.m_onclick_url.clear();
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -445,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();
 
@@ -501,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);
 
@@ -525,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;
@@ -540,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;
 }
@@ -669,7 +710,13 @@ bool CMergeApp::ParseArgsAndDoOpen(MergeCmdLineInfo& cmdInfo, CMainFrame* pMainF
                else if (cmdInfo.m_Files.GetSize() == 1)
                {
                        String sFilepath = cmdInfo.m_Files[0];
-                       if (IsProjectFile(sFilepath))
+                       if (cmdInfo.m_bSelfCompare)
+                       {
+                               strDesc[0] = cmdInfo.m_sLeftDesc;
+                               strDesc[1] = cmdInfo.m_sRightDesc;
+                               bCompared = pMainFrame->DoSelfCompare(IDOK, sFilepath, strDesc);
+                       }
+                       else if (IsProjectFile(sFilepath))
                        {
                                bCompared = LoadAndOpenProjectFile(sFilepath);
                        }
@@ -685,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());
                        }
                }
@@ -799,17 +846,6 @@ void CMergeApp::OpenFileToExternalEditor(const String& file, int nLineNumber/* =
 }
 
 /**
- * @brief Open file, if it exists, else open url
- */
-void CMergeApp::OpenFileOrUrl(LPCTSTR szFile, LPCTSTR szUrl)
-{
-       if (paths::DoesPathExist(szFile) == paths::IS_EXISTING_FILE)
-               ShellExecute(nullptr, _T("open"), _T("notepad.exe"), szFile, nullptr, SW_SHOWNORMAL);
-       else
-               ShellExecute(nullptr, _T("open"), szUrl, nullptr, nullptr, SW_SHOWNORMAL);
-}
-
-/**
  * @brief Show Help - this is for opening help from outside mainframe.
  * @param [in] helpLocation Location inside help, if `nullptr` main help is opened.
  */
@@ -826,7 +862,7 @@ void CMergeApp::ShowHelp(LPCTSTR helpLocation /*= nullptr*/)
                if (paths::DoesPathExist(sPath) == paths::IS_EXISTING_FILE)
                        ::HtmlHelp(nullptr, sPath.c_str(), HH_DISPLAY_TOC, NULL);
                else
-                       ShellExecute(nullptr, _T("open"), DocsURL, nullptr, nullptr, SW_SHOWNORMAL);
+                       shell::Open(DocsURL);
        }
        else
        {
@@ -866,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
@@ -929,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(
@@ -987,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;
@@ -1016,6 +1052,7 @@ int CMergeApp::HandleReadonlySave(String& strSavePath, bool bMultiFile,
                // Overwrite read-only file
                case IDYESTOALL:
                        bApplyToAll = true;  // Don't ask again (no break here)
+                       [[fallthrough]];
                case IDYES:
                        CFile::GetStatus(strSavePath.c_str(), status);
                        status.m_mtime = 0;             // Avoid unwanted changes
@@ -1023,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)
@@ -1103,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.
@@ -1113,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())
        {
@@ -1238,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);