OSDN Git Service

Added a new feature "Ignored Substitutions", which are the changes that will be ignor...
authorEugeneLaptev <40354914+EugeneLaptev@users.noreply.github.com>
Fri, 8 Jan 2021 22:16:23 +0000 (22:16 +0000)
committerGitHub <noreply@github.com>
Fri, 8 Jan 2021 22:16:23 +0000 (07:16 +0900)
Co-authored-by: EugeneSumo <40354914+EugeneSumo@users.noreply.github.com>
34 files changed:
Src/CompareEngines/Wrap_DiffUtils.cpp
Src/CompareEngines/Wrap_DiffUtils.h
Src/DiffContext.cpp
Src/DiffContext.h
Src/DiffWrapper.cpp
Src/DiffWrapper.h
Src/DirDoc.cpp
Src/DirDoc.h
Src/FilterList.cpp
Src/FilterList.h
Src/FolderCmp.cpp
Src/IgnoredSubstitutionsDlg.cpp [new file with mode: 0644]
Src/IgnoredSubstitutionsDlg.h [new file with mode: 0644]
Src/LineFiltersList.cpp
Src/MainFrm.cpp
Src/Merge.cpp
Src/Merge.h
Src/Merge.rc
Src/Merge.vs2017.vcxproj.filters
Src/Merge.vs2019.vcxproj
Src/Merge.vs2019.vcxproj.filters
Src/MergeDoc.cpp
Src/MergeDoc.h
Src/MergeDocLineDiffs.cpp
Src/MergeEditView.cpp
Src/MergeEditView.h
Src/OptionsDef.h
Src/OptionsInit.cpp
Src/SubeditList.cpp [new file with mode: 0644]
Src/SubeditList.h [new file with mode: 0644]
Src/TokenPairList.cpp [new file with mode: 0644]
Src/TokenPairList.h [new file with mode: 0644]
Src/resource.h
Src/subedit.h [new file with mode: 0644]

index 993a078..31c9fb5 100644 (file)
@@ -75,6 +75,12 @@ void DiffUtils::SetFilterList(FilterList * list)
        m_pFilterList = list;
 }
 
+void DiffUtils::SetIgnoredSubstitutionsList(FilterList* list0, FilterList* list1)
+{
+       m_pIgnoredSubstitutionsList0 = list0;
+       m_pIgnoredSubstitutionsList1 = list1;
+}
+
 /**
  * @brief Set filedata.
  * @param [in] items Count of filedata items to set.
index 32af659..a9772c7 100644 (file)
@@ -32,6 +32,7 @@ public:
        ~DiffUtils();
        void SetCompareOptions(const CompareOptions & options);
        void SetFilterList(FilterList * list);
+       void SetIgnoredSubstitutionsList(FilterList* list0, FilterList* list1);
        void ClearFilterList();
        void SetFileData(int items, file_data *data);
        int diffutils_compare_files();
@@ -45,6 +46,8 @@ public:
 private:
        std::unique_ptr<DiffutilsOptions> m_pOptions; /**< Compare options for diffutils. */
        FilterList * m_pFilterList; /**< Filter list for line filters. */
+       FilterList* m_pIgnoredSubstitutionsList0; 
+       FilterList* m_pIgnoredSubstitutionsList1;
        file_data * m_inf; /**< Compared files data (for diffutils). */
        int m_ndiffs; /**< Real diffs found. */
        int m_ntrivialdiffs; /**< Ignored diffs found. */
index 1edb7ce..8e384c3 100644 (file)
@@ -44,6 +44,7 @@ CDiffContext::CDiffContext(const PathContext & paths, int compareMethod)
 , m_piAbortable(nullptr)
 , m_bStopAfterFirstDiff(false)
 , m_pFilterList(nullptr)
+, m_pTokenListsForIs{ nullptr, nullptr }
 , m_pContentCompareOptions(nullptr)
 , m_pQuickCompareOptions(nullptr)
 , m_pOptions(nullptr)
index 96ef3cd..ee05ffc 100644 (file)
@@ -194,6 +194,7 @@ public:
        bool m_bRecursive; /**< Do we include subfolders to compare? */
        bool m_bPluginsEnabled; /**< Are plugins enabled? */
        std::unique_ptr<FilterList> m_pFilterList; /**< Filter list for line filters */
+       std::unique_ptr<FilterList> m_pTokenListsForIs[2]; /// Two lists for Ignored Substitutions
 
 private:
        /**
index aa177f5..bcf7c84 100644 (file)
 #include "parsers/crystallineparser.h"
 #include "SyntaxColors.h"
 #include "MergeApp.h"
+#include "OptionsMgr.h"
+#include "OptionsDef.h"
+#include "TokenPairList.h"
+#include "stringdiffs.h"
+using namespace strdiff;
+
 
 using Poco::Debugger;
 using Poco::format;
@@ -73,6 +79,7 @@ CDiffWrapper::CDiffWrapper()
 , m_pDiffList(nullptr)
 , m_bPathsAreTemp(false)
 , m_pFilterList(nullptr)
+, m_pIgnoredSubstitutionsList{nullptr}
 , m_bPluginsEnabled(false)
 , m_status()
 {
@@ -848,6 +855,99 @@ bool CDiffWrapper::RegExpFilter(int StartPos, int EndPos, const file_data *pinf)
        return linesMatch;
 }
 
+bool MatchDiffVsIngoredSubstitutions
+(
+       const std::string &fullLine0,
+       const std::string &fullLine1,
+       const strdiff::wdiff &diff,
+       const IgnoredSubstitutionsFilterList &ignoredSubstitutionsList,
+       const bool optUseRegexpsForIgnoredSubstitutions,
+       const bool optMatchBothWays
+)
+{
+       int changeStartPos[2] = { diff.begin[0], diff.begin[1] };
+       int changeEndPos[2] = { diff.end[0], diff.end[1] };
+       int changeLen0 = changeEndPos[0] - changeStartPos[0] + 1;
+       int changeLen1 = changeEndPos[1] - changeStartPos[1] + 1;
+       std::string change0 = std::string(fullLine0.c_str() + changeStartPos[0], changeLen0);
+       std::string change1 = std::string(fullLine1.c_str() + changeStartPos[1], changeLen1);
+
+       size_t numIgnoredSubstitutions = ignoredSubstitutionsList.GetCount();
+
+       for (int f = 0; f < numIgnoredSubstitutions; f++)
+       {
+               const IgnoredSusbstitutionItem& filter = ignoredSubstitutionsList[f];
+               // Check if the common prefix and suffix fit into the line around the change
+               if
+               (
+                          changeStartPos[0] < filter.CommonPrefixLength
+                       || changeStartPos[1] < filter.CommonPrefixLength
+                       || changeEndPos[0] >= fullLine0.length() - filter.CommonSuffixLength
+                       || changeEndPos[1] >= fullLine1.length() - filter.CommonSuffixLength
+               )
+                       continue; /// This filter does not fit into the line with its suffix and prefix
+
+               // Check if the common prefix and suffix match
+               bool continueWithNextFilter = false;
+               for (int p = 1; p <= filter.CommonPrefixLength; p++)
+               {
+                       char char0 = fullLine0[changeStartPos[0] - p];
+                       char char1 = fullLine1[changeStartPos[1] - p];
+                       if (char0 != char1)
+                       {
+                               continueWithNextFilter = true;
+                               break;
+                       }
+               }
+
+               for (int s = 1; s <= filter.CommonSuffixLength; s++)
+               {
+                       char char0 = fullLine0[changeEndPos[0] + s];
+                       char char1 = fullLine1[changeEndPos[1] + s];
+                       if (char0 != char1)
+                       {
+                               continueWithNextFilter = true;
+                               break;
+                       }
+               }
+
+               if (continueWithNextFilter)
+                       continue;
+
+               if(optUseRegexpsForIgnoredSubstitutions)
+               {
+
+                       if
+                       (
+                                       ignoredSubstitutionsList.MatchBoth(f, change0, change1)
+                               ||
+                                       optMatchBothWays
+                               && ignoredSubstitutionsList.MatchBoth(f, change1, change0)
+                       )
+                       {
+                               return true; /// a match found
+                       }
+               }
+               else
+               {
+                       if
+                       (
+                                  filter.ChangedPart[0].compare(change0) == 0
+                               && filter.ChangedPart[1].compare(change1) == 0
+                               ||
+                                  optMatchBothWays
+                               && filter.ChangedPart[0].compare(change1) == 0
+                               && filter.ChangedPart[1].compare(change0) == 0
+                       )
+                       {
+                               return true; /// a match found
+                       }
+               }
+       }
+
+       return false; /// n0 match found
+}
+
 /**
  * @brief Walk the diff utils change script, building the WinMerge list of diff blocks
  */
@@ -859,6 +959,10 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const
 
        struct change *next = script;
        
+       const bool optCompletelyBlankOutIgnoredSubstitutions = GetOptionsMgr()->GetBool(OPT_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS);
+       const bool optMatchBothWays = GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS);
+       const bool optUseRegexpsForIgnoredSubstitutions = GetOptionsMgr()->GetBool(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS);
+
        while (next != nullptr)
        {
                /* Find a set of changes that belong together.  */
@@ -937,6 +1041,65 @@ CDiffWrapper::LoadWinMergeDiffsFromDiffUtilsScript(struct change * script, const
                                                op = OP_TRIVIAL;
                                }
 
+                               /// Handling Ignored Substitutions
+                               if
+                               (
+                                          op == OP_DIFF
+                                       //&& next != nullptr
+                                       && QtyLinesLeft > 0
+                                       && QtyLinesRight > 0
+                                       && m_files.GetSize() == 2
+                                       && m_pIgnoredSubstitutionsList
+                                       && m_pIgnoredSubstitutionsList->GetCount()
+                               )
+                               {
+                                       size_t len = file_data_ary[0].linbuf[thisob->line0 + 1] - file_data_ary[0].linbuf[thisob->line0];
+                                       const char* string = file_data_ary[0].linbuf[thisob->line0];
+                                       size_t stringlen = linelen(string, len);
+                                       std::string fullLine0 = std::string(string, stringlen);
+
+                                       len = file_data_ary[1].linbuf[thisob->line1 + 1] - file_data_ary[1].linbuf[thisob->line1];
+                                       string = file_data_ary[1].linbuf[thisob->line1];
+                                       stringlen = linelen(string, len);
+                                       std::string fullLine1 = std::string(string, stringlen);
+
+                                       bool case_sensitive = false;
+                                       bool eol_sensitive = false;
+                                       int whitespace = WHITESPACE_IGNORE_CHANGE;
+                                       int breakType = 1;// breakType==1 means break also on punctuation
+                                       bool byte_level = true;
+                                       std::vector<wdiff> worddiffs = ComputeWordDiffs(
+                                               ucr::toTString(fullLine0.c_str()), ucr::toTString(fullLine1.c_str()),
+                                               case_sensitive, eol_sensitive, whitespace, breakType, byte_level
+                                       );
+
+                                       if (!worddiffs.empty())
+                                       {
+                                               bool lineShouldBeIgnored = true; /// If all changes are ignored the line is ignored
+                                               for (std::vector<strdiff::wdiff>::const_iterator diffIt = worddiffs.begin(); diffIt != worddiffs.end(); ++diffIt)
+                                               {
+                                                       if(!MatchDiffVsIngoredSubstitutions
+                                                       (
+                                                               fullLine0, fullLine1,
+                                                               *diffIt,
+                                                               *m_pIgnoredSubstitutionsList, optUseRegexpsForIgnoredSubstitutions,
+                                                               optMatchBothWays
+                                                       ))
+                                                       {
+                                                               lineShouldBeIgnored = false;
+                                                       }
+                                               }
+
+                                               if (lineShouldBeIgnored)
+                                               {
+                                                       if(optCompletelyBlankOutIgnoredSubstitutions)
+                                                               op = OP_NONE;
+                                                       else
+                                                               op = OP_TRIVIAL;
+                                               }
+                                       }
+                               }
+
                                AddDiffRange(m_pDiffList, trans_a0-1, trans_b0-1, trans_a1-1, trans_b1-1, op);
                        }
                }
