diff --git a/lexilla/Lexilla.vcxproj b/lexilla/Lexilla.vcxproj index 707b43380..38d512edd 100644 --- a/lexilla/Lexilla.vcxproj +++ b/lexilla/Lexilla.vcxproj @@ -72,6 +72,7 @@ + @@ -95,7 +96,6 @@ - diff --git a/lexilla/Lexilla.vcxproj.filters b/lexilla/Lexilla.vcxproj.filters index d6b568044..aa27fac13 100644 --- a/lexilla/Lexilla.vcxproj.filters +++ b/lexilla/Lexilla.vcxproj.filters @@ -272,8 +272,8 @@ lexers - - lexers_x + + lexers diff --git a/lexilla/lexers/LexMarkdown.cxx b/lexilla/lexers/LexMarkdown.cxx new file mode 100644 index 000000000..ac2b9f9ec --- /dev/null +++ b/lexilla/lexers/LexMarkdown.cxx @@ -0,0 +1,486 @@ +/****************************************************************** + * LexMarkdown.cxx + * + * A simple Markdown lexer for scintilla. + * + * Includes highlighting for some extra features from the + * Pandoc implementation; strikeout, using '#.' as a default + * ordered list item marker, and delimited code blocks. + * + * Limitations: + * + * Standard indented code blocks are not highlighted at all, + * as it would conflict with other indentation schemes. Use + * delimited code blocks for blanket highlighting of an + * entire code block. Embedded HTML is not highlighted either. + * Blanket HTML highlighting has issues, because some Markdown + * implementations allow Markdown markup inside of the HTML. Also, + * there is a following blank line issue that can't be ignored, + * explained in the next paragraph. Embedded HTML and code + * blocks would be better supported with language specific + * highlighting. + * + * The highlighting aims to accurately reflect correct syntax, + * but a few restrictions are relaxed. Delimited code blocks are + * highlighted, even if the line following the code block is not blank. + * Requiring a blank line after a block, breaks the highlighting + * in certain cases, because of the way Scintilla ends up calling + * the lexer. + * + * Written by Jon Strait - jstrait@moonloop.net + * + * The License.txt file describes the conditions under which this + * software may be distributed. + * + *****************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "ILexer.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#include "WordList.h" +#include "LexAccessor.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "CharacterSet.h" +#include "LexerModule.h" + +using namespace Lexilla; + +namespace { + +constexpr bool IsNewline(const int ch) { + // sc.GetRelative(i) returns '\0' if out of range + return (ch == '\n' || ch == '\r' || ch == '\0'); +} + +} + +// True if can follow ch down to the end with possibly trailing whitespace +// Does not set the state SCE_MARKDOWN_LINE_BEGIN as to allow further processing +static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) { + Sci_Position i = 0; + while (sc.GetRelative(++i) == ch) + ; + // Skip over whitespace + while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos) + ++i; + if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) { + sc.SetState(state); + sc.Forward(i); + return true; + } + else return false; +} + +// Set the state on text section from current to length characters, +// then set the rest until the newline to default, except for any characters matching token +static void SetStateAndZoom(const int state, const Sci_Position length, const int token, StyleContext &sc) { + sc.SetState(state); + sc.Forward(length); + sc.SetState(SCE_MARKDOWN_DEFAULT); + sc.Forward(); + bool started = false; + while (sc.More() && !IsNewline(sc.ch)) { + if (sc.ch == token && !started) { + sc.SetState(state); + started = true; + } + else if (sc.ch != token) { + sc.SetState(SCE_MARKDOWN_DEFAULT); + started = false; + } + sc.Forward(); + } + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); +} + +// Does the previous line have more than spaces and tabs? +static bool HasPrevLineContent(StyleContext &sc) { + Sci_Position i = 0; + // Go back to the previous newline + while ((--i + (Sci_Position)sc.currentPos) >= 0 && !IsNewline(sc.GetRelative(i))) + ; + while ((--i + (Sci_Position)sc.currentPos) >= 0) { + const int ch = sc.GetRelative(i); + if (ch == '\n') + break; + if (!((ch == '\r' || IsASpaceOrTab(ch)))) + return true; + } + return false; +} + +static bool AtTermStart(StyleContext &sc) { + return sc.currentPos == 0 || sc.chPrev == 0 || isspacechar(sc.chPrev); +} + +static bool IsCompleteStyleRegion(StyleContext &sc, const char *token) { + bool found = false; + const size_t start = strlen(token); + Sci_Position i = static_cast(start); + while (!IsNewline(sc.GetRelative(i))) { + // make sure an empty pair of single-char tokens doesn't match + // with a longer token: {*}{*} != {**} + if (sc.GetRelative(i) == *token && sc.GetRelative(i - 1) != *token) { + found = start > 1U ? sc.GetRelative(i + 1) == token[1] : true; + break; + } + i++; + } + return AtTermStart(sc) && found; +} + +static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) { + int count = 1; + Sci_Position i = 0; + for (;;) { + ++i; + int c = sc.GetRelative(i); + if (c == sc.ch) + ++count; + // hit a terminating character + else if (!IsASpaceOrTab(c) || sc.currentPos + i == endPos) { + // Are we a valid HRULE + if ((IsNewline(c) || sc.currentPos + i == endPos) && + count >= 3 && !HasPrevLineContent(sc)) { + sc.SetState(SCE_MARKDOWN_HRULE); + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + return true; + } + else { + sc.SetState(SCE_MARKDOWN_DEFAULT); + return false; + } + } + } +} + +static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, + WordList **, Accessor &styler) { + Sci_PositionU endPos = startPos + length; + int precharCount = 0; + bool isLinkNameDetecting = false; + // Don't advance on a new loop iteration and retry at the same position. + // Useful in the corner case of having to start at the beginning file position + // in the default state. + bool freezeCursor = false; + + // property lexer.markdown.header.eolfill + // Set to 1 to highlight all ATX header text. + bool headerEOLFill = styler.GetPropertyInt("lexer.markdown.header.eolfill", 0) == 1; + + StyleContext sc(startPos, static_cast(length), initStyle, styler); + + while (sc.More()) { + // Skip past escaped characters + if (sc.ch == '\\') { + sc.Forward(); + continue; + } + + // A blockquotes resets the line semantics + if (sc.state == SCE_MARKDOWN_BLOCKQUOTE) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + + // Conditional state-based actions + if (sc.state == SCE_MARKDOWN_CODE2) { + if (sc.Match("``")) { + const int closingSpan = (sc.GetRelative(2) == '`') ? 3 : 2; + sc.Forward(closingSpan); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_CODE) { + if (sc.ch == '`' && sc.chPrev != ' ') + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + /* De-activated because it gets in the way of other valid indentation + * schemes, for example multiple paragraphs inside a list item. + // Code block + else if (sc.state == SCE_MARKDOWN_CODEBK) { + bool d = true; + if (IsNewline(sc.ch)) { + if (sc.chNext != '\t') { + for (int c = 1; c < 5; ++c) { + if (sc.GetRelative(c) != ' ') + d = false; + } + } + } + else if (sc.atLineStart) { + if (sc.ch != '\t' ) { + for (int i = 0; i < 4; ++i) { + if (sc.GetRelative(i) != ' ') + d = false; + } + } + } + if (!d) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + */ + // Strong + else if (sc.state == SCE_MARKDOWN_STRONG1) { + if ((sc.Match("**") && sc.chPrev != ' ') || IsNewline(sc.GetRelative(2))) { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRONG2) { + if ((sc.Match("__") && sc.chPrev != ' ') || IsNewline(sc.GetRelative(2))) { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Emphasis + else if (sc.state == SCE_MARKDOWN_EM1) { + if ((sc.ch == '*' && sc.chPrev != ' ') || IsNewline(sc.chNext)) + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_EM2) { + if ((sc.ch == '_' && sc.chPrev != ' ') || IsNewline(sc.chNext)) + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.state == SCE_MARKDOWN_CODEBK) { + if (sc.atLineStart && sc.Match("~~~")) { + Sci_Position i = 1; + while (!IsNewline(sc.GetRelative(i)) && sc.currentPos + i < endPos) + i++; + sc.Forward(i); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_STRIKEOUT) { + if ((sc.Match("~~") && sc.chPrev != ' ') || IsNewline(sc.GetRelative(2))) { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + else if (sc.state == SCE_MARKDOWN_LINE_BEGIN) { + // Header + if (sc.Match("######")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER6); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER6, 6, '#', sc); + } + else if (sc.Match("#####")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER5); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER5, 5, '#', sc); + } + else if (sc.Match("####")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER4); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); + } + else if (sc.Match("###")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER3); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER3, 3, '#', sc); + } + else if (sc.Match("##")) { + if (headerEOLFill) + sc.SetState(SCE_MARKDOWN_HEADER2); + else + SetStateAndZoom(SCE_MARKDOWN_HEADER2, 2, '#', sc); + } + else if (sc.Match("#")) { + // Catch the special case of an unordered list + if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + else if (headerEOLFill) { + sc.SetState(SCE_MARKDOWN_HEADER1); + } + else + SetStateAndZoom(SCE_MARKDOWN_HEADER1, 1, '#', sc); + } + // Code block + else if (sc.Match("~~~")) { + if (!HasPrevLineContent(sc)) + sc.SetState(SCE_MARKDOWN_CODEBK); + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '=') { + if (HasPrevLineContent(sc) && FollowToLineEnd('=', SCE_MARKDOWN_HEADER1, endPos, sc)) { + if (!headerEOLFill) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + else + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch == '-') { + if (HasPrevLineContent(sc) && FollowToLineEnd('-', SCE_MARKDOWN_HEADER2, endPos, sc)) { + if (!headerEOLFill) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + else if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + else { + precharCount = 0; + sc.SetState(SCE_MARKDOWN_PRECHAR); + } + } + + // The header lasts until the newline + else if (sc.state == SCE_MARKDOWN_HEADER1 || sc.state == SCE_MARKDOWN_HEADER2 || + sc.state == SCE_MARKDOWN_HEADER3 || sc.state == SCE_MARKDOWN_HEADER4 || + sc.state == SCE_MARKDOWN_HEADER5 || sc.state == SCE_MARKDOWN_HEADER6) { + if (headerEOLFill) { + if (sc.atLineStart) { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + freezeCursor = true; + } + } + else if (IsNewline(sc.ch)) + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + + // New state only within the initial whitespace + if (sc.state == SCE_MARKDOWN_PRECHAR) { + // Blockquote + if (sc.ch == '>' && precharCount < 5) + sc.SetState(SCE_MARKDOWN_BLOCKQUOTE); + /* + // Begin of code block + else if (!HasPrevLineContent(sc) && (sc.chPrev == '\t' || precharCount >= 4)) + sc.SetState(SCE_MARKDOWN_CODEBK); + */ + // HRule - Total of three or more hyphens, asterisks, or underscores + // on a line by themselves + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '_') && IsValidHrule(endPos, sc)) + ; + // Unordered list + else if ((sc.ch == '-' || sc.ch == '*' || sc.ch == '+') && IsASpaceOrTab(sc.chNext)) { + sc.SetState(SCE_MARKDOWN_ULIST_ITEM); + sc.ForwardSetState(SCE_MARKDOWN_DEFAULT); + } + // Ordered list + else if (IsADigit(sc.ch)) { + int digitCount = 0; + while (IsADigit(sc.GetRelative(++digitCount))) + ; + if (sc.GetRelative(digitCount) == '.' && + IsASpaceOrTab(sc.GetRelative(digitCount + 1))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(digitCount + 1); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } else { + // a textual number at the margin should be plain text + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + } + // Alternate Ordered list + else if (sc.ch == '#' && sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) { + sc.SetState(SCE_MARKDOWN_OLIST_ITEM); + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (sc.ch != ' ' || precharCount > 2) + sc.SetState(SCE_MARKDOWN_DEFAULT); + else + ++precharCount; + } + + // Any link + if (sc.state == SCE_MARKDOWN_LINK) { + if (sc.Match("](") && sc.GetRelative(-1) != '\\') { + sc.Forward(2); + isLinkNameDetecting = true; + } + else if (sc.Match("]:") && sc.GetRelative(-1) != '\\') { + sc.Forward(2); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (!isLinkNameDetecting && sc.ch == ']' && sc.GetRelative(-1) != '\\') { + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); + } + else if (isLinkNameDetecting && sc.ch == ')' && sc.GetRelative(-1) != '\\') { + sc.Forward(); + sc.SetState(SCE_MARKDOWN_DEFAULT); + isLinkNameDetecting = false; + } + } + + // New state anywhere in doc + if (sc.state == SCE_MARKDOWN_DEFAULT) { + if (sc.atLineStart && sc.ch == '#') { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + freezeCursor = true; + } + // Links and Images + if (sc.Match("![")) { + sc.SetState(SCE_MARKDOWN_LINK); + sc.Forward(1); + } + else if (sc.ch == '[' && sc.GetRelative(-1) != '\\') { + sc.SetState(SCE_MARKDOWN_LINK); + } + // Code - also a special case for alternate inside spacing + else if (sc.Match("``") && sc.GetRelative(3) != ' ' && AtTermStart(sc)) { + const int openingSpan = (sc.GetRelative(2) == '`') ? 2 : 1; + sc.SetState(SCE_MARKDOWN_CODE2); + sc.Forward(openingSpan); + } + else if (sc.ch == '`' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "`")) { + sc.SetState(SCE_MARKDOWN_CODE); + } + // Strong + else if (sc.Match("**") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "**")) { + sc.SetState(SCE_MARKDOWN_STRONG1); + sc.Forward(); + } + else if (sc.Match("__") && sc.GetRelative(2) != ' ' && IsCompleteStyleRegion(sc, "__")) { + sc.SetState(SCE_MARKDOWN_STRONG2); + sc.Forward(); + } + // Emphasis + else if (sc.ch == '*' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "*")) { + sc.SetState(SCE_MARKDOWN_EM1); + } + else if (sc.ch == '_' && sc.chNext != ' ' && IsCompleteStyleRegion(sc, "_")) { + sc.SetState(SCE_MARKDOWN_EM2); + } + // Strikeout + else if (sc.Match("~~") && !(sc.GetRelative(2) == '~' || sc.GetRelative(2) == ' ') && + IsCompleteStyleRegion(sc, "~~")) { + sc.SetState(SCE_MARKDOWN_STRIKEOUT); + sc.Forward(); + } + // Beginning of line + else if (IsNewline(sc.ch)) { + sc.SetState(SCE_MARKDOWN_LINE_BEGIN); + } + } + // Advance if not holding back the cursor for this iteration. + if (!freezeCursor) + sc.Forward(); + freezeCursor = false; + } + sc.Complete(); +} + +LexerModule lmMarkdown(SCLEX_MARKDOWN, ColorizeMarkdownDoc, "markdown"); diff --git a/lexilla/lexers_x/LexMarkdown.cxx b/lexilla/lexers_x/homebrew/LexMarkdown.cxx similarity index 99% rename from lexilla/lexers_x/LexMarkdown.cxx rename to lexilla/lexers_x/homebrew/LexMarkdown.cxx index 38c398e1e..9daee9913 100644 --- a/lexilla/lexers_x/LexMarkdown.cxx +++ b/lexilla/lexers_x/homebrew/LexMarkdown.cxx @@ -277,7 +277,7 @@ static void ColorizeMarkdownDoc(Sci_PositionU startPos, Sci_Position length, int freezeCursor = true; } else if (sc.Match("####")) { - SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); + SetStateAndZoom(SCE_MARKDOWN_HEADER4, 4, '#', sc); freezeCursor = true; } else if (sc.Match("###")) { diff --git a/lexilla/src/deps.mak b/lexilla/src/deps.mak index 4f7d228b4..6282b581f 100644 --- a/lexilla/src/deps.mak +++ b/lexilla/src/deps.mak @@ -364,6 +364,18 @@ $(DIR_O)/LexMake.o: \ ../lexlib/StyleContext.h \ ../lexlib/CharacterSet.h \ ../lexlib/LexerModule.h +$(DIR_O)/LexMarkdown.o: \ + ../lexers/LexMarkdown.cxx \ + ../../scintilla/include/ILexer.h \ + ../../scintilla/include/Sci_Position.h \ + ../../scintilla/include/Scintilla.h \ + ../include/SciLexer.h \ + ../lexlib/WordList.h \ + ../lexlib/LexAccessor.h \ + ../lexlib/Accessor.h \ + ../lexlib/StyleContext.h \ + ../lexlib/CharacterSet.h \ + ../lexlib/LexerModule.h $(DIR_O)/LexMatlab.o: \ ../lexers/LexMatlab.cxx \ ../../scintilla/include/ILexer.h \ @@ -681,19 +693,6 @@ $(DIR_O)/LexKotlin.o: \ ../include/SciLexer.h \ ../lexers_x/StringUtils.h \ ../lexers_x/LexerUtils.h -$(DIR_O)/LexMarkdown.o: \ - ../lexers_x/LexMarkdown.cxx \ - ../../scintilla/include/ILexer.h \ - ../../scintilla/include/Sci_Position.h \ - ../../scintilla/include/Scintilla.h \ - ../include/SciLexer.h \ - ../lexlib/WordList.h \ - ../lexlib/LexAccessor.h \ - ../lexlib/Accessor.h \ - ../lexlib/StyleContext.h \ - ../lexers_x/CharSetX.h \ - ../lexlib/CharacterSet.h \ - ../lexlib/LexerModule.h $(DIR_O)/LexPython.o: \ ../lexers_x/LexPython.cxx \ ../../scintilla/include/ILexer.h \ diff --git a/lexilla/src/lexilla.mak b/lexilla/src/lexilla.mak index 48d2ecacf..16bc391c3 100644 --- a/lexilla/src/lexilla.mak +++ b/lexilla/src/lexilla.mak @@ -94,6 +94,7 @@ LEX_OBJS=\ $(DIR_O)\LexLaTeX.obj \ $(DIR_O)\LexLua.obj \ $(DIR_O)\LexMake.obj \ + $(DIR_O)\LexMarkdown.obj \ $(DIR_O)\LexMatlab.obj \ $(DIR_O)\LexNim.obj \ $(DIR_O)\LexNsis.obj \ @@ -117,7 +118,6 @@ LEX_OBJS=\ $(DIR_O)\LexerUtils.obj \ $(DIR_O)\LexJSON.obj \ $(DIR_O)\LexKotlin.obj \ - $(DIR_O)\LexMarkdown.obj \ $(DIR_O)\LexPython.obj \ $(DIR_O)\LexTOML.obj \ diff --git a/lexilla/src/nmdeps.mak b/lexilla/src/nmdeps.mak index 1dd98ae85..0e578d8d9 100644 --- a/lexilla/src/nmdeps.mak +++ b/lexilla/src/nmdeps.mak @@ -364,6 +364,18 @@ $(DIR_O)/LexMake.obj: \ ../lexlib/StyleContext.h \ ../lexlib/CharacterSet.h \ ../lexlib/LexerModule.h +$(DIR_O)/LexMarkdown.obj: \ + ../lexers/LexMarkdown.cxx \ + ../../scintilla/include/ILexer.h \ + ../../scintilla/include/Sci_Position.h \ + ../../scintilla/include/Scintilla.h \ + ../include/SciLexer.h \ + ../lexlib/WordList.h \ + ../lexlib/LexAccessor.h \ + ../lexlib/Accessor.h \ + ../lexlib/StyleContext.h \ + ../lexlib/CharacterSet.h \ + ../lexlib/LexerModule.h $(DIR_O)/LexMatlab.obj: \ ../lexers/LexMatlab.cxx \ ../../scintilla/include/ILexer.h \ @@ -681,19 +693,6 @@ $(DIR_O)/LexKotlin.obj: \ ../include/SciLexer.h \ ../lexers_x/StringUtils.h \ ../lexers_x/LexerUtils.h -$(DIR_O)/LexMarkdown.obj: \ - ../lexers_x/LexMarkdown.cxx \ - ../../scintilla/include/ILexer.h \ - ../../scintilla/include/Sci_Position.h \ - ../../scintilla/include/Scintilla.h \ - ../include/SciLexer.h \ - ../lexlib/WordList.h \ - ../lexlib/LexAccessor.h \ - ../lexlib/Accessor.h \ - ../lexlib/StyleContext.h \ - ../lexers_x/CharSetX.h \ - ../lexlib/CharacterSet.h \ - ../lexlib/LexerModule.h $(DIR_O)/LexPython.obj: \ ../lexers_x/LexPython.cxx \ ../../scintilla/include/ILexer.h \ diff --git a/res/StdDarkModeScheme.ini b/res/StdDarkModeScheme.ini index f1ebb8877..709528736 100644 --- a/res/StdDarkModeScheme.ini +++ b/res/StdDarkModeScheme.ini @@ -397,7 +397,7 @@ Preprocessor=fore:#F27E0D [Markdown] Strong=bold Emphasis=italic -Header 1=bold; fore:#272702; back:#848DCF; eolfilled +Header 1=bold; fore:#969685; back:#5A67BE; eolfilled Header 2=bold; fore:#A8CCDA; back:#1F6AB4; eolfilled Header 3=bold; fore:#A8CCDA; back:#154B7F; eolfilled Header 4=bold; fore:#A8CCDA; eolfilled diff --git a/src/StyleLexers/EditLexer.c b/src/StyleLexers/EditLexer.c index 7aab77e00..b1d8dee27 100644 --- a/src/StyleLexers/EditLexer.c +++ b/src/StyleLexers/EditLexer.c @@ -234,6 +234,10 @@ void Lexer_SetLexerSpecificProperties(const int lexerId) { //SciCall_SetProperty("lexer.sql.allow.dotted.word", "0"); break; + case SCLEX_MARKDOWN: + SciCall_SetProperty("lexer.markdown.header.eolfill", "1"); + break; + case SCLEX_NSIS: SciCall_SetProperty("nsis.ignorecase", "1"); break; diff --git a/src/StyleLexers/styleLexMARKDOWN.c b/src/StyleLexers/styleLexMARKDOWN.c index ab3a6b686..9e6721fe2 100644 --- a/src/StyleLexers/styleLexMARKDOWN.c +++ b/src/StyleLexers/styleLexMARKDOWN.c @@ -14,7 +14,7 @@ EDITLEXER lexMARKDOWN = { {SCE_MARKDOWN_LINE_BEGIN}, IDS_LEX_STR_63317, L"Line Begin", L"", L"" }, { {MULTI_STYLE(SCE_MARKDOWN_STRONG1,SCE_MARKDOWN_STRONG2,0,0)}, IDS_LEX_STR_63318, L"Strong", L"bold", L"" }, { {MULTI_STYLE(SCE_MARKDOWN_EM1,SCE_MARKDOWN_EM2,0,0)}, IDS_LEX_STR_63319, L"Emphasis", L"italic", L"" }, - { {SCE_MARKDOWN_HEADER1}, IDS_LEX_STR_63320, L"Header 1", L"bold; fore:#FFFFE2; back:#3D3DD3; eolfilled", L"" }, + { {SCE_MARKDOWN_HEADER1}, IDS_LEX_STR_63320, L"Header 1", L"bold; fore:#FFFFE2; back:#8BA4ED; eolfilled", L"" }, { {SCE_MARKDOWN_HEADER2}, IDS_LEX_STR_63321, L"Header 2", L"bold; fore:#336193; back:#9DCEFF; eolfilled", L"" }, { {SCE_MARKDOWN_HEADER3}, IDS_LEX_STR_63322, L"Header 3", L"bold; fore:#336193; back:#D9ECFF; eolfilled", L"" }, { {SCE_MARKDOWN_HEADER4}, IDS_LEX_STR_63323, L"Header 4", L"bold; fore:#336193; eolfilled", L"" }, diff --git a/test/test_files/StyleLexers/styleLexCPP/Config.cpp b/test/test_files/StyleLexers/styleLexCPP/Config.cpp index 5a4f0ef6d..66365521f 100644 --- a/test/test_files/StyleLexers/styleLexCPP/Config.cpp +++ b/test/test_files/StyleLexers/styleLexCPP/Config.cpp @@ -13,6 +13,17 @@ addindex // TODO : fkdlkldfdl // ---------------------------------------------------------------------------- +namespace { + +1 +2 +3 +5 +4 +6 +7 +8 +9 extern "C" { #include "Version.h" @@ -477,4 +488,24540 @@ extern "C" bool FindIniFile() } //============================================================================= +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//============================================================================= + +extern "C" { +#include "Version.h" +#include "Helpers.h" +#include "Styles.h" +#include "Dialogs.h" +#include "Encoding.h" +#include "Notepad3.h" +#include "resource.h" +} + +extern "C" const int g_FontQuality[4]; +extern "C" WININFO s_WinInfo; +extern "C" WININFO s_DefWinInfo; + +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V1; +extern "C" const WCHAR* const TBBUTTON_DEFAULT_IDS_V2; + +extern "C" prefix_t s_mxSBPrefix[STATUS_SECTOR_COUNT]; +extern "C" prefix_t s_mxSBPostfix[STATUS_SECTOR_COUNT]; +extern "C" bool s_iStatusbarVisible[STATUS_SECTOR_COUNT]; +extern "C" int s_iStatusbarWidthSpec[STATUS_SECTOR_COUNT]; +extern "C" int s_vSBSOrder[STATUS_SECTOR_COUNT]; + +extern "C" WCHAR s_tchToolbarBitmap[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapHot[MAX_PATH]; +extern "C" WCHAR s_tchToolbarBitmapDisabled[MAX_PATH]; + +extern "C" bool s_bEnableSaveSettings; +extern "C" int s_iToolBarTheme; + +extern "C" bool s_flagPosParam; +extern "C" int s_flagWindowPos; +extern "C" int s_flagReuseWindow; +extern "C" int s_flagSingleFileInstance; +extern "C" int s_flagMultiFileArg; +extern "C" int s_flagShellUseSystemMRU; +extern "C" int s_flagPrintFileAndLeave; + + +// ---------------------------------------------------------------------------- + +#include "SimpleIni.h" +#include "Config.h" + +// ============================================================================ + +static bool const s_bIsUTF8 = true; +static bool const s_bUseMultiKey = false; +static bool const s_bUseMultiLine = false; +static bool const s_bSetSpaces = false; + +// ---------------------------------------------------------------------------- + +static int s_iStatusbarSections[STATUS_SECTOR_COUNT] = SBS_INIT_MINUS; + +// ---------------------------------------------------------------------------- + +#define SI_SUCCESS(RC) ((RC) >= SI_OK) + +// ============================================================================ + +static CSimpleIni s_INI(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + + +extern "C" bool LoadIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.Reset(); + SI_Error const rc = s_INI.LoadFile(lpIniFilePath); + return SI_SUCCESS(rc); +} + +extern "C" bool SaveIniFile(LPCWSTR lpIniFilePath) +{ + s_INI.SetSpaces(s_bSetSpaces); + SI_Error const rc = s_INI.SaveFile(lpIniFilePath, true); + if (SI_SUCCESS(rc)) { + s_INI.Reset(); // done + } + return SI_SUCCESS(rc); +} + +extern "C" void ReleaseIniFile() +{ + s_INI.Reset(); +} + + +//============================================================================= +// +// Manipulation of (cached) ini file +// +//============================================================================= + + +extern "C" size_t IniSectionGetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, + s_INI.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" int IniSectionGetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + bool bHasMultiple = false; + int const iValue = (int)s_INI.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; +} +// ============================================================================ + + +extern "C" double IniSectionGetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dDefault) +{ + bool bHasMultiple = false; + double const dValue = s_INI.GetDoubleValue(lpSectionName, lpKeyName, dDefault, &bHasMultiple); + //assert(!bHasMultiple); + return dValue; +} +// ============================================================================ + + +extern "C" bool IniSectionGetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + bool bHasMultiple = false; + bool const bValue = s_INI.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; +} +// ============================================================================ + + +extern "C" bool IniSectionSetString(LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + SI_Error const rc = s_INI.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetInt(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +extern "C" bool IniSectionSetHex(LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + SI_Error const rc = s_INI.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, true, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetDouble(LPCWSTR lpSectionName, LPCWSTR lpKeyName, double dValue) +{ + SI_Error const rc = s_INI.SetDoubleValue(lpSectionName, lpKeyName, dValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionSetBool(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + SI_Error const rc = s_INI.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniSectionDelete(LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + return s_INI.Delete(lpSectionName, lpKeyName, bRemoveEmpty); +} +// ============================================================================ + + +extern "C" bool IniSectionClear(LPCWSTR lpSectionName, bool bRemoveEmpty) +{ + + bool const ok = s_INI.Delete(lpSectionName, nullptr, bRemoveEmpty); + if (!bRemoveEmpty) { + SI_Error const rc = s_INI.SetValue(lpSectionName, nullptr, nullptr); + return SI_SUCCESS(rc); + } + return ok; +} +// ============================================================================ + + +extern "C" bool IniClearAllSections(LPCWSTR lpPrefix, bool bRemoveEmpty) +{ + size_t const len = StringCchLen(lpPrefix, 0); + + CSimpleIni::TNamesDepend Sections; + s_INI.GetAllSections(Sections); + for (const auto& section : Sections) + { + if (StringCchCompareNI(section.pItem, len, lpPrefix, len) == 0) + { + s_INI.Delete(section.pItem, nullptr, bRemoveEmpty); + } + } + return true; +} +// ============================================================================ + + +// ============================================================================ +// ============================================================================ + + +extern "C" size_t IniFileGetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpDefault, + LPWSTR lpReturnedString, size_t cchReturnedString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + StringCchCopyW(lpReturnedString, cchReturnedString, Ini.GetValue(lpSectionName, lpKeyName, lpDefault, &bHasMultiple)); + //assert(!bHasMultiple); + } + return StringCchLenW(lpReturnedString, cchReturnedString); +} +// ============================================================================ + + +extern "C" bool IniFileSetString(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, LPCWSTR lpString) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + SI_Error const res = Ini.SetValue(lpSectionName, lpKeyName, lpString, nullptr, !s_bUseMultiKey); + rc = SI_SUCCESS(res) ? SI_OK : SI_FAIL; + + if (SI_SUCCESS(rc)) { + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" int IniFileGetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + int const iValue = Ini.GetLongValue(lpSectionName, lpKeyName, (long)iDefault, &bHasMultiple); + //assert(!bHasMultiple); + return iValue; + } + return iDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetInt(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, int iValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetLongValue(lpSectionName, lpKeyName, (long)iValue, nullptr, false, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileGetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bDefault) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error const rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + bool bHasMultiple = false; + bool const bValue = Ini.GetBoolValue(lpSectionName, lpKeyName, bDefault, &bHasMultiple); + //assert(!bHasMultiple); + return bValue; + } + return bDefault; +} +// ============================================================================ + + +extern "C" bool IniFileSetBool(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bValue) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) { + Ini.SetBoolValue(lpSectionName, lpKeyName, bValue, nullptr, !s_bUseMultiKey); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +extern "C" bool IniFileDelete(LPCWSTR lpFilePath, LPCWSTR lpSectionName, LPCWSTR lpKeyName, bool bRemoveEmpty) +{ + CSimpleIni Ini(s_bIsUTF8, s_bUseMultiKey, s_bUseMultiLine); + SI_Error rc = Ini.LoadFile(lpFilePath); + if (SI_SUCCESS(rc)) + { + Ini.Delete(lpSectionName, lpKeyName, bRemoveEmpty); + Ini.SetSpaces(s_bSetSpaces); + rc = Ini.SaveFile(Globals.IniFile, true); + } + return SI_SUCCESS(rc); +} +// ============================================================================ + + +//============================================================================= +// +// _CheckIniFile() +// +static bool _CheckIniFile(LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(lpszFile, tchFileExpanded, COUNTOF(tchFileExpanded)); + + if (PathIsRelative(tchFileExpanded)) + { + WCHAR tchBuild[MAX_PATH] = { L'\0' }; + // program directory + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + StringCchCopy(PathFindFileName(tchBuild), COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // sub directory (.\np3\) + StringCchCopy(tchBuild, COUNTOF(tchBuild), lpszModule); + PathCchRemoveFileSpec(tchBuild, COUNTOF(tchBuild)); + StringCchCat(tchBuild, COUNTOF(tchBuild), L"\\np3\\"); + StringCchCat(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + // Application Data (%APPDATA%) + if (GetKnownFolderPath(FOLDERID_RoamingAppData, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + // Home (%HOMEPATH%) user's profile dir + if (GetKnownFolderPath(FOLDERID_Profile, tchBuild, COUNTOF(tchBuild))) { + PathCchAppend(tchBuild, COUNTOF(tchBuild), tchFileExpanded); + if (PathFileExists(tchBuild)) { + StringCchCopy(lpszFile, MAX_PATH, tchBuild); + return true; + } + } + //~// in general search path + //~if (SearchPath(NULL,tchFileExpanded,L".ini",COUNTOF(tchBuild),tchBuild,NULL)) { + //~ StringCchCopy(lpszFile,MAX_PATH,tchBuild); + //~ return true; + //~} + } + else if (PathFileExists(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +static bool _CheckIniFileRedirect(LPWSTR lpszAppName, LPWSTR lpszKeyName, LPWSTR lpszFile, LPCWSTR lpszModule) +{ + WCHAR tch[MAX_PATH] = { L'\0' }; + if (GetPrivateProfileString(lpszAppName, lpszKeyName, L"", tch, COUNTOF(tch), lpszFile)) { + if (_CheckIniFile(tch, lpszModule)) { + StringCchCopy(lpszFile, MAX_PATH, tch); + return true; + } + WCHAR tchFileExpanded[MAX_PATH] = { L'\0' }; + ExpandEnvironmentStrings(tch, tchFileExpanded, COUNTOF(tchFileExpanded)); + if (PathIsRelative(tchFileExpanded)) { + StringCchCopy(lpszFile, MAX_PATH, lpszModule); + StringCchCopy(PathFindFileName(lpszFile), MAX_PATH, tchFileExpanded); + return true; + } + StringCchCopy(lpszFile, MAX_PATH, tchFileExpanded); + return true; + } + return false; +} +// ============================================================================ + + +extern "C" bool FindIniFile() +{ + bool bFound = false; + WCHAR tchPath[MAX_PATH] = { L'\0' }; + WCHAR tchModule[MAX_PATH] = { L'\0' }; + + GetModuleFileName(NULL, tchModule, COUNTOF(tchModule)); + + // set env path to module dir + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + SetEnvironmentVariable(NOTEPAD3_MODULE_DIR_ENV_VAR, tchPath); + + if (StrIsNotEmpty(Globals.IniFile)) { + if (StringCchCompareXI(Globals.IniFile, L"*?") == 0) { + return bFound; + } + if (!_CheckIniFile(Globals.IniFile, tchModule)) { + ExpandEnvironmentStringsEx(Globals.IniFile, COUNTOF(Globals.IniFile)); + if (PathIsRelative(Globals.IniFile)) { + StringCchCopy(tchPath, COUNTOF(tchPath), tchModule); + PathCchRemoveFileSpec(tchPath, COUNTOF(tchPath)); + PathCchAppend(tchPath, COUNTOF(tchPath), Globals.IniFile); + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + } + } + else { + StringCchCopy(tchPath, COUNTOF(tchPath), PathFindFileName(tchModule)); + PathCchRenameExtension(tchPath, COUNTOF(tchPath), L".ini"); + + bFound = _CheckIniFile(tchPath, tchModule); + + if (!bFound) { + StringCchCopy(tchPath, COUNTOF(tchPath), L"Notepad3.ini"); + bFound = _CheckIniFile(tchPath, tchModule); + } + + if (bFound) + { + // allow two redirections: administrator -> user -> custom + if (_CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule)) + { + _CheckIniFileRedirect(_W(SAPPNAME), _W(SAPPNAME) L".ini", tchPath, tchModule); + } + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchPath); + } + else { + StringCchCopy(Globals.IniFile, COUNTOF(Globals.IniFile), tchModule); + PathCchRenameExtension(Globals.IniFile, COUNTOF(Globals.IniFile), L".ini"); + } + } + + NormalizePathEx(Globals.IniFile, COUNTOF(Globals.IniFile), true, false); + + return bFound; +} +//=============================================================================