@@ -1352,6 +1515,53 @@ void CDiffWrapper::SetFilterList(const FilterList* pFilterList)
        }
 }
 
+IgnoredSubstitutionsFilterList* CDiffWrapper::GetIgnoredSubstitutionsList()
+{
+       return m_pIgnoredSubstitutionsList.get();
+}
+
+void CDiffWrapper::SetIgnoredSubstitutionsList(const FilterList* pIgnoredSubstitutionsList0, const FilterList* pIgnoredSubstitutionsList1)
+{
+       m_pIgnoredSubstitutionsList.reset(new IgnoredSubstitutionsFilterList());
+
+       if (!pIgnoredSubstitutionsList0 || pIgnoredSubstitutionsList0->GetCount() == 0)
+       {
+               m_pIgnoredSubstitutionsList.reset();
+               return;
+       }
+
+       const bool optUseRegexpsForIgnoredSubstitutions = GetOptionsMgr()->GetBool(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS);
+
+       for (int f = 0; f < pIgnoredSubstitutionsList0->GetCount(); f++)
+       {
+               std::string s0 = (*pIgnoredSubstitutionsList0)[f].filterAsString;
+               std::string s1 = (*pIgnoredSubstitutionsList1)[f].filterAsString;
+               m_pIgnoredSubstitutionsList->Add(s0, s1, !optUseRegexpsForIgnoredSubstitutions);
+       }
+}
+
+void CDiffWrapper::SetIgnoredSubstitutionsList(const TokenPairList *ignoredSubstitutionsList)
+{
+       m_pIgnoredSubstitutionsList.reset(new IgnoredSubstitutionsFilterList());
+
+       // Remove filterlist if new filter is empty
+       if (!ignoredSubstitutionsList || ignoredSubstitutionsList->GetCount() == 0)
+       {
+               m_pIgnoredSubstitutionsList.reset();
+               return;
+       }
+
+       const bool optUseRegexpsForIgnoredSubstitutions = GetOptionsMgr()->GetBool(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS);
+
+       for (int f = 0; f < ignoredSubstitutionsList->GetCount(); f++)
+       {
+               std::string s0 = ucr::toUTF8(ignoredSubstitutionsList->GetAt(f).filterStr0.c_str());
+               std::string s1 = ucr::toUTF8(ignoredSubstitutionsList->GetAt(f).filterStr1.c_str());
+               if(s0 != s1)
+                       m_pIgnoredSubstitutionsList->Add(s0, s1, !optUseRegexpsForIgnoredSubstitutions);
+       }
+}
+
 void CDiffWrapper::SetFilterCommentsSourceDef(const String& ext)
 {
        m_pFilterCommentsDef = CrystalLineParser::GetTextType(ext.c_str());
index c3efa65..c04858e 100644 (file)
@@ -24,6 +24,7 @@ class PathContext;
 struct file_data;
 class MovedLines;
 class FilterList;
+class IgnoredSubstitutionsFilterList;
 namespace CrystalLineParser { struct TextDefinition; };
 
 /** @enum COMPARE_TYPE
@@ -183,6 +184,9 @@ public:
        void WritePatchFileTerminator(enum output_style output_style);
        void SetFilterList(const String& filterStr);
        void SetFilterList(const FilterList *pFilterList);
+       IgnoredSubstitutionsFilterList *GetIgnoredSubstitutionsList();
+       void SetIgnoredSubstitutionsList(const FilterList* pIgnoredSubstitutionsList0, const FilterList* pIgnoredSubstitutionsList1);
+       void SetIgnoredSubstitutionsList(const class TokenPairList *ignoredSubstitutionsList);
        void SetFilterCommentsSourceDef(CrystalLineParser::TextDefinition *def) { m_pFilterCommentsDef = def; };
        void SetFilterCommentsSourceDef(const String& ext);
        void EnablePlugins(bool enable);
@@ -202,10 +206,17 @@ public:
        static void FreeDiffUtilsScript(struct change * & script);
        bool RegExpFilter(int StartPos, int EndPos, const file_data * pinf) const;
 
+       IgnoredSubstitutionsFilterList *GetIgnoredSubstitutionsList(int index) const
+       {
+               return m_pIgnoredSubstitutionsList.get();
+       }
+
 private:
        DiffutilsOptions m_options;
        DIFFSTATUS m_status; /**< Status of last compare */
        std::unique_ptr<FilterList> m_pFilterList; /**< List of linefilters. */
+       std::unique_ptr<IgnoredSubstitutionsFilterList> m_pIgnoredSubstitutionsList;
+
        PathContext m_files; /**< Full path to diff'ed file. */
        PathContext m_alternativePaths; /**< file's alternative path (may be relative). */
        PathContext m_originalFile; /**< file's original (NON-TEMP) path. */
index 5531f1c..bbf75f5 100644 (file)
@@ -30,6 +30,7 @@
 #include "OptionsMgr.h"
 #include "OptionsDiffOptions.h"
 #include "LineFiltersList.h"
+#include "TokenPairList.h"
 #include "FileFilterHelper.h"
 #include "unicoder.h"
 #include "DirActions.h"
@@ -199,6 +200,36 @@ void CDirDoc::LoadLineFilterList(CDiffContext *pCtxt)
                pCtxt->m_pFilterList->AddRegExp(*it);
 }
 
+void CDirDoc::LoadTokensForIgnoredSubstitutions(CDiffContext* pCtxt)
+{
+       ASSERT(pCtxt != nullptr);
+
+       bool ignoredSubstitutionsAreEnabled = GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED);
+       if (!ignoredSubstitutionsAreEnabled || theApp.m_pTokensForIs->GetCount() == 0)
+       {
+               pCtxt->m_pTokenListsForIs[0].reset();
+               pCtxt->m_pTokenListsForIs[1].reset();
+               return;
+       }
+
+       if (pCtxt->m_pTokenListsForIs[0])
+               pCtxt->m_pTokenListsForIs[0]->RemoveAllFilters();
+       else
+               pCtxt->m_pTokenListsForIs[0].reset(new FilterList());
+
+       if (pCtxt->m_pTokenListsForIs[1])
+               pCtxt->m_pTokenListsForIs[1]->RemoveAllFilters();
+       else
+               pCtxt->m_pTokenListsForIs[1].reset(new FilterList());
+
+       for (int f = 0; f < theApp.m_pTokensForIs->GetCount(); f++)
+       {
+               const TokenPair &tokenPair = theApp.m_pTokensForIs->GetAt(f);
+               pCtxt->m_pTokenListsForIs[0]->AddRegExp(ucr::toUTF8(tokenPair.filterStr0));
+               pCtxt->m_pTokenListsForIs[1]->AddRegExp(ucr::toUTF8(tokenPair.filterStr1));
+       }
+}
+
 void CDirDoc::DiffThreadCallback(int& state)
 {
        PostMessage(m_pDirView->GetSafeHwnd(), MSG_UI_UPDATE, state, false);
@@ -207,6 +238,7 @@ void CDirDoc::DiffThreadCallback(int& state)
 void CDirDoc::InitDiffContext(CDiffContext *pCtxt)
 {
        LoadLineFilterList(pCtxt);
+       LoadTokensForIgnoredSubstitutions(pCtxt);
 
        DIFFOPTIONS options = {0};
        Options::DiffOptions::Load(GetOptionsMgr(), options);
index 41f15dc..4dbe80c 100644 (file)
@@ -117,6 +117,7 @@ public:
 protected:
        void InitDiffContext(CDiffContext *pCtxt);
        void LoadLineFilterList(CDiffContext *pCtxt);
+       void LoadTokensForIgnoredSubstitutions(CDiffContext* pCtxt);
 
        // Generated message map functions
        //{{AFX_MSG(CDirDoc)
index a4bf586..c93fe21 100644 (file)
@@ -97,3 +97,164 @@ bool FilterList::Match(const std::string& string, int codepage/*=CP_UTF8*/)
        return retval;
 }
 
+static std::string ExtractCommonPrefix(const std::string& str0, const std::string& str1)
+{
+       size_t strlen0 = str0.length();
+       size_t strlen1 = str1.length();
+       size_t minStrlen = strlen0 < strlen1 ? strlen0 : strlen1;
+       for (size_t c = 0; c < minStrlen; c++)
+       {
+               if (str0[c] != str1[c])
+                       return str0.substr(0, c);
+       }
+       return strlen0 < strlen1 ? str0 : str1;
+}
+
+static std::string ExtractCommonSuffix(const std::string& str0, const std::string& str1)
+{
+       size_t strlen0 = str0.length();
+       size_t strlen1 = str1.length();
+       size_t minStrlen = strlen0 < strlen1 ? strlen0 : strlen1;
+       for (size_t c = 0; c < minStrlen; c++)
+       {
+               if (str0[strlen0 - 1 - c] != str1[strlen1 - 1 - c])
+                       return str0.substr(strlen0 - c, c);
+       }
+       return strlen0 < strlen1 ? str0 : str1;
+}
+
+template<int index>
+static std::string ExtractMiddleChangedPart(const std::string& str0, const std::string& str1)
+{
+       size_t strlen[2]{ str0.length(), str1.length() };
+       size_t minStrlen = strlen[0] < strlen[1] ? strlen[0] : strlen[1];
+       size_t diffStart = 0;
+       while (diffStart < minStrlen && str0[diffStart] == str1[diffStart])
+               diffStart++;
+       size_t diffEnd = minStrlen - 1;
+       while (diffEnd < minStrlen && str0[strlen[0] - 1 - diffEnd] == str1[strlen[1] - 1 - diffEnd])
+               diffEnd++;
+
+       const std::string* strs[2]{ &str0, &str1 };
+       return strs[index]->substr(diffStart, strlen[index] - diffEnd - 1);
+}
+
+IgnoredSusbstitutionItem::IgnoredSusbstitutionItem
+(
+       const std::string& filter0, const std::string& filter1,
+       int regexpCompileOptions, bool extractCommonSufixAndPrefix
+)
+       : CommonPrefix(extractCommonSufixAndPrefix ? ExtractCommonPrefix(filter0, filter1) : "")
+       , CommonPrefixLength(CommonPrefix.length())
+       , ChangedPart
+       {
+               extractCommonSufixAndPrefix ? ExtractMiddleChangedPart<0>(filter0, filter1) : filter0,
+               extractCommonSufixAndPrefix ? ExtractMiddleChangedPart<1>(filter0, filter1) : filter1
+       }
+       , CommonSuffix(extractCommonSufixAndPrefix ? ExtractCommonSuffix(filter0, filter1) : "")
+       , CommonSuffixLength(CommonSuffix.length())
+       , ChangedPartRegexp
+       {
+               Poco::RegularExpression(ChangedPart[0], regexpCompileOptions),
+               Poco::RegularExpression(ChangedPart[1], regexpCompileOptions)
+       }
+{
+}
+
+IgnoredSubstitutionsFilterList::IgnoredSubstitutionsFilterList()
+{
+}
+
+IgnoredSubstitutionsFilterList::~IgnoredSubstitutionsFilterList()
+{
+       RemoveAllFilters();
+}
+
+void IgnoredSubstitutionsFilterList::Add(const std::string& change0, const std::string& change1, bool extractCommonSufixAndPrefix)
+{
+       try
+       {
+               m_list.push_back
+               (
+                       std::shared_ptr<IgnoredSusbstitutionItem>(
+                               new IgnoredSusbstitutionItem(change0, change1, RegularExpression::RE_UTF8, extractCommonSufixAndPrefix)
+               ));
+       }
+       catch (...)
+       {
+               // TODO:
+       }
+}
+
+bool IgnoredSubstitutionsFilterList::MatchBoth
+(
+       size_t filterIndex,
+       const std::string& string0,
+       const std::string& string1,
+       int codepage/*=CP_UTF8*/
+) const
+{
+       bool retval = false;
+       const size_t count = m_list.size();
+
+       if (filterIndex >= count)
+               return false;
+
+       // convert string into UTF-8
+       ucr::buffer buf0(string0.length() * 2);
+       ucr::buffer buf1(string1.length() * 2);
+
+       if (codepage != ucr::CP_UTF_8)
+       {
+               ucr::convert(ucr::NONE, codepage, reinterpret_cast<const unsigned char*>(string0.c_str()),
+                       string0.length(), ucr::UTF8, ucr::CP_UTF_8, &buf0);
+               ucr::convert(ucr::NONE, codepage, reinterpret_cast<const unsigned char*>(string1.c_str()),
+                       string1.length(), ucr::UTF8, ucr::CP_UTF_8, &buf1);
+       }
+
+       const IgnoredSusbstitutionItem &item = *m_list[filterIndex];
+       int result = 0;
+       RegularExpression::Match match;
+       try
+       {
+               if (buf0.size > 0 && buf1.size > 0)
+               {
+                       result =
+                          item.ChangedPartRegexp[0].match(std::string(reinterpret_cast<const char*>(buf0.ptr), buf0.size), 0, match)
+                       && item.ChangedPartRegexp[1].match(std::string(reinterpret_cast<const char*>(buf1.ptr), buf1.size), 0, match);
+               }
+               else
+               {
+                       result =
+                          item.ChangedPartRegexp[0].match(string0, 0, match)
+                       && item.ChangedPartRegexp[1].match(string1, 0, match);
+               }
+       }
+       catch (...)
+       {
+               // TODO:
+       }
+       if (result > 0)
+       {
+               retval = true;
+       }
+
+       return retval;
+}
+
+void IgnoredSubstitutionsFilterList::RemoveAllFilters()
+{
+       m_list.clear();
+}
+
+bool IgnoredSubstitutionsFilterList::HasRegExps() const
+{
+       return !m_list.empty();
+}
+
+const IgnoredSusbstitutionItem &IgnoredSubstitutionsFilterList::operator[](int index) const
+{
+       return *m_list[index];
+}
+
+
index ae8e2d1..ce8d0a4 100644 (file)
@@ -40,8 +40,10 @@ public:
        void AddRegExp(const std::string& regularExpression);
        void RemoveAllFilters();
        bool HasRegExps() const;
+       size_t GetCount() const { return m_list.size(); }
        bool Match(const std::string& string, int codepage = ucr::CP_UTF_8);
        const char * GetLastMatchExpression() const;
+       const filter_item& operator[](int index) const;
 
 private:
        std::vector <filter_item_ptr> m_list;
@@ -49,6 +51,45 @@ private:
 
 };
 
+struct IgnoredSusbstitutionItem
+{
+       std::string CommonPrefix;
+       size_t CommonPrefixLength;
+       std::string ChangedPart[2];
+       std::string CommonSuffix;
+       size_t CommonSuffixLength;
+       Poco::RegularExpression ChangedPartRegexp[2]; /**< Compiled regular expression */
+
+       IgnoredSusbstitutionItem
+       (
+               const std::string& filter0, const std::string& filter1,
+               int regexpCompileOptions, bool extractCommonSufixAndPrefix
+       );
+};
+
+class IgnoredSubstitutionsFilterList
+{
+public:
+       IgnoredSubstitutionsFilterList();
+       ~IgnoredSubstitutionsFilterList();
+
+       void Add(const std::string& change0, const std::string& change1, bool extractCommonSufixAndPrefix);
+       void RemoveAllFilters();
+       bool HasRegExps() const;
+       size_t GetCount() const { return m_list.size(); }
+       bool MatchBoth
+       (
+               size_t filterIndex,
+               const std::string& string0,
+               const std::string& string1,
+               int codepage = CP_UTF8
+       ) const;
+       const IgnoredSusbstitutionItem &operator[](int index) const;
+
+private:
+       std::vector<std::shared_ptr<IgnoredSusbstitutionItem>> m_list;
+};
+
 /** 
  * @brief Removes all expressions from the list.
  */
@@ -75,3 +116,10 @@ inline const char * FilterList::GetLastMatchExpression() const
 {
        return m_lastMatchExpression->c_str();
 }
+
+inline const filter_item& FilterList::operator[](int index) const
+{
+       const filter_item_ptr &item = m_list[index];
+       return *item;
+}
+
index 057732e..0b1d37a 100644 (file)
@@ -263,6 +263,7 @@ int FolderCmp::prepAndCompareFiles(DIFFITEM &di)
                                dw.SetCompareFiles(tFiles);
                                dw.SetOptions(m_pCtxt->GetOptions());
                                dw.SetFilterList(m_pCtxt->m_pFilterList.get());
+                               dw.SetIgnoredSubstitutionsList(m_pCtxt->m_pTokenListsForIs[0].get(), m_pCtxt->m_pTokenListsForIs[1].get());
                                dw.SetFilterCommentsSourceDef(Ext);
                                dw.SetCreateDiffList(&diffList);
                                dw.LoadWinMergeDiffsFromDiffUtilsScript3(
diff --git a/Src/IgnoredSubstitutionsDlg.cpp b/Src/IgnoredSubstitutionsDlg.cpp
new file mode 100644 (file)
index 0000000..e6f6415
--- /dev/null
@@ -0,0 +1,203 @@
+/**
+ *  @file IgnoredSubstitutionsFiltersDlg.cpp
+ *
+ *  @brief Implementation of Line Filter dialog
+ */ 
+
+#include "stdafx.h"
+#include "TokenPairList.h"
+#include "Merge.h"
+#include "IgnoredSubstitutionsDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+/** @brief Location for file compare specific help to open. */
+static TCHAR FilterHelpLocation[] = _T("::/htmlhelp/Filters.html");
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropLineFilter property page
+
+IMPLEMENT_DYNAMIC(IgnoredSubstitutionsDlg, CTrPropertyPage)
+
+/**
+ * @brief Constructor.
+ */
+IgnoredSubstitutionsDlg::IgnoredSubstitutionsDlg()
+       : CTrPropertyPage(IgnoredSubstitutionsDlg::IDD)
+       , m_pExternalRenameList(nullptr)
+       , InPlaceEdit(nullptr)
+{
+       //{{AFX_DATA_INIT(IgnoredSubstitutionsFiltersDlg)
+       m_IgnoredSubstitutionsAreEnabled = false;
+       m_IgnoredSubstitutionsWorkBothWays = false;
+       m_CompletelyBlankOutIgnoredSubstitutions = false;
+       m_UseRegexpsForIgnoredSubstitutions = false;
+       //}}AFX_DATA_INIT
+       m_strCaption = theApp.LoadDialogCaption(m_lpszTemplateName).c_str();
+       m_psp.pszTitle = m_strCaption;
+       m_psp.dwFlags |= PSP_USETITLE;
+       m_psp.hIcon = AfxGetApp()->LoadIcon(IDI_LINEFILTER);
+       m_psp.dwFlags |= PSP_USEHICON;
+}
+
+void IgnoredSubstitutionsDlg::DoDataExchange(CDataExchange* pDX)
+{
+       CPropertyPage::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(IgnoredSubstitutionsFiltersDlg)
+       DDX_Check(pDX, IDC_IGNORED_SUSBSTITUTIONS_ARE_ENABLED, m_IgnoredSubstitutionsAreEnabled);
+       DDX_Check(pDX, IDC_IGNORED_SUSBSTITUTIONS_WORK_BOTH_WAYS, m_IgnoredSubstitutionsWorkBothWays);
+       DDX_Check(pDX, IDC_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS, m_CompletelyBlankOutIgnoredSubstitutions);
+       DDX_Check(pDX, IDC_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS, m_UseRegexpsForIgnoredSubstitutions);
+       //}}AFX_DATA_MAP
+       DDX_Control(pDX, IDC_IGNORED_SUBSTITUTIONS_FILTER, m_VisibleFiltersList);
+}
+
+BEGIN_MESSAGE_MAP(IgnoredSubstitutionsDlg, CTrPropertyPage)
+       //{{AFX_MSG_MAP(IgnoredSubstitutionsFiltersDlg)
+       ON_COMMAND(ID_HELP, OnHelp)
+       //}}AFX_MSG_MAP
+       ON_BN_CLICKED(IDC_LFILTER_ADDBTN, OnBnClickedAddBtn)
+       ON_BN_CLICKED(IDC_LFILTER_CLEARBTN, OnBnClickedClearBtn)
+       ON_BN_CLICKED(IDC_LFILTER_REMOVEBTN, OnBnClickedRemovebtn)
+       ON_NOTIFY(LVN_ENDLABELEDIT, IDC_IGNORED_SUBSTITUTIONS_FILTER, OnEndLabelEdit)
+END_MESSAGE_MAP()
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CPropLineFilter message handlers
+
+/**
+ * @brief Initialize the dialog.
+ */
+BOOL IgnoredSubstitutionsDlg::OnInitDialog()
+{
+       CTrPropertyPage::OnInitDialog();
+
+       InitList();
+       
+       return TRUE;  // return TRUE unless you set the focus to a control
+                     // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+
+void IgnoredSubstitutionsDlg::InitList()
+{
+       m_VisibleFiltersList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+       //m_VisibleFiltersList.SetExtendedStyle(LVS_EX_INFOTIP | LVS_EX_GRIDLINES);
+
+       const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
+       auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
+
+       m_VisibleFiltersList.InsertColumn(0, _("On one panel").c_str(), LVCFMT_LEFT, pointToPixel(112));
+       m_VisibleFiltersList.InsertColumn(1, _("On the other panel").c_str(), LVCFMT_LEFT, pointToPixel(262));
+
+       if (m_pExternalRenameList)
+       {
+               for (int i = 0; i < (int)m_pExternalRenameList->GetCount(); i++)
+               {
+                       const TokenPair& item = m_pExternalRenameList->GetAt(i);
+                       m_VisibleFiltersList.InsertItem(i, item.filterStr0.c_str());
+                       m_VisibleFiltersList.SetItemText(i, 1, item.filterStr1.c_str());
+               }
+       }
+}
+
+/**
+ * @brief Open help from mainframe when user presses F1.
+ */
+void IgnoredSubstitutionsDlg::OnHelp()
+{
+       theApp.ShowHelp(FilterHelpLocation);
+}
+
+/**
+ * @brief Called when Add-button is clicked.
+ */
+void IgnoredSubstitutionsDlg::OnBnClickedAddBtn()
+{
+       int num = m_VisibleFiltersList.GetItemCount();
+       int ind = m_VisibleFiltersList.InsertItem(num, _("<Edit here>").c_str());
+       m_VisibleFiltersList.SetItemText(num, 1, _("<Edit here>").c_str());
+
+       if (ind >= -1)
+       {
+               m_VisibleFiltersList.SetItemState(ind, LVIS_SELECTED, LVIS_SELECTED);
+               m_VisibleFiltersList.EnsureVisible(ind, FALSE);
+               //EditSelectedFilter();
+       }
+}
+
+/**
+ * @brief Called when Clear-button is clicked.
+ */
+void IgnoredSubstitutionsDlg::OnBnClickedClearBtn()
+{
+       m_VisibleFiltersList.DeleteAllItems();
+}
+
+/**
+ * @brief Save filters to list when exiting the dialog.
+ */
+void IgnoredSubstitutionsDlg::OnOK()
+{
+       m_pExternalRenameList->Empty();
+
+       for (int i = 0; i < m_VisibleFiltersList.GetItemCount(); i++)
+       {
+               String symbolBeforeRename = m_VisibleFiltersList.GetItemText(i, 0);
+               String symbolAfterRename = m_VisibleFiltersList.GetItemText(i, 1);
+               if(symbolBeforeRename != _("<Edit here>") && symbolAfterRename != _("<Edit here>"))
+                       m_pExternalRenameList->AddFilter(symbolBeforeRename, symbolAfterRename);
+       }
+
+       CPropertyPage::OnClose();
+       //CDialog::OnOK(); //?
+}
+
+/**
+ * @brief Sets external filter list.
+ * @param [in] list External filter list.
+ */
+void IgnoredSubstitutionsDlg::SetList(TokenPairList *list)
+{
+       m_pExternalRenameList = list;
+}
+
+/**
+ * @brief Called when Remove button is clicked.
+ */
+void IgnoredSubstitutionsDlg::OnBnClickedRemovebtn()
+{
+       int sel = m_VisibleFiltersList.GetNextItem(-1, LVNI_SELECTED);
+       if (sel != -1)
+       {
+               m_VisibleFiltersList.DeleteItem(sel);
+       }
+
+       int newSel = min(m_VisibleFiltersList.GetItemCount() - 1, sel);
+       if (newSel >= -1)
+       {
+               m_VisibleFiltersList.SetItemState(newSel, LVIS_SELECTED, LVIS_SELECTED);
+               bool bPartialOk = false;
+               m_VisibleFiltersList.EnsureVisible(newSel, bPartialOk);
+       }
+}
+
+/**
+ * @brief Called when the user activates an item.
+ */
+// void IgnoredSubstitutionsFiltersDlg::OnLvnItemActivate(NMHDR *pNMHDR, LRESULT *pResult)
+// {
+//     EditSelectedFilter();
+//     *pResult = 0;
+// }
+
+/**
+ * @brief Called when in-place editing has finished.
+ */
+void IgnoredSubstitutionsDlg::OnEndLabelEdit(NMHDR *pNMHDR, LRESULT *pResult)
+{
+       m_VisibleFiltersList.OnEndLabelEdit(pNMHDR, pResult);
+}
diff --git a/Src/IgnoredSubstitutionsDlg.h b/Src/IgnoredSubstitutionsDlg.h
new file mode 100644 (file)
index 0000000..b30c0cb
--- /dev/null
@@ -0,0 +1,67 @@
+/** 
+ * @file  IgnoredSubstitutionsDlg.h
+ *
+ * @brief Declaration file for Line Filter dialog
+ *
+ */
+#pragma once
+
+#include "TrDialogs.h"
+#include "SubeditList.h"
+
+class IgnoredSubstitutionFiltersList;
+
+class IgnoredSubstitutionsDlg : public CTrPropertyPage
+{
+       DECLARE_DYNAMIC(IgnoredSubstitutionsDlg)
+
+// Construction
+public:
+       IgnoredSubstitutionsDlg();
+
+       void SetList(TokenPairList *list);
+
+// Dialog Data
+       //{{AFX_DATA(IgnoredSubstitutionsDlg)
+       enum { IDD = IDD_IGNORED_SUSBSTITUTIONS_DLG };
+       bool m_IgnoredSubstitutionsAreEnabled;
+       bool m_IgnoredSubstitutionsWorkBothWays;
+       bool m_CompletelyBlankOutIgnoredSubstitutions;
+       bool m_UseRegexpsForIgnoredSubstitutions;
+       //}}AFX_DATA
+
+// Overrides
+       // ClassWizard generate virtual function overrides
+       //{{AFX_VIRTUAL(IgnoredSubstitutionsDlg)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+       // Generated message map functions
+       //{{AFX_MSG(IgnoredSubstitutionsDlg)
+       virtual BOOL OnInitDialog() override;
+       afx_msg void OnHelp();
+       virtual void OnOK() override;
+       afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+       afx_msg void OnBnClickedAddBtn();
+       afx_msg void OnBnClickedClearBtn();
+       afx_msg void OnBnClickedEditbtn();
+       afx_msg void OnBnClickedRemovebtn();
+       afx_msg void OnLvnItemActivate(NMHDR *pNMHDR, LRESULT *pResult);
+       afx_msg void OnLvnKeyDown(NMHDR *pNMHDR, LRESULT *pResult);
+       afx_msg void OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+
+       void InitList();
+
+private:
+       std::unique_ptr<CInPlaceEdit> InPlaceEdit;
+
+       CSubeditList m_VisibleFiltersList; /**< List control having filter strings */
+
+       TokenPairList *m_pExternalRenameList;
+};
index 9f0cf38..f9c1a18 100644 (file)
@@ -14,7 +14,7 @@
 using std::vector;
 
 /** @brief Registry key for saving linefilters. */
-static const TCHAR FiltersRegPath[] =_T("LineFilters");
+static const TCHAR LineFiltersRegPath[] = _T("LineFilters");
 
 /**
  * @brief Default constructor.
@@ -132,7 +132,7 @@ bool LineFiltersList::Compare(const LineFiltersList *list) const
 void LineFiltersList::Initialize(COptionsMgr *pOptionsMgr)
 {
        assert(pOptionsMgr != nullptr);
-       String valuename(FiltersRegPath);
+       String valuename(LineFiltersRegPath);
 
        m_pOptionsMgr = pOptionsMgr;
 
@@ -143,11 +143,11 @@ void LineFiltersList::Initialize(COptionsMgr *pOptionsMgr)
 
        for (unsigned i = 0; i < count; i++)
        {
-               String name = strutils::format(_T("%s/Filter%02u"), FiltersRegPath, i);
+               String name = strutils::format(_T("%s/Filter%02u"), LineFiltersRegPath, i);
                m_pOptionsMgr->InitOption(name, _T(""));
                String filter = m_pOptionsMgr->GetString(name);
 
-               name = strutils::format(_T("%s/Enabled%02u"), FiltersRegPath, i);
+               name = strutils::format(_T("%s/Enabled%02u"), LineFiltersRegPath, i);
                m_pOptionsMgr->InitOption(name, (int)true);
                int enabled = m_pOptionsMgr->GetInt(name);
                bool bEnabled = enabled ? true : false;
@@ -162,7 +162,7 @@ void LineFiltersList::Initialize(COptionsMgr *pOptionsMgr)
 void LineFiltersList::SaveFilters()
 {
        assert(m_pOptionsMgr != nullptr);
-       String valuename(FiltersRegPath);
+       String valuename(LineFiltersRegPath);
 
        size_t count = m_items.size();
        valuename += _T("/Values");
@@ -172,29 +172,29 @@ void LineFiltersList::SaveFilters()
        {
                const LineFilterItemPtr& item = m_items[i];
 
-               String name = strutils::format(_T("%s/Filter%02u"), FiltersRegPath, i);
+               String name = strutils::format(_T("%s/Filter%02u"), LineFiltersRegPath, i);
                m_pOptionsMgr->InitOption(name, _T(""));
                m_pOptionsMgr->SaveOption(name, item->filterStr);
 
-               name = strutils::format(_T("%s/Enabled%02u"), FiltersRegPath, i);
+               name = strutils::format(_T("%s/Enabled%02u"), LineFiltersRegPath, i);
                m_pOptionsMgr->InitOption(name, 0);
                m_pOptionsMgr->SaveOption(name, (int)item->enabled);
        }
 
        // Remove options we don't need anymore
        // We could have earlier 10 pcs but now we only need 5
-       String filter = strutils::format(_T("%s/Filter%02u"), FiltersRegPath, count);
+       String filter = strutils::format(_T("%s/Filter%02u"), LineFiltersRegPath, count);
        int retval1 = m_pOptionsMgr->RemoveOption(filter);
 
-       filter = strutils::format(_T("%s/Enabled%02u"), FiltersRegPath, count);
+       filter = strutils::format(_T("%s/Enabled%02u"), LineFiltersRegPath, count);
        int retval2 = m_pOptionsMgr->RemoveOption(filter);
        
        while (retval1 == COption::OPT_OK || retval2 == COption::OPT_OK)
        {
                ++count;
-               filter = strutils::format(_T("%s/Filter%02u"), FiltersRegPath, count);
+               filter = strutils::format(_T("%s/Filter%02u"), LineFiltersRegPath, count);
                retval1 = m_pOptionsMgr->RemoveOption(filter);
-               filter = strutils::format(_T("%s/Enabled%02u"), FiltersRegPath, count);
+               filter = strutils::format(_T("%s/Enabled%02u"), LineFiltersRegPath, count);
                retval2 = m_pOptionsMgr->RemoveOption(filter);
        }
 }
index c41c838..3e64de5 100644 (file)
 #include "HexMergeView.h"
 #include "ImgMergeFrm.h"
 #include "LineFiltersList.h"
+#include "TokenPairList.h"
 #include "ConflictFileParser.h"
 #include "LineFiltersDlg.h"
+#include "IgnoredSubstitutionsDlg.h"
 #include "paths.h"
 #include "Environment.h"
 #include "PatchTool.h"
@@ -1627,12 +1629,15 @@ void CMainFrame::OnToolsFilters()
        String title = _("Filters");
        CPropertySheet sht(title.c_str());
        LineFiltersDlg lineFiltersDlg;
+       IgnoredSubstitutionsDlg ignoredSubstitutionsFiltersDlg;
        FileFiltersDlg fileFiltersDlg;
        std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
+       std::unique_ptr<TokenPairList> ignoredSubstitutionsFilters(new TokenPairList());
        String selectedFilter;
        const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
        sht.AddPage(&fileFiltersDlg);
        sht.AddPage(&lineFiltersDlg);
+       sht.AddPage(&ignoredSubstitutionsFiltersDlg);
        sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
 
        // Make sure all filters are up-to-date
@@ -1646,6 +1651,19 @@ void CMainFrame::OnToolsFilters()
        lineFilters->CloneFrom(theApp.m_pLineFilters.get());
        lineFiltersDlg.SetList(lineFilters.get());
 
+       const bool ignoredSubstitutionsAreEnabledOrig = GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED);
+       const bool ignoredSubstitutionsWorkBothWaysOrig = GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS);
+       const bool completelyBlankOutIgnoredSubstitutionsOrig = GetOptionsMgr()->GetBool(OPT_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS);
+       const bool optUseRegexpsForSubstitutionsOrig = GetOptionsMgr()->GetBool(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS);
+
+       ignoredSubstitutionsFiltersDlg.m_IgnoredSubstitutionsAreEnabled = ignoredSubstitutionsAreEnabledOrig;
+       ignoredSubstitutionsFiltersDlg.m_IgnoredSubstitutionsWorkBothWays = ignoredSubstitutionsWorkBothWaysOrig;
+       ignoredSubstitutionsFiltersDlg.m_CompletelyBlankOutIgnoredSubstitutions = completelyBlankOutIgnoredSubstitutionsOrig;
+       ignoredSubstitutionsFiltersDlg.m_UseRegexpsForIgnoredSubstitutions = optUseRegexpsForSubstitutionsOrig;
+       
+       ignoredSubstitutionsFilters->CloneFrom(theApp.m_pTokensForIs.get());
+       ignoredSubstitutionsFiltersDlg.SetList(ignoredSubstitutionsFilters.get());
+
        if (sht.DoModal() == IDOK)
        {
                String strNone = _("<None>");
@@ -1670,6 +1688,19 @@ void CMainFrame::OnToolsFilters()
                bool linefiltersEnabled = lineFiltersDlg.m_bIgnoreRegExp;
                GetOptionsMgr()->SaveOption(OPT_LINEFILTER_ENABLED, linefiltersEnabled);
 
+               bool ignoredSubstitutionsAreEnabled = ignoredSubstitutionsFiltersDlg.m_IgnoredSubstitutionsAreEnabled;
+               GetOptionsMgr()->SaveOption(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED, ignoredSubstitutionsAreEnabled);
+
+               bool ignoredSubstitutionsWorkBothWays = ignoredSubstitutionsFiltersDlg.m_IgnoredSubstitutionsWorkBothWays;
+               GetOptionsMgr()->SaveOption(OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS, ignoredSubstitutionsWorkBothWays);
+
+               bool completelyBlankOutIgnoredSubstitutions = ignoredSubstitutionsFiltersDlg.m_CompletelyBlankOutIgnoredSubstitutions;
+               GetOptionsMgr()->SaveOption(OPT_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS, completelyBlankOutIgnoredSubstitutions);
+               
+               bool optUseRegexpsForSubstitutions = GetOptionsMgr()->GetBool(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS);
+               GetOptionsMgr()->SaveOption(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS, optUseRegexpsForSubstitutions);
+
+
                // Check if compare documents need rescanning
                bool bFileCompareRescan = false;
                bool bFolderCompareRescan = false;
@@ -1677,8 +1708,16 @@ void CMainFrame::OnToolsFilters()
                FRAMETYPE frame = GetFrameType(pFrame);
                if (frame == FRAME_FILE)
                {
-                       if (lineFiltersEnabledOrig != linefiltersEnabled ||
-                                       !theApp.m_pLineFilters->Compare(lineFilters.get()))
+                       if
+                       (
+                                  linefiltersEnabled != lineFiltersEnabledOrig
+                               || ignoredSubstitutionsAreEnabled != ignoredSubstitutionsAreEnabledOrig
+                               || ignoredSubstitutionsWorkBothWays != ignoredSubstitutionsWorkBothWaysOrig
+                               || completelyBlankOutIgnoredSubstitutions != completelyBlankOutIgnoredSubstitutionsOrig
+                               || optUseRegexpsForSubstitutions != optUseRegexpsForSubstitutionsOrig
+                               || !lineFilters->Compare(theApp.m_pLineFilters.get())
+                               || !ignoredSubstitutionsFilters->Compare(theApp.m_pTokensForIs.get())
+                       )
                        {
                                bFileCompareRescan = true;
                        }
@@ -1699,6 +1738,10 @@ void CMainFrame::OnToolsFilters()
                theApp.m_pLineFilters->CloneFrom(lineFilters.get());
                theApp.m_pLineFilters->SaveFilters();
 
+               theApp.m_pTokensForIs->CloneFrom(ignoredSubstitutionsFilters.get());
+               theApp.m_pTokensForIs->SaveFilters();
+
+
                if (bFileCompareRescan)
                {
                        for (auto pMergeDoc : GetAllMergeDocs())
index 5b6e7ad..9d44b67 100644 (file)
@@ -38,6 +38,7 @@
 #include "paths.h"
 #include "FileFilterHelper.h"
 #include "LineFiltersList.h"
+#include "TokenPairList.h"
 #include "SyntaxColors.h"
 #include "CCrystalTextMarkers.h"
 #include "OptionsSyntaxColors.h"
@@ -105,6 +106,7 @@ CMergeApp::CMergeApp() :
 , m_bEscShutdown(false)
 , m_bExitIfNoDiff(MergeCmdLineInfo::Disabled)
 , m_pLineFilters(new LineFiltersList())
+, m_pTokensForIs(new TokenPairList())
 , m_pSyntaxColors(new SyntaxColors())
 , m_pMarkers(new CCrystalTextMarkers())
 , m_bMergingMode(false)
@@ -307,6 +309,9 @@ BOOL CMergeApp::InitInstance()
                        m_pLineFilters->Import(oldFilter);
        }
 
+       if (m_pTokensForIs != nullptr)
+               m_pTokensForIs->Initialize(GetOptionsMgr());
+
        // Check if filter folder is set, and create it if not
        String pathMyFolders = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
        if (pathMyFolders.empty())
index 7040216..0b9d7eb 100644 (file)
@@ -34,6 +34,7 @@ class MergeCmdLineInfo;
 class ProjectFile;
 class COptionsMgr;
 class LineFiltersList;
+class TokenPairList;
 class SyntaxColors;
 class CCrystalTextMarkers;
 
@@ -65,6 +66,7 @@ public:
        CCrystalTextMarkers * GetMainMarkers() const { return m_pMarkers.get(); }
        MergeCmdLineInfo::ExitNoDiff m_bExitIfNoDiff; /**< Exit if files are identical? */
        std::unique_ptr<LineFiltersList> m_pLineFilters; /**< List of linefilters */
+       std::unique_ptr<TokenPairList> m_pTokensForIs;
 
        WORD GetLangId() const;
        void SetIndicators(CStatusBar &, const UINT *, int) const;
index dfee303..bf33a57 100644 (file)
@@ -72,6 +72,7 @@ BEGIN
         MENUITEM "Copy fro&m Right",            ID_COPY_FROM_RIGHT\r
         MENUITEM SEPARATOR\r
         MENUITEM "&Select Line Difference\tF4", ID_SELECTLINEDIFF\r
+        MENUITEM "Add this change to &Ignored Substitutions", ID_ADD_TO_IGNORED_SUBSTITUTIONS\r
         MENUITEM SEPARATOR\r
         MENUITEM "&Undo",                       ID_EDIT_UNDO\r
         MENUITEM "&Redo",                       ID_EDIT_REDO\r
@@ -1165,7 +1166,7 @@ END
 \r
 IDD_PROPPAGE_FILTER DIALOGEX 0, 0, 535, 188\r
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION\r
-CAPTION "Linefilters"\r
+CAPTION "Line Filters"\r
 FONT 8, "MS Shell Dlg", 0, 0, 0x1\r
 BEGIN\r
     CONTROL         "Enable Line Filters",IDC_IGNOREREGEXP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,351,15\r
@@ -1176,6 +1177,25 @@ BEGIN
     PUSHBUTTON      "Remove",IDC_LFILTER_REMOVEBTN,116,167,50,14\r
 END\r
 \r
+IDD_IGNORED_SUSBSTITUTIONS_DLG DIALOGEX 0, 0, 365, 281\r
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION\r
+CAPTION "Ignored Substitutions"\r
+FONT 8, "MS Shell Dlg", 0, 0, 0x1\r
+BEGIN\r
+    CONTROL         "Enable",IDC_IGNORED_SUSBSTITUTIONS_ARE_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,28,351,9\r
+    CONTROL         "",IDC_IGNORED_SUBSTITUTIONS_FILTER,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_EDITLABELS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,8,87,350,166\r
+    PUSHBUTTON      "Add",IDC_LFILTER_ADDBTN,7,260,50,14\r
+    PUSHBUTTON      "Remove",IDC_LFILTER_REMOVEBTN,63,260,50,14\r
+    LTEXT           "The changes that appear on the panels as the listed pairs below will be ignored or marked as insignificant. Patches are unaffected.",IDC_STATIC,8,6,346,19\r
+    CONTROL         "Ignore changes in both directions",IDC_IGNORED_SUSBSTITUTIONS_WORK_BOTH_WAYS,\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,57,351,10\r
+    CONTROL         "Completely unhighlight the ignored changes",IDC_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS,\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,43,341,10\r
+    CONTROL         "Use regular expressions",IDC_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS,\r
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,72,350,10\r
+    PUSHBUTTON      "Clear",IDC_LFILTER_CLEARBTN,307,261,50,14\r
+END\r
+\r
 IDD_PROPPAGE_COLOR_SCHEMES DIALOGEX 0, 0, 255, 242\r
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION\r
 CAPTION "Colors"\r
@@ -1451,7 +1471,7 @@ END
 \r
 IDD_FILEFILTERS DIALOGEX 0, 0, 537, 188\r
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION\r
-CAPTION "Filefilters"\r
+CAPTION "File Filters"\r
 FONT 8, "MS Shell Dlg", 0, 0, 0x1\r
 BEGIN\r
     CONTROL         "",IDC_FILTERFILE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,10,7,517,153\r
@@ -1991,6 +2011,11 @@ BEGIN
     BEGIN\r
     END\r
 \r
+    IDD_IGNORED_SUSBSTITUTIONS_DLG, DIALOG\r
+    BEGIN\r
+        BOTTOMMARGIN, 196\r
+    END\r
+\r
     IDD_PROPPAGE_COLOR_SCHEMES, DIALOG\r
     BEGIN\r
     END\r
index 61cbfda..6273b8a 100644 (file)
     <ClCompile Include="LineFiltersDlg.cpp">\r
       <Filter>MFCGui\Dialogs\Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="SubeditList.cpp">\r
+      <Filter>MFCGui\Dialogs\Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="LoadSaveCodepageDlg.cpp">\r
       <Filter>MFCGui\Dialogs\Source Files</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\Externals\crystaledit\editlib\utils\hqbitmap.cpp">\r
       <Filter>EditLib\Utils</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="IgnoredSubstitutionsDlg.cpp">\r
+      <Filter>MFCGui\Dialogs\Source Files</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="TokenPairList.cpp">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="..\Externals\crystaledit\editlib\parsers\javascript.cpp">\r
       <Filter>EditLib\Parsers\Source Files</Filter>\r
     </ClCompile>\r
     <ClInclude Include="IntToIntMap.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="LineFiltersList.h">\r
-      <Filter>Header Files</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="locality.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\Externals\crystaledit\editlib\utils\hqbitmap.h">\r
       <Filter>EditLib\Utils</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="LineFiltersList.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="IgnoredSubstitutionsDlg.h">\r
+      <Filter>MFCGui\Dialogs\Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="TokenPairList.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="res\binarydiff.ico">\r
index 07b195a..591c645 100644 (file)
       <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)2.pch</PrecompiledHeaderOutputFile>\r
     </ClCompile>\r
     <ClCompile Include="LineFiltersDlg.cpp" />\r
+    <ClCompile Include="IgnoredSubstitutionsDlg.cpp" />\r
+    <ClCompile Include="SubeditList.cpp" />\r
     <ClCompile Include="LineFiltersList.cpp">\r
       <PrecompiledHeader>Use</PrecompiledHeader>\r
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)2.pch</PrecompiledHeaderOutputFile>\r
     </ClCompile>\r
+    <ClCompile Include="TokenPairList.cpp">\r
+      <PrecompiledHeader>Use</PrecompiledHeader>\r
+      <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>\r
+      <PrecompiledHeaderOutputFile>$(IntDir)$(TargetName)2.pch</PrecompiledHeaderOutputFile>\r
+    </ClCompile>\r
     <ClCompile Include="LoadSaveCodepageDlg.cpp" />\r
     <ClCompile Include="locality.cpp">\r
       <PrecompiledHeader>Use</PrecompiledHeader>\r
     <ClInclude Include="Common\LanguageSelect.h" />\r
     <ClInclude Include="JumpList.h" />\r
     <ClInclude Include="LineFiltersDlg.h" />\r
+    <ClInclude Include="IgnoredSubstitutionsDlg.h" />\r
     <ClInclude Include="LineFiltersList.h" />\r
+    <ClInclude Include="TokenPairList.h" />\r
     <ClInclude Include="LoadSaveCodepageDlg.h" />\r
     <ClInclude Include="locality.h" />\r
     <ClInclude Include="LocationBar.h" />\r
index 61cbfda..6273b8a 100644 (file)
     <ClCompile Include="LineFiltersDlg.cpp">\r
       <Filter>MFCGui\Dialogs\Source Files</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="SubeditList.cpp">\r
+      <Filter>MFCGui\Dialogs\Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="LoadSaveCodepageDlg.cpp">\r
       <Filter>MFCGui\Dialogs\Source Files</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\Externals\crystaledit\editlib\utils\hqbitmap.cpp">\r
       <Filter>EditLib\Utils</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="IgnoredSubstitutionsDlg.cpp">\r
+      <Filter>MFCGui\Dialogs\Source Files</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="TokenPairList.cpp">\r
+      <Filter>Source Files</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="..\Externals\crystaledit\editlib\parsers\javascript.cpp">\r
       <Filter>EditLib\Parsers\Source Files</Filter>\r
     </ClCompile>\r
     <ClInclude Include="IntToIntMap.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="LineFiltersList.h">\r
-      <Filter>Header Files</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="locality.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\Externals\crystaledit\editlib\utils\hqbitmap.h">\r
       <Filter>EditLib\Utils</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="LineFiltersList.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="IgnoredSubstitutionsDlg.h">\r
+      <Filter>MFCGui\Dialogs\Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="TokenPairList.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="res\binarydiff.ico">\r
index 85fd142..d7948ab 100644 (file)
@@ -39,6 +39,7 @@
 #include "MergeLineFlags.h"
 #include "FileOrFolderSelect.h"
 #include "LineFiltersList.h"
+#include "TokenPairList.h"
 #include "TempFile.h"
 #include "codepage_detect.h"
 #include "SelectUnpackerDlg.h"
@@ -301,6 +302,16 @@ int CMergeDoc::Rescan(bool &bBinary, IDENTLEVEL &identical,
        {
                m_diffWrapper.SetFilterList(_T(""));
        }
+
+       if (GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED) && theApp.m_pTokensForIs)
+       {
+               m_diffWrapper.SetIgnoredSubstitutionsList(theApp.m_pTokensForIs.get());
+       }
+       else
+       {
+               m_diffWrapper.SetIgnoredSubstitutionsList(nullptr);
+       }
+
        if (GetView(0, 0)->m_CurSourceDef->type != 0)
                m_diffWrapper.SetFilterCommentsSourceDef(GetView(0, 0)->m_CurSourceDef);
        else
index b2e800b..228cad0 100644 (file)
@@ -258,6 +258,7 @@ public:
 public:
        typedef enum { BYTEDIFF, WORDDIFF } DIFFLEVEL;
        void Showlinediff(CMergeEditView *pView, bool bReversed = false);
+       void AddToIgnoredSubstitutions(CMergeEditView* pView, bool bReversed = false);
        std::vector<WordDiff> GetWordDiffArrayInDiffBlock(int nDiff);
        std::vector<WordDiff> GetWordDiffArray(int nLineIndex);
        void ClearWordDiffCache(int nDiff = -1);
index 237d522..f033b93 100644 (file)
 #include "DiffTextBuffer.h"
 #include "stringdiffs.h"
 #include "UnicodeString.h"
+#include "TokenPairList.h"
+#include "OptionsMgr.h"
+#include "OptionsDef.h"
+#include "Merge.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -49,22 +53,87 @@ HighlightDiffRect(CMergeEditView * pView, const CRect & rc)
 void CMergeDoc::Showlinediff(CMergeEditView *pView, bool bReversed)
 {
        CRect rc[3];
-       int nBuffer;
 
        Computelinediff(pView, rc, bReversed);
 
        if (std::all_of(rc, rc + m_nBuffers, [](auto& rc) { return rc.top == -1; }))
        {
                String caption = _("Line difference");
-               String msg = _("No difference");
+               String msg = _("No differences to select found");
                MessageBox(pView->GetSafeHwnd(), msg.c_str(), caption.c_str(), MB_OK);
                return;
        }
 
        // Actually display selection areas on screen in both edit panels
-       for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
-               for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
-                       HighlightDiffRect(m_pView[nGroup][nBuffer], rc[nBuffer]);
+       for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+               HighlightDiffRect(m_pView[pView->m_nThisGroup][nBuffer], rc[nBuffer]);
+}
+
+void CMergeDoc::AddToIgnoredSubstitutions(CMergeEditView* pView, bool bReversed)
+{
+       if (m_nBuffers != 2)
+               return; /// Not clear what to do for a 3-way merge
+
+       CRect rc[3];
+
+       Computelinediff(pView, rc, bReversed);
+
+       bool optIgnoredSubstitutionsWorkBothWays = GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS);
+
+       if (std::all_of(rc, rc + m_nBuffers, [](auto& rc) { return rc.top == -1; }))
+       {
+               String caption = _("Line difference");
+               String msg = _("No differences found to add as ignored substitution");
+               MessageBox(pView->GetSafeHwnd(), msg.c_str(), caption.c_str(), MB_OK);
+               return;
+       }
+
+       // Actually display selection areas on screen in both edit panels
+       String selectedText[3];
+       for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+       {
+               HighlightDiffRect(m_pView[pView->m_nThisGroup][nBuffer], rc[nBuffer]);
+               selectedText[nBuffer] = String(m_pView[pView->m_nThisGroup][nBuffer]->GetSelectedText());
+       }
+
+       if (selectedText[0].empty() && selectedText[1].empty())
+       {
+               return;
+       }
+
+
+       /// Check whether the pair is already registered with Ignored Substitutions
+       TokenPairList &ignoredSubstitutionsList = *theApp.m_pTokensForIs.get();
+       for (int f = 0; f < ignoredSubstitutionsList.GetCount(); f++)
+       {
+               String str0 = ignoredSubstitutionsList.GetAt(f).filterStr0;
+               String str1 = ignoredSubstitutionsList.GetAt(f).filterStr1;
+               if
+               (
+                          str0 == selectedText[0]
+                       && str1 == selectedText[1]
+                       ||
+                          optIgnoredSubstitutionsWorkBothWays
+                       && str1 == selectedText[0]
+                       && str0 == selectedText[1]
+               )
+               {
+                       String caption = _("The pair is already present in the list of Ignored Substiturions");
+                       String msg = strutils::format(_T("\"%s\" <-> \"%s\""), selectedText[0], selectedText[1]);
+                       MessageBox(pView->GetSafeHwnd(), msg.c_str(), caption.c_str(), MB_OK);
+                       return; /// The substitution pair is already registered
+               }
+       }
+
+       String caption = _("Add this change to Ignored Substitutions?");
+       String msg = strutils::format(_T("\"%s\" <-> \"%s\""), selectedText[0], selectedText[1]);
+       if (MessageBox(pView->GetSafeHwnd(), msg.c_str(), caption.c_str(), MB_YESNO) == IDYES)
+       {
+               ignoredSubstitutionsList.AddFilter(selectedText[0], selectedText[1]);
+               FlushAndRescan(true);
+               //Rescan();
+       }
+       return;
 }
 
 static inline bool IsDiffPerLine(bool bTableEditing, const DIFFRANGE& cd)
index 73efd3b..23a3564 100644 (file)
@@ -159,6 +159,8 @@ BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
        ON_UPDATE_COMMAND_UI(ID_SELECTLINEDIFF, OnUpdateSelectLineDiff)
        ON_COMMAND(ID_SELECTPREVLINEDIFF, OnSelectLineDiff<true>)
        ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
+       ON_COMMAND(ID_ADD_TO_IGNORED_SUBSTITUTIONS, OnAddToIgnoredSubstitutions<false>)
+       ON_UPDATE_COMMAND_UI(ID_ADD_TO_IGNORED_SUBSTITUTIONS, OnUpdateAddToIgnoredSubstitutions)
        ON_WM_CONTEXTMENU()
        ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
        ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
@@ -2696,6 +2698,19 @@ void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
        pCmdUI->Enable(!GetDocument()->IsEditedAfterRescan());
 }
 
+template<bool reversed>
+void CMergeEditView::OnAddToIgnoredSubstitutions()
+{
+       // Pass this to the document, to compare this file to other
+       GetDocument()->AddToIgnoredSubstitutions(this, reversed);
+}
+
+void CMergeEditView::OnUpdateAddToIgnoredSubstitutions(CCmdUI* pCmdUI)
+{
+       pCmdUI->Enable(GetDocument()->m_nBuffers == 2 && !GetDocument()->IsEditedAfterRescan());
+}
+
+
 /**
  * @brief Enable/disable Replace-menuitem
  */
index 192560b..67b11f5 100644 (file)
@@ -283,6 +283,9 @@ protected:
        template<bool reversed>
        afx_msg void OnSelectLineDiff();
        afx_msg void OnUpdateSelectLineDiff(CCmdUI* pCmdUI);
+       template<bool reversed>
+       afx_msg void OnAddToIgnoredSubstitutions();
+       afx_msg void OnUpdateAddToIgnoredSubstitutions(CCmdUI* pCmdUI);
        afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
        afx_msg void OnUpdateEditReplace(CCmdUI* pCmdUI);
        afx_msg void OnLeftReadOnly();
index 72652d6..a9ec05c 100644 (file)
@@ -248,6 +248,12 @@ extern const String OPT_FILEFILTER_CURRENT OP("Settings/FileFilterCurrent");
 extern const String OPT_FILTER_USERPATH OP("Settings/UserFilterPath");
 extern const String OPT_FILEFILTER_SHARED OP("Settings/Filters/Shared");
 
+/// Ignored Susbstitutions
+extern const String OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED OP("Settings/IgnoredSubstitutionsAreEnabled");
+extern const String OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS OP("Settings/IgnoredSubstitutionsWorkBothWays");
+extern const String OPT_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS OP("Settings/CompletelyBlankOutIgnoredSusbstitutions");
+extern const String OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS OP("Settings/UseRegexpsForIgnoredSubstitutions");
+
 // Archive support
 extern const String OPT_ARCHIVE_ENABLE OP("Merge7z/Enable");
 extern const String OPT_ARCHIVE_PROBETYPE OP("Merge7z/ProbeSignature");
index e4f7adc..8e0c757 100644 (file)
@@ -165,6 +165,11 @@ void Init(COptionsMgr *pOptions)
        pOptions->InitOption(OPT_CUSTOM_TEMP_PATH, _T(""));
 
        pOptions->InitOption(OPT_LINEFILTER_ENABLED, false);
+       pOptions->InitOption(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED, false);
+       pOptions->InitOption(OPT_IGNORED_SUBSTITUTIONS_WORK_BOTH_WAYS, false);
+       pOptions->InitOption(OPT_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS, false);
+       pOptions->InitOption(OPT_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS, false);
+
        pOptions->InitOption(OPT_FILEFILTER_CURRENT, _T("*.*"));
        // CMainFrame initializes this when it is empty.
        pOptions->InitOption(OPT_FILTER_USERPATH, _T(""));
diff --git a/Src/SubeditList.cpp b/Src/SubeditList.cpp
new file mode 100644 (file)
index 0000000..af6aa27
--- /dev/null
@@ -0,0 +1,353 @@
+// SubeditList.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "subedit.h"
+#include "SubeditList.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#define IDC_IPEDIT 1000
+
+/// Some stuff is from https://www.codeguru.com/cpp/controls/listview/editingitemsandsubitem/article.php/c923/Editable-subitems.htm
+
+/////////////////////////////////////////////////////////////////////////////
+// CSubeditList
+
+CSubeditList::CSubeditList()
+{
+}
+
+CSubeditList::~CSubeditList()
+{
+}
+
+// HitTestEx   - Determine the row index and column index for a point
+// Returns     - the row index or -1 if point is not over a row
+// point       - point to be tested.
+// col         - to hold the column index
+int CSubeditList::HitTestEx(CPoint &point, int *col) const
+{
+       int colnum = 0;
+       int row = HitTest( point, NULL );
+       
+       if( col ) *col = 0;
+       // Make sure that the ListView is in LVS_REPORT
+       if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
+               return row;
+       // Get the top and bottom row visible
+       row = GetTopIndex();
+       int bottom = row + GetCountPerPage();
+       if( bottom > GetItemCount() )
+               bottom = GetItemCount();
+       
+       // Get the number of columns
+       CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
+       int nColumnCount = pHeader->GetItemCount();
+       // Loop through the visible rows
+       for( ;row <=bottom;row++)
+       {
+               // Get bounding rect of item and check whether point falls in it.
+               CRect rect;
+               GetItemRect( row, &rect, LVIR_BOUNDS );
+               if( rect.PtInRect(point) )
+               {
+                       // Now find the column
+                       for( colnum = 0; colnum < nColumnCount; colnum++ )
+                       {
+                               int colwidth = GetColumnWidth(colnum);
+                               if( point.x >= rect.left 
+                                       && point.x <= (rect.left + colwidth ) )
+                               {
+                                       if( col ) *col = colnum;
+                                       return row;
+                               }
+                               rect.left += colwidth;
+                       }
+               }
+       }
+       return -1;
+}
+
+
+BEGIN_MESSAGE_MAP(CSubeditList, CListCtrl)
+       //{{AFX_MSG_MAP(CSubeditList)
+               // NOTE - the ClassWizard will add and remove mapping macros here.
+       ON_NOTIFY(LVN_ENDLABELEDIT, IDC_IGNORED_SUBSTITUTIONS_FILTER, OnEndLabelEdit)
+       ON_WM_LBUTTONDOWN()
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CSubeditList message handlers
+
+
+// EditSubLabel                - Start edit of a sub item label
+// Returns             - Temporary pointer to the new edit control
+// nItem               - The row index of the item to edit
+// nCol                        - The column of the sub item.
+//CEdit* CSubeditList::EditSubLabel(int nItem, int nCol)
+CInPlaceEdit* CSubeditList::EditSubLabel( int nItem, int nCol )
+{
+    // The returned pointer should not be saved
+     
+    // Make sure that the item is visible
+    if( !EnsureVisible( nItem, TRUE ) ) return NULL;
+     
+    // Make sure that nCol is valid
+    CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
+    int nColumnCount = pHeader->GetItemCount();
+    if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
+       return NULL;
+     
+    // Get the column offset
+    int offset = 0;
+    for( int i = 0; i < nCol; i++ )
+       offset += GetColumnWidth( i );
+     
+    CRect rect;
+    GetItemRect( nItem, &rect, LVIR_BOUNDS );
+     
+    // Now scroll if we need to expose the column
+    CRect rcClient;
+    GetClientRect( &rcClient );
+    if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
+    {
+       CSize size;
+       size.cx = offset + rect.left;
+       size.cy = 0;
+       Scroll( size );
+       rect.left -= size.cx;
+    }
+     
+    // Get Column alignment
+    LV_COLUMN lvcol;
+    lvcol.mask = LVCF_FMT;
+    GetColumn( nCol, &lvcol );
+    DWORD dwStyle ;
+    if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
+       dwStyle = ES_LEFT;
+    else if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
+       dwStyle = ES_RIGHT;
+    else dwStyle = ES_CENTER;
+     
+    rect.left += offset+4;
+    rect.right = rect.left + GetColumnWidth( nCol ) - 3 ;
+    if( rect.right > rcClient.right) rect.right = rcClient.right;
+     
+    dwStyle |= WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
+    CInPlaceEdit *pEdit = new CInPlaceEdit(nItem, nCol, GetItemText( nItem, nCol ));
+    pEdit->Create( dwStyle, rect, this, IDC_IPEDIT );
+     
+    return pEdit;
+}
+
+void CSubeditList::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
+{
+    if( GetFocus() != this ) SetFocus();
+    CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
+}
+     
+void CSubeditList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
+{
+    if( GetFocus() != this ) SetFocus();
+    CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
+}
+
+void CSubeditList::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
+{
+    LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
+    LV_ITEM    *plvItem = &plvDispInfo->item;
+     
+    if (plvItem->pszText != NULL)
+    {
+       SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
+    }
+    *pResult = FALSE;
+}
+
+void CSubeditList::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
+{
+       LV_DISPINFO* plvDispInfo = (LV_DISPINFO*)pNMHDR;
+       LV_ITEM* plvItem = &plvDispInfo->item;
+    plvItem->iSubItem = 1;
+
+//     if (plvItem->pszText != NULL)
+//     {
+//             SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
+//     }
+       *pResult = FALSE;
+}
+
+void CSubeditList::OnLButtonDown(UINT nFlags, CPoint point)
+{
+    int index;
+    CListCtrl::OnLButtonDown(nFlags, point);
+     
+    int colnum;
+    if( ( index = HitTestEx( point, &colnum )) != -1 )
+    {
+       UINT flag = LVIS_FOCUSED;
+               //if ((GetItemState(index, flag) & flag) == flag && colnum > 0)
+               if ((GetItemState(index, flag) & flag) == flag)
+       {
+               // Add check for LVS_EDITLABELS
+               if( GetWindowLong(m_hWnd, GWL_STYLE) & LVS_EDITLABELS )
+                       EditSubLabel( index, colnum );
+       }
+       else
+               SetItemState( index, LVIS_SELECTED | LVIS_FOCUSED ,
+                                   LVIS_SELECTED | LVIS_FOCUSED); 
+    }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CInPlaceEdit
+     
+CInPlaceEdit::CInPlaceEdit(int iItem, int iSubItem, CString sInitText)
+:m_sInitText( sInitText )
+{
+    m_iItem = iItem;
+    m_iSubItem = iSubItem;
+    m_bESC = FALSE;
+}
+     
+CInPlaceEdit::~CInPlaceEdit()
+{
+}
+     
+     
+BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)
+    //{{AFX_MSG_MAP(CInPlaceEdit)
+    ON_WM_KILLFOCUS()
+    ON_WM_NCDESTROY()
+    ON_WM_CHAR()
+    ON_WM_CREATE()
+    //}}AFX_MSG_MAP
+       ON_WM_LBUTTONDOWN()
+END_MESSAGE_MAP()
+     
+/////////////////////////////////////////////////////////////////////////////
+// CInPlaceEdit message handlers
+     
+BOOL CInPlaceEdit::PreTranslateMessage(MSG* pMsg)
+{
+    if( pMsg->message == WM_KEYDOWN )
+    {
+       if(pMsg->wParam == VK_RETURN
+                       || pMsg->wParam == VK_DELETE
+                       || pMsg->wParam == VK_ESCAPE
+                       || GetKeyState( VK_CONTROL)
+                       )
+       {
+               ::TranslateMessage(pMsg);
+               ::DispatchMessage(pMsg);
+               return TRUE;                    // DO NOT process further
+       }
+    }
+     
+    return CEdit::PreTranslateMessage(pMsg);
+}
+     
+     
+void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd)
+{
+    CEdit::OnKillFocus(pNewWnd);
+     
+    CString str;
+    GetWindowText(str);
+     
+    // Send Notification to parent of ListView ctrl
+    LV_DISPINFO dispinfo;
+    dispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
+    dispinfo.hdr.idFrom = GetDlgCtrlID();
+    dispinfo.hdr.code = LVN_ENDLABELEDIT;
+     
+    dispinfo.item.mask = LVIF_TEXT;
+    dispinfo.item.iItem = m_iItem;
+    dispinfo.item.iSubItem = m_iSubItem;
+    dispinfo.item.pszText = m_bESC ? NULL : LPTSTR((LPCTSTR)str);
+    dispinfo.item.cchTextMax = str.GetLength();
+     
+    GetParent()->GetParent()->SendMessage( WM_NOTIFY, GetParent()->GetDlgCtrlID(), 
+                               (LPARAM)&dispinfo );
+     
+    DestroyWindow();
+}
+     
+void CInPlaceEdit::OnNcDestroy()
+{
+    CEdit::OnNcDestroy();
+     
+    delete this;
+}
+     
+     
+void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
+{
+    if( nChar == VK_ESCAPE || nChar == VK_RETURN)
+    {
+       if( nChar == VK_ESCAPE )
+               m_bESC = TRUE;
+       GetParent()->SetFocus();
+       return;
+    }
+     
+     
+    CEdit::OnChar(nChar, nRepCnt, nFlags);
+     
+    // Resize edit control if needed
+     
+    // Get text extent
+    CString str;
+     
+    GetWindowText( str );
+    CWindowDC dc(this);
+    CFont *pFont = GetParent()->GetFont();
+    CFont *pFontDC = dc.SelectObject( pFont );
+    CSize size = dc.GetTextExtent( str );
+    dc.SelectObject( pFontDC );
+    size.cx += 5;                              // add some extra buffer
+     
+    // Get client rect
+    CRect rect, parentrect;
+    GetClientRect( &rect );
+    GetParent()->GetClientRect( &parentrect );
+     
+    // Transform rect to parent coordinates
+    ClientToScreen( &rect );
+    GetParent()->ScreenToClient( &rect );
+     
+    // Check whether control needs to be resized
+    // and whether there is space to grow
+    if( size.cx > rect.Width() )
+    {
+       if( size.cx + rect.left < parentrect.right )
+               rect.right = rect.left + size.cx;
+       else
+               rect.right = parentrect.right;
+       MoveWindow( &rect );
+    }
+}
+     
+int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+    if (CEdit::OnCreate(lpCreateStruct) == -1)
+       return -1;
+     
+    // Set the proper font
+    CFont* font = GetParent()->GetFont();
+    SetFont(font);
+     
+    SetWindowText( m_sInitText );
+    SetFocus();
+    SetSel( 0, -1 );
+    return 0;
+}
diff --git a/Src/SubeditList.h b/Src/SubeditList.h
new file mode 100644 (file)
index 0000000..e7d9ba2
--- /dev/null
@@ -0,0 +1,90 @@
+#if !defined(AFX_SUBEDITLIST_H__335134F3_37B4_4739_AC9B_4AFB32C37E60__INCLUDED_)
+#define AFX_SUBEDITLIST_H__335134F3_37B4_4739_AC9B_4AFB32C37E60__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// SubeditList.h : header file
+//
+
+class CInPlaceEdit : public CEdit
+{
+// Construction
+public:
+    CInPlaceEdit(int iItem, int iSubItem, CString sInitText);
+     
+// Attributes
+public:
+     
+// Operations
+public:
+     
+// Overrides
+    // ClassWizard generated virtual function overrides
+    //{{AFX_VIRTUAL(CInPlaceEdit)
+    public:
+    virtual BOOL PreTranslateMessage(MSG* pMsg);
+    //}}AFX_VIRTUAL
+     
+// Implementation
+public:
+    virtual ~CInPlaceEdit();
+     
+    // Generated message map functions
+protected:
+    //{{AFX_MSG(CInPlaceEdit)
+    afx_msg void OnKillFocus(CWnd* pNewWnd);
+    afx_msg void OnNcDestroy();
+    afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
+    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+    //}}AFX_MSG
+     
+    DECLARE_MESSAGE_MAP()
+private:
+    int m_iItem;
+    int m_iSubItem;
+    CString m_sInitText;
+    BOOL    m_bESC;            // To indicate whether ESC key was pressed
+};
+     
+class CSubeditList : public CListCtrl
+{
+// Construction
+public:
+       CSubeditList();
+
+// Attributes
+public:
+
+// Operations
+public:
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CSubeditList)
+       //}}AFX_VIRTUAL
+
+// Implementation
+public:
+       virtual ~CSubeditList();
+
+    CInPlaceEdit *EditSubLabel(int nItem, int nCol);
+       // Generated message map functions
+//protected:
+       //{{AFX_MSG(CSubeditList)
+       // NOTE - the ClassWizard will add and remove member functions here.
+    afx_msg int HitTestEx(CPoint& point, int* col) const;
+    afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
+    afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
+       afx_msg void OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);
+    afx_msg void OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult);
+    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+       //}}AFX_MSG
+
+       DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_SUBEDITLIST_H__335134F3_37B4_4739_AC9B_4AFB32C37E60__INCLUDED_)
diff --git a/Src/TokenPairList.cpp b/Src/TokenPairList.cpp
new file mode 100644 (file)
index 0000000..e741141
--- /dev/null
@@ -0,0 +1,180 @@
+/** 
+ * @file  TokenPairList.cpp
+ *
+ * @brief Implementation for IgnoredSubstitutionsFiltersList class.
+ */
+
+#include "pch.h"
+#include "TokenPairList.h"
+#include <vector>
+#include <cassert>
+#include "OptionsMgr.h"
+#include "UnicodeString.h"
+
+using std::vector;
+
+/** @brief Registry key for saving Ignored Substitutions filters. */
+static const TCHAR IgnoredSubstitutionsRegPath[] = _T("IgnoredSubstitutions");
+
+/**
+ * @brief Default constructor.
+ */
+TokenPairList::TokenPairList()
+: m_pOptionsMgr(nullptr)
+{
+}
+
+/**
+ * @brief Destructor, empties the list.
+ */
+TokenPairList::~TokenPairList()
+{
+}
+
+/**
+ * @brief Add new filter to the list.
+ * @param [in] filter Filter string to add.
+ * @param [in] enabled Is filter enabled?
+ */
+void TokenPairList::AddFilter(const String& filter0, const String& filter1)
+{
+       std::shared_ptr<TokenPair> item(new TokenPair());
+       item->filterStr0 = filter0;
+       item->filterStr1 = filter1;
+       m_items.push_back(item);
+}
+
+/**
+ * @brief Return filter from given index.
+ * @param [in] ind Index of filter.
+ * @return Filter item from the index. If the index is beyond table limit,
+ *  return the last item in the list.
+ */
+const TokenPair & TokenPairList::GetAt(size_t ind) const
+{
+       if (ind < m_items.size())
+               return *m_items[ind];
+       else
+               return *m_items.back();
+}
+
+/**
+ * @brief Clone filter list from another list.
+ * This function clones filter list from another list. Current items in the
+ * list are removed and new items added from the given list.
+ * @param [in] list List to clone.
+ */
+void TokenPairList::CloneFrom(const TokenPairList *list)
+{
+       Empty();
+       size_t count = list->GetCount();
+
+       for (size_t i = 0; i < count; i++)
+       {
+               const TokenPair &item = list->GetAt(i);
+               AddFilter(item.filterStr0, item.filterStr1);
+       }
+}
+
+/**
+ * @brief Compare filter lists.
+ * @param [in] list List to compare.
+ * @return true if lists are identical, false otherwise.
+ */
+bool TokenPairList::Compare(const TokenPairList *list) const
+{
+       if (list->GetCount() != GetCount())
+               return false;
+
+       for (size_t i = 0; i < GetCount(); i++)
+       {
+               const TokenPair &item1 = list->GetAt(i);
+               const TokenPair &item2 = GetAt(i);
+
+               if
+               (
+                          item1.filterStr0 != item2.filterStr0
+                       || item1.filterStr1 != item2.filterStr1
+               )
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * @brief Read filter list from the options system.
+ * @param [in] pOptionsMgr Pointer to options system.
+ */
+void TokenPairList::Initialize(COptionsMgr *pOptionsMgr)
+{
+       assert(pOptionsMgr != nullptr);
+       String valuename(IgnoredSubstitutionsRegPath);
+
+       m_pOptionsMgr = pOptionsMgr;
+
+       size_t count = m_items.size();
+       valuename += _T("/Values");
+       m_pOptionsMgr->InitOption(valuename, static_cast<int>(count));
+       count = m_pOptionsMgr->GetInt(valuename);
+
+       for (unsigned i = 0; i < count; i++)
+       {
+               String name0 = strutils::format(_T("%s/Filter%02u_0"), IgnoredSubstitutionsRegPath, i);
+               m_pOptionsMgr->InitOption(name0, _T(""));
+               String filterStr0 = m_pOptionsMgr->GetString(name0);
+
+               String name1 = strutils::format(_T("%s/Filter%02u_1"), IgnoredSubstitutionsRegPath, i);
+               m_pOptionsMgr->InitOption(name1, _T(""));
+               String filterStr1 = m_pOptionsMgr->GetString(name1);
+
+               AddFilter(filterStr0, filterStr1);
+       }
+}
+
+/**
+ * @brief Save Ignored Substitutions to options system.
+ */
+void TokenPairList::SaveFilters()
+{
+       assert(m_pOptionsMgr != nullptr);
+       String valuename(IgnoredSubstitutionsRegPath);
+
+       size_t count = m_items.size();
+       valuename += _T("/Values");
+       m_pOptionsMgr->SaveOption(valuename, static_cast<int>(count));
+
+       for (size_t i = 0; i < count; i++)
+       {
+               const std::shared_ptr<TokenPair> &item = m_items[i];
+
+               String name0 = strutils::format(_T("%s/Filter%02u_0"), IgnoredSubstitutionsRegPath, i);
+               m_pOptionsMgr->InitOption(name0, _T(""));
+               m_pOptionsMgr->SaveOption(name0, item->filterStr0);
+
+               String name1 = strutils::format(_T("%s/Filter%02u_1"), IgnoredSubstitutionsRegPath, i);
+               m_pOptionsMgr->InitOption(name1, _T(""));
+               m_pOptionsMgr->SaveOption(name1, item->filterStr1);
+       }
+
+       // Remove options we don't need anymore
+       // We could have earlier 10 pcs but now we only need 5
+       String filter = strutils::format(_T("%s/Enabled%02u"), IgnoredSubstitutionsRegPath, count);
+       int retval = m_pOptionsMgr->RemoveOption(filter);
+
+       String filter0 = strutils::format(_T("%s/Filter%02u_0"), IgnoredSubstitutionsRegPath, count);
+       int retval0 = m_pOptionsMgr->RemoveOption(filter0);
+
+       String filter1 = strutils::format(_T("%s/Filter%02u_1"), IgnoredSubstitutionsRegPath, count);
+       int retval1 = m_pOptionsMgr->RemoveOption(filter1);
+
+       while (retval == COption::OPT_OK || retval0 == COption::OPT_OK || retval1 == COption::OPT_OK)
+       {
+               ++count;
+               filter = strutils::format(_T("%s/Enabled%02u"), IgnoredSubstitutionsRegPath, count);
+               retval = m_pOptionsMgr->RemoveOption(filter);
+               filter0 = strutils::format(_T("%s/Filter%02u_0"), IgnoredSubstitutionsRegPath, count);
+               retval0 = m_pOptionsMgr->RemoveOption(filter);
+               filter1 = strutils::format(_T("%s/Filter%02u_1"), IgnoredSubstitutionsRegPath, count);
+               retval1 = m_pOptionsMgr->RemoveOption(filter);
+       }
+}
diff --git a/Src/TokenPairList.h b/Src/TokenPairList.h
new file mode 100644 (file)
index 0000000..310edb0
--- /dev/null
@@ -0,0 +1,62 @@
+/** 
+ * @file IgnoredSubstitutionsFiltersList.h
+ *
+ * @brief Declaration file for IgnoredSubstitutionsFiltersList class
+ */
+#pragma once
+
+#include <vector>
+#include <memory>
+#include "UnicodeString.h"
+
+class COptionsMgr;
+
+/**
+ @brief Structure for filter.
+ */
+struct TokenPair
+{
+       String filterStr0;
+       String filterStr1;
+};
+
+/**
+ @brief List of raw Ignored Substitution pairs.
+ */
+class TokenPairList
+{
+public:
+       TokenPairList();
+       ~TokenPairList();
+
+       void AddFilter(const String& filter0, const String& filter1);
+       size_t GetCount() const;
+       void Empty();
+       const TokenPair &GetAt(size_t ind) const;
+       void CloneFrom(const TokenPairList *list);
+       bool Compare(const TokenPairList *list) const;
+
+       void Initialize(COptionsMgr *pOptionsMgr);
+       void SaveFilters();
+
+private:
+       std::vector<std::shared_ptr<TokenPair>> m_items; /**< List for linefilter items */
+       COptionsMgr * m_pOptionsMgr; /**< Options-manager for storage */
+};
+
+/**
+ * @brief Returns count of items in the list.
+ * @return Count of filters in the list.
+ */
+inline size_t TokenPairList::GetCount() const
+{
+       return m_items.size();
+}
+
+/**
+ * @brief Empties the list.
+ */
+inline void TokenPairList::Empty()
+{
+       m_items.clear();
+}
index 925e0eb..f4d93ce 100644 (file)
@@ -22,6 +22,7 @@
 #define IDD_OPEN                        202\r
 #define IDD_PROPPAGE_GENERAL            205\r
 #define IDD_PROPPAGE_FILTER             207\r
+#define IDD_IGNORED_SUSBSTITUTIONS_DLG  208\r
 #define IDD_PROPPAGE_SYSTEM             209\r
 #define IDD_EDITOR_HEADERBAR            210\r
 #define IDD_GENERATE_PATCH              211\r
 #define IDC_ALL_WHITE                   1027\r
 #define IDC_WHITE_CHANGE                1028\r
 #define IDC_WHITESPACE                  1029\r
+#define IDC_IGNORED_SUSBSTITUTIONS_ARE_ENABLED  1030\r
 #define IDC_EOL_SENSITIVE               1032\r
 #define IDC_CP_SENSITIVE                1033\r
 #define IDC_DIFFERENCE_COLOR            1035\r
 #define IDC_LFILTER_ADDBTN              1321\r
 #define IDC_LFILTER_EDITBTN             1322\r
 #define IDC_LFILTER_REMOVEBTN           1323\r
+#define IDC_IGNORED_SUBSTITUTIONS_FILTER 1324\r
 #define IDC_ASK_MULTIWINDOW_CLOSE       1326\r
 #define IDC_COLDLG_LIST                 1327\r
 #define IDC_PRESERVE_FILETIME           1328\r
 #define IDC_INDENT_HEURISTIC            8829\r
 #define IDC_LIST_FILE                   8830\r
 #define IDC_FLDCONFIRM_DONTASKAGAIN     8831\r
+#define IDC_IGNORED_SUSBSTITUTIONS_WORK_BOTH_WAYS 8832\r
+#define IDC_COMPLETELY_BLANK_OUT_IGNORED_SUBSTITUTIONS 8833\r
+#define IDC_USE_REGEXPS_FOR_IGNORED_SUBSTITUTIONS 8834\r
+#define IDC_LFILTER_CLEARBTN            8836\r
 #define IDS_SPLASH_DEVELOPERS           8976\r
 #define IDS_SPLASH_GPLTEXT              8977\r
 #define IDS_MESSAGEBOX_OK               9001\r
 #define ID_SWAPPANES_SWAP12             34170\r
 #define ID_SWAPPANES_SWAP23             34171\r
 #define ID_SWAPPANES_SWAP13             34172\r
+#define ID_ADD_TO_IGNORED_SUBSTITUTIONS 34173\r
 \r
 // Next default values for new objects\r
 // \r
 #ifndef APSTUDIO_READONLY_SYMBOLS\r
 #define _APS_3D_CONTROLS                     1\r
 #define _APS_NEXT_RESOURCE_VALUE        253\r
-#define _APS_NEXT_COMMAND_VALUE         34173\r
+#define _APS_NEXT_COMMAND_VALUE         34174\r
 #define _APS_NEXT_CONTROL_VALUE         8832\r
 #define _APS_NEXT_SYMED_VALUE           117\r
 #endif\r
diff --git a/Src/subedit.h b/Src/subedit.h
new file mode 100644 (file)
index 0000000..e869d25
--- /dev/null
@@ -0,0 +1,49 @@
+// subedit.h : main header file for the SUBEDIT application
+//
+
+#if !defined(AFX_SUBEDIT_H__071E4499_6320_49E5_B602_E6F7873ED145__INCLUDED_)
+#define AFX_SUBEDIT_H__071E4499_6320_49E5_B602_E6F7873ED145__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+       #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h"       // main symbols
+
+/////////////////////////////////////////////////////////////////////////////
+// CSubeditApp:
+// See subedit.cpp for the implementation of this class
+//
+
+class CSubeditApp : public CWinApp
+{
+public:
+       CSubeditApp();
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CSubeditApp)
+       public:
+       virtual BOOL InitInstance();
+       //}}AFX_VIRTUAL
+
+// Implementation
+       //{{AFX_MSG(CSubeditApp)
+       afx_msg void OnAppAbout();
+               // NOTE - the ClassWizard will add and remove member functions here.
+               //    DO NOT EDIT what you see in these blocks of generated code !
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_SUBEDIT_H__071E4499_6320_49E5_B602_E6F7873ED145__INCLUDED_)