├── .clang-format ├── .github ├── dependabot.yml └── workflows │ ├── ci_build.yml │ └── codeql.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── external └── npp │ ├── Docking.h │ ├── DockingDlgInterface.h │ ├── Notepad_plus_msgs.h │ ├── PluginInterface.h │ ├── Scintilla.h │ ├── StaticDialog.cpp │ ├── StaticDialog.h │ ├── URLCtrl.cpp │ ├── URLCtrl.h │ ├── Window.h │ ├── dockingResource.h │ └── menuCmdID.h ├── src ├── NPPJSONViewer.sln ├── NppJsonViewer │ ├── AboutDlg.cpp │ ├── AboutDlg.h │ ├── Define.h │ ├── JsonHandler.cpp │ ├── JsonHandler.h │ ├── JsonNode.h │ ├── JsonViewDlg.cpp │ ├── JsonViewDlg.h │ ├── NPPJSONViewer.vcxproj │ ├── NPPJSONViewer.vcxproj.filters │ ├── NppJsonPlugin.cpp │ ├── NppJsonPlugin.h │ ├── Profile.cpp │ ├── Profile.h │ ├── RapidJsonHandler.cpp │ ├── RapidJsonHandler.h │ ├── ScintillaEditor.cpp │ ├── ScintillaEditor.h │ ├── SettingsDlg.cpp │ ├── SettingsDlg.h │ ├── ShortcutCommand.cpp │ ├── ShortcutCommand.h │ ├── SliderCtrl.cpp │ ├── SliderCtrl.h │ ├── StopWatch.h │ ├── TrackingStream.h │ ├── TreeHandler.h │ ├── TreeViewCtrl.cpp │ ├── TreeViewCtrl.h │ ├── dllmain.cpp │ ├── res │ │ ├── Icon.ico │ │ ├── Refresh.ico │ │ ├── format.ico │ │ ├── search.ico │ │ └── validate.ico │ ├── resource.h │ └── resource.rc ├── NppJsonViewerCommon.props └── UtilityLib │ ├── Execute.cpp │ ├── Execute.h │ ├── StringHelper.cpp │ ├── StringHelper.h │ ├── Utility.cpp │ ├── Utility.h │ ├── UtilityLib.vcxproj │ └── UtilityLib.vcxproj.filters └── tests └── UnitTest ├── JsonCompressTest.cpp ├── JsonFormatTest.cpp ├── JsonHandlerTest.cpp ├── NavigationTest.cpp ├── ProfileTest.cpp ├── StringHelperTest.cpp ├── UnitTest.vcxproj ├── UnitTest.vcxproj.filters ├── UtilityTest.cpp └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Microsoft 3 | AccessModifierOffset: '-4' 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveMacros: 'true' 6 | AlignConsecutiveAssignments: 'true' 7 | AlignConsecutiveDeclarations: 'true' 8 | AlignEscapedNewlines: Left 9 | AlignOperands: 'true' 10 | AlignTrailingComments: 'true' 11 | AllowAllArgumentsOnNextLine: 'false' 12 | AllowAllConstructorInitializersOnNextLine: 'true' 13 | AllowAllParametersOfDeclarationOnNextLine: 'false' 14 | AllowShortBlocksOnASingleLine: 'true' 15 | AllowShortCaseLabelsOnASingleLine: 'false' 16 | AllowShortFunctionsOnASingleLine: Empty 17 | AllowShortEnumsOnASingleLine: 'true' 18 | AllowShortIfStatementsOnASingleLine: Never 19 | AllowShortLambdasOnASingleLine: Empty 20 | AllowShortLoopsOnASingleLine: 'false' 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: 'false' 23 | AlwaysBreakTemplateDeclarations: 'Yes' 24 | BinPackArguments: 'false' 25 | BinPackParameters: 'false' 26 | BreakBeforeBinaryOperators: All 27 | BreakBeforeBraces: Allman 28 | BreakBeforeTernaryOperators: 'true' 29 | BreakConstructorInitializers: BeforeComma 30 | BreakInheritanceList: BeforeComma 31 | BreakStringLiterals: 'true' 32 | ColumnLimit: '180' 33 | CompactNamespaces: 'false' 34 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 35 | Cpp11BracedListStyle: 'true' 36 | DisableFormat: 'false' 37 | FixNamespaceComments: 'true' 38 | IncludeBlocks: Regroup 39 | IndentPPDirectives: BeforeHash 40 | IndentWidth: '4' 41 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 42 | Language: Cpp 43 | MaxEmptyLinesToKeep: '2' 44 | NamespaceIndentation: All 45 | PointerAlignment: Left 46 | ReflowComments: 'true' 47 | SortIncludes: 'false' 48 | SortUsingDeclarations: 'true' 49 | SpaceAfterCStyleCast: 'false' 50 | SpaceAfterLogicalNot: 'false' 51 | SpaceAfterTemplateKeyword: 'true' 52 | SpaceBeforeAssignmentOperators: 'true' 53 | SpaceBeforeCpp11BracedList: 'true' 54 | SpaceBeforeCtorInitializerColon: 'true' 55 | SpaceBeforeInheritanceColon: 'true' 56 | SpaceBeforeParens: ControlStatements 57 | SpaceBeforeRangeBasedForLoopColon: 'true' 58 | SpaceInEmptyParentheses: 'false' 59 | SpacesBeforeTrailingComments: '4' 60 | SpacesInAngles: 'false' 61 | SpacesInCStyleCastParentheses: 'false' 62 | SpacesInContainerLiterals: 'false' 63 | SpacesInParentheses: 'false' 64 | SpacesInSquareBrackets: 'false' 65 | Standard: Cpp11 66 | TabWidth: '4' 67 | UseTab: Never 68 | 69 | ... 70 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic set up for updating github-actions 2 | 3 | version: 2 4 | updates: 5 | 6 | # Maintain dependencies for GitHub Actions 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci_build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | BUILD_DIR: src\Build\Bin 11 | MSBUILD_TOOLSET: v143 12 | ARTIFACT_PREFIX: NppJSONViewer_ 13 | 14 | jobs: 15 | build: 16 | runs-on: windows-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | build_configuration: [Release, Debug] 21 | build_platform: [Win32, x64, ARM64] 22 | 23 | steps: 24 | # Step 1: Check out the code from the repo 25 | - name: Checkout repo 26 | uses: actions/checkout@v4 27 | with: 28 | submodules: recursive 29 | 30 | # Step 2: Prepare for build 31 | - name: Setup MSBuild 32 | uses: microsoft/setup-msbuild@v2 33 | 34 | # Step 3: Build projects and unit test 35 | - name: Build code 36 | working-directory: src 37 | run: msbuild NppJSONViewer.sln /m /p:configuration="${{matrix.build_configuration}}" /p:platform="${{matrix.build_platform}}" /p:PlatformToolset=${{env.MSBUILD_TOOLSET}} 38 | 39 | # Step 4: Upload build binary artifacts for deployment 40 | - name: Archive binaries artifacts 41 | uses: actions/upload-artifact@v4 42 | with: 43 | name: ${{env.ARTIFACT_PREFIX}}${{matrix.build_platform}}_${{matrix.build_configuration}} 44 | path: ${{env.BUILD_DIR }}\${{matrix.build_configuration}}\${{matrix.build_platform}}\NPPJSONViewer.dll 45 | 46 | # Step 5: Upload build pdb artifacts 47 | - name: Archive symbols artifacts 48 | uses: actions/upload-artifact@v4 49 | with: 50 | name: ${{env.ARTIFACT_PREFIX}}${{matrix.build_platform}}_${{matrix.build_configuration}}_pdb 51 | path: ${{env.BUILD_DIR }}\${{matrix.build_configuration}}\${{matrix.build_platform}}\NPPJSONViewer.pdb 52 | 53 | # Step 6: Run unit tests for x86 and x64 54 | - name: Run unit tests 55 | if: matrix.build_platform == 'Win32' || matrix.build_platform == 'x64' 56 | run: | 57 | cd ${{env.BUILD_DIR}}\${{matrix.build_configuration}}\${{matrix.build_platform}} 58 | ./UnitTest.exe 59 | 60 | upload-full-artifacts: 61 | # Trigger this job only after all 'build' jobs are complete 62 | needs: build 63 | runs-on: windows-latest 64 | strategy: 65 | fail-fast: true 66 | 67 | steps: 68 | # Step 7: Download all artifacts from the build job 69 | - name: Download all artifacts 70 | uses: actions/download-artifact@v4 71 | with: 72 | pattern: ${{env.ARTIFACT_PREFIX}}* 73 | path: all_artifacts\ 74 | 75 | # Step 8: Upload consolidated artifacts as a single artifact 76 | - name: Upload full artifact 77 | uses: actions/upload-artifact@v4 78 | with: 79 | name: ${{env.ARTIFACT_PREFIX}}ALL 80 | path: all_artifacts\** 81 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "Code Scanning" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | schedule: 20 | - cron: '34 11 * * 1' 21 | 22 | permissions: 23 | contents: read 24 | 25 | concurrency: 26 | group: ${{ github.workflow }}-${{ github.ref || github.run_id }} 27 | cancel-in-progress: true 28 | 29 | jobs: 30 | analyze: 31 | name: Analyze 32 | runs-on: windows-latest 33 | timeout-minutes: 20 34 | permissions: 35 | actions: read 36 | contents: read 37 | security-events: write 38 | 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | language: [ 'c-cpp' ] 43 | 44 | steps: 45 | - name: Checkout repository 46 | uses: actions/checkout@v4 47 | with: 48 | submodules: recursive 49 | 50 | # Initializes the CodeQL tools for scanning. 51 | - name: Initialize CodeQL 52 | uses: github/codeql-action/init@v3 53 | with: 54 | languages: ${{ matrix.language }} 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v3 60 | 61 | - name: Perform CodeQL Analysis 62 | uses: github/codeql-action/analyze@v3 63 | 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # Visual Studio/ VSCode cache/options directory 11 | .vs/ 12 | .vscode/ 13 | 14 | # Visual C++ cache files 15 | *.aps 16 | *.ncb 17 | *.opendb 18 | *.opensdf 19 | *.sdf 20 | *.cachefile 21 | *.obj 22 | *.tlog 23 | *.exp 24 | *.log 25 | *.pch 26 | *.ilk 27 | *.idb 28 | 29 | # Build Results/artifacts 30 | src/Build/ 31 | *.dll 32 | *.exe 33 | *.lib 34 | *.pdb 35 | *.recipe 36 | 37 | # Others 38 | *.zip 39 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/rapidjson"] 2 | path = external/rapidjson 3 | url = https://github.com/NPP-JSONViewer/rapidjson.git 4 | [submodule "external/googletest"] 5 | path = external/googletest 6 | url = https://github.com/google/googletest.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kapil Ratnani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSONViewer for Notepad++ 2 | [![GitHub release](https://img.shields.io/github/release/kapilratnani/JSON-Viewer.svg)](../../releases/latest) 3 |     [![CI Build](https://github.com/kapilratnani/JSON-Viewer/actions/workflows/ci_build.yml/badge.svg)](https://github.com/kapilratnani/JSON-Viewer/actions/workflows/ci_build.yml) 4 |     [![Code Scanning](https://github.com/kapilratnani/JSON-Viewer/actions/workflows/codeql.yml/badge.svg)](https://github.com/kapilratnani/JSON-Viewer/actions/workflows/codeql.yml) 5 | 6 | This plugin is designed to display JSON strings in a Treeview format and highlight the error position if any parsing issues occur. It's a simple and efficient tool, compatible with [Notepad++](https://github.com/notepad-plus-plus/notepad-plus-plus). 7 | 8 | 9 | ## Instructions: 10 | 1. Copy the file `NPPJSONViewer.dll` to the `plugins\NPPJSONViewer` folder in the Notepad++ installation directory.. 11 | 2. Restart Notepad++ and ensure the plugin appears under the Plugins menu. 12 | 3. Open a document containing a JSON string (or paste in some JSON text). 13 | 4. Select the JSON fragment and go to Plugins > JSON Viewer > Show JSON Viewer or press or press Ctrl+Alt+Shift+J. 14 | 5. Voila, that's it! If the JSON is valid, it will be displayed in a Treeview format. 15 | 16 | 17 | ## Latest Updates: 18 | 19 | ### 2.1.1.0 20 | 21 | 1. New features: 22 | 1. Zoom tree view with ctrl + `mouse wheel` or slider. 23 | 24 | 25 | 2. Bug/regression fixes: 26 | 1. Updated to the latest RapidJSON parser. 27 | 2. Minor code enhancements for improved performance. 28 | 3. Added unit tests 29 | 30 | 31 | ### 2.1.0.0 32 | 33 | 1. New features: 34 | 1. Navigate directly to the JSON node in the editor upon node selection (using left mouse click or arrow keys). 35 | 2. Select the JSON key in the editor when double-clicking on a node. 36 | 37 | 38 | 2. Bug/regression fixes: 39 | 1. Updated to the latest RapidJSON parser. 40 | 2. Minor code enhancements for improved performance. 41 | 3. Added unit tests 42 | 43 | 44 | ### 2.0.8.0 45 | 46 | 1. New features: 47 | 1. Sort ascending by key 48 | 2. Add file name in the title for visibility 49 | 50 | 51 | 2. Bug/regression fixes: 52 | 1. Updated license text on UI as per GitHub link 53 | 2. Explicit callout for no support on multi selection 54 | 3. Some other minor code and UI enhancements 55 | 56 | 57 | ### 2.0.7.0 58 | Bug/regression fixes: 59 | 1. "Copy value" / "Copy" not always return complete text 60 | 2. Format JSON option does not work if focused tab is in other view 61 | 3. Corrected some typos on setting dialog 62 | 4. Updated icons to match with dark mode as well 63 | 5. Removed quotes from the key in Treeview 64 | 6. Some other minor enhancements 65 | 66 | ### 2.0.6.0 67 | 1. New feature: 68 | 1. Replace value 'undefined' with 'null'. This is configurable feature. 69 | 2. Bug/regression fixes: 70 | 1. Cyrillic text is not properly shown in json tree view dialog 71 | 72 | ### 2.0.5.0 73 | 1. New feature: 74 | 1. Make json highlighter configurable 75 | 2. Bug/regression fixes: 76 | 1. Handle json for both the views. 77 | 2. Update treeview on reopen 78 | 3. When file type is json, then error message is shown twice on npp launch if viewer dock was kept opened on previous instance 79 | 4. Don't show error message on startup for non json files 80 | 81 | ### 2.0.4.0 82 | 1. New feature: 83 | 1. Show element count for list/array 84 | 2. Bug/regression fixes: 85 | 1. Crash fix: Setting dialog is not shown if about dialog is opened before it. 86 | 2. Set language type JSON properly 87 | 3. Some minor UI enhancements 88 | 89 | ### 2.0.3.0 90 | 1. New feature: 91 | 1. Search in json tree window 92 | 2. Handle NaN, Inf, -Inf, Infinity, -Infinity properly 93 | 2. Bug/regression fixes: 94 | 1. Handle all types of arrays which does not have any key 95 | 2. Corrected typos in setting json 96 | 3. Don't use double qoutes for other than string type 97 | 98 | 99 | ### 2.0.2.0 100 | 1. Provided UI to control formatting option via setting dialog 101 | 1. Setting for indentation 102 | 2. Setting for line ending 103 | 3. Setting for line format 104 | 4. Make json parsing configurable e.g. ignore trailing comma, ignore comment 105 | 2. Added couple of new feature 106 | 1. Follow json tree for current tab if it is json file 107 | 2. Auto format json file when opened (by direct or by tab switching) 108 | 3. Few bug/regression fixes 109 | 110 | ### 2.0.1.0 111 | 1. Redeveloped UI 112 | 1. Provided menu icon 113 | 2. Json view panel is redesigned which is button like, refresh, validate, format etc. 114 | 3. It uses well performed class instead of plain function 115 | 2. Current selected node path is given on the bottom of json view window 116 | 3. Many feature support such as copy node, copy value, copy path, expand/collapse all etc. 117 | 4. Few bug fixes 118 | 119 | 120 | ### 1.41 121 | 1. Support for ARM64 122 | 2. Dropped Windows XP support as Notepad++ is no more supporting Windows XP. 123 | 3. Upgrade Visual Studio to 2022 124 | 125 | 126 | ### 1.40 127 | 1. issue-55 Format JSON should also set the language to JSON #FeatureRequest 128 | 2. issue-56 Format Should Follow Line Break Settings 129 | 3. issue-57, issue-60 "Should add a function to remove line breaks and spaces" Thanks @neoarc 130 | 4. issue-68 Crashing Notepad++ 131 | 5. issue-72 tab setting from notepad++ settings are not honored 132 | 6. issue-73 Display tree for Array of Object 133 | 7. issue-80 use line ending setting from editor 134 | 8. Relaxed parsing. Supports trailing commas, comments(only parsing), NaN and infinity JS literals. 135 | 136 | ### 1.34 137 | 1. Fix Access Violation issue #51 138 | 2. Fix issue #47 139 | 3. Fix issue #43 140 | 141 | ### 1.31 142 | 1. Reads tab setting from notepad++ settings 143 | 144 | ### 1.30a 145 | 1. Now using rapidjson 146 | 147 | ### 1.24 148 | 1. 64 bit support. Thanks @chcg 149 | 150 | ### 1.23 151 | 1. Select all text when no selection 152 | Thanks @vakio 153 | 2. fix memory leak and close About dialog when clicking "Close" button 154 | Thanks @quangnh89 155 | 156 | ### 1.22 157 | 1. Fixed display of boolean values. Now displaying as "key":True/False 158 | Thanks @yoyokenny 159 | 2. Fixed hang on faulty JSON. 160 | Thanks @vancekic 161 | 162 | ### 1.21 163 | 1. Fixed display of UTF-8 characters. 164 | 165 | ### 1.20 166 | 1. Fixed bug "#3 quoted doublequotes-Jan Huschauer" 167 | 168 | ### 1.19 169 | 1. Added a command to format JSON 170 | 171 | ### 1.175 172 | 1. Now displays a message box when JSON string is not selected. 173 | 174 | ### 1.17 175 | 1. Fixed dialog display issue, that occurred in 1.16 release. 176 | 177 | ### 1.16 178 | 1. Fixed bug - 3305433 do not find error on second try and do not build tree 179 | 2. Fixed a memory leak..was not de-allocating memory allocated to json strings 180 | 181 | ### 1.15 182 | 1. Fixed bug 3203739 "Unable to parse JSON Arrays" 183 | 184 | ### 1.1 185 | 1. Fixed hotkey, now press CTRL+SHIFT+ALT+J(default). 186 | 2. Marks error position in JSON 187 | 188 | 189 | ## Contributors 190 | 191 |
192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /external/npp/Docking.h: -------------------------------------------------------------------------------- 1 | /* 2 | this file is part of Function List Plugin for Notepad++ 3 | Copyright (C)2005 Jens Lorenz 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either 8 | version 2 of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #ifndef DOCKING_H 21 | #define DOCKING_H 22 | 23 | // ATTENTION : It's a part of interface header, so don't include the others header here 24 | 25 | // styles for containers 26 | #define CAPTION_TOP TRUE 27 | #define CAPTION_BOTTOM FALSE 28 | 29 | // defines for docking manager 30 | #define CONT_LEFT 0 31 | #define CONT_RIGHT 1 32 | #define CONT_TOP 2 33 | #define CONT_BOTTOM 3 34 | #define DOCKCONT_MAX 4 35 | 36 | // mask params for plugins of internal dialogs 37 | #define DWS_ICONTAB 0x00000001 // Icon for tabs are available 38 | #define DWS_ICONBAR 0x00000002 // Icon for icon bar are available (currently not supported) 39 | #define DWS_ADDINFO 0x00000004 // Additional information are in use 40 | #define DWS_PARAMSALL (DWS_ICONTAB|DWS_ICONBAR|DWS_ADDINFO) 41 | 42 | // default docking values for first call of plugin 43 | #define DWS_DF_CONT_LEFT (CONT_LEFT << 28) // default docking on left 44 | #define DWS_DF_CONT_RIGHT (CONT_RIGHT << 28) // default docking on right 45 | #define DWS_DF_CONT_TOP (CONT_TOP << 28) // default docking on top 46 | #define DWS_DF_CONT_BOTTOM (CONT_BOTTOM << 28) // default docking on bottom 47 | #define DWS_DF_FLOATING 0x80000000 // default state is floating 48 | 49 | 50 | typedef struct { 51 | HWND hClient; // client Window Handle 52 | TCHAR *pszName; // name of plugin (shown in window) 53 | int dlgID; // a funcItem provides the function pointer to start a dialog. Please parse here these ID 54 | 55 | // user modifications 56 | UINT uMask; // mask params: look to above defines 57 | HICON hIconTab; // icon for tabs 58 | TCHAR *pszAddInfo; // for plugin to display additional informations 59 | 60 | // internal data, do not use !!! 61 | RECT rcFloat; // floating position 62 | int iPrevCont; // stores the privious container (toggling between float and dock) 63 | const TCHAR* pszModuleName; // it's the plugin file name. It's used to identify the plugin 64 | } tTbData; 65 | 66 | 67 | typedef struct { 68 | HWND hWnd; // the docking manager wnd 69 | RECT rcRegion[DOCKCONT_MAX]; // position of docked dialogs 70 | } tDockMgr; 71 | 72 | 73 | #define HIT_TEST_THICKNESS 20 74 | #define SPLITTER_WIDTH 4 75 | 76 | 77 | #endif // DOCKING_H 78 | -------------------------------------------------------------------------------- /external/npp/DockingDlgInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | this file is part of Function List Plugin for Notepad++ 3 | Copyright (C)2005 Jens Lorenz 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either 8 | version 2 of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "StaticDialog.h" 23 | #include "dockingResource.h" 24 | #include "Docking.h" 25 | #include 26 | 27 | 28 | class DockingDlgInterface : public StaticDialog 29 | { 30 | public: 31 | DockingDlgInterface() : StaticDialog() {}; 32 | DockingDlgInterface(int dlgID) : StaticDialog(), _dlgID(dlgID) {}; 33 | 34 | virtual void init(HINSTANCE hInst, HWND parent) 35 | { 36 | StaticDialog::init(hInst, parent); 37 | ::GetModuleFileName((HMODULE)hInst, _moduleName, MAX_PATH); 38 | lstrcpy(_moduleName, PathFindFileName(_moduleName)); 39 | } 40 | 41 | void create(tTbData * data, bool isRTL = false) 42 | { 43 | StaticDialog::create(_dlgID, isRTL); 44 | ::GetWindowText(_hSelf, _pluginName, _countof(_pluginName)); 45 | 46 | // user information 47 | data->hClient = _hSelf; 48 | data->pszName = _pluginName; 49 | 50 | // supported features by plugin 51 | data->uMask = 0; 52 | 53 | // additional info 54 | data->pszAddInfo = NULL; 55 | _data = data; 56 | 57 | } 58 | 59 | virtual void updateDockingDlg(void) 60 | { 61 | ::SendMessage(_hParent, NPPM_DMMUPDATEDISPINFO, 0, (LPARAM)_hSelf); 62 | } 63 | 64 | virtual void destroy() {} 65 | 66 | virtual void display(bool toShow = true) const 67 | { 68 | ::SendMessage(_hParent, toShow ? NPPM_DMMSHOW : NPPM_DMMHIDE, 0, (LPARAM)_hSelf); 69 | } 70 | 71 | const TCHAR * getPluginFileName() const { return _moduleName; } 72 | 73 | protected: 74 | virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM /*wParam*/, LPARAM lParam) 75 | { 76 | switch (message) 77 | { 78 | 79 | case WM_NOTIFY: 80 | { 81 | LPNMHDR pnmh = (LPNMHDR)lParam; 82 | 83 | if (pnmh->hwndFrom == _hParent) 84 | { 85 | switch (LOWORD(pnmh->code)) 86 | { 87 | case DMN_CLOSE: 88 | { 89 | break; 90 | } 91 | case DMN_FLOAT: 92 | { 93 | _isFloating = true; 94 | break; 95 | } 96 | case DMN_DOCK: 97 | { 98 | _isFloating = false; 99 | break; 100 | } 101 | default: 102 | break; 103 | } 104 | } 105 | break; 106 | } 107 | default: 108 | break; 109 | } 110 | return FALSE; 111 | }; 112 | 113 | // Handles 114 | HWND _HSource = nullptr; 115 | tTbData* _data = nullptr; 116 | int _dlgID = 0; 117 | bool _isFloating = false; 118 | TCHAR _moduleName[MAX_PATH] = {}; 119 | TCHAR _pluginName[MAX_PATH] = {}; 120 | }; 121 | -------------------------------------------------------------------------------- /external/npp/PluginInterface.h: -------------------------------------------------------------------------------- 1 | // This file is part of Notepad++ project 2 | // Copyright (C)2003 Don HO 3 | // 4 | // This program is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU General Public License 6 | // as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // Note that the GPL places important restrictions on "derived works", yet 10 | // it does not provide a detailed definition of that term. To avoid 11 | // misunderstandings, we consider an application to constitute a 12 | // "derivative work" for the purpose of this license if it does any of the 13 | // following: 14 | // 1. Integrates source code from Notepad++. 15 | // 2. Integrates/includes/aggregates Notepad++ into a proprietary executable 16 | // installer, such as those produced by InstallShield. 17 | // 3. Links to a library or executes a program that does any of the above. 18 | // 19 | // This program is distributed in the hope that it will be useful, 20 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | // GNU General Public License for more details. 23 | // 24 | // You should have received a copy of the GNU General Public License 25 | // along with this program; if not, write to the Free Software 26 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27 | 28 | 29 | #ifndef PLUGININTERFACE_H 30 | #define PLUGININTERFACE_H 31 | 32 | #ifndef SCINTILLA_H 33 | #include "Scintilla.h" 34 | #endif //SCINTILLA_H 35 | 36 | #ifndef NOTEPAD_PLUS_MSGS_H 37 | #include "Notepad_plus_msgs.h" 38 | #endif //NOTEPAD_PLUS_MSGS_H 39 | 40 | const int nbChar = 64; 41 | 42 | typedef const TCHAR * (__cdecl * PFUNCGETNAME)(); 43 | 44 | struct NppData 45 | { 46 | HWND _nppHandle; 47 | HWND _scintillaMainHandle; 48 | HWND _scintillaSecondHandle; 49 | }; 50 | 51 | typedef void (__cdecl * PFUNCSETINFO)(NppData); 52 | typedef void (__cdecl * PFUNCPLUGINCMD)(); 53 | typedef void (__cdecl * PBENOTIFIED)(SCNotification *); 54 | typedef LRESULT (__cdecl * PMESSAGEPROC)(UINT Message, WPARAM wParam, LPARAM lParam); 55 | 56 | 57 | struct ShortcutKey 58 | { 59 | bool _isCtrl; 60 | bool _isAlt; 61 | bool _isShift; 62 | UCHAR _key; 63 | }; 64 | 65 | struct FuncItem 66 | { 67 | TCHAR _itemName[nbChar]; 68 | PFUNCPLUGINCMD _pFunc; 69 | int _cmdID; 70 | bool _init2Check; 71 | ShortcutKey *_pShKey; 72 | }; 73 | 74 | typedef FuncItem * (__cdecl * PFUNCGETFUNCSARRAY)(int *); 75 | 76 | // You should implement (or define an empty function body) those functions which are called by Notepad++ plugin manager 77 | extern "C" __declspec(dllexport) void setInfo(NppData); 78 | extern "C" __declspec(dllexport) const TCHAR * getName(); 79 | extern "C" __declspec(dllexport) FuncItem * getFuncsArray(int *); 80 | extern "C" __declspec(dllexport) void beNotified(SCNotification *); 81 | extern "C" __declspec(dllexport) LRESULT messageProc(UINT Message, WPARAM wParam, LPARAM lParam); 82 | 83 | // This API return always true now, since Notepad++ isn't compiled in ANSI mode anymore 84 | extern "C" __declspec(dllexport) BOOL isUnicode(); 85 | 86 | 87 | #endif //PLUGININTERFACE_H 88 | -------------------------------------------------------------------------------- /external/npp/StaticDialog.cpp: -------------------------------------------------------------------------------- 1 | //this file is part of notepad++ 2 | //Copyright (C)2003 Don HO ( donho@altern.org ) 3 | // 4 | //This program is free software; you can redistribute it and/or 5 | //modify it under the terms of the GNU General Public License 6 | //as published by the Free Software Foundation; either 7 | //version 2 of the License, or (at your option) any later version. 8 | // 9 | //This program is distributed in the hope that it will be useful, 10 | //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | //GNU General Public License for more details. 13 | // 14 | //You should have received a copy of the GNU General Public License 15 | //along with this program; if not, write to the Free Software 16 | //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | 18 | #include 19 | #include "StaticDialog.h" 20 | 21 | void StaticDialog::goToCenter() 22 | { 23 | RECT rc; 24 | ::GetClientRect(_hParent, &rc); 25 | POINT center; 26 | center.x = rc.left + (rc.right - rc.left)/2; 27 | center.y = rc.top + (rc.bottom - rc.top)/2; 28 | ::ClientToScreen(_hParent, ¢er); 29 | 30 | int x = center.x - (_rc.right - _rc.left)/2; 31 | int y = center.y - (_rc.bottom - _rc.top)/2; 32 | 33 | ::SetWindowPos(_hSelf, HWND_TOP, x, y, _rc.right - _rc.left, _rc.bottom - _rc.top, SWP_SHOWWINDOW); 34 | } 35 | 36 | HGLOBAL StaticDialog::makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate) 37 | { 38 | // Get Dlg Template resource 39 | HRSRC hDialogRC = ::FindResource(_hInst, MAKEINTRESOURCE(dialogID), RT_DIALOG); 40 | if (!hDialogRC) 41 | return NULL; 42 | 43 | HGLOBAL hDlgTemplate = ::LoadResource(_hInst, hDialogRC); 44 | if (!hDlgTemplate) 45 | return NULL; 46 | 47 | DLGTEMPLATE *pDlgTemplate = reinterpret_cast(::LockResource(hDlgTemplate)); 48 | if (!pDlgTemplate) 49 | return NULL; 50 | 51 | // Duplicate Dlg Template resource 52 | unsigned long sizeDlg = ::SizeofResource(_hInst, hDialogRC); 53 | HGLOBAL hMyDlgTemplate = ::GlobalAlloc(GPTR, sizeDlg); 54 | if (hMyDlgTemplate) 55 | { 56 | *ppMyDlgTemplate = reinterpret_cast(::GlobalLock(hMyDlgTemplate)); 57 | if (*ppMyDlgTemplate) 58 | { 59 | ::memcpy(*ppMyDlgTemplate, pDlgTemplate, sizeDlg); 60 | 61 | DLGTEMPLATEEX *pMyDlgTemplateEx = reinterpret_cast(*ppMyDlgTemplate); 62 | if (pMyDlgTemplateEx->signature == 0xFFFF) 63 | pMyDlgTemplateEx->exStyle |= WS_EX_LAYOUTRTL; 64 | else 65 | (*ppMyDlgTemplate)->dwExtendedStyle |= WS_EX_LAYOUTRTL; 66 | } 67 | } 68 | 69 | return hMyDlgTemplate; 70 | } 71 | 72 | void StaticDialog::create(int dialogID, bool isRTL) 73 | { 74 | if (isRTL) 75 | { 76 | DLGTEMPLATE *pMyDlgTemplate = NULL; 77 | HGLOBAL hMyDlgTemplate = makeRTLResource(dialogID, &pMyDlgTemplate); 78 | _hSelf = ::CreateDialogIndirectParam(_hInst, pMyDlgTemplate, _hParent, dlgProc, reinterpret_cast(this)); 79 | ::GlobalFree(hMyDlgTemplate); 80 | } 81 | else 82 | _hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, dlgProc, reinterpret_cast(this)); 83 | 84 | if (!_hSelf) 85 | { 86 | DWORD err = ::GetLastError(); 87 | char errMsg[256] {}; 88 | sprintf_s(errMsg, "CreateDialogParam() return NULL.\rGetLastError() == %lu", err); 89 | ::MessageBoxA(NULL, errMsg, "In StaticDialog::create()", MB_OK); 90 | return; 91 | } 92 | 93 | // if the destination of message NPPM_MODELESSDIALOG is not its parent, then it's the grand-parent 94 | ::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGADD, reinterpret_cast(_hSelf)); 95 | } 96 | 97 | INT_PTR CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 98 | { 99 | switch (message) 100 | { 101 | case WM_INITDIALOG: 102 | { 103 | StaticDialog *pStaticDlg = reinterpret_cast(lParam); 104 | pStaticDlg->_hSelf = hwnd; 105 | ::SetWindowLongPtr(hwnd, GWLP_USERDATA, static_cast(lParam)); 106 | ::GetWindowRect(hwnd, &(pStaticDlg->_rc)); 107 | pStaticDlg->run_dlgProc(message, wParam, lParam); 108 | 109 | return TRUE; 110 | } 111 | 112 | default: 113 | { 114 | StaticDialog *pStaticDlg = reinterpret_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); 115 | if (!pStaticDlg) 116 | return FALSE; 117 | return pStaticDlg->run_dlgProc(message, wParam, lParam); 118 | } 119 | } 120 | } 121 | 122 | void StaticDialog::alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT & point) 123 | { 124 | RECT rc, rc2; 125 | ::GetWindowRect(handle, &rc); 126 | 127 | point.x = rc.left; 128 | point.y = rc.top; 129 | 130 | switch (pos) 131 | { 132 | case PosAlign::left: 133 | { 134 | ::GetWindowRect(handle2Align, &rc2); 135 | point.x -= rc2.right - rc2.left; 136 | break; 137 | } 138 | case PosAlign::right: 139 | { 140 | ::GetWindowRect(handle, &rc2); 141 | point.x += rc2.right - rc2.left; 142 | break; 143 | } 144 | case PosAlign::top: 145 | { 146 | ::GetWindowRect(handle2Align, &rc2); 147 | point.y -= rc2.bottom - rc2.top; 148 | break; 149 | } 150 | case PosAlign::bottom: 151 | { 152 | ::GetWindowRect(handle, &rc2); 153 | point.y += rc2.bottom - rc2.top; 154 | break; 155 | } 156 | } 157 | 158 | ::ScreenToClient(_hSelf, &point); 159 | } 160 | 161 | -------------------------------------------------------------------------------- /external/npp/StaticDialog.h: -------------------------------------------------------------------------------- 1 | //this file is part of notepad++ 2 | //Copyright (C)2003 Don HO ( donho@altern.org ) 3 | // 4 | //This program is free software; you can redistribute it and/or 5 | //modify it under the terms of the GNU General Public License 6 | //as published by the Free Software Foundation; either 7 | //version 2 of the License, or (at your option) any later version. 8 | // 9 | //This program is distributed in the hope that it will be useful, 10 | //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | //GNU General Public License for more details. 13 | // 14 | //You should have received a copy of the GNU General Public License 15 | //along with this program; if not, write to the Free Software 16 | //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | 18 | #pragma once 19 | 20 | #include "Window.h" 21 | #include "Notepad_plus_msgs.h" 22 | typedef HRESULT(WINAPI* ETDTProc) (HWND, DWORD); 23 | 24 | enum class PosAlign { left, right, top, bottom }; 25 | 26 | struct DLGTEMPLATEEX { 27 | WORD dlgVer; 28 | WORD signature; 29 | DWORD helpID; 30 | DWORD exStyle; 31 | DWORD style; 32 | WORD cDlgItems; 33 | short x; 34 | short y; 35 | short cx; 36 | short cy; 37 | // The structure has more fields but are variable length 38 | } ; 39 | 40 | class StaticDialog : public Window 41 | { 42 | public : 43 | StaticDialog() : Window() {}; 44 | ~StaticDialog(){ 45 | if (isCreated()) { 46 | ::SetWindowLongPtr(_hSelf, GWLP_USERDATA, (long)NULL); //Prevent run_dlgProc from doing anything, since its virtual 47 | destroy(); 48 | } 49 | }; 50 | virtual void create(int dialogID, bool isRTL = false); 51 | 52 | virtual bool isCreated() const { 53 | return (_hSelf != NULL); 54 | }; 55 | 56 | void goToCenter(); 57 | void destroy() { 58 | ::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGREMOVE, (WPARAM)_hSelf); 59 | ::DestroyWindow(_hSelf); 60 | }; 61 | 62 | protected : 63 | RECT _rc{}; 64 | static INT_PTR CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 65 | virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0; 66 | 67 | void alignWith(HWND handle, HWND handle2Align, PosAlign pos, POINT & point); 68 | HGLOBAL makeRTLResource(int dialogID, DLGTEMPLATE **ppMyDlgTemplate); 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /external/npp/URLCtrl.cpp: -------------------------------------------------------------------------------- 1 | // This file is part of Notepad++ project 2 | // Copyright (C)2003 Don HO 3 | // 4 | // This program is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU General Public License 6 | // as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // Note that the GPL places important restrictions on "derived works", yet 10 | // it does not provide a detailed definition of that term. To avoid 11 | // misunderstandings, we consider an application to constitute a 12 | // "derivative work" for the purpose of this license if it does any of the 13 | // following: 14 | // 1. Integrates source code from Notepad++. 15 | // 2. Integrates/includes/aggregates Notepad++ into a proprietary executable 16 | // installer, such as those produced by InstallShield. 17 | // 3. Links to a library or executes a program that does any of the above. 18 | // 19 | // This program is distributed in the hope that it will be useful, 20 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | // GNU General Public License for more details. 23 | // 24 | // You should have received a copy of the GNU General Public License 25 | // along with this program; if not, write to the Free Software 26 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27 | 28 | 29 | #include "URLCtrl.h" 30 | 31 | void URLCtrl::create(HWND itemHandle, const TCHAR* link, COLORREF linkColor) 32 | { 33 | // turn on notify style 34 | ::SetWindowLongPtr(itemHandle, GWL_STYLE, ::GetWindowLongPtr(itemHandle, GWL_STYLE) | SS_NOTIFY); 35 | 36 | // set the URL text (not the display text) 37 | if (link) 38 | _URL = link; 39 | 40 | // set the hyperlink colour 41 | _linkColor = linkColor; 42 | 43 | // set the visited colour 44 | _visitedColor = RGB(128, 0, 128); 45 | 46 | // subclass the static control 47 | _oldproc = reinterpret_cast(::SetWindowLongPtr(itemHandle, GWLP_WNDPROC, reinterpret_cast(URLCtrlProc))); 48 | 49 | // associate the URL structure with the static control 50 | ::SetWindowLongPtr(itemHandle, GWLP_USERDATA, reinterpret_cast(this)); 51 | 52 | // save hwnd 53 | _hSelf = itemHandle; 54 | } 55 | void URLCtrl::create(HWND itemHandle, int cmd, HWND msgDest) 56 | { 57 | // turn on notify style 58 | ::SetWindowLongPtr(itemHandle, GWL_STYLE, ::GetWindowLongPtr(itemHandle, GWL_STYLE) | SS_NOTIFY); 59 | 60 | _cmdID = cmd; 61 | _msgDest = msgDest; 62 | 63 | // set the hyperlink colour 64 | _linkColor = RGB(0, 0, 255); 65 | 66 | // subclass the static control 67 | _oldproc = reinterpret_cast(::SetWindowLongPtr(itemHandle, GWLP_WNDPROC, reinterpret_cast(URLCtrlProc))); 68 | 69 | // associate the URL structure with the static control 70 | ::SetWindowLongPtr(itemHandle, GWLP_USERDATA, reinterpret_cast(this)); 71 | 72 | // save hwnd 73 | _hSelf = itemHandle; 74 | } 75 | 76 | void URLCtrl::destroy() 77 | { 78 | if (_hfUnderlined) 79 | ::DeleteObject(_hfUnderlined); 80 | if (_hCursor) 81 | ::DestroyCursor(_hCursor); 82 | } 83 | 84 | void URLCtrl::action() 85 | { 86 | if (_cmdID) 87 | { 88 | ::SendMessage(_msgDest ? _msgDest : _hParent, WM_COMMAND, _cmdID, 0); 89 | } 90 | else 91 | { 92 | _linkColor = _visitedColor; 93 | 94 | ::InvalidateRect(_hSelf, 0, 0); 95 | ::UpdateWindow(_hSelf); 96 | 97 | // Open a browser 98 | if (_URL != TEXT("")) 99 | { 100 | ::ShellExecute(NULL, TEXT("open"), _URL.c_str(), NULL, NULL, SW_SHOWNORMAL); 101 | } 102 | else 103 | { 104 | TCHAR szWinText[MAX_PATH]; 105 | ::GetWindowText(_hSelf, szWinText, MAX_PATH); 106 | ::ShellExecute(NULL, TEXT("open"), szWinText, NULL, NULL, SW_SHOWNORMAL); 107 | } 108 | } 109 | } 110 | 111 | COLORREF URLCtrl::getCtrlBgColor(HWND hWnd) 112 | { 113 | COLORREF crRet = CLR_INVALID; 114 | if (hWnd && IsWindow(hWnd)) 115 | { 116 | RECT rc; 117 | if (GetClientRect(hWnd, &rc)) 118 | { 119 | HDC hDC = GetDC(hWnd); 120 | if (hDC) 121 | { 122 | HDC hdcMem = CreateCompatibleDC(hDC); 123 | if (hdcMem) 124 | { 125 | HBITMAP hBmp = CreateCompatibleBitmap(hDC, 126 | rc.right, rc.bottom); 127 | if (hBmp) 128 | { 129 | HGDIOBJ hOld = SelectObject(hdcMem, hBmp); 130 | if (hOld) 131 | { 132 | if (SendMessage(hWnd, WM_ERASEBKGND, reinterpret_cast(hdcMem), 0)) 133 | { 134 | crRet = GetPixel(hdcMem, 2, 2); // 0, 0 is usually on the border 135 | } 136 | SelectObject(hdcMem, hOld); 137 | } 138 | DeleteObject(hBmp); 139 | } 140 | DeleteDC(hdcMem); 141 | } 142 | ReleaseDC(hWnd, hDC); 143 | } 144 | } 145 | } 146 | return crRet; 147 | } 148 | 149 | LRESULT URLCtrl::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 150 | { 151 | switch (Message) 152 | { 153 | // Free up the structure we allocated 154 | case WM_NCDESTROY: 155 | //HeapFree(GetProcessHeap(), 0, url); 156 | break; 157 | 158 | // Paint the static control using our custom 159 | // colours, and with an underline text style 160 | case WM_PAINT: 161 | { 162 | DWORD dwStyle = static_cast(::GetWindowLongPtr(hwnd, GWL_STYLE)); 163 | DWORD dwDTStyle = DT_SINGLELINE; 164 | 165 | //Test if centered horizontally or vertically 166 | if (dwStyle & SS_CENTER) dwDTStyle |= DT_CENTER; 167 | if (dwStyle & SS_RIGHT) dwDTStyle |= DT_RIGHT; 168 | if (dwStyle & SS_CENTERIMAGE) dwDTStyle |= DT_VCENTER; 169 | 170 | RECT rect; 171 | ::GetClientRect(hwnd, &rect); 172 | 173 | PAINTSTRUCT ps; 174 | HDC hdc = ::BeginPaint(hwnd, &ps); 175 | 176 | ::SetTextColor(hdc, _linkColor); 177 | 178 | ::SetBkColor(hdc, getCtrlBgColor(GetParent(hwnd))); ///*::GetSysColor(COLOR_3DFACE)*/); 179 | 180 | // Create an underline font 181 | if (_hfUnderlined == 0) 182 | { 183 | // Get the default GUI font 184 | LOGFONT lf; 185 | HFONT hf = (HFONT)::GetStockObject(DEFAULT_GUI_FONT); 186 | 187 | // Add UNDERLINE attribute 188 | GetObject(hf, sizeof lf, &lf); 189 | lf.lfUnderline = TRUE; 190 | 191 | // Create a new font 192 | _hfUnderlined = ::CreateFontIndirect(&lf); 193 | } 194 | 195 | HANDLE hOld = SelectObject(hdc, _hfUnderlined); 196 | 197 | // Draw the text! 198 | TCHAR szWinText[MAX_PATH]; 199 | ::GetWindowText(hwnd, szWinText, MAX_PATH); 200 | ::DrawText(hdc, szWinText, -1, &rect, dwDTStyle); 201 | 202 | ::SelectObject(hdc, hOld); 203 | 204 | ::EndPaint(hwnd, &ps); 205 | 206 | return 0; 207 | } 208 | 209 | case WM_SETTEXT: 210 | { 211 | LRESULT ret = ::CallWindowProc(_oldproc, hwnd, Message, wParam, lParam); 212 | ::InvalidateRect(hwnd, 0, 0); 213 | return ret; 214 | } 215 | // Provide a hand cursor when the mouse moves over us 216 | case WM_SETCURSOR: 217 | case WM_MOUSEMOVE: 218 | { 219 | if (_hCursor == 0) 220 | _hCursor = LoadCursor(NULL, IDC_HAND); 221 | 222 | SetCursor(_hCursor); 223 | return TRUE; 224 | } 225 | 226 | case WM_LBUTTONDOWN: 227 | _clicking = true; 228 | break; 229 | 230 | case WM_LBUTTONUP: 231 | if (_clicking) 232 | { 233 | _clicking = false; 234 | 235 | action(); 236 | } 237 | 238 | break; 239 | 240 | //Support using space to activate this object 241 | case WM_KEYDOWN: 242 | if (wParam == VK_SPACE) 243 | _clicking = true; 244 | break; 245 | 246 | case WM_KEYUP: 247 | if (wParam == VK_SPACE && _clicking) 248 | { 249 | _clicking = false; 250 | 251 | action(); 252 | } 253 | break; 254 | 255 | // A standard static control returns HTTRANSPARENT here, which 256 | // prevents us from receiving any mouse messages. So, return 257 | // HTCLIENT instead. 258 | case WM_NCHITTEST: 259 | return HTCLIENT; 260 | } 261 | return ::CallWindowProc(_oldproc, hwnd, Message, wParam, lParam); 262 | } 263 | -------------------------------------------------------------------------------- /external/npp/URLCtrl.h: -------------------------------------------------------------------------------- 1 | // This file is part of Notepad++ project 2 | // Copyright (C)2003 Don HO 3 | // 4 | // This program is free software; you can redistribute it and/or 5 | // modify it under the terms of the GNU General Public License 6 | // as published by the Free Software Foundation; either 7 | // version 2 of the License, or (at your option) any later version. 8 | // 9 | // Note that the GPL places important restrictions on "derived works", yet 10 | // it does not provide a detailed definition of that term. To avoid 11 | // misunderstandings, we consider an application to constitute a 12 | // "derivative work" for the purpose of this license if it does any of the 13 | // following: 14 | // 1. Integrates source code from Notepad++. 15 | // 2. Integrates/includes/aggregates Notepad++ into a proprietary executable 16 | // installer, such as those produced by InstallShield. 17 | // 3. Links to a library or executes a program that does any of the above. 18 | // 19 | // This program is distributed in the hope that it will be useful, 20 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | // GNU General Public License for more details. 23 | // 24 | // You should have received a copy of the GNU General Public License 25 | // along with this program; if not, write to the Free Software 26 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 27 | 28 | 29 | #pragma once 30 | 31 | #include "Window.h" 32 | #include 33 | 34 | class URLCtrl : public Window 35 | { 36 | public: 37 | URLCtrl() = default; 38 | 39 | void create(HWND itemHandle, const TCHAR* link, COLORREF linkColor = RGB(0, 0, 255)); 40 | void create(HWND itemHandle, int cmd, HWND msgDest = NULL); 41 | void destroy(); 42 | 43 | private: 44 | void action(); 45 | COLORREF getCtrlBgColor(HWND hWnd); 46 | 47 | protected: 48 | std::wstring _URL = TEXT(""); 49 | HFONT _hfUnderlined = nullptr; 50 | HCURSOR _hCursor = nullptr; 51 | HWND _msgDest = nullptr; 52 | unsigned long _cmdID = 0; 53 | 54 | WNDPROC _oldproc = nullptr; 55 | COLORREF _linkColor; 56 | COLORREF _visitedColor; 57 | bool _clicking = false; 58 | 59 | static LRESULT CALLBACK URLCtrlProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 60 | { 61 | return (reinterpret_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA)))->runProc(hwnd, Message, wParam, lParam); 62 | } 63 | 64 | LRESULT runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam); 65 | }; 66 | 67 | -------------------------------------------------------------------------------- /external/npp/Window.h: -------------------------------------------------------------------------------- 1 | //this file is part of notepad++ 2 | //Copyright (C)2003 Don HO 3 | // 4 | //This program is free software; you can redistribute it and/or 5 | //modify it under the terms of the GNU General Public License 6 | //as published by the Free Software Foundation; either 7 | //version 2 of the License, or (at your option) any later version. 8 | // 9 | //This program is distributed in the hope that it will be useful, 10 | //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | //GNU General Public License for more details. 13 | // 14 | //You should have received a copy of the GNU General Public License 15 | //along with this program; if not, write to the Free Software 16 | //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | 18 | #ifndef WINDOW_CONTROL_H 19 | #define WINDOW_CONTROL_H 20 | 21 | #include 22 | 23 | class Window 24 | { 25 | public: 26 | Window(): _hInst(NULL), _hParent(NULL), _hSelf(NULL){}; 27 | virtual ~Window() {}; 28 | 29 | virtual void init(HINSTANCE hInst, HWND parent) 30 | { 31 | _hInst = hInst; 32 | _hParent = parent; 33 | } 34 | 35 | virtual void destroy() = 0; 36 | 37 | virtual void display(bool toShow = true) const { 38 | ::ShowWindow(_hSelf, toShow?SW_SHOW:SW_HIDE); 39 | }; 40 | 41 | virtual void reSizeTo(RECT & rc) // should NEVER be const !!! 42 | { 43 | ::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE); 44 | redraw(); 45 | }; 46 | 47 | virtual void reSizeToWH(RECT & rc) // should NEVER be const !!! 48 | { 49 | ::MoveWindow(_hSelf, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); 50 | redraw(); 51 | }; 52 | 53 | virtual void redraw(bool forceUpdate = false) const { 54 | ::InvalidateRect(_hSelf, NULL, TRUE); 55 | if (forceUpdate) 56 | ::UpdateWindow(_hSelf); 57 | }; 58 | 59 | virtual void getClientRect(RECT & rc) const { 60 | ::GetClientRect(_hSelf, &rc); 61 | }; 62 | 63 | virtual void getWindowRect(RECT & rc) const { 64 | ::GetWindowRect(_hSelf, &rc); 65 | }; 66 | 67 | virtual int getWidth() const { 68 | RECT rc; 69 | ::GetClientRect(_hSelf, &rc); 70 | return (rc.right - rc.left); 71 | }; 72 | 73 | virtual int getHeight() const { 74 | RECT rc; 75 | ::GetClientRect(_hSelf, &rc); 76 | if (::IsWindowVisible(_hSelf) == TRUE) 77 | return (rc.bottom - rc.top); 78 | return 0; 79 | }; 80 | 81 | virtual bool isVisible() const { 82 | return (::IsWindowVisible(_hSelf)?true:false); 83 | }; 84 | 85 | HWND getHSelf() const { 86 | //assert(_hSelf); 87 | return _hSelf; 88 | }; 89 | 90 | HWND getHParent() const { 91 | return _hParent; 92 | }; 93 | 94 | void getFocus() const { 95 | ::SetFocus(_hSelf); 96 | }; 97 | 98 | HINSTANCE getHinst() const { 99 | if (!_hInst) 100 | { 101 | ::MessageBox(NULL, TEXT("_hInst == NULL"), TEXT("class Window"), MB_OK); 102 | throw int(1999); 103 | } 104 | return _hInst; 105 | }; 106 | protected: 107 | HINSTANCE _hInst; 108 | HWND _hParent; 109 | HWND _hSelf; 110 | }; 111 | 112 | #endif //WINDOW_CONTROL_H 113 | 114 | 115 | -------------------------------------------------------------------------------- /external/npp/dockingResource.h: -------------------------------------------------------------------------------- 1 | //this file is part of docking functionality for Notepad++ 2 | //Copyright (C)2006 Jens Lorenz 3 | // 4 | //This program is free software; you can redistribute it and/or 5 | //modify it under the terms of the GNU General Public License 6 | //as published by the Free Software Foundation; either 7 | //version 2 of the License, or (at your option) any later version. 8 | // 9 | //This program is distributed in the hope that it will be useful, 10 | //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | //GNU General Public License for more details. 13 | // 14 | //You should have received a copy of the GNU General Public License 15 | //along with this program; if not, write to the Free Software 16 | //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 | 18 | #ifndef DOCKING_RESOURCE_H 19 | #define DOCKING_RESOURCE_H 20 | 21 | #define IDD_PLUGIN_DLG 103 22 | #define IDC_EDIT1 1000 23 | 24 | 25 | #define IDB_CLOSE_DOWN 137 26 | #define IDB_CLOSE_UP 138 27 | #define IDD_CONTAINER_DLG 139 28 | 29 | #define IDC_TAB_CONT 1027 30 | #define IDC_CLIENT_TAB 1028 31 | #define IDC_BTN_CAPTION 1050 32 | 33 | #define DMM_MSG 0x5000 34 | #define DMM_CLOSE (DMM_MSG + 1) 35 | #define DMM_DOCK (DMM_MSG + 2) 36 | #define DMM_FLOAT (DMM_MSG + 3) 37 | #define DMM_DOCKALL (DMM_MSG + 4) 38 | #define DMM_FLOATALL (DMM_MSG + 5) 39 | #define DMM_MOVE (DMM_MSG + 6) 40 | #define DMM_UPDATEDISPINFO (DMM_MSG + 7) 41 | #define DMM_GETIMAGELIST (DMM_MSG + 8) 42 | #define DMM_GETICONPOS (DMM_MSG + 9) 43 | #define DMM_DROPDATA (DMM_MSG + 10) 44 | #define DMM_MOVE_SPLITTER (DMM_MSG + 11) 45 | #define DMM_CANCEL_MOVE (DMM_MSG + 12) 46 | #define DMM_LBUTTONUP (DMM_MSG + 13) 47 | 48 | #define DMN_FIRST 1050 49 | #define DMN_CLOSE (DMN_FIRST + 1) 50 | //nmhdr.code = DWORD(DMN_CLOSE, 0)); 51 | //nmhdr.hwndFrom = hwndNpp; 52 | //nmhdr.idFrom = ctrlIdNpp; 53 | 54 | #define DMN_DOCK (DMN_FIRST + 2) 55 | #define DMN_FLOAT (DMN_FIRST + 3) 56 | //nmhdr.code = DWORD(DMN_XXX, int newContainer); 57 | //nmhdr.hwndFrom = hwndNpp; 58 | //nmhdr.idFrom = ctrlIdNpp; 59 | 60 | 61 | 62 | #endif //DOCKING_RESOURCE_H 63 | 64 | -------------------------------------------------------------------------------- /src/NPPJSONViewer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32804.467 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NPPJSONViewer", "NppJsonViewer\NPPJSONViewer.vcxproj", "{1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UtilityLib", "UtilityLib\UtilityLib.vcxproj", "{171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTest", "..\tests\UnitTest\UnitTest.vcxproj", "{5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|ARM64 = Debug|ARM64 15 | Debug|Win32 = Debug|Win32 16 | Debug|x64 = Debug|x64 17 | Release|ARM64 = Release|ARM64 18 | Release|Win32 = Release|Win32 19 | Release|x64 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|ARM64.ActiveCfg = Debug|ARM64 23 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|ARM64.Build.0 = Debug|ARM64 24 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|Win32.ActiveCfg = Debug|Win32 25 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|Win32.Build.0 = Debug|Win32 26 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|x64.ActiveCfg = Debug|x64 27 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Debug|x64.Build.0 = Debug|x64 28 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|ARM64.ActiveCfg = Release|ARM64 29 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|ARM64.Build.0 = Release|ARM64 30 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|Win32.ActiveCfg = Release|Win32 31 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|Win32.Build.0 = Release|Win32 32 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|x64.ActiveCfg = Release|x64 33 | {1590D7CD-7D3A-4AB7-A355-EE02F7FB987D}.Release|x64.Build.0 = Release|x64 34 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|ARM64.ActiveCfg = Debug|ARM64 35 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|ARM64.Build.0 = Debug|ARM64 36 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|Win32.ActiveCfg = Debug|Win32 37 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|Win32.Build.0 = Debug|Win32 38 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|x64.ActiveCfg = Debug|x64 39 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Debug|x64.Build.0 = Debug|x64 40 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|ARM64.ActiveCfg = Release|ARM64 41 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|ARM64.Build.0 = Release|ARM64 42 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|Win32.ActiveCfg = Release|Win32 43 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|Win32.Build.0 = Release|Win32 44 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|x64.ActiveCfg = Release|x64 45 | {171CAFC6-E679-4B81-BF5B-049AC0FAB4F8}.Release|x64.Build.0 = Release|x64 46 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|ARM64.ActiveCfg = Debug|ARM64 47 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|ARM64.Build.0 = Debug|ARM64 48 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|Win32.ActiveCfg = Debug|Win32 49 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|Win32.Build.0 = Debug|Win32 50 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|x64.ActiveCfg = Debug|x64 51 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Debug|x64.Build.0 = Debug|x64 52 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|ARM64.ActiveCfg = Release|ARM64 53 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|ARM64.Build.0 = Release|ARM64 54 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|Win32.ActiveCfg = Release|Win32 55 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|Win32.Build.0 = Release|Win32 56 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|x64.ActiveCfg = Release|x64 57 | {5A15FD53-E7C1-4F10-85FA-B7C3BB5D4D64}.Release|x64.Build.0 = Release|x64 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {66FFEF3A-7122-49C7-8CE4-4704DFB30594} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /src/NppJsonViewer/AboutDlg.cpp: -------------------------------------------------------------------------------- 1 | #include "AboutDlg.h" 2 | #include "resource.h" 3 | #include "Utility.h" 4 | #include "StringHelper.h" 5 | #include "Define.h" 6 | #include 7 | #include 8 | 9 | 10 | AboutDlg::AboutDlg(HINSTANCE hInstance, HWND hParent, int nCmdId) 11 | : m_nCmdId(nCmdId) 12 | , StaticDialog() 13 | { 14 | init(hInstance, hParent); 15 | } 16 | 17 | 18 | bool AboutDlg::ShowDlg(bool bShow) 19 | { 20 | bool bShouldShow = bShow && !isVisible(); 21 | if (bShouldShow) 22 | { 23 | if (!isCreated()) 24 | create(IDD_ABOUTDLG); 25 | 26 | // Adjust the position of AboutBox 27 | goToCenter(); 28 | } 29 | else 30 | { 31 | SendMessage(_hSelf, WM_COMMAND, IDCANCEL, NULL); 32 | } 33 | return bShouldShow; 34 | } 35 | 36 | 37 | INT_PTR AboutDlg::run_dlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 38 | { 39 | AboutDlg* pSelf = nullptr; 40 | switch (uMsg) 41 | { 42 | case WM_INITDIALOG: 43 | { 44 | ::SetWindowLongPtr(_hSelf, DWLP_USER, lParam); 45 | 46 | pSelf = reinterpret_cast(static_cast(::GetWindowLongPtr(_hSelf, DWLP_USER))); 47 | if (pSelf) 48 | { 49 | pSelf->SetVersion(_hSelf); 50 | } 51 | SetFocus(GetDlgItem(_hSelf, IDOK)); 52 | 53 | // Set links 54 | std::wstring urlIssue(TEXT("__URL__")); 55 | std::wstring urlRepo = urlIssue; 56 | 57 | urlIssue = StringHelper::ReplaceAll(urlIssue, TEXT("__URL__"), URL_REPORT_ISSUE); 58 | urlRepo = StringHelper::ReplaceAll(urlRepo, TEXT("__URL__"), URL_SOURCE_CODE); 59 | 60 | SetWindowText(::GetDlgItem(_hSelf, IDC_WEB_ISSUE), urlIssue.c_str()); 61 | SetWindowText(::GetDlgItem(_hSelf, IDC_WEB_SOURCE), urlRepo.c_str()); 62 | 63 | return TRUE; 64 | } 65 | 66 | case WM_NOTIFY: 67 | { 68 | switch (reinterpret_cast(lParam)->code) 69 | { 70 | case NM_CLICK: 71 | case NM_RETURN: 72 | { 73 | auto nmLink = reinterpret_cast(lParam); 74 | LITEM item = nmLink->item; 75 | 76 | ShellExecute(nullptr, L"open", item.szUrl, nullptr, nullptr, SW_SHOW); 77 | return TRUE; 78 | } 79 | } 80 | return FALSE; 81 | } 82 | 83 | case WM_COMMAND: 84 | { 85 | pSelf = reinterpret_cast(static_cast(::GetWindowLongPtr(_hSelf, DWLP_USER))); 86 | switch (LOWORD(wParam)) 87 | { 88 | case IDCANCEL: // Close this dialog when clicking to close button 89 | case IDOK: 90 | if (pSelf) 91 | ::SendMessage(pSelf->_hParent, NPPM_SETMENUITEMCHECK, static_cast(pSelf->m_nCmdId), false); 92 | EndDialog(_hSelf, wParam); 93 | _hSelf = nullptr; 94 | return TRUE; 95 | } 96 | } 97 | } 98 | return FALSE; 99 | } 100 | 101 | void AboutDlg::SetVersion(HWND hWnd) 102 | { 103 | std::wstring version; 104 | 105 | // Get module path 106 | wchar_t moduleFileName[MAX_PATH + 1] = {}; 107 | ::GetModuleFileName(static_cast(getHinst()), moduleFileName, _MAX_PATH); 108 | 109 | version = CUtility::GetVersion(moduleFileName); 110 | if (!version.empty()) 111 | { 112 | std::wstring text(PLUGIN_NAME); 113 | text += TEXT(" ("); 114 | text += STR_VERSION; 115 | text += version; 116 | text += TEXT(") "); 117 | ::SetWindowText(::GetDlgItem(hWnd, IDC_GB_TITLE), text.c_str()); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/NppJsonViewer/AboutDlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "StaticDialog.h" 3 | 4 | class AboutDlg : public StaticDialog 5 | { 6 | public: 7 | AboutDlg(HINSTANCE hInstance, HWND hParent, int nCmdId); 8 | ~AboutDlg() = default; 9 | 10 | bool ShowDlg(bool bShow); 11 | 12 | protected: 13 | virtual INT_PTR CALLBACK run_dlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 14 | 15 | void SetVersion(HWND hWnd); 16 | 17 | private: 18 | int m_nCmdId = -1; 19 | }; 20 | -------------------------------------------------------------------------------- /src/NppJsonViewer/Define.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PluginInterface.h" 3 | 4 | // Define the number of plugin commands here 5 | enum class CallBackID : int 6 | { 7 | SHOW_DOC_PANEL = 0, 8 | FORMAT, 9 | COMPRESS, 10 | SORT_BY_KEY, 11 | SEP_1, 12 | SETTING, 13 | ABOUT 14 | }; 15 | constexpr const int nTotalCommandCount = static_cast(CallBackID::ABOUT) + 1; 16 | 17 | // Define plugin name here 18 | const TCHAR PLUGIN_NAME[] = TEXT("JSON Viewer"); 19 | const TCHAR PLUGIN_CONFIG[] = TEXT("JSONViewer.ini"); 20 | 21 | // Text which can be considered for localization 22 | const TCHAR TITLE_JSON_PANEL[] = TEXT("JSON Viewer"); 23 | const TCHAR MENU_SHOW_JSON_PANEL[] = TEXT("Show &JSON Viewer"); 24 | const TCHAR MENU_FORMAT_JSON[] = TEXT("&Format JSON"); 25 | const TCHAR MENU_COMPRESS_JSON[] = TEXT("&Compress JSON"); 26 | const TCHAR MENU_SORT_BY_KEY[] = TEXT("Sort by &key (ascending)"); 27 | const TCHAR MENU_SETTING[] = TEXT("&Settings"); 28 | const TCHAR MENU_ABOUT[] = TEXT("&About"); 29 | const TCHAR MENU_SEPERATOR[] = TEXT("-SEPARATOR-"); 30 | 31 | const TCHAR TOOLTIP_REFRESH[] = TEXT("Refresh JSON tree"); 32 | const TCHAR TOOLTIP_VALIDATE[] = TEXT("Validate JSON to detect any errors"); 33 | const TCHAR TOOLTIP_FORMAT[] = TEXT("Format JSON to beautify it"); 34 | const TCHAR TOOLTIP_SEARCH[] = TEXT("Search in JSON"); 35 | 36 | const TCHAR URL_SOURCE_CODE[] = TEXT("https://github.com/NPP-JSONViewer/JSON-Viewer"); 37 | const TCHAR URL_REPORT_ISSUE[] = TEXT("https://github.com/NPP-JSONViewer/JSON-Viewer/issues/new"); 38 | 39 | const TCHAR JSON_ROOT[] = TEXT("JSON"); 40 | 41 | const TCHAR JSON_ERROR_TITLE[] = TEXT("JSON Viewer: Error"); 42 | const TCHAR JSON_WARNING_TITLE[] = TEXT("JSON Viewer: Warning"); 43 | const TCHAR JSON_INFO_TITLE[] = TEXT("JSON Viewer: Information"); 44 | 45 | const TCHAR JSON_ERR_PARSE[] = TEXT("Unable to parse JSON. Please ensure a valid JSON string is selected."); 46 | const TCHAR JSON_ERR_VALIDATE[] = TEXT("An error occurred while parsing the JSON. Check the current selection for the potential issue."); 47 | const TCHAR JSON_ERR_VALIDATE_SUCCESS[] = TEXT("The JSON appears valid. No errors were found during validation."); 48 | const TCHAR JSON_ERR_SAVE_SETTING[] = TEXT("Could not save the settings. Please try again."); 49 | const TCHAR JSON_ERR_MULTI_SELECTION[] = TEXT("JSON-Viewer does not currently support multiple selections."); 50 | 51 | const TCHAR STR_VERSION[] = TEXT("Version: "); 52 | const TCHAR STR_COPY[] = TEXT("Copy"); 53 | const TCHAR STR_COPYNAME[] = TEXT("Copy name"); 54 | const TCHAR STR_COPYVALUE[] = TEXT("Copy value"); 55 | const TCHAR STR_COPYPATH[] = TEXT("Copy path"); 56 | const TCHAR STR_EXPANDALL[] = TEXT("Expand all"); 57 | const TCHAR STR_COLLAPSEALL[] = TEXT("Collapse all"); 58 | 59 | const TCHAR STR_INI_FORMATTING_SEC[] = TEXT("Formatting"); 60 | const TCHAR STR_INI_FORMATTING_EOL[] = TEXT("EOL"); 61 | const TCHAR STR_INI_FORMATTING_LINE[] = TEXT("LINE_FORMATTING"); 62 | const TCHAR STR_INI_FORMATTING_INDENT[] = TEXT("INDENTATION"); 63 | const TCHAR STR_INI_FORMATTING_INDENTCOUNT[] = TEXT("INDENTATION_COUNT"); 64 | 65 | const TCHAR STR_INI_OTHER_SEC[] = TEXT("Others"); 66 | const TCHAR STR_INI_OTHER_FOLLOW_TAB[] = TEXT("FOLLOW_TAB"); 67 | const TCHAR STR_INI_OTHER_AUTO_FORMAT[] = TEXT("AUTO_FORMAT"); 68 | const TCHAR STR_INI_OTHER_USE_HIGHLIGHT[] = TEXT("USE_JSON_HIGHLIGHT"); 69 | const TCHAR STR_INI_OTHER_IGNORE_COMMENT[] = TEXT("IGNORE_COMMENT"); 70 | const TCHAR STR_INI_OTHER_IGNORE_COMMA[] = TEXT("IGNORE_TRAILLING_COMMA"); 71 | const TCHAR STR_INI_OTHER_REPLACE_UNDEFINED[] = TEXT("REPLACE_VALUE_UNDEFINED"); 72 | 73 | const TCHAR STR_SRCH_SEARCHING[] = TEXT("Searching for: "); 74 | const TCHAR STR_SRCH_NOTFOUND[] = TEXT("Not found: "); 75 | const TCHAR STR_SRCH_NOMOREFOUND[] = TEXT("No more found: "); 76 | 77 | enum class LineEnding 78 | { 79 | AUTO, 80 | WINDOWS, 81 | UNIX, 82 | MAC 83 | }; 84 | 85 | enum class LineFormat 86 | { 87 | DEFAULT, 88 | SINGLELINE 89 | }; 90 | 91 | enum class IndentStyle 92 | { 93 | AUTO, 94 | TAB, 95 | SPACE 96 | }; 97 | 98 | struct Indent 99 | { 100 | unsigned len = 4; 101 | IndentStyle style = IndentStyle::AUTO; 102 | }; 103 | 104 | struct ParseOptions 105 | { 106 | bool bIgnoreComment = true; 107 | bool bIgnoreTrailingComma = true; 108 | bool bReplaceUndefined = false; 109 | }; 110 | 111 | struct Setting 112 | { 113 | LineEnding lineEnding = LineEnding::AUTO; 114 | LineFormat lineFormat = LineFormat::DEFAULT; 115 | Indent indent {}; 116 | bool bFollowCurrentTab = false; 117 | bool bAutoFormat = false; 118 | bool bUseJsonHighlight = true; 119 | ParseOptions parseOptions {}; 120 | }; 121 | -------------------------------------------------------------------------------- /src/NppJsonViewer/JsonHandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "JsonHandler.h" 5 | 6 | namespace rj = rapidjson; 7 | 8 | 9 | JsonHandler::JsonHandler(const ParseOptions& options) 10 | : m_parseOptions(options) 11 | { 12 | } 13 | 14 | auto JsonHandler::GetCompressedJson(const std::string& jsonText) -> const Result 15 | { 16 | rj::StringBuffer sb; 17 | rj::Writer, rj::UTF8<>, rj::CrtAllocator, rj::kWriteNanAndInfFlag> handler(sb); 18 | 19 | return ParseJson(jsonText, sb, handler); 20 | } 21 | 22 | auto JsonHandler::FormatJson(const std::string& jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result 23 | { 24 | rj::StringBuffer sb; 25 | rj::PrettyWriter, rj::UTF8<>, rj::CrtAllocator, rj::kWriteNanAndInfFlag> handler(sb); 26 | handler.SetLineEnding(le); 27 | handler.SetFormatOptions(lf); 28 | handler.SetIndent(indentChar, indentLen); 29 | 30 | return ParseJson(jsonText, sb, handler); 31 | } 32 | 33 | auto JsonHandler::SortJsonByKey(const std::string& jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result 34 | { 35 | auto res = ValidateJson(jsonText); 36 | if (res.success) 37 | { 38 | // Sort the JSON string 39 | auto sorted = SortJsonText(jsonText); 40 | res = FormatJson(sorted, le, lf, indentChar, indentLen); 41 | } 42 | return res; 43 | } 44 | 45 | auto JsonHandler::ValidateJson(const std::string& jsonText) -> const Result 46 | { 47 | rj::StringBuffer sb; 48 | rj::Writer, rj::UTF8<>, rj::CrtAllocator, rj::kWriteNanAndInfFlag> handler(sb); 49 | 50 | return ParseJson(jsonText, sb, handler); 51 | } 52 | 53 | void JsonHandler::SortJsonObject(rj::Value& jsonObject, rj::Document::AllocatorType& allocator) const 54 | { 55 | if (!jsonObject.IsObject()) 56 | { 57 | return; 58 | } 59 | 60 | std::vector keys; 61 | 62 | // Collect keys 63 | for (rj::Value::ConstMemberIterator itr = jsonObject.MemberBegin(); itr != jsonObject.MemberEnd(); ++itr) 64 | { 65 | keys.push_back(itr->name.GetString()); 66 | } 67 | 68 | // Sort keys alphabetically 69 | std::sort(keys.begin(), keys.end()); 70 | 71 | // Create a new sorted object 72 | rj::Value sortedObject(rj::kObjectType); 73 | 74 | // Add members to the sorted object in sorted order 75 | for (const auto& key : keys) 76 | { 77 | rj::Value name(key.c_str(), allocator); // Create key as a RapidJSON value 78 | rj::Value& value = jsonObject[key.c_str()]; // Get corresponding value 79 | sortedObject.AddMember(name, value, allocator); // Add key-value pair to sorted object 80 | } 81 | 82 | // Replace the original object with the sorted one 83 | jsonObject = std::move(sortedObject); 84 | } 85 | 86 | void JsonHandler::SortJsonRecursively(rj::Value& jsonValue, rj::Document::AllocatorType& allocator) const 87 | { 88 | if (jsonValue.IsObject()) 89 | { 90 | SortJsonObject(jsonValue, allocator); 91 | 92 | // Recursively sort any nested objects 93 | for (rj::Value::MemberIterator itr = jsonValue.MemberBegin(); itr != jsonValue.MemberEnd(); ++itr) 94 | { 95 | SortJsonRecursively(itr->value, allocator); 96 | } 97 | } 98 | else if (jsonValue.IsArray()) 99 | { 100 | // If it's an array, sort each element (in case of nested objects) 101 | for (rj::SizeType i = 0; i < jsonValue.Size(); i++) 102 | { 103 | SortJsonRecursively(jsonValue[i], allocator); 104 | } 105 | } 106 | } 107 | 108 | auto JsonHandler::SortJsonText(const std::string& jsonString) const -> std::string 109 | { 110 | rj::Document document; 111 | 112 | // TODO: Find some better way 113 | constexpr auto flgBase_comment = flgBaseReader | rj::kParseCommentsFlag; 114 | constexpr auto flgBase_comma = flgBaseReader | rj::kParseTrailingCommasFlag; 115 | constexpr auto flgBase_Both = flgBase_comma | flgBase_comment; 116 | 117 | if (m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma) 118 | { 119 | if (document.Parse(jsonString.c_str()).HasParseError()) 120 | { 121 | return ""; 122 | } 123 | } 124 | 125 | else if (!m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma) 126 | { 127 | if (document.Parse(jsonString.c_str()).HasParseError()) 128 | { 129 | return ""; 130 | } 131 | } 132 | 133 | else if (m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma) 134 | { 135 | if (document.Parse(jsonString.c_str()).HasParseError()) 136 | { 137 | return ""; 138 | } 139 | } 140 | 141 | else if (!m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma) 142 | { 143 | if (document.Parse(jsonString.c_str()).HasParseError()) 144 | { 145 | return ""; 146 | } 147 | } 148 | 149 | SortJsonRecursively(document, document.GetAllocator()); 150 | 151 | rj::StringBuffer buffer; 152 | rj::Writer writer(buffer); 153 | document.Accept(writer); 154 | 155 | return buffer.GetString(); 156 | } 157 | -------------------------------------------------------------------------------- /src/NppJsonViewer/JsonHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Define.h" 13 | #include "TrackingStream.h" 14 | 15 | namespace rj = rapidjson; 16 | 17 | struct Result 18 | { 19 | bool success = false; 20 | int error_pos = -1; 21 | int error_code = -1; 22 | std::string error_str; 23 | std::string response; 24 | }; 25 | 26 | using LE = rj::LineEndingOption; 27 | using LF = rj::PrettyFormatOptions; 28 | 29 | constexpr auto flgBaseReader = rj::kParseEscapedApostropheFlag | rj::kParseNanAndInfFlag | rj::kParseNumbersAsStringsFlag; 30 | constexpr auto flgBaseWriter = rj::kParseEscapedApostropheFlag | rj::kParseNanAndInfFlag | rj::kParseFullPrecisionFlag; 31 | 32 | class JsonHandler 33 | { 34 | ParseOptions m_parseOptions {}; 35 | 36 | public: 37 | explicit JsonHandler(const ParseOptions& options); 38 | ~JsonHandler() = default; 39 | 40 | auto GetCompressedJson(const std::string& jsonText) -> const Result; 41 | auto FormatJson(const std::string& jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result; 42 | auto SortJsonByKey(const std::string& jsonText, LE le, LF lf, char indentChar, unsigned indentLen) -> const Result; 43 | auto ValidateJson(const std::string& jsonText) -> const Result; 44 | 45 | template 46 | auto ParseJson(const std::string& jsonText, rj::StringBuffer& sb, Handler& handler, TrackingStreamSharedPtr pTS = nullptr) -> const Result; 47 | 48 | private: 49 | void SortJsonObject(rj::Value& jsonObject, rj::Document::AllocatorType& allocator) const; 50 | void SortJsonRecursively(rj::Value& jsonValue, rj::Document::AllocatorType& allocator) const; 51 | auto SortJsonText(const std::string& jsonString) const -> std::string; 52 | }; 53 | 54 | template 55 | inline auto JsonHandler::ParseJson(const std::string& jsonText, rj::StringBuffer& sb, Handler& handler, TrackingStreamSharedPtr pTS) -> const Result 56 | { 57 | Result retVal {}; 58 | 59 | bool success = false; 60 | rj::Reader reader; 61 | 62 | std::shared_ptr pSS = nullptr; 63 | if (!pTS) 64 | { 65 | pSS = std::make_shared(jsonText.c_str()); 66 | } 67 | 68 | // TODO: Find some better way 69 | constexpr auto flgBase_comment = flgBase | rj::kParseCommentsFlag; 70 | constexpr auto flgBase_comma = flgBase | rj::kParseTrailingCommasFlag; 71 | constexpr auto flgBase_Both = flgBase_comma | flgBase_comment; 72 | 73 | if (m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma) 74 | { 75 | success = pTS ? reader.Parse(*pTS, handler) && sb.GetString() : reader.Parse(*pSS, handler) && sb.GetString(); 76 | } 77 | 78 | else if (!m_parseOptions.bIgnoreComment && m_parseOptions.bIgnoreTrailingComma) 79 | { 80 | success = pTS ? reader.Parse(*pTS, handler) && sb.GetString() : reader.Parse(*pSS, handler) && sb.GetString(); 81 | } 82 | 83 | else if (m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma) 84 | { 85 | success = pTS ? reader.Parse(*pTS, handler) && sb.GetString() : reader.Parse(*pSS, handler) && sb.GetString(); 86 | } 87 | 88 | else if (!m_parseOptions.bIgnoreComment && !m_parseOptions.bIgnoreTrailingComma) 89 | { 90 | success = pTS ? reader.Parse(*pTS, handler) && sb.GetString() : reader.Parse(*pSS, handler) && sb.GetString(); 91 | } 92 | 93 | if (success) 94 | { 95 | retVal.success = true; 96 | retVal.response = sb.GetString(); 97 | retVal.error_code = retVal.error_pos = -1; 98 | retVal.error_str.clear(); 99 | } 100 | else 101 | { 102 | retVal.success = false; 103 | retVal.error_str = rj::GetParseError_En(reader.GetParseErrorCode()); 104 | retVal.error_pos = static_cast(reader.GetErrorOffset()); 105 | retVal.error_code = reader.GetParseErrorCode(); 106 | retVal.response.clear(); 107 | } 108 | 109 | return retVal; 110 | } 111 | -------------------------------------------------------------------------------- /src/NppJsonViewer/JsonNode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | enum class JsonNodeType : short 5 | { 6 | UNKNOWN, 7 | STRING, 8 | NUMBER, 9 | BOOL, 10 | ARRAY, 11 | OBJECT, 12 | }; 13 | 14 | struct Position 15 | { 16 | size_t nLine {}; 17 | size_t nColumn {}; 18 | size_t nKeyLength {}; 19 | 20 | void clear() 21 | { 22 | nLine = nColumn = nKeyLength = 0; 23 | } 24 | }; 25 | 26 | struct JsonKey 27 | { 28 | Position pos {}; 29 | std::string strKey; 30 | 31 | void clear() 32 | { 33 | pos.clear(); 34 | strKey.clear(); 35 | } 36 | }; 37 | 38 | struct JsonNode 39 | { 40 | JsonKey key; 41 | std::string value; 42 | JsonNodeType type = JsonNodeType::UNKNOWN; 43 | }; 44 | -------------------------------------------------------------------------------- /src/NppJsonViewer/JsonViewDlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DockingDlgInterface.h" 9 | #include "PluginInterface.h" 10 | #include "resource.h" 11 | #include "TreeViewCtrl.h" 12 | #include "SliderCtrl.h" 13 | #include "ScintillaEditor.h" 14 | #include "JsonHandler.h" 15 | #include "JsonNode.h" 16 | #include "TreeHandler.h" 17 | 18 | 19 | class JsonViewDlg 20 | : public DockingDlgInterface 21 | , public TreeHandler 22 | { 23 | enum class eButton 24 | { 25 | eRefresh, 26 | eValidate, 27 | eFormat, 28 | eSearch 29 | }; 30 | 31 | enum class eMethod 32 | { 33 | FormatJson, 34 | GetCompressedJson, 35 | ParseJson, 36 | ValidateJson, 37 | SortJsonByKey 38 | }; 39 | 40 | public: 41 | JsonViewDlg(HINSTANCE hInstance, const NppData& nppData, const bool& isReady, int nCmdId, std::shared_ptr& pSetting); 42 | virtual ~JsonViewDlg(); 43 | 44 | void ShowDlg(bool bShow); 45 | void FormatJson(); 46 | void CompressJson(); 47 | void SortJsonByKey(); 48 | void HandleTabActivated(); 49 | void UpdateTitle(); 50 | 51 | HTREEITEM InsertToTree(HTREEITEM parent, const std::string& text) override; 52 | HTREEITEM InsertToTree(HTREEITEM parent, const std::string& text, const Position& pos) override; 53 | void AppendNodeCount(HTREEITEM node, unsigned elementCount, bool bArray) override; 54 | 55 | private: 56 | void DrawJsonTree(); 57 | void ReDrawJsonTree(bool bForce = false); 58 | void HighlightAsJson(bool bForcefully = false) const; 59 | auto PopulateTreeUsingSax(HTREEITEM tree_root, const std::string& jsonText) -> std::optional; 60 | 61 | void ValidateJson(); 62 | 63 | void UpdateNodePath(HTREEITEM htiNode) const; 64 | void GoToLine(size_t nLineToGo) const; 65 | void GoToPosition(size_t nLineToGo, size_t nPos, size_t nLen) const; 66 | 67 | void SearchInTree(); 68 | 69 | auto GetTitleFileName() const -> std::wstring; 70 | void PrepareButtons(); 71 | void SetIconAndTooltip(eButton ctrlType, const std::wstring& toolTip); 72 | 73 | void AdjustDocPanelSize(int nWidth, int nHeight); 74 | 75 | // Context menu related functions 76 | void ShowContextMenu(int x, int y); 77 | void ShowContextMenu(HTREEITEM htiNode, LPPOINT lppScreen); 78 | void ContextMenuExpand(bool bExpand); 79 | 80 | auto CopyName() const -> std::wstring; 81 | auto CopyKey() const -> std::wstring; 82 | auto CopyValue() const -> std::wstring; 83 | auto CopyPath() const -> std::wstring; 84 | 85 | int ShowMessage(const std::wstring& title, const std::wstring& msg, int flag, bool bDonotShow = false); 86 | void ReportError(const Result& result); 87 | 88 | void ToggleMenuItemState(bool bVisible); 89 | 90 | void ShowControls(const std::vector& ids, bool show); 91 | void EnableControls(const std::vector& ids, bool enable); 92 | 93 | auto GetZoomLevel() const -> int; 94 | void SetZoomLevel(int pos) const; 95 | void SetTreeViewZoom(double dwZoomFactor) const; 96 | void UpdateUIOnZoom(int zoomPercentage) const; 97 | void HandleZoomOnScroll(WPARAM wParam) const; 98 | 99 | void HandleTreeEvents(LPARAM lParam) const; 100 | 101 | auto GetFormatSetting() const -> std::tuple; 102 | 103 | bool CheckForTokenUndefined(eMethod method, std::string selectedText, Result& res, HTREEITEM tree_root); 104 | 105 | bool IsMultiSelection(const ScintillaData& scintillaData) const; 106 | auto IsSelectionValidJson(const ScintillaData& scintillaData) const -> std::optional; 107 | void ProcessScintillaData(const ScintillaData& scintillaData, std::string& text, ScintillaCode& code) const; 108 | 109 | protected: 110 | virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) override; 111 | 112 | private: 113 | int m_nDlgId = -1; 114 | NppData m_NppData = {}; 115 | HICON m_hBtnIcon[4] = {}; 116 | const bool& m_IsNppReady; 117 | 118 | // To handle doc panel resizing 119 | LONG m_lfDeltaWidth = 0; 120 | LONG m_lfDeltaHeight = 0; 121 | LONG m_lfInitialClientWidth = 0; 122 | LONG m_lfInitialClientHeight = 0; 123 | RECT m_rcInitialWindowRect = {}; 124 | 125 | std::unique_ptr m_pCurrFileName; 126 | std::unique_ptr m_pEditor = nullptr; 127 | std::unique_ptr m_pTreeView = nullptr; 128 | std::unique_ptr m_pTreeViewZoom = nullptr; 129 | std::shared_ptr m_pSetting = nullptr; 130 | }; 131 | -------------------------------------------------------------------------------- /src/NppJsonViewer/NPPJSONViewer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx 15 | 16 | 17 | {3a31aa3c-9f15-4a33-bf5c-b9153d1e757a} 18 | 19 | 20 | {5b794f9e-baf6-4faf-98a6-741b4918d410} 21 | 22 | 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | ThirdParty\npp 59 | 60 | 61 | Header Files 62 | 63 | 64 | Source Files 65 | 66 | 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | ThirdParty\npp 112 | 113 | 114 | ThirdParty\npp 115 | 116 | 117 | ThirdParty\npp 118 | 119 | 120 | ThirdParty\npp 121 | 122 | 123 | ThirdParty\npp 124 | 125 | 126 | ThirdParty\npp 127 | 128 | 129 | ThirdParty\npp 130 | 131 | 132 | ThirdParty\npp 133 | 134 | 135 | ThirdParty\npp 136 | 137 | 138 | Header Files 139 | 140 | 141 | Header Files 142 | 143 | 144 | 145 | 146 | Resource Files 147 | 148 | 149 | 150 | 151 | Resource Files 152 | 153 | 154 | Resource Files 155 | 156 | 157 | Resource Files 158 | 159 | 160 | Resource Files 161 | 162 | 163 | Resource Files 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/NppJsonViewer/NppJsonPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include "NppJsonPlugin.h" 2 | #include "resource.h" 3 | #include "Profile.h" 4 | #include 5 | 6 | NppJsonPlugin* NppJsonPlugin::Callback::m_pNppJsonPlugin = nullptr; 7 | 8 | NppJsonPlugin::NppJsonPlugin() 9 | : m_shortcutCommands(nTotalCommandCount) 10 | { 11 | NppJsonPlugin::Callback::m_pNppJsonPlugin = this; 12 | } 13 | 14 | void NppJsonPlugin::PluginInit(HMODULE hModule) 15 | { 16 | m_hModule = hModule; 17 | } 18 | 19 | void NppJsonPlugin::PluginCleanup() {} 20 | 21 | void NppJsonPlugin::SetInfo(const NppData& nppData) 22 | { 23 | m_NppData = nppData; 24 | InitCommandMenu(); 25 | InitToolbarIcon(); 26 | InitConfigPath(); 27 | } 28 | 29 | const TCHAR* NppJsonPlugin::GetPluginName() const 30 | { 31 | return PLUGIN_NAME; 32 | } 33 | 34 | FuncItem* NppJsonPlugin::GetFuncsArray(int* nbF) 35 | { 36 | *nbF = nTotalCommandCount; 37 | return m_shortcutCommands.GetFuncItem(); 38 | } 39 | 40 | void NppJsonPlugin::ProcessNotification(const SCNotification* notifyCode) 41 | { 42 | switch (notifyCode->nmhdr.code) 43 | { 44 | case NPPN_TBMODIFICATION: 45 | { 46 | SetMenuIcon(); 47 | break; 48 | } 49 | 50 | case NPPN_SHUTDOWN: 51 | { 52 | PluginCleanup(); 53 | break; 54 | } 55 | 56 | case NPPN_BUFFERACTIVATED: 57 | { 58 | if (m_pJsonViewDlg && m_bNppReady && !m_bAboutToClose) 59 | { 60 | m_pJsonViewDlg->HandleTabActivated(); 61 | } 62 | break; 63 | } 64 | 65 | case NPPN_BEFORESHUTDOWN: 66 | { 67 | m_bAboutToClose = true; 68 | break; 69 | } 70 | 71 | case NPPN_READY: 72 | { 73 | // This is workaround where dialog does not show tree on launch 74 | if (m_pJsonViewDlg && m_pJsonViewDlg->isVisible() && !m_bAboutToClose) 75 | { 76 | ::SendMessage(m_pJsonViewDlg->getHSelf(), WM_COMMAND, IDC_BTN_REFRESH, 0); 77 | m_pJsonViewDlg->UpdateTitle(); 78 | } 79 | m_bNppReady = true; 80 | break; 81 | } 82 | 83 | default: 84 | return; 85 | } 86 | } 87 | 88 | LRESULT NppJsonPlugin::MessageProc(UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) 89 | { 90 | return TRUE; 91 | } 92 | 93 | BOOL NppJsonPlugin::IsUnicode() 94 | { 95 | #ifdef _UNICODE 96 | return TRUE; 97 | #else 98 | return FALSE; 99 | #endif // _UNICODE 100 | } 101 | 102 | void NppJsonPlugin::SetMenuIcon() 103 | { 104 | if (m_hMenuIcon.hToolbarIcon || m_hMenuIcon.hToolbarBmp) 105 | { 106 | toolbarIcons tbIcon {}; 107 | tbIcon.hToolbarBmp = m_hMenuIcon.hToolbarBmp; 108 | tbIcon.hToolbarIcon = m_hMenuIcon.hToolbarIcon; 109 | auto nCommandId = m_shortcutCommands.GetCommandID(CallBackID::SHOW_DOC_PANEL); 110 | ::SendMessage(m_NppData._nppHandle, NPPM_ADDTOOLBARICON, reinterpret_cast(nCommandId), reinterpret_cast(&tbIcon)); 111 | } 112 | } 113 | 114 | void NppJsonPlugin::InitCommandMenu() 115 | { 116 | m_shortcutCommands.SetShortCut(CallBackID::SHOW_DOC_PANEL, {true, true, true, 'J'}); 117 | m_shortcutCommands.SetCommand(CallBackID::SHOW_DOC_PANEL, MENU_SHOW_JSON_PANEL, Callback::ShowJsonDlg, false); 118 | 119 | m_shortcutCommands.SetShortCut(CallBackID::FORMAT, {true, true, true, 'M'}); 120 | m_shortcutCommands.SetCommand(CallBackID::FORMAT, MENU_FORMAT_JSON, Callback::FormatJson, false); 121 | 122 | m_shortcutCommands.SetShortCut(CallBackID::COMPRESS, {true, true, true, 'C'}); 123 | m_shortcutCommands.SetCommand(CallBackID::COMPRESS, MENU_COMPRESS_JSON, Callback::CompressJson, false); 124 | 125 | m_shortcutCommands.SetShortCut(CallBackID::SORT_BY_KEY, {true, true, true, 'K'}); 126 | m_shortcutCommands.SetCommand(CallBackID::SORT_BY_KEY, MENU_SORT_BY_KEY, Callback::SortJsonByKey, false); 127 | 128 | m_shortcutCommands.SetCommand(CallBackID::SEP_1, MENU_SEPERATOR, NULL, true); 129 | 130 | m_shortcutCommands.SetCommand(CallBackID::SETTING, MENU_SETTING, Callback::OpenSettingDlg, false); 131 | m_shortcutCommands.SetCommand(CallBackID::ABOUT, MENU_ABOUT, Callback::ShowAboutDlg, false); 132 | } 133 | 134 | void NppJsonPlugin::InitToolbarIcon() 135 | { 136 | auto dpi = GetDeviceCaps(GetWindowDC(m_NppData._nppHandle), LOGPIXELSX); 137 | int size = 16 * dpi / 96; 138 | m_hMenuIcon.hToolbarIcon = reinterpret_cast(::LoadImage(static_cast(m_hModule), MAKEINTRESOURCE(IDI_ICON_TOOLBAR), IMAGE_ICON, size, size, 0)); 139 | ICONINFO iconInfo; 140 | GetIconInfo(m_hMenuIcon.hToolbarIcon, &iconInfo); 141 | m_hMenuIcon.hToolbarBmp = iconInfo.hbmColor; 142 | } 143 | 144 | void NppJsonPlugin::InitConfigPath() 145 | { 146 | // Get config dir path 147 | WCHAR szPath[_MAX_PATH] {}; 148 | SendMessage(m_NppData._nppHandle, NPPM_GETPLUGINSCONFIGDIR, MAX_PATH, reinterpret_cast(&szPath)); 149 | m_configPath = std::wstring(szPath) + TEXT("\\") + PLUGIN_CONFIG; 150 | } 151 | 152 | void NppJsonPlugin::ToggleMenuItemState(int nCmdId, bool bVisible) 153 | { 154 | ::SendMessage(m_NppData._nppHandle, NPPM_SETMENUITEMCHECK, static_cast(nCmdId), bVisible); 155 | } 156 | 157 | void NppJsonPlugin::ConstructJsonDlg() 158 | { 159 | if (!m_pJsonViewDlg) 160 | { 161 | ConstructSetting(); 162 | auto nCmdId = m_shortcutCommands.GetCommandID(CallBackID::SHOW_DOC_PANEL); 163 | m_pJsonViewDlg = std::make_unique(reinterpret_cast(m_hModule), m_NppData, m_bNppReady, nCmdId, m_pSetting); 164 | } 165 | } 166 | 167 | void NppJsonPlugin::ConstructSetting() 168 | { 169 | if (!m_pSetting) 170 | { 171 | m_pSetting = std::make_shared(); 172 | ProfileSetting(m_configPath).GetSettings(*m_pSetting); 173 | } 174 | } 175 | 176 | void NppJsonPlugin::ShowJsonDlg() 177 | { 178 | ConstructJsonDlg(); 179 | 180 | if (m_pJsonViewDlg) // Hope it is constructed by now. 181 | { 182 | bool bVisible = !m_pJsonViewDlg->isVisible(); 183 | m_pJsonViewDlg->ShowDlg(bVisible); 184 | } 185 | } 186 | 187 | void NppJsonPlugin::FormatJson() 188 | { 189 | ConstructJsonDlg(); 190 | 191 | if (m_pJsonViewDlg) // Hope it is constructed by now. 192 | { 193 | m_pJsonViewDlg->FormatJson(); 194 | } 195 | } 196 | 197 | void NppJsonPlugin::CompressJson() 198 | { 199 | ConstructJsonDlg(); 200 | 201 | if (m_pJsonViewDlg) // Hope it is constructed by now. 202 | { 203 | m_pJsonViewDlg->CompressJson(); 204 | } 205 | } 206 | 207 | void NppJsonPlugin::SortJsonByKey() 208 | { 209 | ConstructJsonDlg(); 210 | 211 | if (m_pJsonViewDlg) // Hope it is constructed by now. 212 | { 213 | m_pJsonViewDlg->SortJsonByKey(); 214 | } 215 | } 216 | 217 | void NppJsonPlugin::OpenSettingDlg() 218 | { 219 | ConstructSetting(); 220 | auto nCmdId = m_shortcutCommands.GetCommandID(CallBackID::SETTING); 221 | 222 | if (!m_pSettingsDlg) 223 | m_pSettingsDlg = std::make_unique(reinterpret_cast(m_hModule), m_NppData._nppHandle, nCmdId, m_configPath, m_pSetting); 224 | bool isShown = m_pSettingsDlg->ShowDlg(true); 225 | 226 | ToggleMenuItemState(nCmdId, isShown); 227 | } 228 | 229 | void NppJsonPlugin::ShowAboutDlg() 230 | { 231 | auto nCmdId = m_shortcutCommands.GetCommandID(CallBackID::ABOUT); 232 | 233 | if (!m_pAboutDlg) 234 | m_pAboutDlg = std::make_unique(reinterpret_cast(m_hModule), m_NppData._nppHandle, nCmdId); 235 | bool isShown = m_pAboutDlg->ShowDlg(true); 236 | 237 | ToggleMenuItemState(nCmdId, isShown); 238 | } 239 | -------------------------------------------------------------------------------- /src/NppJsonViewer/NppJsonPlugin.h: -------------------------------------------------------------------------------- 1 | #pragma 2 | 3 | #include 4 | #include 5 | #include "Define.h" 6 | #include "Notepad_plus_msgs.h" 7 | #include "ShortcutCommand.h" 8 | #include "AboutDlg.h" 9 | #include "JsonViewDlg.h" 10 | #include "SettingsDlg.h" 11 | 12 | 13 | class NppJsonPlugin 14 | { 15 | public: 16 | NppJsonPlugin(); 17 | ~NppJsonPlugin() = default; 18 | 19 | void PluginInit(HMODULE hModule); 20 | void PluginCleanup(); 21 | 22 | // Notepad++ APIs to be implemented 23 | void SetInfo(const NppData& nppData); 24 | 25 | const TCHAR* GetPluginName() const; 26 | 27 | FuncItem* GetFuncsArray(int* nbF); 28 | 29 | void ProcessNotification(const SCNotification* notifyCode); 30 | 31 | LRESULT MessageProc(UINT msg, WPARAM wParam, LPARAM lParam); 32 | 33 | BOOL IsUnicode(); 34 | 35 | private: 36 | class Callback 37 | { 38 | friend class NppJsonPlugin; 39 | static NppJsonPlugin* m_pNppJsonPlugin; 40 | 41 | public: 42 | Callback() = default; 43 | ~Callback() = default; 44 | 45 | static void ShowJsonDlg() 46 | { 47 | m_pNppJsonPlugin->ShowJsonDlg(); 48 | } 49 | static void FormatJson() 50 | { 51 | m_pNppJsonPlugin->FormatJson(); 52 | } 53 | static void CompressJson() 54 | { 55 | m_pNppJsonPlugin->CompressJson(); 56 | } 57 | static void SortJsonByKey() 58 | { 59 | m_pNppJsonPlugin->SortJsonByKey(); 60 | } 61 | static void OpenSettingDlg() 62 | { 63 | m_pNppJsonPlugin->OpenSettingDlg(); 64 | } 65 | static void ShowAboutDlg() 66 | { 67 | m_pNppJsonPlugin->ShowAboutDlg(); 68 | } 69 | }; 70 | 71 | void SetMenuIcon(); 72 | void InitCommandMenu(); 73 | void InitToolbarIcon(); 74 | void InitConfigPath(); 75 | 76 | void ToggleMenuItemState(int nCmdId, bool bVisible); 77 | 78 | void ConstructJsonDlg(); 79 | void ConstructSetting(); 80 | 81 | void ShowJsonDlg(); 82 | void FormatJson(); 83 | void CompressJson(); 84 | void SortJsonByKey(); 85 | void OpenSettingDlg(); 86 | void ShowAboutDlg(); 87 | 88 | private: 89 | HMODULE m_hModule = nullptr; 90 | toolbarIcons m_hMenuIcon = {}; 91 | ShortcutCommand m_shortcutCommands; 92 | NppData m_NppData = {}; 93 | std::wstring m_configPath; 94 | bool m_bAboutToClose = false; 95 | bool m_bNppReady = false; 96 | std::unique_ptr m_pAboutDlg = nullptr; 97 | std::unique_ptr m_pJsonViewDlg = nullptr; 98 | std::unique_ptr m_pSettingsDlg = nullptr; 99 | std::shared_ptr m_pSetting = nullptr; 100 | }; 101 | -------------------------------------------------------------------------------- /src/NppJsonViewer/Profile.cpp: -------------------------------------------------------------------------------- 1 | #include "Profile.h" 2 | #include "Utility.h" 3 | #include "Define.h" 4 | #include "StringHelper.h" 5 | #include 6 | #include 7 | 8 | Profile::Profile(const std::wstring& path) 9 | : m_ProfileFilePath(path) 10 | { 11 | if (path.empty()) 12 | Init(); 13 | } 14 | 15 | bool Profile::ReadValue(const std::wstring& section, const std::wstring& key, int& retVal, int defaultVal) const 16 | { 17 | retVal = GetPrivateProfileInt(section.c_str(), key.c_str(), defaultVal, m_ProfileFilePath.c_str()); 18 | 19 | return true; 20 | } 21 | 22 | bool Profile::ReadValue(const std::wstring& section, const std::wstring& key, std::wstring& retVal, const std::wstring& defaultVal) const 23 | { 24 | bool bRetVal = false; 25 | 26 | // Try with MAX_PATH 27 | constexpr DWORD nBufSize = MAX_PATH * 2; 28 | auto pData = std::make_unique(nBufSize); 29 | GetPrivateProfileString(section.c_str(), key.c_str(), defaultVal.c_str(), pData.get(), nBufSize, m_ProfileFilePath.c_str()); 30 | 31 | if (pData) 32 | { 33 | bRetVal = true; 34 | retVal = pData.get(); 35 | } 36 | 37 | return bRetVal; 38 | } 39 | 40 | bool Profile::WriteValue(const std::wstring& section, const std::wstring& key, int value) const 41 | { 42 | return WriteValue(section, key, std::to_wstring(value)); 43 | } 44 | 45 | bool Profile::WriteValue(const std::wstring& section, const std::wstring& key, const std::wstring& value) const 46 | { 47 | return WritePrivateProfileString(section.c_str(), key.c_str(), value.c_str(), m_ProfileFilePath.c_str()) ? true : false; 48 | } 49 | 50 | void Profile::Init() 51 | { 52 | auto appDataPath = CUtility::GetSpecialFolderLocation(CSIDL_APPDATA); 53 | if (appDataPath.empty()) 54 | { 55 | ::MessageBox(NULL, L"Failed to get %appdata% path. Please contact developer. Inconvenience regretted.", JSON_ERROR_TITLE, MB_OK | MB_ICONERROR); 56 | return; 57 | } 58 | 59 | appDataPath += L"\\config"; 60 | if (!CUtility::DirExist(appDataPath) && !CUtility::CreateDir(appDataPath)) 61 | { 62 | std::wstring msg = L"Failed to get below directory. Please contact developer. Inconvenience regretted."; 63 | msg += L"\n\n" + appDataPath; 64 | 65 | ::MessageBox(NULL, msg.c_str(), JSON_ERROR_TITLE, MB_OK | MB_ICONERROR); 66 | return; 67 | } 68 | 69 | m_ProfileFilePath = appDataPath + L"\\" + PLUGIN_CONFIG; 70 | } 71 | 72 | bool ProfileSetting::GetSettings(Setting& info) const 73 | { 74 | bool bRetVal = true; 75 | 76 | int nVal = 0; 77 | bRetVal = bRetVal && ReadValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_EOL, nVal, static_cast(info.lineEnding)); 78 | if (bRetVal) 79 | info.lineEnding = static_cast(nVal); 80 | 81 | bRetVal = bRetVal && ReadValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_LINE, nVal, static_cast(info.lineFormat)); 82 | if (bRetVal) 83 | info.lineFormat = static_cast(nVal); 84 | 85 | bRetVal = bRetVal && ReadValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_INDENT, nVal, static_cast(info.indent.style)); 86 | if (bRetVal) 87 | info.indent.style = static_cast(nVal); 88 | 89 | bRetVal = bRetVal && ReadValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_INDENTCOUNT, nVal, info.indent.len); 90 | if (bRetVal) 91 | info.indent.len = nVal; 92 | 93 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_FOLLOW_TAB, nVal, info.bFollowCurrentTab); 94 | if (bRetVal) 95 | info.bFollowCurrentTab = static_cast(nVal); 96 | 97 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_AUTO_FORMAT, nVal, info.bAutoFormat); 98 | if (bRetVal) 99 | info.bAutoFormat = static_cast(nVal); 100 | 101 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_USE_HIGHLIGHT, nVal, info.bUseJsonHighlight); 102 | if (bRetVal) 103 | info.bUseJsonHighlight = static_cast(nVal); 104 | 105 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_IGNORE_COMMENT, nVal, info.parseOptions.bIgnoreComment); 106 | if (bRetVal) 107 | info.parseOptions.bIgnoreComment = static_cast(nVal); 108 | 109 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_IGNORE_COMMA, nVal, info.parseOptions.bIgnoreTrailingComma); 110 | if (bRetVal) 111 | info.parseOptions.bIgnoreTrailingComma = static_cast(nVal); 112 | 113 | bRetVal = bRetVal && ReadValue(STR_INI_OTHER_SEC, STR_INI_OTHER_REPLACE_UNDEFINED, nVal, info.parseOptions.bReplaceUndefined); 114 | if (bRetVal) 115 | info.parseOptions.bReplaceUndefined = static_cast(nVal); 116 | 117 | return bRetVal; 118 | } 119 | 120 | bool ProfileSetting::SetSettings(const Setting& info) const 121 | { 122 | bool bRetVal = true; 123 | 124 | bRetVal = bRetVal && WriteValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_EOL, static_cast(info.lineEnding)); 125 | bRetVal = bRetVal && WriteValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_LINE, static_cast(info.lineFormat)); 126 | bRetVal = bRetVal && WriteValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_INDENT, static_cast(info.indent.style)); 127 | bRetVal = bRetVal && WriteValue(STR_INI_FORMATTING_SEC, STR_INI_FORMATTING_INDENTCOUNT, info.indent.len); 128 | 129 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_FOLLOW_TAB, info.bFollowCurrentTab); 130 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_AUTO_FORMAT, info.bAutoFormat); 131 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_USE_HIGHLIGHT, info.bUseJsonHighlight); 132 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_IGNORE_COMMENT, info.parseOptions.bIgnoreComment); 133 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_IGNORE_COMMA, info.parseOptions.bIgnoreTrailingComma); 134 | bRetVal = bRetVal && WriteValue(STR_INI_OTHER_SEC, STR_INI_OTHER_REPLACE_UNDEFINED, info.parseOptions.bReplaceUndefined); 135 | 136 | return bRetVal; 137 | } 138 | -------------------------------------------------------------------------------- /src/NppJsonViewer/Profile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Define.h" 4 | 5 | class Profile 6 | { 7 | protected: 8 | std::wstring m_ProfileFilePath; 9 | 10 | public: 11 | explicit Profile(const std::wstring& path); 12 | virtual ~Profile() = default; 13 | 14 | protected: 15 | bool ReadValue(const std::wstring& section, const std::wstring& key, int& retVal, int defaultVal = 0) const; 16 | bool ReadValue(const std::wstring& section, const std::wstring& key, std::wstring& retVal, const std::wstring& defaultVal = {}) const; 17 | 18 | bool WriteValue(const std::wstring& section, const std::wstring& key, int value) const; 19 | bool WriteValue(const std::wstring& section, const std::wstring& key, const std::wstring& value) const; 20 | 21 | private: 22 | void Init(); 23 | }; 24 | 25 | 26 | class ProfileSetting : public Profile 27 | { 28 | public: 29 | explicit ProfileSetting(const std::wstring& path) 30 | : Profile(path) 31 | { 32 | } 33 | ~ProfileSetting() = default; 34 | 35 | bool GetSettings(Setting& info) const; 36 | bool SetSettings(const Setting& info) const; 37 | }; 38 | -------------------------------------------------------------------------------- /src/NppJsonViewer/RapidJsonHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "RapidJsonHandler.h" 2 | #include "TreeHandler.h" 3 | 4 | const char* const STR_NULL = "null"; 5 | const char* const STR_TRUE = "true"; 6 | const char* const STR_FALSE = "false"; 7 | 8 | 9 | bool RapidJsonHandler::Null() 10 | { 11 | if (!m_NodeStack.size()) 12 | return false; 13 | 14 | TreeNode* parent = m_NodeStack.top(); 15 | InsertToTree(parent, STR_NULL, false); 16 | return true; 17 | } 18 | 19 | bool RapidJsonHandler::Bool(bool b) 20 | { 21 | if (!m_NodeStack.size()) 22 | return false; 23 | 24 | TreeNode* parent = m_NodeStack.top(); 25 | InsertToTree(parent, b ? STR_TRUE : STR_FALSE, false); 26 | return true; 27 | } 28 | 29 | bool RapidJsonHandler::Int(int /*i*/) 30 | { 31 | return true; 32 | } 33 | 34 | bool RapidJsonHandler::Uint(unsigned /*i*/) 35 | { 36 | return true; 37 | } 38 | 39 | bool RapidJsonHandler::Int64(int64_t /*i*/) 40 | { 41 | return true; 42 | } 43 | 44 | bool RapidJsonHandler::Uint64(uint64_t /*i*/) 45 | { 46 | return true; 47 | } 48 | 49 | bool RapidJsonHandler::Double(double /*d*/) 50 | { 51 | return true; 52 | } 53 | 54 | bool RapidJsonHandler::RawNumber(const Ch* str, unsigned /*length*/, bool /*copy*/) 55 | { 56 | if (!m_NodeStack.size()) 57 | return false; 58 | 59 | TreeNode* parent = m_NodeStack.top(); 60 | InsertToTree(parent, str, false); 61 | return true; 62 | } 63 | 64 | bool RapidJsonHandler::String(const Ch* str, unsigned /*length*/, bool /*copy*/) 65 | { 66 | if (!str) 67 | return false; 68 | 69 | // handle case, when there is only a value in input 70 | if (m_NodeStack.empty()) 71 | { 72 | m_pTreeHandler->InsertToTree(m_treeRoot, str); 73 | return true; 74 | } 75 | 76 | TreeNode* parent = m_NodeStack.top(); 77 | InsertToTree(parent, str, true); 78 | 79 | return true; 80 | } 81 | 82 | bool RapidJsonHandler::Key(const Ch* str, unsigned length, bool /*copy*/) 83 | { 84 | m_jsonLastKey.strKey = str; 85 | m_jsonLastKey.pos.nLine = m_pTS->getLine(); 86 | m_jsonLastKey.pos.nColumn = m_pTS->getColumn() - length - 1; 87 | m_jsonLastKey.pos.nKeyLength = length; 88 | return true; 89 | } 90 | 91 | bool RapidJsonHandler::StartObject() 92 | { 93 | TreeNode* parent = nullptr; 94 | if (m_NodeStack.empty()) 95 | { 96 | parent = new TreeNode; 97 | parent->node.type = JsonNodeType::OBJECT; 98 | parent->subRoot = m_treeRoot; 99 | parent->counter = 0; 100 | m_NodeStack.push(parent); 101 | } 102 | else 103 | { 104 | parent = m_NodeStack.top(); 105 | } 106 | 107 | if (!m_jsonLastKey.strKey.empty() || parent->node.type == JsonNodeType::ARRAY) 108 | { 109 | HTREEITEM newNode = nullptr; 110 | if (parent->node.type != JsonNodeType::ARRAY) 111 | { 112 | newNode = m_pTreeHandler->InsertToTree(parent->subRoot, m_jsonLastKey.strKey, m_jsonLastKey.pos); 113 | m_jsonLastKey.clear(); 114 | } 115 | else 116 | { 117 | // It is an array 118 | std::string arr = "[" + std::to_string(parent->counter) + "]"; 119 | newNode = m_pTreeHandler->InsertToTree(parent->subRoot, arr); 120 | } 121 | 122 | parent->counter++; 123 | TreeNode* newTreeNode = new TreeNode; 124 | newTreeNode->node.type = JsonNodeType::OBJECT; 125 | newTreeNode->subRoot = newNode; 126 | newTreeNode->counter = 0; 127 | m_NodeStack.push(newTreeNode); 128 | } 129 | 130 | return true; 131 | } 132 | 133 | bool RapidJsonHandler::EndObject(unsigned memberCount) 134 | { 135 | AppendNodeCount(memberCount, false); 136 | return true; 137 | } 138 | 139 | bool RapidJsonHandler::StartArray() 140 | { 141 | TreeNode* parent = nullptr; 142 | if (m_NodeStack.empty()) 143 | { 144 | parent = new TreeNode; 145 | parent->node.type = JsonNodeType::ARRAY; 146 | parent->subRoot = m_treeRoot; 147 | parent->counter = 0; 148 | m_NodeStack.push(parent); 149 | return true; 150 | } 151 | else 152 | { 153 | parent = m_NodeStack.top(); 154 | } 155 | 156 | if (!m_jsonLastKey.strKey.empty() || parent->node.type == JsonNodeType::ARRAY) 157 | { 158 | HTREEITEM newNode; 159 | if (parent->node.type != JsonNodeType::ARRAY) 160 | { 161 | newNode = m_pTreeHandler->InsertToTree(parent->subRoot, m_jsonLastKey.strKey, m_jsonLastKey.pos); 162 | m_jsonLastKey.clear(); 163 | } 164 | else 165 | { 166 | // It is an array 167 | std::string arr = "[" + std::to_string(parent->counter) + "]"; 168 | newNode = m_pTreeHandler->InsertToTree(parent->subRoot, arr); 169 | } 170 | 171 | parent->counter++; 172 | TreeNode* newTreeNode = new TreeNode; 173 | newTreeNode->node.type = JsonNodeType::ARRAY; 174 | newTreeNode->subRoot = newNode; 175 | newTreeNode->counter = 0; 176 | m_NodeStack.push(newTreeNode); 177 | } 178 | return true; 179 | } 180 | 181 | bool RapidJsonHandler::EndArray(unsigned elementCount) 182 | { 183 | AppendNodeCount(elementCount, true); 184 | return true; 185 | } 186 | 187 | void RapidJsonHandler::InsertToTree(TreeNode* node, const char* const str, bool bQuote) 188 | { 189 | if (!node || !str) 190 | return; 191 | 192 | if (node->node.type != JsonNodeType::ARRAY) 193 | { 194 | node->node.key = m_jsonLastKey; 195 | node->node.value = str; 196 | m_jsonLastKey.clear(); 197 | } 198 | else 199 | { 200 | node->node.key.strKey = "[" + std::to_string(node->counter) + "]"; 201 | node->node.value = str; 202 | 203 | size_t valueLen = node->node.value.size(); 204 | node->node.key.pos.nLine = m_pTS->getLine(); 205 | node->node.key.pos.nColumn = m_pTS->getColumn() - valueLen - (bQuote ? 1 : 0); // -1 to deal with double quote in string 206 | node->node.key.pos.nKeyLength = valueLen; 207 | 208 | node->counter++; 209 | } 210 | 211 | // Insert item to tree 212 | if (bQuote) 213 | m_pTreeHandler->InsertToTree(node->subRoot, node->node.key.strKey + " : \"" + node->node.value + "\"", node->node.key.pos); 214 | else 215 | m_pTreeHandler->InsertToTree(node->subRoot, node->node.key.strKey + " : " + node->node.value, node->node.key.pos); 216 | } 217 | 218 | void RapidJsonHandler::AppendNodeCount(unsigned elementCount, bool bArray) 219 | { 220 | if (!m_NodeStack.empty()) 221 | { 222 | TreeNode* node = m_NodeStack.top(); 223 | m_NodeStack.pop(); 224 | 225 | if (node->subRoot && node->subRoot != m_treeRoot) 226 | m_pTreeHandler->AppendNodeCount(node->subRoot, elementCount, bArray); 227 | 228 | delete node; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/NppJsonViewer/RapidJsonHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "JsonNode.h" 11 | #include "TrackingStream.h" 12 | 13 | class TreeHandler; 14 | 15 | struct TreeNode 16 | { 17 | HTREEITEM subRoot {}; 18 | JsonNode node {}; 19 | int counter {}; 20 | }; 21 | 22 | 23 | class RapidJsonHandler : public rapidjson::BaseReaderHandler, RapidJsonHandler> 24 | { 25 | JsonKey m_jsonLastKey {}; 26 | std::stack m_NodeStack; 27 | 28 | TrackingStreamSharedPtr m_pTS; 29 | TreeHandler* m_pTreeHandler = nullptr; 30 | HTREEITEM m_treeRoot = nullptr; 31 | 32 | public: 33 | RapidJsonHandler(TreeHandler* handler, HTREEITEM treeRoot, TrackingStreamSharedPtr pTS = nullptr) 34 | : m_pTS(pTS ? pTS->GetShared() : nullptr) 35 | , m_pTreeHandler(handler) 36 | , m_treeRoot(treeRoot) 37 | { 38 | } 39 | virtual ~RapidJsonHandler() = default; 40 | 41 | bool Null(); 42 | bool Bool(bool b); 43 | bool Int(int i); 44 | bool Uint(unsigned i); 45 | bool Int64(int64_t i); 46 | bool Uint64(uint64_t i); 47 | bool Double(double d); 48 | bool RawNumber(const Ch* str, unsigned length, bool copy); 49 | bool String(const Ch* str, unsigned length, bool copy); 50 | bool Key(const Ch* str, unsigned length, bool copy); 51 | bool StartObject(); 52 | bool EndObject(unsigned memberCount); 53 | bool StartArray(); 54 | bool EndArray(unsigned elementCount); 55 | 56 | private: 57 | void InsertToTree(TreeNode* node, const char* const str, bool bQuote); 58 | void AppendNodeCount(unsigned elementCount, bool bArray); 59 | }; 60 | -------------------------------------------------------------------------------- /src/NppJsonViewer/ScintillaEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "ScintillaEditor.h" 2 | #include 3 | #include 4 | 5 | ScintillaEditor::ScintillaEditor(const NppData& nppData) 6 | : m_NppData(nppData) 7 | { 8 | RefreshViewHandle(); 9 | } 10 | 11 | void ScintillaEditor::RefreshViewHandle() 12 | { 13 | int which = -1; 14 | ::SendMessage(m_NppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, reinterpret_cast(&which)); 15 | assert(which != -1); 16 | if (which != -1) 17 | m_hScintilla = (which == 0) ? m_NppData._scintillaMainHandle : m_NppData._scintillaSecondHandle; 18 | } 19 | 20 | auto ScintillaEditor::GetJsonText() -> ScintillaData 21 | { 22 | if (!m_hScintilla) 23 | return ScintillaCode::NotInitialized; 24 | 25 | // Multi selection is not supported 26 | size_t nSelections = ::SendMessage(m_hScintilla, SCI_GETSELECTIONS, 0, 0); 27 | if (nSelections > 1) 28 | return ScintillaCode::MultiLineSelection; 29 | 30 | // Adjust the selection position 31 | RefreshSelectionPos(); 32 | 33 | // Get only selected text if any else select all text 34 | size_t asciiTextLen = m_nEndPos - m_nStartPos; 35 | if (asciiTextLen == 0) 36 | { 37 | asciiTextLen = ::SendMessage(m_hScintilla, SCI_GETLENGTH, 0, 0); 38 | ::SendMessage(m_hScintilla, SCI_SETSELECTIONSTART, 0, 0); 39 | ::SendMessage(m_hScintilla, SCI_SETSELECTIONEND, asciiTextLen, 0); 40 | } 41 | std::unique_ptr chSelectedText = std::make_unique(asciiTextLen + 1); 42 | ::SendMessage(m_hScintilla, SCI_GETSELTEXT, 0, reinterpret_cast(chSelectedText.get())); 43 | 44 | // Update selection position 45 | RefreshSelectionPos(); 46 | 47 | return chSelectedText.get(); 48 | } 49 | 50 | void ScintillaEditor::SetLangAsJson() const 51 | { 52 | ::SendMessage(m_NppData._nppHandle, NPPM_SETCURRENTLANGTYPE, 0, LangType::L_JSON); 53 | } 54 | 55 | bool ScintillaEditor::IsJsonFile() const 56 | { 57 | unsigned languageType = 0; 58 | ::SendMessage(m_NppData._nppHandle, NPPM_GETCURRENTLANGTYPE, 0, reinterpret_cast(&languageType)); 59 | return languageType == LangType::L_JSON; 60 | } 61 | 62 | auto ScintillaEditor::GetCurrentFileName() const -> std::wstring 63 | { 64 | wchar_t fileName[MAX_PATH] {}; 65 | ::SendMessage(m_NppData._nppHandle, NPPM_GETFILENAME, 0, reinterpret_cast(&fileName)); 66 | return fileName; 67 | } 68 | 69 | void ScintillaEditor::ReplaceSelection(const std::string& text) const 70 | { 71 | ::SendMessage(m_hScintilla, SCI_REPLACESEL, 0, reinterpret_cast(text.c_str())); 72 | 73 | // Restore the selection 74 | MakeSelection(m_nStartPos, m_nStartPos + text.length()); 75 | } 76 | 77 | void ScintillaEditor::MakeSelection(size_t start, size_t end) const 78 | { 79 | ::SendMessage(m_hScintilla, SCI_SETSEL, start, end); 80 | } 81 | 82 | auto ScintillaEditor::GetEOL() const -> unsigned 83 | { 84 | LRESULT eolMode = ::SendMessage(m_hScintilla, SCI_GETEOLMODE, 0, 0); 85 | return static_cast(eolMode); 86 | } 87 | 88 | auto ScintillaEditor::GetIndent() const -> std::tuple 89 | { 90 | bool useTabs = ::SendMessage(m_hScintilla, SCI_GETUSETABS, 0, 0); 91 | char indentChar = useTabs ? '\t' : ' '; 92 | unsigned indentLen = useTabs ? 1 : static_cast(::SendMessage(m_hScintilla, SCI_GETTABWIDTH, 0, 0)); 93 | return std::tuple(indentChar, indentLen); 94 | } 95 | 96 | void ScintillaEditor::RefreshSelectionPos() 97 | { 98 | m_nStartPos = ::SendMessage(m_hScintilla, SCI_GETSELECTIONSTART, 0, 0); 99 | m_nEndPos = ::SendMessage(m_hScintilla, SCI_GETSELECTIONEND, 0, 0); 100 | 101 | if (m_nEndPos < m_nStartPos) 102 | std::swap(m_nStartPos, m_nEndPos); 103 | 104 | m_nStartLine = ::SendMessage(m_hScintilla, SCI_LINEFROMPOSITION, m_nStartPos, 0); 105 | } 106 | 107 | void ScintillaEditor::GoToLine(size_t nLineToGo) const 108 | { 109 | ::SendMessage(m_hScintilla, SCI_GOTOLINE, m_nStartLine + nLineToGo, 0); 110 | } 111 | 112 | void ScintillaEditor::GoToPosition(size_t nLineToGo, size_t nColumnIndex, size_t nWordLen, bool selectWord /*= true*/) const 113 | { 114 | size_t lineStartPos = SendMessage(m_hScintilla, SCI_POSITIONFROMLINE, m_nStartLine + nLineToGo, 0); 115 | size_t targetPos = lineStartPos + nColumnIndex; 116 | ::SendMessage(m_hScintilla, SCI_GOTOPOS, targetPos, 0); 117 | if (selectWord) 118 | { 119 | MakeSelection(targetPos, targetPos + nWordLen); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/NppJsonViewer/ScintillaEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Define.h" 3 | #include 4 | #include 5 | #include 6 | 7 | enum class ScintillaCode : short 8 | { 9 | Unknown, 10 | Success, 11 | NotInitialized, 12 | NoSelection, 13 | MultiLineSelection 14 | }; 15 | 16 | using ScintillaData = std::variant; 17 | 18 | class ScintillaEditor 19 | { 20 | public: 21 | explicit ScintillaEditor(const NppData& nppData); 22 | ~ScintillaEditor() = default; 23 | 24 | void RefreshViewHandle(); 25 | auto GetJsonText() -> ScintillaData; 26 | void SetLangAsJson() const; 27 | bool IsJsonFile() const; 28 | auto GetCurrentFileName() const -> std::wstring; 29 | 30 | void ReplaceSelection(const std::string& text) const; 31 | 32 | void MakeSelection(size_t start, size_t end) const; 33 | inline auto GetSelectionStart() const -> size_t 34 | { 35 | return m_nStartPos; 36 | } 37 | inline auto GetSelectionEnd() const -> size_t 38 | { 39 | return m_nEndPos; 40 | } 41 | inline auto GetSelectionStartLine() const -> size_t 42 | { 43 | return m_nStartLine; 44 | } 45 | 46 | auto GetEOL() const -> unsigned; 47 | auto GetIndent() const -> std::tuple; 48 | 49 | void RefreshSelectionPos(); 50 | 51 | void GoToLine(size_t nLineToGo) const; 52 | void GoToPosition(size_t nLineToGo, size_t nColumnIndex, size_t nWordLen, bool selectWord = true) const; 53 | 54 | private: 55 | NppData m_NppData = {}; 56 | HWND m_hScintilla = nullptr; 57 | 58 | size_t m_nStartLine = 0; 59 | size_t m_nStartPos = 0; 60 | size_t m_nEndPos = 0; 61 | }; 62 | -------------------------------------------------------------------------------- /src/NppJsonViewer/SettingsDlg.cpp: -------------------------------------------------------------------------------- 1 | #include "SettingsDlg.h" 2 | #include "resource.h" 3 | #include "Utility.h" 4 | #include "Profile.h" 5 | #include 6 | #include 7 | 8 | 9 | SettingsDlg::SettingsDlg(HINSTANCE hInstance, HWND hParent, int nCmdId, const std::wstring& configPath, std::shared_ptr& pSetting) 10 | : m_nCmdId(nCmdId) 11 | , m_configPath(configPath) 12 | , StaticDialog() 13 | , m_pSetting(pSetting) 14 | { 15 | init(hInstance, hParent); 16 | } 17 | 18 | 19 | bool SettingsDlg::ShowDlg(bool bShow) 20 | { 21 | bool bShouldShow = bShow && !isVisible(); 22 | if (bShouldShow) 23 | { 24 | if (!isCreated()) 25 | create(IDD_SETTING); 26 | 27 | // Adjust the position of Setting dialog 28 | goToCenter(); 29 | } 30 | else 31 | { 32 | SendMessage(_hSelf, WM_COMMAND, IDCANCEL, NULL); 33 | } 34 | return bShouldShow; 35 | } 36 | 37 | INT_PTR SettingsDlg::run_dlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 38 | { 39 | switch (uMsg) 40 | { 41 | case WM_INITDIALOG: 42 | { 43 | ::SetWindowLongPtr(_hSelf, DWLP_USER, lParam); 44 | 45 | auto enable_dlg_theme = reinterpret_cast(::SendMessage(_hParent, NPPM_GETENABLETHEMETEXTUREFUNC, 0, 0)); 46 | if (enable_dlg_theme != nullptr) 47 | enable_dlg_theme(_hSelf, ETDT_ENABLETAB); 48 | 49 | InitDlg(); 50 | 51 | SetFocus(GetDlgItem(_hSelf, IDOK)); 52 | 53 | return TRUE; 54 | } 55 | 56 | case WM_COMMAND: 57 | { 58 | switch (LOWORD(wParam)) 59 | { 60 | case IDOK: 61 | if (Apply()) 62 | { 63 | ::SendMessage(_hParent, NPPM_SETMENUITEMCHECK, static_cast(m_nCmdId), false); 64 | EndDialog(_hSelf, wParam); 65 | } 66 | else 67 | { 68 | ::MessageBox(_hSelf, JSON_ERR_SAVE_SETTING, JSON_ERROR_TITLE, MB_OK | MB_ICONERROR); 69 | } 70 | return TRUE; 71 | 72 | case IDCANCEL: // Close this dialog when clicking to close button 73 | ::SendMessage(_hParent, NPPM_SETMENUITEMCHECK, static_cast(m_nCmdId), false); 74 | EndDialog(_hSelf, wParam); 75 | return TRUE; 76 | 77 | case IDC_RADIO_INDENT_AUTO: 78 | case IDC_RADIO_INDENT_TAB: 79 | ShowSpaceCountCtrls(false); 80 | return TRUE; 81 | 82 | case IDC_RADIO_INDENT_SPACE: 83 | ShowSpaceCountCtrls(true); 84 | return TRUE; 85 | } 86 | } 87 | } 88 | return FALSE; 89 | } 90 | 91 | bool SettingsDlg::Apply() 92 | { 93 | // Get all the data from the UI 94 | 95 | // Line Ending setting 96 | if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINE_AUTO)) 97 | m_pSetting->lineEnding = LineEnding::AUTO; 98 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINE_WINDOW)) 99 | m_pSetting->lineEnding = LineEnding::WINDOWS; 100 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINE_UNIX)) 101 | m_pSetting->lineEnding = LineEnding::UNIX; 102 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINE_MAC)) 103 | m_pSetting->lineEnding = LineEnding::MAC; 104 | 105 | // Indentation setting 106 | if (IsDlgButtonChecked(_hSelf, IDC_RADIO_INDENT_AUTO)) 107 | m_pSetting->indent.style = IndentStyle::AUTO; 108 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_INDENT_TAB)) 109 | m_pSetting->indent.style = IndentStyle::TAB; 110 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_INDENT_SPACE)) 111 | m_pSetting->indent.style = IndentStyle::SPACE; 112 | 113 | auto indent_len = CUtility::GetNumber(CUtility::GetEditCtrlText(::GetDlgItem(_hSelf, IDC_EDT_INDENT_SPACECOUNT))); 114 | if (indent_len.has_value()) 115 | { 116 | m_pSetting->indent.len = indent_len.value(); 117 | } 118 | 119 | // Line Ending setting 120 | if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINEFORMAT_DEFAULT)) 121 | m_pSetting->lineFormat = LineFormat::DEFAULT; 122 | else if (IsDlgButtonChecked(_hSelf, IDC_RADIO_LINEFORMAT_SINGLE)) 123 | m_pSetting->lineFormat = LineFormat::SINGLELINE; 124 | 125 | m_pSetting->bFollowCurrentTab = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FOLLOW_CURRENT_DOC)); 126 | m_pSetting->bAutoFormat = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FORMAT_ON_OPEN)); 127 | m_pSetting->bUseJsonHighlight = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_JSON_HIGHLIGHT)); 128 | m_pSetting->parseOptions.bIgnoreTrailingComma = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMA)); 129 | m_pSetting->parseOptions.bIgnoreComment = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMENT)); 130 | m_pSetting->parseOptions.bReplaceUndefined = CUtility::GetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_REPLACE_UNDEFINED)); 131 | 132 | return WriteINI(); 133 | } 134 | 135 | void SettingsDlg::destroy() {} 136 | 137 | bool SettingsDlg::ReadINI() 138 | { 139 | return ProfileSetting(m_configPath).GetSettings(*m_pSetting); 140 | } 141 | 142 | bool SettingsDlg::WriteINI() 143 | { 144 | return ProfileSetting(m_configPath).SetSettings(*m_pSetting); 145 | } 146 | 147 | void SettingsDlg::InitDlg() 148 | { 149 | ReadINI(); 150 | 151 | // Set UI control state here 152 | 153 | int nCtrlID = IDC_RADIO_LINE_AUTO; 154 | switch (m_pSetting->lineEnding) 155 | { 156 | case LineEnding::WINDOWS: 157 | nCtrlID = IDC_RADIO_LINE_WINDOW; 158 | break; 159 | case LineEnding::UNIX: 160 | nCtrlID = IDC_RADIO_LINE_UNIX; 161 | break; 162 | case LineEnding::MAC: 163 | nCtrlID = IDC_RADIO_LINE_MAC; 164 | break; 165 | case LineEnding::AUTO: 166 | nCtrlID = IDC_RADIO_LINE_AUTO; 167 | break; 168 | default: 169 | break; 170 | } 171 | CheckRadioButton(_hSelf, IDC_RADIO_LINE_AUTO, IDC_RADIO_LINE_MAC, nCtrlID); 172 | 173 | 174 | nCtrlID = IDC_RADIO_LINEFORMAT_DEFAULT; 175 | switch (m_pSetting->lineFormat) 176 | { 177 | case LineFormat::SINGLELINE: 178 | nCtrlID = IDC_RADIO_LINEFORMAT_SINGLE; 179 | break; 180 | case LineFormat::DEFAULT: 181 | nCtrlID = IDC_RADIO_LINEFORMAT_DEFAULT; 182 | break; 183 | default: 184 | break; 185 | } 186 | CheckRadioButton(_hSelf, IDC_RADIO_LINEFORMAT_DEFAULT, IDC_RADIO_LINEFORMAT_SINGLE, nCtrlID); 187 | 188 | 189 | switch (m_pSetting->indent.style) 190 | { 191 | case IndentStyle::AUTO: 192 | nCtrlID = IDC_RADIO_INDENT_AUTO; 193 | break; 194 | case IndentStyle::TAB: 195 | nCtrlID = IDC_RADIO_INDENT_TAB; 196 | break; 197 | case IndentStyle::SPACE: 198 | nCtrlID = IDC_RADIO_INDENT_SPACE; 199 | break; 200 | default: 201 | break; 202 | } 203 | CheckRadioButton(_hSelf, IDC_RADIO_INDENT_AUTO, IDC_RADIO_INDENT_SPACE, nCtrlID); 204 | CUtility::SetEditCtrlText(::GetDlgItem(_hSelf, IDC_EDT_INDENT_SPACECOUNT), std::to_wstring(m_pSetting->indent.len)); 205 | ShowSpaceCountCtrls(m_pSetting->indent.style == IndentStyle::SPACE); 206 | 207 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FOLLOW_CURRENT_DOC), m_pSetting->bFollowCurrentTab); 208 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_FORMAT_ON_OPEN), m_pSetting->bAutoFormat); 209 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_JSON_HIGHLIGHT), m_pSetting->bUseJsonHighlight); 210 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMA), m_pSetting->parseOptions.bIgnoreTrailingComma); 211 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_IGNORE_COMMENT), m_pSetting->parseOptions.bIgnoreComment); 212 | CUtility::SetCheckboxStatus(::GetDlgItem(_hSelf, IDC_CHK_REPLACE_UNDEFINED), m_pSetting->parseOptions.bReplaceUndefined); 213 | } 214 | 215 | void SettingsDlg::ShowSpaceCountCtrls(bool bShow) 216 | { 217 | auto show = bShow ? SW_SHOW : SW_HIDE; 218 | ShowWindow(::GetDlgItem(_hSelf, IDC_EDT_INDENT_SPACECOUNT), show); 219 | ShowWindow(::GetDlgItem(_hSelf, IDC_STATIC_SPACECOUNT), show); 220 | } 221 | -------------------------------------------------------------------------------- /src/NppJsonViewer/SettingsDlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "StaticDialog.h" 3 | #include "Define.h" 4 | #include 5 | #include 6 | 7 | class SettingsDlg : public StaticDialog 8 | { 9 | public: 10 | SettingsDlg(HINSTANCE hInstance, HWND hParent, int nCmdId, const std::wstring& configPath, std::shared_ptr& pSetting); 11 | ~SettingsDlg() = default; 12 | 13 | bool ShowDlg(bool bShow); 14 | 15 | protected: 16 | virtual INT_PTR CALLBACK run_dlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 17 | 18 | private: 19 | bool Apply(); 20 | void destroy() override; 21 | bool ReadINI(); 22 | bool WriteINI(); 23 | void InitDlg(); 24 | void ShowSpaceCountCtrls(bool bShow); 25 | 26 | private: 27 | int m_nCmdId = -1; 28 | std::wstring m_configPath; 29 | std::shared_ptr m_pSetting = nullptr; 30 | }; 31 | -------------------------------------------------------------------------------- /src/NppJsonViewer/ShortcutCommand.cpp: -------------------------------------------------------------------------------- 1 | #include "ShortcutCommand.h" 2 | #include 3 | 4 | ShortcutCommand::ShortcutCommand(int nCommandCount) 5 | : m_nCmdCount(nCommandCount) 6 | , m_pFuncItem(std::make_unique(nCommandCount)) 7 | , m_pShortcutKeys(std::make_unique(nCommandCount)) 8 | { 9 | } 10 | 11 | bool ShortcutCommand::SetCommand(CallBackID id, const TCHAR* cmdName, const PFUNCPLUGINCMD pFunc, bool checkOnInit) 12 | { 13 | int nIndex = static_cast(id); 14 | 15 | if (m_nCmdCount <= nIndex || !pFunc) 16 | return false; 17 | 18 | _tcscpy_s(m_pFuncItem[nIndex]._itemName, cmdName); 19 | m_pFuncItem[nIndex]._pFunc = pFunc; 20 | m_pFuncItem[nIndex]._init2Check = checkOnInit; 21 | m_pFuncItem[nIndex]._pShKey = &m_pShortcutKeys[nIndex]; 22 | 23 | return true; 24 | } 25 | 26 | bool ShortcutCommand::SetShortCut(CallBackID id, const ShortcutKey& scKey) 27 | { 28 | int nIndex = static_cast(id); 29 | 30 | if (m_nCmdCount <= nIndex) 31 | return false; 32 | 33 | m_pShortcutKeys[At(id)] = scKey; 34 | 35 | return true; 36 | } 37 | -------------------------------------------------------------------------------- /src/NppJsonViewer/ShortcutCommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Define.h" 3 | #include 4 | 5 | class ShortcutCommand 6 | { 7 | public: 8 | explicit ShortcutCommand(int nCommandCount); 9 | ~ShortcutCommand() = default; 10 | 11 | auto GetCommandID(CallBackID id) const -> int 12 | { 13 | return m_pFuncItem[At(id)]._cmdID; 14 | } 15 | auto GetFuncItem() const -> FuncItem* 16 | { 17 | return m_pFuncItem.get(); 18 | } 19 | 20 | bool SetCommand(CallBackID id, const TCHAR* cmdName, const PFUNCPLUGINCMD pFunc, bool checkOnInit); 21 | bool SetShortCut(CallBackID id, const ShortcutKey& scKey); 22 | 23 | private: 24 | int At(CallBackID id) const 25 | { 26 | return static_cast(id); 27 | } 28 | 29 | private: 30 | std::unique_ptr m_pFuncItem = nullptr; 31 | std::unique_ptr m_pShortcutKeys = nullptr; 32 | int m_nCmdCount = 0; 33 | }; 34 | -------------------------------------------------------------------------------- /src/NppJsonViewer/SliderCtrl.cpp: -------------------------------------------------------------------------------- 1 | #include "SliderCtrl.h" 2 | 3 | #include 4 | 5 | SliderCtrl::SliderCtrl(const SliderRange& sliderRange) 6 | : m_sliderRange(sliderRange) 7 | { 8 | } 9 | 10 | SliderCtrl::~SliderCtrl() 11 | { 12 | // Restore the original window procedure on cleanup 13 | SetWindowLongPtr(m_hSelf, GWLP_WNDPROC, reinterpret_cast(m_oldSliderProc)); 14 | } 15 | 16 | void SliderCtrl::OnInit(HWND hParent, int sliderID, int sliderInfoID) 17 | { 18 | m_hParent = hParent; 19 | m_hSelf = GetDlgItem(m_hParent, sliderID); 20 | m_hSelfInfo = GetDlgItem(m_hParent, sliderInfoID); 21 | 22 | // Set slider range and initial position 23 | SendMessage(m_hSelf, TBM_SETRANGE, TRUE, MAKELPARAM(m_sliderRange.m_nMinZoom, m_sliderRange.m_nMaxZoom)); 24 | SendMessage(m_hSelf, TBM_SETPOS, TRUE, m_sliderRange.m_nDefault); 25 | SendMessage(m_hSelf, TBM_SETPAGESIZE, 0, m_sliderRange.m_nSteps); 26 | 27 | UpdateInfo(m_sliderRange.m_nDefault); 28 | 29 | // Subclass the slider control to handle double-click events 30 | m_oldSliderProc = reinterpret_cast(SetWindowLongPtr(m_hSelfInfo, GWLP_WNDPROC, reinterpret_cast(runWinProc))); 31 | 32 | // Save this instance for access in the static window procedure 33 | SetWindowLongPtr(m_hSelfInfo, GWLP_USERDATA, reinterpret_cast(this)); 34 | } 35 | 36 | auto SliderCtrl::GetPosition() const -> int 37 | { 38 | int pos = static_cast(SendMessage(m_hSelf, TBM_GETPOS, 0, 0)); 39 | return pos; 40 | } 41 | 42 | void SliderCtrl::SetPosition(int pos) const 43 | { 44 | // Set slider position 45 | SendMessage(m_hSelf, TBM_SETPOS, TRUE, pos); 46 | 47 | // Set slider position in text value 48 | UpdateInfo(pos); 49 | } 50 | 51 | void SliderCtrl::UpdateInfo(int zoomPercentage) const 52 | { 53 | std::wstring sliderInfoText = std::to_wstring(zoomPercentage) + L"%"; 54 | SetWindowText(m_hSelfInfo, sliderInfoText.c_str()); 55 | } 56 | 57 | LRESULT SliderCtrl::runWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 58 | { 59 | SliderCtrl* pThis = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); 60 | 61 | if (pThis) 62 | { 63 | if (message == WM_LBUTTONDBLCLK) 64 | { 65 | // Reset slider to 100% on double-click 66 | // Also notify its parent to adjust tree view as well 67 | pThis->SetPosition(100); 68 | SendMessage(pThis->m_hParent, WM_HSCROLL, NULL, reinterpret_cast(pThis->m_hSelf)); 69 | } 70 | 71 | // Call the original window procedure for other messages 72 | return CallWindowProc(pThis->m_oldSliderProc, hWnd, message, wParam, lParam); 73 | } 74 | 75 | return FALSE; 76 | } 77 | -------------------------------------------------------------------------------- /src/NppJsonViewer/SliderCtrl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | 7 | struct SliderRange 8 | { 9 | int m_nDefault = 100; 10 | int m_nMinZoom = 80; 11 | int m_nMaxZoom = 250; 12 | int m_nSteps = 10; 13 | }; // namespace SliderRange 14 | 15 | class SliderCtrl 16 | { 17 | HWND m_hParent = nullptr; 18 | HWND m_hSelf = nullptr; 19 | HWND m_hSelfInfo = nullptr; 20 | HWND m_hTreeView = nullptr; 21 | WNDPROC m_oldSliderProc = nullptr; 22 | const SliderRange m_sliderRange {}; 23 | 24 | public: 25 | explicit SliderCtrl(const SliderRange& sliderRange = {}); 26 | 27 | ~SliderCtrl(); 28 | 29 | void OnInit(HWND hParent, int sliderID, int sliderInfoID); 30 | 31 | HWND GetSliderHandle() const 32 | { 33 | return m_hSelf; 34 | } 35 | 36 | HWND GetSliderInfoHandle() const 37 | { 38 | return m_hSelfInfo; 39 | } 40 | 41 | auto GetRange() const -> const SliderRange& 42 | { 43 | return m_sliderRange; 44 | } 45 | 46 | auto GetPosition() const -> int; 47 | void SetPosition(int pos) const; 48 | void UpdateInfo(int zoomPercentage) const; 49 | 50 | private: 51 | // Static window procedure for the slider 52 | static LRESULT CALLBACK runWinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 53 | }; 54 | -------------------------------------------------------------------------------- /src/NppJsonViewer/StopWatch.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Stopwatch.h -- A simple stopwatch implementation, based on Windows 4 | // high-performance timers. 5 | // Can come in handy when measuring elapsed times of 6 | // portions of C++ code. 7 | // 8 | // Copyright (C) 2016 by Giovanni Dicanio 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #pragma once 14 | 15 | #include // For _ASSERTE 16 | #include // For high-performance timers 17 | 18 | 19 | namespace win32 20 | { 21 | 22 | 23 | //------------------------------------------------------------------------------ 24 | // Class to measure time intervals, for benchmarking portions of code. 25 | // It's a convenient wrapper around the Win32 high-resolution timer APIs 26 | // QueryPerformanceCounter() and QueryPerformanceFrequency(). 27 | //------------------------------------------------------------------------------ 28 | class Stopwatch 29 | { 30 | public: 31 | // Initialize the stopwatch to a safe initial state 32 | Stopwatch() noexcept; 33 | 34 | // Clear the stopwatch state 35 | void Reset() noexcept; 36 | 37 | // Start measuring time. 38 | // When finished, call Stop(). 39 | // Can call ElapsedTime() also before calling Stop(): in this case, 40 | // the elapsed time is measured since the Start() call. 41 | void Start() noexcept; 42 | 43 | // Stop measuring time. 44 | // Call ElapsedMilliseconds() to get the elapsed time from the Start() call. 45 | void Stop() noexcept; 46 | 47 | // Return elapsed time interval duration, in milliseconds. 48 | // Can be called both after Stop() and before it. 49 | // (Start() must have been called to initiate time interval measurements). 50 | double ElapsedMilliseconds() const noexcept; 51 | 52 | 53 | // 54 | // Ban copy 55 | // 56 | private: 57 | Stopwatch(const Stopwatch&) = delete; 58 | Stopwatch& operator=(const Stopwatch&) = delete; 59 | 60 | 61 | // 62 | // *** IMPLEMENTATION *** 63 | // 64 | private: 65 | bool m_running; // is the timer running? 66 | long long m_start; // start tick count 67 | long long m_finish; // end tick count 68 | const long long m_frequency; // cached frequency value 69 | 70 | // 71 | // According to MSDN documentation: 72 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx 73 | // 74 | // The frequency of the performance counter is fixed at system boot and 75 | // is consistent across all processors. 76 | // Therefore, the frequency need only be queried upon application 77 | // initialization, and the result can be cached. 78 | // 79 | 80 | // Wrapper to Win32 API QueryPerformanceCounter() 81 | static long long Counter() noexcept; 82 | 83 | // Wrapper to Win32 API QueryPerformanceFrequency() 84 | static long long Frequency() noexcept; 85 | 86 | // Calculate elapsed time in milliseconds, 87 | // given a start tick and end tick counts. 88 | double ElapsedMilliseconds(long long start, long long finish) const noexcept; 89 | }; 90 | 91 | 92 | 93 | // 94 | // Inline implementations 95 | // 96 | 97 | 98 | inline Stopwatch::Stopwatch() noexcept 99 | : m_running{ false } 100 | , m_start{ 0 } 101 | , m_finish{ 0 } 102 | , m_frequency{ Frequency() } 103 | {} 104 | 105 | 106 | inline void Stopwatch::Reset() noexcept 107 | { 108 | m_finish = m_start = 0; 109 | m_running = false; 110 | } 111 | 112 | 113 | inline void Stopwatch::Start() noexcept 114 | { 115 | m_running = true; 116 | m_finish = 0; 117 | 118 | m_start = Counter(); 119 | } 120 | 121 | 122 | inline void Stopwatch::Stop() noexcept 123 | { 124 | m_finish = Counter(); 125 | m_running = false; 126 | } 127 | 128 | 129 | inline double Stopwatch::ElapsedMilliseconds() const noexcept 130 | { 131 | if (m_running) 132 | { 133 | const long long current{ Counter() }; 134 | return ElapsedMilliseconds(m_start, current); 135 | } 136 | 137 | return ElapsedMilliseconds(m_start, m_finish); 138 | } 139 | 140 | 141 | inline long long Stopwatch::Counter() noexcept 142 | { 143 | LARGE_INTEGER li; 144 | ::QueryPerformanceCounter(&li); 145 | return li.QuadPart; 146 | } 147 | 148 | 149 | inline long long Stopwatch::Frequency() noexcept 150 | { 151 | LARGE_INTEGER li; 152 | ::QueryPerformanceFrequency(&li); 153 | return li.QuadPart; 154 | } 155 | 156 | 157 | inline double Stopwatch::ElapsedMilliseconds(long long start, long long finish) const noexcept 158 | { 159 | _ASSERTE(start >= 0); 160 | _ASSERTE(finish >= 0); 161 | _ASSERTE(start <= finish); 162 | 163 | return ((finish - start) * 1000.0) / m_frequency; 164 | } 165 | 166 | 167 | } // namespace win32 168 | -------------------------------------------------------------------------------- /src/NppJsonViewer/TrackingStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | 9 | class TrackingStream : public std::enable_shared_from_this 10 | { 11 | private: 12 | char m_chPrevChar {}; // Store previous character for handling column postion 13 | size_t m_nLine {}; 14 | size_t m_nColumn {}; 15 | rapidjson::StringStream m_ss; 16 | 17 | public: 18 | using Ch = char; // Define Ch to conform to RapidJSON's expectations 19 | 20 | TrackingStream(const std::string& jsonText) 21 | : m_ss(jsonText.c_str()) 22 | , m_nLine(0) 23 | , m_nColumn(0) 24 | , m_chPrevChar('\0') 25 | { 26 | } 27 | 28 | std::shared_ptr GetShared() 29 | { 30 | return shared_from_this(); 31 | } 32 | 33 | inline size_t getLine() const 34 | { 35 | return m_nLine; 36 | } 37 | 38 | inline size_t getColumn() const 39 | { 40 | return m_nColumn; 41 | } 42 | 43 | // Read the next character and update line/column numbers 44 | Ch Take() 45 | { 46 | Ch ch = m_ss.Take(); 47 | if (ch == '\n') 48 | { 49 | ++m_nLine; 50 | m_nColumn = 0; 51 | } 52 | else 53 | { 54 | ++m_nColumn; 55 | } 56 | m_chPrevChar = ch; 57 | return ch; 58 | } 59 | 60 | Ch Peek() const 61 | { 62 | return m_ss.Peek(); 63 | } 64 | 65 | size_t Tell() const 66 | { 67 | return m_ss.Tell(); 68 | } 69 | 70 | Ch* PutBegin() 71 | { 72 | return m_ss.PutBegin(); 73 | } 74 | 75 | size_t PutEnd(Ch* pCh) 76 | { 77 | return m_ss.PutEnd(pCh); 78 | } 79 | 80 | void Put(Ch ch) 81 | { 82 | m_ss.Put(ch); 83 | } 84 | }; 85 | 86 | using TrackingStreamSharedPtr = std::shared_ptr; 87 | -------------------------------------------------------------------------------- /src/NppJsonViewer/TreeHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "JsonNode.h" 8 | 9 | class TreeHandler 10 | { 11 | public: 12 | virtual ~TreeHandler() = default; 13 | 14 | virtual HTREEITEM InsertToTree(HTREEITEM parent, const std::string& text) = 0; 15 | virtual HTREEITEM InsertToTree(HTREEITEM parent, const std::string& text, const Position& pos) = 0; 16 | virtual void AppendNodeCount(HTREEITEM node, unsigned elementCount, bool bArray) = 0; 17 | }; 18 | -------------------------------------------------------------------------------- /src/NppJsonViewer/TreeViewCtrl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "JsonNode.h" 9 | 10 | class TreeViewCtrl 11 | { 12 | HWND m_hTree = nullptr; 13 | HWND m_hParent = nullptr; 14 | int m_nCtrlID = 0; 15 | size_t m_nMaxNodeTextLength = 0; 16 | 17 | public: 18 | TreeViewCtrl() = default; 19 | ~TreeViewCtrl() = default; 20 | 21 | void OnInit(HWND hParent, int ctrlID); 22 | 23 | HWND GetTreeViewHandle() const 24 | { 25 | return m_hTree; 26 | } 27 | 28 | auto InitTree() -> HTREEITEM; 29 | auto InsertNode(const std::wstring& text, LPARAM lparam, HTREEITEM parentNode) -> HTREEITEM; 30 | void UpdateNodeText(HTREEITEM node, const std::wstring& text); 31 | auto GetNodeCount() const -> unsigned int; 32 | 33 | bool IsExpanded(HTREEITEM node) const; 34 | bool IsThisOrAnyChildExpanded(HTREEITEM node) const; 35 | bool IsThisOrAnyChildCollapsed(HTREEITEM node) const; 36 | 37 | void Expand(HTREEITEM node) const; 38 | void Collapse(HTREEITEM node) const; 39 | 40 | BOOL ScreenToTreeView(LPPOINT lpPoint) const; 41 | auto HitTest(LPTVHITTESTINFO lpHTInfo) const -> HTREEITEM; 42 | 43 | auto GetRoot() const -> HTREEITEM; 44 | bool SelectItem(HTREEITEM hti, bool firstVisible = false) const; 45 | bool HasChild(HTREEITEM hti) const; 46 | 47 | HTREEITEM GetSelection() const; 48 | void SetSelection(HTREEITEM hItem) const; 49 | 50 | bool IsItemVisible(HTREEITEM hti) const; 51 | 52 | HTREEITEM NextItem(HTREEITEM htiCurrent) const 53 | { 54 | return NextItem(htiCurrent, nullptr); 55 | } 56 | 57 | HTREEITEM NextItem(HTREEITEM htiCurrent, HTREEITEM htiNextRoot) const; 58 | 59 | auto GetNodeName(HTREEITEM hti, bool removeTrailingCount) const -> std::wstring; 60 | auto GetNodePos(HTREEITEM hti) const -> LPARAM; 61 | auto GetNodeKey(HTREEITEM hti) const -> std::wstring; 62 | auto GetNodeValue(HTREEITEM hti) const -> std::wstring; 63 | auto GetNodePath(HTREEITEM hti) const -> std::wstring; 64 | auto GetNodePosition(HTREEITEM hti) const -> Position*; 65 | 66 | private: 67 | void ExpandOrCollapse(HTREEITEM node, UINT_PTR code) const; 68 | 69 | HTREEITEM GetParentItem(HTREEITEM hti) const; 70 | 71 | bool GetTVItem(HTREEITEM hti, TVITEM* tvi) const; 72 | bool SetTVItem(TVITEM* tvi) const; 73 | 74 | void FreeNodeData(HTREEITEM hItem); 75 | void DeleteAllNodes(); 76 | }; 77 | -------------------------------------------------------------------------------- /src/NppJsonViewer/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // DllMain.cpp : Defines the entry point for the DLL application. 2 | 3 | #include "NppJsonPlugin.h" 4 | 5 | NppJsonPlugin g_NppJsonPlugin; 6 | 7 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | g_NppJsonPlugin.PluginInit(hModule); 13 | break; 14 | 15 | case DLL_PROCESS_DETACH: 16 | g_NppJsonPlugin.PluginCleanup(); 17 | break; 18 | 19 | case DLL_THREAD_ATTACH: 20 | case DLL_THREAD_DETACH: 21 | break; 22 | } 23 | return TRUE; 24 | } 25 | 26 | // Below are the mandatory function to be implemented as Notepad++ requires them 27 | 28 | extern "C" __declspec(dllexport) void setInfo(NppData notepadPlusData) 29 | { 30 | g_NppJsonPlugin.SetInfo(notepadPlusData); 31 | } 32 | 33 | extern "C" __declspec(dllexport) const TCHAR* getName() 34 | { 35 | return g_NppJsonPlugin.GetPluginName(); 36 | } 37 | 38 | extern "C" __declspec(dllexport) FuncItem* getFuncsArray(int* nbF) 39 | { 40 | return g_NppJsonPlugin.GetFuncsArray(nbF); 41 | } 42 | 43 | extern "C" __declspec(dllexport) void beNotified(SCNotification* notifyCode) 44 | { 45 | g_NppJsonPlugin.ProcessNotification(notifyCode); 46 | } 47 | 48 | // Here you can process the Npp Messages 49 | // I will make the messages accessible little by little, according to the need of plugin development. 50 | // Please let me know if you need to access to some messages : 51 | // http://sourceforge.net/forum/forum.php?forum_id=482781 52 | // 53 | extern "C" __declspec(dllexport) LRESULT messageProc(UINT msg, WPARAM wParam, LPARAM lParam) 54 | { 55 | return g_NppJsonPlugin.MessageProc(msg, wParam, lParam); 56 | } 57 | 58 | #ifdef UNICODE 59 | extern "C" __declspec(dllexport) BOOL isUnicode() 60 | { 61 | return g_NppJsonPlugin.IsUnicode(); 62 | } 63 | #endif // UNICODE 64 | -------------------------------------------------------------------------------- /src/NppJsonViewer/res/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/res/Icon.ico -------------------------------------------------------------------------------- /src/NppJsonViewer/res/Refresh.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/res/Refresh.ico -------------------------------------------------------------------------------- /src/NppJsonViewer/res/format.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/res/format.ico -------------------------------------------------------------------------------- /src/NppJsonViewer/res/search.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/res/search.ico -------------------------------------------------------------------------------- /src/NppJsonViewer/res/validate.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/res/validate.ico -------------------------------------------------------------------------------- /src/NppJsonViewer/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by resource.rc 4 | // 5 | #define IDD_TREEDLG 101 6 | #define IDD_SETTING 102 7 | #define IDD_ABOUTDLG 103 8 | #define IDR_TREEITEM_MENU 104 9 | #define IDI_ICON_REFRESH 105 10 | #define IDI_ICON_SEARCH 106 11 | #define IDI_ICON_FORMAT 107 12 | #define IDI_ICON_VALIDATE 108 13 | #define IDI_ICON_TOOLBAR 109 14 | #define IDC_TREE 1001 15 | #define IDC_WEB_SOURCE 1002 16 | #define IDC_WEB_ISSUE 1003 17 | #define IDC_GB_TITLE 1004 18 | #define IDC_SYSLINK_EMAIL 1006 19 | #define IDC_BTN_REFRESH 1007 20 | #define IDC_BTN_FORMAT 1008 21 | #define IDC_EDT_NODEPATH 1009 22 | #define IDC_EDT_SEARCH 1010 23 | #define IDC_BTN_SEARCH 1011 24 | #define IDC_DIVIDER 1012 25 | #define IDC_BTN_VALIDATE 1013 26 | #define IDC_HOR_BAR_TOP 1014 27 | #define IDC_HOR_BAR_BOTTOM 1015 28 | #define IDC_RADIO_LINE_AUTO 1016 29 | #define IDC_RADIO_LINE_WINDOW 1017 30 | #define IDC_RADIO_LINE_UNIX 1018 31 | #define IDC_RADIO_LINE_MAC 1019 32 | #define IDC_RADIO_INDENT_AUTO 1020 33 | #define IDC_RADIO_INDENT_TAB 1021 34 | #define IDC_RADIO_INDENT_SPACE 1022 35 | #define IDC_EDT_INDENT_SPACECOUNT 1023 36 | #define IDC_STATIC_SPACECOUNT 1024 37 | #define IDC_RADIO_LINEFORMAT_DEFAULT 1025 38 | #define IDC_RADIO_LINEFORMAT_SINGLE 1026 39 | #define IDC_CHK_FOLLOW_CURRENT_DOC 1028 40 | #define IDC_CHK_FORMAT_ON_OPEN 1029 41 | #define IDC_CHK_IGNORE_COMMA 1030 42 | #define IDC_CHK_IGNORE_COMMENT 1031 43 | #define IDC_CHK_JSON_HIGHLIGHT 1032 44 | #define IDC_CHK_REPLACE_UNDEFINED 1033 45 | #define IDC_ZOOM_SLIDER 1034 46 | #define IDC_ZOOM_PERCENT 1035 47 | #define IDM_COPY_TREEITEM 40001 48 | #define IDM_COPY_NODENAME 40002 49 | #define IDM_COPY_NODEVALUE 40003 50 | #define IDM_COPY_NODEPATH 40004 51 | #define IDM_EXPANDALL 40005 52 | #define IDM_COLLAPSEALL 40006 53 | 54 | // Next default values for new objects 55 | // 56 | #ifdef APSTUDIO_INVOKED 57 | #ifndef APSTUDIO_READONLY_SYMBOLS 58 | #define _APS_NEXT_RESOURCE_VALUE 110 59 | #define _APS_NEXT_COMMAND_VALUE 40007 60 | #define _APS_NEXT_CONTROL_VALUE 1036 61 | #define _APS_NEXT_SYMED_VALUE 101 62 | #endif 63 | #endif 64 | -------------------------------------------------------------------------------- /src/NppJsonViewer/resource.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NPP-JSONViewer/JSON-Viewer/d0a79bc83df3d1c25ceb3d345bf1b74604dce8b1/src/NppJsonViewer/resource.rc -------------------------------------------------------------------------------- /src/NppJsonViewerCommon.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(SolutionDir)Build\Bin\$(Configuration)\$(Platform)\ 7 | $(SolutionDir)Build\Intermediate\$(ProjectName)\$(Configuration)\$(Platform)\ 8 | $(SolutionDir)..\external\npp;$(SolutionDir)..\external\rapidjson\include;$(ExternalIncludePath) 9 | $(SolutionDir)UtilityLib;$(IncludePath) 10 | 11 | 12 | 13 | Level4 14 | true 15 | true 16 | true 17 | stdcpp20 18 | true 19 | true 20 | 21 | 22 | true 23 | 24 | 25 | 26 | 27 | MultiThreaded 28 | 29 | 30 | true 31 | true 32 | 33 | 34 | 35 | 36 | MultiThreadedDebug 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/UtilityLib/Execute.cpp: -------------------------------------------------------------------------------- 1 | #include "Execute.h" 2 | 3 | Execute::Execute(const TCHAR* cmd, const TCHAR* args, const TCHAR* cDir, bool show) 4 | : m_Command(cmd) 5 | , m_Args(args) 6 | , m_CurDir(cDir) 7 | , m_bShow(show) 8 | { 9 | m_ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); 10 | m_ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; 11 | m_ShExecInfo.hwnd = NULL; 12 | m_ShExecInfo.lpFile = m_Command.c_str(); 13 | m_ShExecInfo.lpParameters = m_Args.c_str(); 14 | m_ShExecInfo.lpDirectory = m_CurDir.c_str(); 15 | m_ShExecInfo.nShow = show ? SW_SHOWNORMAL : SW_HIDE; 16 | m_ShExecInfo.hInstApp = NULL; 17 | } 18 | 19 | bool Execute::Run(bool isElevationRequired) 20 | { 21 | m_ShExecInfo.lpVerb = isElevationRequired ? TEXT("runas") : TEXT("open"); 22 | 23 | auto shellExecRes = ::ShellExecuteEx(&m_ShExecInfo); 24 | return shellExecRes ? false : true; 25 | } 26 | 27 | DWORD Execute::RunSync(bool isElevationRequired) 28 | { 29 | m_ShExecInfo.lpVerb = isElevationRequired ? TEXT("runas") : TEXT("open"); 30 | 31 | ShellExecuteEx(&m_ShExecInfo); 32 | WaitForSingleObject(m_ShExecInfo.hProcess, INFINITE); 33 | 34 | DWORD exitCode = 0; 35 | if (::GetExitCodeProcess(m_ShExecInfo.hProcess, &exitCode) == FALSE) 36 | { 37 | exitCode = GetLastError(); 38 | } 39 | 40 | return exitCode; 41 | } 42 | -------------------------------------------------------------------------------- /src/UtilityLib/Execute.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | class Execute 8 | { 9 | public: 10 | Execute(const TCHAR* cmd, const TCHAR* args, const TCHAR* cDir, bool show = false); 11 | 12 | bool Run(bool isElevationRequired = false); 13 | DWORD RunSync(bool isElevationRequired = false); 14 | 15 | private: 16 | std::wstring m_Command; 17 | std::wstring m_Args; 18 | std::wstring m_CurDir; 19 | bool m_bShow = false; 20 | SHELLEXECUTEINFO m_ShExecInfo = {}; 21 | }; 22 | -------------------------------------------------------------------------------- /src/UtilityLib/StringHelper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "StringHelper.h" 4 | 5 | std::string StringHelper::ReplaceAll(const std::string& str, const std::string& search, const std::string& replace) 6 | { 7 | return std::regex_replace(str, std::regex(search), replace); 8 | } 9 | 10 | std::wstring StringHelper::ReplaceAll(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace) 11 | { 12 | return std::regex_replace(wstr, std::wregex(search), replace); 13 | } 14 | 15 | std::wstring StringHelper::ToWstring(const std::string& str, UINT codePage) 16 | { 17 | std::wstring wstr; 18 | 19 | if (!str.empty()) 20 | { 21 | auto required = ::MultiByteToWideChar(codePage, 0, str.data(), static_cast(str.size()), NULL, 0); 22 | if (0 != required) 23 | { 24 | wstr.resize(required); 25 | 26 | auto converted = ::MultiByteToWideChar(codePage, 0, str.data(), static_cast(str.size()), &wstr[0], static_cast(wstr.capacity())); 27 | if (0 == converted) 28 | { 29 | wstr.clear(); 30 | } 31 | } 32 | } 33 | 34 | return wstr; 35 | } 36 | 37 | std::string StringHelper::ToString(const std::wstring& wstr, UINT codePage) 38 | { 39 | std::string str; 40 | if (!wstr.empty()) 41 | { 42 | auto required = ::WideCharToMultiByte(codePage, 0, wstr.data(), static_cast(wstr.size()), NULL, 0, NULL, NULL); 43 | if (0 != required) 44 | { 45 | str.resize(required); 46 | 47 | auto converted = ::WideCharToMultiByte(codePage, 0, wstr.data(), static_cast(wstr.size()), &str[0], static_cast(str.capacity()), NULL, NULL); 48 | if (0 == converted) 49 | { 50 | str.clear(); 51 | } 52 | } 53 | } 54 | 55 | return str; 56 | } 57 | 58 | std::vector StringHelper::Split(const std::string& input, const std::string& delim) 59 | { 60 | // Vector is created on stack and copied on return 61 | std::vector tokens; 62 | 63 | // Skip delimiters at beginning. 64 | auto lastPos = input.find_first_not_of(delim, 0); 65 | // Find first "non-delimiter". 66 | auto pos = input.find_first_of(delim, lastPos); 67 | 68 | while (pos != std::string::npos || lastPos != std::string::npos) 69 | { 70 | // Found a token, add it to the vector. 71 | tokens.push_back(input.substr(lastPos, pos - lastPos)); 72 | // Skip delimiters. Note the "not_of" 73 | lastPos = input.find_first_not_of(delim, pos); 74 | // Find next "non-delimiter" 75 | pos = input.find_first_of(delim, lastPos); 76 | } 77 | return tokens; 78 | } 79 | 80 | std::vector StringHelper::Split(const std::wstring& input, const std::wstring& delim) 81 | { 82 | // Vector is created on stack and copied on return 83 | std::vector tokens; 84 | 85 | // Skip delimiters at beginning. 86 | auto lastPos = input.find_first_not_of(delim, 0); 87 | // Find first "non-delimiter". 88 | auto pos = input.find_first_of(delim, lastPos); 89 | 90 | while (pos != std::wstring::npos || lastPos != std::wstring::npos) 91 | { 92 | // Found a token, add it to the vector. 93 | tokens.push_back(input.substr(lastPos, pos - lastPos)); 94 | // Skip delimiters. Note the "not_of" 95 | lastPos = input.find_first_not_of(delim, pos); 96 | // Find next "non-delimiter" 97 | pos = input.find_first_of(delim, lastPos); 98 | } 99 | return tokens; 100 | } 101 | 102 | bool StringHelper::Contains(const std::string& input, const std::string& search, bool ignoreCase) 103 | { 104 | return Contains(ToWstring(input), ToWstring(search), ignoreCase); 105 | } 106 | 107 | bool StringHelper::Contains(const std::wstring& input, const std::wstring& search, bool ignoreCase) 108 | { 109 | std::wstring lower_input = input; 110 | std::wstring lower_search = search; 111 | if (ignoreCase) 112 | { 113 | ToLower(lower_input); 114 | ToLower(lower_search); 115 | } 116 | 117 | return lower_input.find(lower_search) != std::wstring::npos; 118 | } 119 | 120 | void StringHelper::ToLower(std::string& input) 121 | { 122 | auto s = ToWstring(input); 123 | ToLower(s); 124 | input = ToString(s); 125 | } 126 | 127 | void StringHelper::ToLower(std::wstring& input) 128 | { 129 | std::transform(input.begin(), input.end(), input.begin(), ::towlower); 130 | } 131 | -------------------------------------------------------------------------------- /src/UtilityLib/StringHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class StringHelper 7 | { 8 | public: 9 | StringHelper() = default; 10 | ~StringHelper() = default; 11 | 12 | static std::string ReplaceAll(const std::string& str, const std::string& search, const std::string& replace); 13 | static std::wstring ReplaceAll(const std::wstring& wstr, const std::wstring& search, const std::wstring& replace); 14 | 15 | static std::wstring ToWstring(const std::string& str, UINT codePage = CP_THREAD_ACP); 16 | static std::string ToString(const std::wstring& wstr, UINT codePage = CP_THREAD_ACP); 17 | 18 | static std::vector Split(const std::string& input, const std::string& delim); 19 | static std::vector Split(const std::wstring& input, const std::wstring& delim); 20 | 21 | static bool Contains(const std::string& input, const std::string& search, bool ignorecase = true); 22 | static bool Contains(const std::wstring& input, const std::wstring& search, bool ignorecase = true); 23 | 24 | static void ToLower(std::string& input); 25 | static void ToLower(std::wstring& input); 26 | }; 27 | -------------------------------------------------------------------------------- /src/UtilityLib/Utility.cpp: -------------------------------------------------------------------------------- 1 | #include "Utility.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #pragma comment(lib, "Version.lib") 12 | #pragma comment(lib, "shlwapi.lib") 13 | #pragma comment(lib, "Comctl32.lib") 14 | 15 | 16 | std::wstring CUtility::GetVersion(const std::wstring& filePath) 17 | { 18 | std::wstring retVer; 19 | 20 | if (!filePath.empty() && ::PathFileExists(filePath.c_str())) 21 | { 22 | DWORD handle = 0; 23 | DWORD bufferSize = ::GetFileVersionInfoSize(filePath.c_str(), &handle); 24 | 25 | if (bufferSize > 0) 26 | { 27 | auto buffer = std::make_unique(bufferSize); 28 | ::GetFileVersionInfo(filePath.c_str(), 0, bufferSize, reinterpret_cast(buffer.get())); 29 | 30 | VS_FIXEDFILEINFO* lpFileInfo = nullptr; 31 | UINT cbFileInfo = 0; 32 | VerQueryValue(buffer.get(), TEXT("\\"), reinterpret_cast(&lpFileInfo), &cbFileInfo); 33 | if (cbFileInfo) 34 | { 35 | std::wostringstream os; 36 | os << ((lpFileInfo->dwFileVersionMS & 0xFFFF0000) >> 16); 37 | os << '.'; 38 | os << (lpFileInfo->dwFileVersionMS & 0x0000FFFF); 39 | os << '.'; 40 | os << ((lpFileInfo->dwFileVersionLS & 0xFFFF0000) >> 16); 41 | os << '.'; 42 | os << (lpFileInfo->dwFileVersionLS & 0x0000FFFF); 43 | 44 | retVer = os.str(); 45 | } 46 | } 47 | } 48 | 49 | return retVer; 50 | } 51 | 52 | HWND CUtility::CreateToolTip(HWND hWnd, int nCtrlID, const std::wstring& tooltipText, HINSTANCE hInst) 53 | { 54 | if (nCtrlID == 0 || hWnd == nullptr || tooltipText.empty()) 55 | { 56 | return nullptr; 57 | } 58 | 59 | // Get the window of the tool. 60 | HWND hControl = GetDlgItem(hWnd, nCtrlID); 61 | 62 | // Create the tooltip. g_hInst is the global instance handle. 63 | HWND hToolTip 64 | = CreateWindowEx(NULL, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, nullptr, hInst, nullptr); 65 | 66 | if (hControl == nullptr || hToolTip == nullptr) 67 | { 68 | return nullptr; 69 | } 70 | 71 | // Associate the tooltip with the control. 72 | TOOLINFO toolInfo = {}; 73 | toolInfo.cbSize = sizeof(toolInfo); 74 | toolInfo.hwnd = hWnd; 75 | toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; 76 | toolInfo.uId = reinterpret_cast(hControl); 77 | toolInfo.lpszText = const_cast(tooltipText.c_str()); 78 | SendMessage(hToolTip, TTM_ADDTOOL, 0, reinterpret_cast(&toolInfo)); 79 | 80 | return hToolTip; 81 | } 82 | 83 | float CUtility::GetDesktopScale(HWND hWnd) 84 | { 85 | HDC hdc = GetDC(hWnd); 86 | float fScale = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f; 87 | ReleaseDC(hWnd, hdc); 88 | return fScale; 89 | } 90 | 91 | short CUtility::GetXFromLPARAM(LPARAM lp) 92 | { 93 | return static_cast(LOWORD(lp)); 94 | } 95 | 96 | short CUtility::GetYFromLPARAM(LPARAM lp) 97 | { 98 | return static_cast(HIWORD(lp)); 99 | } 100 | 101 | std::wstring CUtility::GetEditCtrlText(HWND hWnd) 102 | { 103 | auto length = Edit_GetTextLength(hWnd); 104 | std::vector buf(length + 1); 105 | Edit_GetText(hWnd, buf.data(), static_cast(buf.size())); 106 | return buf.data(); 107 | } 108 | 109 | void CUtility::SetEditCtrlText(HWND hWnd, const std::wstring& txt) 110 | { 111 | Edit_SetText(hWnd, txt.data()); 112 | } 113 | 114 | bool CUtility::GetCheckboxStatus(HWND hWnd) 115 | { 116 | return Button_GetCheck(hWnd); 117 | } 118 | 119 | void CUtility::SetCheckboxStatus(HWND hWnd, bool bCheck) 120 | { 121 | Button_SetCheck(hWnd, bCheck); 122 | } 123 | 124 | bool CUtility::DirExist(const std::wstring& dirPath) 125 | { 126 | return std::filesystem::exists(dirPath); 127 | } 128 | 129 | bool CUtility::FileExist(const std::wstring& filePath) 130 | { 131 | bool r = std::filesystem::exists(filePath); 132 | return r; 133 | } 134 | 135 | long CUtility::FileSize(const std::wstring& filePath) 136 | { 137 | auto r = std::filesystem::file_size(filePath); 138 | return static_cast(r); 139 | } 140 | 141 | bool CUtility::CreateDir(const std::wstring& dirPath) 142 | { 143 | bool r = std::filesystem::create_directories(dirPath); 144 | return r; 145 | } 146 | 147 | bool CUtility::Copy(const std::wstring& srcFile, const std::wstring& dstFile) 148 | { 149 | bool r = std::filesystem::copy_file(srcFile, dstFile); 150 | return r; 151 | } 152 | 153 | auto CUtility::GetFileName(const std::wstring& fullPath, bool withExtension) -> std::wstring 154 | { 155 | std::filesystem::path pathObj(fullPath); 156 | 157 | // Check if file name is required without extension 158 | if (withExtension == false) 159 | { 160 | // Check if file has stem i.e. filename without extension 161 | if (pathObj.has_stem()) 162 | { 163 | // return the stem (file name without extension) from path object 164 | return pathObj.stem().wstring(); 165 | } 166 | } 167 | 168 | // return the file name with extension from path object 169 | return pathObj.filename().wstring(); 170 | } 171 | 172 | auto CUtility::GetFileExtension(const std::wstring& fileName) -> std::wstring 173 | { 174 | std::wstring retVal; 175 | 176 | std::filesystem::path pathObj(fileName); 177 | 178 | // Check if file has stem i.e. filename without extension 179 | if (pathObj.has_stem()) 180 | { 181 | retVal = pathObj.filename().extension().wstring(); 182 | } 183 | return retVal; 184 | } 185 | 186 | auto CUtility::GetTempFilePath() -> std::wstring 187 | { 188 | TCHAR tmpDir[1024] {}; 189 | GetTempPath(1024, tmpDir); 190 | return tmpDir; 191 | } 192 | 193 | auto CUtility::GetSpecialFolderLocation(int folderKind) -> std::wstring 194 | { 195 | wchar_t path[MAX_PATH] = {}; 196 | const HRESULT specialLocationResult = SHGetFolderPath(nullptr, folderKind, nullptr, SHGFP_TYPE_CURRENT, path); 197 | 198 | std::wstring result; 199 | if (SUCCEEDED(specialLocationResult)) 200 | { 201 | result = path; 202 | } 203 | return result; 204 | } 205 | 206 | bool CUtility::OpenFileDlg(std::wstring& filePath, const std::wstring& dlgTitle, const std::vector& dlgFilter, DWORD flags) 207 | { 208 | bool bRetVal = false; 209 | 210 | OPENFILENAME ofn; 211 | ::memset(&ofn, 0, sizeof(ofn)); 212 | wchar_t fileName[MAX_PATH] = {}; 213 | ofn.lStructSize = sizeof(ofn); 214 | ofn.lpstrTitle = dlgTitle.c_str(); 215 | ofn.lpstrFilter = dlgFilter.data(); 216 | ofn.nFilterIndex = 1; 217 | ofn.lpstrFile = fileName; 218 | ofn.nMaxFile = MAX_PATH; 219 | ofn.Flags = flags ? flags : OFN_FILEMUSTEXIST; 220 | 221 | if (::GetOpenFileName(&ofn) != FALSE) 222 | { 223 | filePath = ofn.lpstrFile; // will have the full path and file name. 224 | bRetVal = true; 225 | } 226 | 227 | return bRetVal; 228 | } 229 | 230 | bool CUtility::CopyToClipboard(const std::wstring& str2cpy, HWND hWnd) 231 | { 232 | size_t len2Allocate = (str2cpy.size() + 1) * sizeof(TCHAR); 233 | HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, len2Allocate); 234 | if (hglbCopy == NULL) 235 | { 236 | return false; 237 | } 238 | 239 | if (!::OpenClipboard(hWnd)) 240 | { 241 | ::GlobalFree(hglbCopy); 242 | ::CloseClipboard(); 243 | return false; 244 | } 245 | 246 | if (!::EmptyClipboard()) 247 | { 248 | ::GlobalFree(hglbCopy); 249 | ::CloseClipboard(); 250 | return false; 251 | } 252 | 253 | // Lock the handle and copy the text to the buffer. 254 | wchar_t* pStr = reinterpret_cast(::GlobalLock(hglbCopy)); 255 | if (pStr == NULL) 256 | { 257 | ::GlobalUnlock(hglbCopy); 258 | ::GlobalFree(hglbCopy); 259 | ::CloseClipboard(); 260 | return false; 261 | } 262 | 263 | wcscpy_s(pStr, len2Allocate / sizeof(wchar_t), str2cpy.c_str()); 264 | ::GlobalUnlock(hglbCopy); 265 | 266 | // Place the handle on the clipboard. 267 | unsigned int clipBoardFormat = CF_UNICODETEXT; 268 | if (::SetClipboardData(clipBoardFormat, hglbCopy) == NULL) 269 | { 270 | ::GlobalFree(hglbCopy); 271 | ::CloseClipboard(); 272 | return false; 273 | } 274 | 275 | if (!::CloseClipboard()) 276 | { 277 | return false; 278 | } 279 | 280 | return true; 281 | } 282 | 283 | bool CUtility::IsNumber(const std::wstring& str) 284 | { 285 | return !str.empty() 286 | && std::find_if( 287 | str.begin(), 288 | str.end(), 289 | [](wchar_t c) 290 | { 291 | return !std::isdigit(c); 292 | }) 293 | == str.end(); 294 | } 295 | 296 | auto CUtility::GetNumber(const std::wstring& str) -> std::optional 297 | { 298 | std::optional retVal = std::nullopt; 299 | if (IsNumber(str)) 300 | retVal = std::make_optional(std::stoi(str)); 301 | 302 | return retVal; 303 | } 304 | -------------------------------------------------------------------------------- /src/UtilityLib/Utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class CUtility 9 | { 10 | public: 11 | CUtility() = default; 12 | ~CUtility() = default; 13 | 14 | static std::wstring GetVersion(const std::wstring& filePath); 15 | 16 | static HWND CreateToolTip(HWND hWnd, int nCtrlID, const std::wstring& tooltipText, HINSTANCE hInst = nullptr); 17 | 18 | static float GetDesktopScale(HWND hWnd); 19 | 20 | static short GetXFromLPARAM(LPARAM lp); 21 | static short GetYFromLPARAM(LPARAM lp); 22 | 23 | static auto GetEditCtrlText(HWND hWnd) -> std::wstring; 24 | static void SetEditCtrlText(HWND hWnd, const std::wstring& txt); 25 | 26 | static bool GetCheckboxStatus(HWND hWnd); 27 | static void SetCheckboxStatus(HWND hWnd, bool bCheck); 28 | 29 | static bool DirExist(const std::wstring& dirPath); 30 | static bool FileExist(const std::wstring& filePath); 31 | 32 | static long FileSize(const std::wstring& filePath); 33 | 34 | static bool CreateDir(const std::wstring& dirPath); 35 | 36 | static bool Copy(const std::wstring& srcFile, const std::wstring& dstFile); 37 | 38 | static auto GetFileName(const std::wstring& fullPath, bool withExtension = true) -> std::wstring; 39 | static auto GetFileExtension(const std::wstring& fileName) -> std::wstring; 40 | 41 | static auto GetTempFilePath() -> std::wstring; 42 | static auto GetSpecialFolderLocation(int folderKind) -> std::wstring; 43 | 44 | static bool OpenFileDlg(std::wstring& filePath, const std::wstring& dlgTitle, const std::vector& dlgFilter, DWORD flags = 0); 45 | 46 | static bool CopyToClipboard(const std::wstring& str2cpy, HWND hWnd); 47 | 48 | static bool IsNumber(const std::wstring& str); 49 | static auto GetNumber(const std::wstring& str) -> std::optional; 50 | }; 51 | -------------------------------------------------------------------------------- /src/UtilityLib/UtilityLib.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | ARM64 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Release 14 | ARM64 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | Debug 22 | x64 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 16.0 41 | Win32Proj 42 | {171cafc6-e679-4b81-bf5b-049ac0fab4f8} 43 | UtilityLib 44 | 10.0 45 | 46 | 47 | 48 | StaticLibrary 49 | true 50 | v143 51 | Unicode 52 | 53 | 54 | StaticLibrary 55 | false 56 | v143 57 | true 58 | Unicode 59 | 60 | 61 | StaticLibrary 62 | true 63 | v143 64 | Unicode 65 | 66 | 67 | StaticLibrary 68 | true 69 | v143 70 | Unicode 71 | 72 | 73 | StaticLibrary 74 | false 75 | v143 76 | true 77 | Unicode 78 | 79 | 80 | StaticLibrary 81 | false 82 | v143 83 | true 84 | Unicode 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 119 | 120 | 121 | 122 | 123 | true 124 | 125 | 126 | 127 | 128 | true 129 | true 130 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 131 | 132 | 133 | 134 | 135 | true 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | _DEBUG;_LIB;%(PreprocessorDefinitions) 143 | 144 | 145 | 146 | 147 | true 148 | 149 | 150 | 151 | 152 | _DEBUG;_LIB;%(PreprocessorDefinitions) 153 | 154 | 155 | 156 | 157 | true 158 | 159 | 160 | 161 | 162 | true 163 | true 164 | NDEBUG;_LIB;%(PreprocessorDefinitions) 165 | 166 | 167 | 168 | 169 | true 170 | true 171 | true 172 | 173 | 174 | 175 | 176 | true 177 | true 178 | NDEBUG;_LIB;%(PreprocessorDefinitions) 179 | 180 | 181 | 182 | 183 | true 184 | true 185 | true 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/UtilityLib/UtilityLib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/UnitTest/JsonCompressTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "JsonHandler.h" 7 | 8 | namespace JsonCompress 9 | { 10 | class JsonCompressTest : public ::testing::Test 11 | { 12 | protected: 13 | JsonHandler m_jsonHandler {{}}; 14 | 15 | protected: 16 | void SetUp() override {} 17 | void TearDown() override {} 18 | 19 | void setParseOptions(const ParseOptions& opt) 20 | { 21 | m_jsonHandler = JsonHandler(opt); 22 | } 23 | }; 24 | 25 | // Test 1: Test valid JSON with default formatting options 26 | TEST_F(JsonCompressTest, CompressJson_ValidJson) 27 | { 28 | const std::string inputJson = R"({"name": "John", "age": 30})"; 29 | 30 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 31 | 32 | EXPECT_TRUE(result.success); 33 | EXPECT_EQ(result.error_code, -1); 34 | EXPECT_EQ(result.error_pos, -1); 35 | EXPECT_EQ(result.error_str, ""); 36 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 37 | } 38 | 39 | // Test 2: Test invalid JSON input 40 | TEST_F(JsonCompressTest, CompressJson_InvalidJson) 41 | { 42 | const std::string inputJson = R"({"name": "John", "age": })"; // Invalid JSON (missing age value) 43 | 44 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 45 | 46 | EXPECT_FALSE(result.success); 47 | EXPECT_NE(result.error_code, -1); 48 | EXPECT_NE(result.error_pos, -1); 49 | EXPECT_FALSE(result.error_str.empty()); 50 | EXPECT_EQ(result.response, ""); 51 | } 52 | 53 | // Test 3: Test valid JSON with tab indentation 54 | TEST_F(JsonCompressTest, CompressJson_ValidJson_TabIndentation) 55 | { 56 | const std::string inputJson = "{\n\t\"name\": \"John\",\n\t\"age\": 30\n}"; 57 | 58 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 59 | 60 | EXPECT_TRUE(result.success); 61 | EXPECT_EQ(result.error_code, -1); 62 | EXPECT_EQ(result.error_pos, -1); 63 | EXPECT_EQ(result.error_str, ""); 64 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 65 | } 66 | 67 | // Test 4: Test valid JSON with Windows line endings 68 | TEST_F(JsonCompressTest, CompressJson_ValidJson_WindowsLineEndings) 69 | { 70 | const std::string inputJson = "{\r\n \"name\": \"John\",\r\n \"age\": 30}"; 71 | 72 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 73 | 74 | EXPECT_TRUE(result.success); 75 | EXPECT_EQ(result.error_code, -1); 76 | EXPECT_EQ(result.error_pos, -1); 77 | EXPECT_EQ(result.error_str, ""); 78 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 79 | } 80 | 81 | // Test 5: Test valid JSON with no indentation (compact format) 82 | TEST_F(JsonCompressTest, CompressJson_ValidJson_NoIndentation) 83 | { 84 | std::string inputJson = R"({"name":"John","age":30})"; 85 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 86 | 87 | EXPECT_TRUE(result.success); 88 | EXPECT_EQ(result.error_code, -1); 89 | EXPECT_EQ(result.error_pos, -1); 90 | EXPECT_EQ(result.error_str, ""); 91 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 92 | } 93 | 94 | // Test 6: Test JSON with comments ignored 95 | TEST_F(JsonCompressTest, CompressJson_IgnoreComments) 96 | { 97 | const std::string inputJson = R"({ 98 | // Comment here 99 | "name": "John", 100 | "age": 30 101 | })"; 102 | 103 | // Set parse options to ignore comments 104 | ParseOptions parseOptions {.bIgnoreComment = true, .bIgnoreTrailingComma = false, .bReplaceUndefined = false}; 105 | setParseOptions(parseOptions); 106 | 107 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 108 | 109 | EXPECT_TRUE(result.success); 110 | EXPECT_EQ(result.error_code, -1); 111 | EXPECT_EQ(result.error_pos, -1); 112 | EXPECT_EQ(result.error_str, ""); 113 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 114 | } 115 | 116 | // Test 7: Test JSON with trailing commas ignored 117 | TEST_F(JsonCompressTest, CompressJson_IgnoreTrailingCommas) 118 | { 119 | const std::string inputJson = R"({ 120 | "name": "John", 121 | "age": 30, 122 | })"; // Trailing comma is invalid in standard JSON 123 | 124 | // Set parse options to ignore trailing commas 125 | ParseOptions parseOptions {.bIgnoreComment = false, .bIgnoreTrailingComma = true, .bReplaceUndefined = false}; 126 | setParseOptions(parseOptions); 127 | 128 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 129 | 130 | EXPECT_TRUE(result.success); 131 | EXPECT_EQ(result.error_code, -1); 132 | EXPECT_EQ(result.error_pos, -1); 133 | EXPECT_EQ(result.error_str, ""); 134 | EXPECT_EQ(result.response, R"({"name":"John","age":30})"); 135 | } 136 | 137 | // Test 8: Test with infinity NaN inf 138 | TEST_F(JsonCompressTest, CompressJson_Infinity_NaN_Null) 139 | { 140 | std::unordered_map testData { 141 | {"{\r\n \"NaN\": NaN\r\n}", R"({"NaN":NaN})"}, 142 | {"{\r\n \"Mixed\": [\r\n null,\r\n null,\r\n \"power\"\r\n ]\r\n}", R"({"Mixed":[null,null,"power"]})"}, 143 | {"{\n \"Inf\": [\n -Infinity,\n Infinity,\n -Inf,\n Inf\n ]\n}", R"({"Inf":[-Infinity,Infinity,-Infinity,Infinity]})"}, 144 | }; 145 | 146 | for (const auto& [input, output] : testData) 147 | { 148 | auto result = m_jsonHandler.GetCompressedJson(input); 149 | 150 | EXPECT_TRUE(result.success); 151 | EXPECT_EQ(result.error_code, -1); 152 | EXPECT_EQ(result.error_pos, -1); 153 | EXPECT_EQ(result.error_str, ""); 154 | EXPECT_EQ(result.response, output); 155 | } 156 | } 157 | 158 | // Test 9: Test valid JSON with unicode sequence 159 | TEST_F(JsonCompressTest, CompressJson_UnicodeSequence) 160 | { 161 | const std::string inputJson = R"( 162 | { 163 | "FreeTextInput": "\u003Cscript\u003Ealert(\u0022links\u0022);\u003C/script\u003E" 164 | })"; 165 | const std::string output = R"({"FreeTextInput":""})"; 166 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 167 | 168 | EXPECT_TRUE(result.success); 169 | EXPECT_EQ(result.error_code, -1); 170 | EXPECT_EQ(result.error_pos, -1); 171 | EXPECT_EQ(result.error_str, ""); 172 | EXPECT_EQ(result.response, output); 173 | } 174 | 175 | // Test 10: Test with numbers (with and without precision) 176 | TEST_F(JsonCompressTest, CompressJson_numbers) 177 | { 178 | std::unordered_map testData { 179 | {R"({"num": 12.148681171238422})", "12.148681171238422"}, // All good 180 | {R"({"num": 42.835353759876654})", "42.835353759876654"}, // All good 181 | {R"({"num": 5.107091491635510056019771245})", "5.10709149163551"}, // Fine: Rounded up 182 | {R"({"num": 100000000302052988.0})", "100000000302052990.0"}, // Fine: Rounded up 183 | {R"({"num": 12.148681171238427111})", "12.148681171238428"}, // Fine: Rounded down 184 | {R"({"num": 42.8353537598766541666})", "42.835353759876654"}, // Fine: Rounded up 185 | {R"({"num": -1722.1864265316147})", "-1722.1864265316146"}, // This is interesting. Why last digit changed. 186 | {R"({"num": -1722.1864265316148})", "-1722.1864265316149"}, // This is interesting. Why last digit changed. 187 | {R"({"num": -172345.18642653167979})", "-172345.18642653167"}, // This is interesting. Why not rounded up. 188 | {R"({"num": 1.234e5})", "123400.0"}, // Don't know how to fix. 189 | {R"({"num": 0.0000001000})", "1e-7"}, // Don't know how to fix. 190 | }; 191 | 192 | for (const auto& [input, output] : testData) 193 | { 194 | auto result = m_jsonHandler.GetCompressedJson(input); 195 | bool foundNumber = result.response.find(output) != std::string::npos; 196 | EXPECT_TRUE(result.success); 197 | EXPECT_EQ(result.error_code, -1); 198 | EXPECT_EQ(result.error_pos, -1); 199 | EXPECT_EQ(result.error_str, ""); 200 | EXPECT_EQ(foundNumber, true) << "Number: '" << output.c_str() << "' not found in '" << result.response.c_str() << "'.\n"; 201 | } 202 | } 203 | 204 | // Test 11: Test with different type of data 205 | TEST_F(JsonCompressTest, CompressJson_MultipleDataType) 206 | { 207 | const std::string inputJson = R"({ 208 | "name": "npp-pluginList", 209 | "version": "1.5.3", 210 | "List": { 211 | "defaultTimeInterval": 123400.0 212 | }, 213 | "Array": [ 214 | { 215 | "Text": "I am text", 216 | "Bool": true, 217 | "Number": 123456 218 | }, 219 | { 220 | "Text": "", 221 | "Bool": false, 222 | "Number": 0, 223 | "null": null 224 | } 225 | ] 226 | })"; 227 | const std::string outputJson 228 | = R"({"name":"npp-pluginList","version":"1.5.3","List":{"defaultTimeInterval":123400.0},"Array":[{"Text":"I am text","Bool":true,"Number":123456},{"Text":"","Bool":false,"Number":0,"null":null}]})"; 229 | 230 | // Set parse options to ignore trailing commas 231 | ParseOptions parseOptions {.bIgnoreComment = false, .bIgnoreTrailingComma = true, .bReplaceUndefined = false}; 232 | setParseOptions(parseOptions); 233 | 234 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 235 | 236 | EXPECT_TRUE(result.success); 237 | EXPECT_EQ(result.error_code, -1); 238 | EXPECT_EQ(result.error_pos, -1); 239 | EXPECT_EQ(result.error_str, ""); 240 | EXPECT_EQ(result.response, outputJson); 241 | } 242 | 243 | } // namespace JsonCompress 244 | -------------------------------------------------------------------------------- /tests/UnitTest/JsonHandlerTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "JsonHandler.h" 6 | 7 | namespace JsonParsing 8 | { 9 | class JsonHandlerTest : public ::testing::Test 10 | { 11 | protected: 12 | JsonHandler m_jsonHandler {{}}; 13 | 14 | protected: 15 | void SetUp() override {} 16 | void TearDown() override {} 17 | }; 18 | 19 | TEST_F(JsonHandlerTest, TestGetCompressedJson_Success) 20 | { 21 | std::string inputJson = R"({"key": "value"})"; 22 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 23 | 24 | ASSERT_TRUE(result.success); 25 | ASSERT_EQ(result.response, R"({"key":"value"})"); 26 | } 27 | 28 | TEST_F(JsonHandlerTest, TestGetCompressedJson_InvalidJson) 29 | { 30 | std::string inputJson = R"({"key": "value")"; // Missing closing brace 31 | auto result = m_jsonHandler.GetCompressedJson(inputJson); 32 | 33 | ASSERT_FALSE(result.success); 34 | ASSERT_TRUE(result.response.empty()); 35 | } 36 | 37 | TEST_F(JsonHandlerTest, TestFormatJson_Success) 38 | { 39 | std::string inputJson = R"({"key": "value"})"; 40 | auto result = m_jsonHandler.FormatJson(inputJson, {}, {}, ' ', 4); 41 | 42 | ASSERT_TRUE(result.success); 43 | ASSERT_EQ(result.response, "{\n \"key\": \"value\"\n}"); 44 | } 45 | 46 | TEST_F(JsonHandlerTest, TestFormatJson_InvalidJson) 47 | { 48 | std::string inputJson = R"({"key": "value")"; // Invalid JSON 49 | auto result = m_jsonHandler.FormatJson(inputJson, {}, {}, ' ', 4); 50 | 51 | ASSERT_FALSE(result.success); 52 | } 53 | 54 | // Test ValidateJson 55 | TEST_F(JsonHandlerTest, TestValidateJson_Success) 56 | { 57 | std::string inputJson = R"({"key": "value"})"; 58 | auto result = m_jsonHandler.ValidateJson(inputJson); 59 | 60 | ASSERT_TRUE(result.success); 61 | } 62 | 63 | TEST_F(JsonHandlerTest, TestValidateJson_InvalidJson) 64 | { 65 | std::string inputJson = R"({"key": "value")"; // Invalid JSON 66 | auto result = m_jsonHandler.ValidateJson(inputJson); 67 | 68 | ASSERT_FALSE(result.success); 69 | } 70 | 71 | // Test SortJsonByKey 72 | TEST_F(JsonHandlerTest, TestSortJsonByKey_Success) 73 | { 74 | std::string inputJson = R"({"b": "valueB", "a": "valueA"})"; 75 | auto result = m_jsonHandler.SortJsonByKey(inputJson, {}, {}, ' ', 4); 76 | 77 | ASSERT_TRUE(result.success); 78 | ASSERT_EQ(result.response, "{\n \"a\": \"valueA\",\n \"b\": \"valueB\"\n}"); 79 | } 80 | 81 | TEST_F(JsonHandlerTest, TestSortJsonByKey_InvalidJson) 82 | { 83 | std::string inputJson = R"({"b": "valueB", "a": "valueA")"; // Invalid JSON 84 | auto result = m_jsonHandler.SortJsonByKey(inputJson, {}, {}, ' ', 4); 85 | 86 | ASSERT_FALSE(result.success); 87 | } 88 | } // namespace JsonParsing 89 | -------------------------------------------------------------------------------- /tests/UnitTest/NavigationTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "TreeHandler.h" 8 | #include "JsonHandler.h" 9 | #include "TrackingStream.h" 10 | #include "RapidJsonHandler.h" 11 | 12 | 13 | class TreeHandlerTest : public TreeHandler 14 | { 15 | std::unordered_map m_mapKeyPosition; 16 | 17 | public: 18 | virtual ~TreeHandlerTest() = default; 19 | 20 | 21 | // Inherited via TreeHandler 22 | HTREEITEM InsertToTree(HTREEITEM /*parent*/, const std::string& text) override 23 | { 24 | m_mapKeyPosition[text] = {}; 25 | return HTREEITEM(); 26 | } 27 | 28 | HTREEITEM InsertToTree(HTREEITEM /*parent*/, const std::string& text, const Position& pos) override 29 | { 30 | m_mapKeyPosition[text] = pos; 31 | return HTREEITEM(); 32 | } 33 | 34 | void AppendNodeCount(HTREEITEM /*node*/, unsigned /*elementCount*/, bool /*bArray*/) override {} 35 | 36 | auto GetPosition(const std::string& text) const -> std::optional 37 | { 38 | auto find = m_mapKeyPosition.find(text); 39 | 40 | std::optional retVal = std::nullopt; 41 | if (find != m_mapKeyPosition.cend()) 42 | retVal = find->second; 43 | 44 | return retVal; 45 | } 46 | }; 47 | 48 | namespace JsonNavigation 49 | { 50 | using KeyPos = std::pair; 51 | struct TestData 52 | { 53 | std::string input; 54 | std::vector output; 55 | }; 56 | 57 | class NavigationTest : public ::testing::Test 58 | { 59 | protected: 60 | TestData m_testData; 61 | JsonHandler m_jsonHandler {{}}; 62 | TreeHandlerTest m_TreeHandler; 63 | TrackingStreamSharedPtr m_pTSS = nullptr; 64 | std::unique_ptr m_pHandler; 65 | }; 66 | 67 | TEST_F(NavigationTest, StringKey) 68 | { 69 | const std::string jsonText = R"({ 70 | "key1" : "value1", 71 | "key2" : "value2", 72 | "keyLong" : "Value very very long", 73 | "k" : "V" 74 | })"; 75 | 76 | m_testData.input = jsonText; 77 | m_testData.output = { 78 | {R"(key1 : "value1")", Position {.nLine = 1, .nColumn = 5, .nKeyLength = 4}}, 79 | {R"(key2 : "value2")", Position {.nLine = 2, .nColumn = 5, .nKeyLength = 4}}, 80 | {R"(keyLong : "Value very very long")", Position {.nLine = 3, .nColumn = 5, .nKeyLength = 7}}, 81 | {R"(k : "V")", Position {.nLine = 4, .nColumn = 5, .nKeyLength = 1}}, 82 | }; 83 | 84 | m_pTSS = std::make_shared(jsonText); 85 | m_pHandler = std::make_unique(&m_TreeHandler, nullptr, m_pTSS); 86 | rapidjson::StringBuffer sb; 87 | 88 | Result res = m_jsonHandler.ParseJson(jsonText, sb, *m_pHandler, m_pTSS); 89 | ASSERT_TRUE(res.success); 90 | 91 | for (const auto& each : m_testData.output) 92 | { 93 | const auto pos = m_TreeHandler.GetPosition(each.first); 94 | ASSERT_TRUE(pos.has_value()) << "For key: " << each.first << std::endl; 95 | 96 | auto posValue = pos.value(); 97 | ASSERT_EQ(posValue.nLine, each.second.nLine) << "For key: " << each.first << std::endl; 98 | ASSERT_EQ(posValue.nColumn, each.second.nColumn) << "For key: " << each.first << std::endl; 99 | ASSERT_EQ(posValue.nKeyLength, each.second.nKeyLength) << "For key: " << each.first << std::endl; 100 | } 101 | } 102 | 103 | TEST_F(NavigationTest, StringKeyCompressed) 104 | { 105 | const std::string jsonText = R"({"key1":"value1","key2":"value2","keyLong":"Value very very long","k":"V"})"; 106 | 107 | m_testData.input = jsonText; 108 | m_testData.output = { 109 | {R"(key1 : "value1")", Position {.nLine = 0, .nColumn = 2, .nKeyLength = 4}}, 110 | {R"(key2 : "value2")", Position {.nLine = 0, .nColumn = 18, .nKeyLength = 4}}, 111 | {R"(keyLong : "Value very very long")", Position {.nLine = 0, .nColumn = 34, .nKeyLength = 7}}, 112 | {R"(k : "V")", Position {.nLine = 0, .nColumn = 67, .nKeyLength = 1}}, 113 | }; 114 | 115 | m_pTSS = std::make_shared(jsonText); 116 | m_pHandler = std::make_unique(&m_TreeHandler, nullptr, m_pTSS); 117 | rapidjson::StringBuffer sb; 118 | 119 | Result res = m_jsonHandler.ParseJson(jsonText, sb, *m_pHandler, m_pTSS); 120 | ASSERT_TRUE(res.success); 121 | 122 | for (const auto& each : m_testData.output) 123 | { 124 | const auto pos = m_TreeHandler.GetPosition(each.first); 125 | ASSERT_TRUE(pos.has_value()) << "For key: " << each.first << std::endl; 126 | 127 | auto posValue = pos.value(); 128 | ASSERT_EQ(posValue.nLine, each.second.nLine) << "For key: " << each.first << std::endl; 129 | ASSERT_EQ(posValue.nColumn, each.second.nColumn) << "For key: " << each.first << std::endl; 130 | ASSERT_EQ(posValue.nKeyLength, each.second.nKeyLength) << "For key: " << each.first << std::endl; 131 | } 132 | } 133 | 134 | TEST_F(NavigationTest, MixedKeys) 135 | { 136 | const std::string jsonText = R"({ 137 | // this is comment 138 | "Id": 100000000302052988.0, 139 | "num": [12.148681171238422,42.835353759876654], 140 | "timeInfo":1.234e5, 141 | "FreeTextInput":"\u003Cscript\u003Ealert(\u0022links\u0022);\u003C/script\u003E", 142 | "text1":["Hello", "World"], 143 | "text2":[true, false, true], 144 | "text3":[null, null, null, "power"], 145 | "Test":[ 146 | { 147 | "ok": [ 148 | "HelloWorld" 149 | ] 150 | }, 151 | { 152 | "Tata": [ 153 | "TataByeBye" 154 | ] 155 | } 156 | ], 157 | "Nan": NaN, 158 | "Inf":[ 159 | -Infinity, 160 | Infinity, 161 | -Infinity, 162 | Infinity 163 | ], 164 | "Object":{"Test1":"Test1", "Test2":"Test2"} 165 | })"; 166 | 167 | m_testData.input = jsonText; 168 | m_testData.output = { 169 | {R"(Id : 100000000302052988.0)", Position {.nLine = 2, .nColumn = 3, .nKeyLength = 2}}, 170 | {R"(num)", Position {.nLine = 3, .nColumn = 3, .nKeyLength = 3}}, 171 | {R"([0] : 12.148681171238422)", Position {.nLine = 3, .nColumn = 10, .nKeyLength = 18}}, 172 | {R"([1] : 42.835353759876654)", Position {.nLine = 3, .nColumn = 29, .nKeyLength = 18}}, 173 | {R"(timeInfo : 1.234e5)", Position {.nLine = 4, .nColumn = 3, .nKeyLength = 8}}, 174 | {R"(FreeTextInput : "")", Position {.nLine = 5, .nColumn = 3, .nKeyLength = 13}}, 175 | 176 | {R"(text1)", Position {.nLine = 6, .nColumn = 3, .nKeyLength = 5}}, 177 | {R"([0] : "Hello")", Position {.nLine = 6, .nColumn = 12, .nKeyLength = 5}}, 178 | {R"([1] : "World")", Position {.nLine = 6, .nColumn = 21, .nKeyLength = 5}}, 179 | 180 | {R"(text2)", Position {.nLine = 7, .nColumn = 3, .nKeyLength = 5}}, 181 | {R"([0] : true)", Position {.nLine = 7, .nColumn = 11, .nKeyLength = 4}}, 182 | {R"([1] : false)", Position {.nLine = 7, .nColumn = 17, .nKeyLength = 5}}, 183 | {R"([2] : true)", Position {.nLine = 7, .nColumn = 24, .nKeyLength = 4}}, 184 | 185 | {R"(text3)", Position {.nLine = 8, .nColumn = 3, .nKeyLength = 5}}, 186 | {R"([0] : null)", Position {.nLine = 8, .nColumn = 11, .nKeyLength = 4}}, 187 | {R"([1] : null)", Position {.nLine = 8, .nColumn = 17, .nKeyLength = 4}}, 188 | {R"([2] : null)", Position {.nLine = 8, .nColumn = 23, .nKeyLength = 4}}, 189 | {R"([3] : "power")", Position {.nLine = 8, .nColumn = 30, .nKeyLength = 5}}, 190 | 191 | {R"(Test)", Position {.nLine = 9, .nColumn = 3, .nKeyLength = 4}}, 192 | {R"(ok)", Position {.nLine = 11, .nColumn = 7, .nKeyLength = 2}}, 193 | {R"([0] : "HelloWorld")", Position {.nLine = 12, .nColumn = 9, .nKeyLength = 10}}, 194 | {R"(Tata)", Position {.nLine = 16, .nColumn = 7, .nKeyLength = 4}}, 195 | {R"([0] : "TataByeBye")", Position {.nLine = 17, .nColumn = 9, .nKeyLength = 10}}, 196 | 197 | {R"(Nan : NaN)", Position {.nLine = 21, .nColumn = 3, .nKeyLength = 3}}, 198 | 199 | {R"(Inf)", Position {.nLine = 22, .nColumn = 3, .nKeyLength = 3}}, 200 | {R"([0] : -Infinity)", Position {.nLine = 23, .nColumn = 4, .nKeyLength = 9}}, 201 | {R"([1] : Infinity)", Position {.nLine = 24, .nColumn = 4, .nKeyLength = 8}}, 202 | {R"([2] : -Infinity)", Position {.nLine = 25, .nColumn = 4, .nKeyLength = 9}}, 203 | {R"([3] : Infinity)", Position {.nLine = 26, .nColumn = 4, .nKeyLength = 8}}, 204 | 205 | {R"(Object)", Position {.nLine = 28, .nColumn = 3, .nKeyLength = 6}}, 206 | {R"(Test1 : "Test1")", Position {.nLine = 28, .nColumn = 13, .nKeyLength = 5}}, 207 | {R"(Test2 : "Test2")", Position {.nLine = 28, .nColumn = 30, .nKeyLength = 5}}, 208 | }; 209 | 210 | m_pTSS = std::make_shared(jsonText); 211 | m_pHandler = std::make_unique(&m_TreeHandler, nullptr, m_pTSS); 212 | rapidjson::StringBuffer sb; 213 | 214 | Result res = m_jsonHandler.ParseJson(jsonText, sb, *m_pHandler, m_pTSS); 215 | ASSERT_TRUE(res.success); 216 | 217 | for (const auto& each : m_testData.output) 218 | { 219 | const auto pos = m_TreeHandler.GetPosition(each.first); 220 | ASSERT_TRUE(pos.has_value()) << "For key: " << each.first << std::endl; 221 | 222 | auto posValue = pos.value(); 223 | ASSERT_EQ(posValue.nLine, each.second.nLine) << "For key: " << each.first << std::endl; 224 | ASSERT_EQ(posValue.nColumn, each.second.nColumn) << "For key: " << each.first << std::endl; 225 | ASSERT_EQ(posValue.nKeyLength, each.second.nKeyLength) << "For key: " << each.first << std::endl; 226 | } 227 | } 228 | } // namespace JsonNavigation 229 | -------------------------------------------------------------------------------- /tests/UnitTest/ProfileTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "Profile.h" 7 | 8 | namespace ProfileSettingTests 9 | { 10 | class ProfileSettingEx : public ProfileSetting 11 | { 12 | public: 13 | ProfileSettingEx(const std::wstring& path) 14 | : ProfileSetting(path) 15 | { 16 | // any left over from previous run 17 | RemoveProfileFile(); 18 | } 19 | 20 | ~ProfileSettingEx() 21 | { 22 | RemoveProfileFile(); 23 | } 24 | 25 | bool ReadValue(const std::wstring& section, const std::wstring& key, int& retVal, int defaultVal = 0) const 26 | { 27 | return Profile::ReadValue(section, key, retVal, defaultVal); 28 | } 29 | 30 | bool ReadValue(const std::wstring& section, const std::wstring& key, std::wstring& retVal, const std::wstring& defaultVal = {}) const 31 | { 32 | return Profile::ReadValue(section, key, retVal, defaultVal); 33 | } 34 | 35 | bool WriteValue(const std::wstring& section, const std::wstring& key, int value) const 36 | { 37 | return Profile::WriteValue(section, key, value); 38 | } 39 | 40 | bool WriteValue(const std::wstring& section, const std::wstring& key, const std::wstring& value) const 41 | { 42 | return Profile::WriteValue(section, key, value); 43 | } 44 | 45 | private: 46 | void RemoveProfileFile() 47 | { 48 | std::error_code ec {}; 49 | std::filesystem::remove(m_ProfileFilePath, ec); 50 | } 51 | }; 52 | 53 | class ProfileTest : public ::testing::Test 54 | { 55 | protected: 56 | std::unique_ptr m_pProfile; 57 | 58 | void SetUp() override 59 | { 60 | std::wstring exePath; 61 | wchar_t path[MAX_PATH]; 62 | DWORD size = GetModuleFileName(nullptr, path, MAX_PATH); 63 | if (size) 64 | { 65 | exePath = path; 66 | size_t pos = exePath.find_last_of(L"\\/"); 67 | if (pos != std::wstring::npos) 68 | { 69 | // Remove the executable name, leave only the directory 70 | exePath = exePath.substr(0, pos + 1); 71 | } 72 | } 73 | exePath += L"test_profile.ini"; 74 | 75 | m_pProfile = std::make_unique(exePath); 76 | } 77 | 78 | void TearDown() override {} 79 | }; 80 | 81 | TEST_F(ProfileTest, ReadValueInt_Default) 82 | { 83 | int result = 0; 84 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeIntegerKey", result, 42)); 85 | EXPECT_EQ(result, 42); 86 | } 87 | 88 | TEST_F(ProfileTest, ReadValueInt_Positive) 89 | { 90 | int result = 0; 91 | EXPECT_TRUE(m_pProfile->WriteValue(L"Settings", L"SomeIntegerKey", 100)); 92 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeIntegerKey", result)); 93 | EXPECT_EQ(result, 100); 94 | } 95 | 96 | TEST_F(ProfileTest, ReadValueInt_Missing) 97 | { 98 | int result = 0; 99 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeIntegerKey", result)); 100 | EXPECT_EQ(result, 0); 101 | } 102 | 103 | TEST_F(ProfileTest, ReadValueString_Default) 104 | { 105 | std::wstring result; 106 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeStringKey", result, L"default")); 107 | EXPECT_EQ(result, L"default"); 108 | } 109 | 110 | TEST_F(ProfileTest, ReadValueString_Positive) 111 | { 112 | std::wstring result; 113 | EXPECT_TRUE(m_pProfile->WriteValue(L"Settings", L"SomeStringKey", L"It will be written there.")); 114 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeStringKey", result)); 115 | EXPECT_EQ(result, L"It will be written there."); 116 | } 117 | 118 | TEST_F(ProfileTest, ReadValueString_Missing) 119 | { 120 | std::wstring result; 121 | EXPECT_TRUE(m_pProfile->ReadValue(L"Settings", L"SomeStringKey", result)); 122 | EXPECT_EQ(result, L""); 123 | } 124 | 125 | TEST_F(ProfileTest, GetSettings_Defualt) 126 | { 127 | Setting setting {}; 128 | EXPECT_TRUE(m_pProfile->GetSettings(setting)); 129 | 130 | EXPECT_EQ(setting.lineEnding, LineEnding::AUTO); 131 | 132 | EXPECT_EQ(setting.lineFormat, LineFormat::DEFAULT); 133 | 134 | EXPECT_EQ(setting.indent.len, 4u); 135 | EXPECT_EQ(setting.indent.style, IndentStyle::AUTO); 136 | 137 | EXPECT_EQ(setting.bFollowCurrentTab, false); 138 | EXPECT_EQ(setting.bAutoFormat, false); 139 | EXPECT_EQ(setting.bUseJsonHighlight, true); 140 | 141 | EXPECT_EQ(setting.parseOptions.bIgnoreComment, true); 142 | EXPECT_EQ(setting.parseOptions.bIgnoreTrailingComma, true); 143 | EXPECT_EQ(setting.parseOptions.bReplaceUndefined, false); 144 | } 145 | 146 | TEST_F(ProfileTest, SetSettings_Positive) 147 | { 148 | Setting expected, actual; 149 | expected.lineEnding = LineEnding::WINDOWS; 150 | expected.lineFormat, LineFormat::SINGLELINE; 151 | expected.indent.len = 2u; 152 | expected.indent.style = IndentStyle::TAB; 153 | 154 | expected.bAutoFormat = true; 155 | expected.bFollowCurrentTab = true; 156 | expected.bAutoFormat = true; 157 | expected.bUseJsonHighlight = false; 158 | 159 | expected.parseOptions.bIgnoreComment = false; 160 | expected.parseOptions.bIgnoreTrailingComma = false; 161 | expected.parseOptions.bReplaceUndefined = true; 162 | 163 | EXPECT_TRUE(m_pProfile->SetSettings(expected)); 164 | EXPECT_TRUE(m_pProfile->GetSettings(actual)); 165 | 166 | EXPECT_EQ(actual.lineEnding, expected.lineEnding); 167 | 168 | EXPECT_EQ(actual.lineFormat, expected.lineFormat); 169 | 170 | EXPECT_EQ(actual.indent.len, expected.indent.len); 171 | EXPECT_EQ(actual.indent.style, expected.indent.style); 172 | 173 | EXPECT_EQ(actual.bFollowCurrentTab, expected.bFollowCurrentTab); 174 | EXPECT_EQ(actual.bAutoFormat, expected.bAutoFormat); 175 | EXPECT_EQ(actual.bUseJsonHighlight, expected.bUseJsonHighlight); 176 | 177 | EXPECT_EQ(actual.parseOptions.bIgnoreComment, expected.parseOptions.bIgnoreComment); 178 | EXPECT_EQ(actual.parseOptions.bIgnoreTrailingComma, expected.parseOptions.bIgnoreTrailingComma); 179 | EXPECT_EQ(actual.parseOptions.bReplaceUndefined, expected.parseOptions.bReplaceUndefined); 180 | } 181 | } // namespace ProfileSettingTests 182 | -------------------------------------------------------------------------------- /tests/UnitTest/StringHelperTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "StringHelper.h" 7 | 8 | namespace UtilityTest 9 | { 10 | class StringHelperTest : public ::testing::Test 11 | { 12 | protected: 13 | void SetUp() override {} 14 | void TearDown() override {} 15 | }; 16 | 17 | // Test ReplaceAll with simple strings 18 | TEST_F(StringHelperTest, ReplaceAll_ShouldReplaceSubstring) 19 | { 20 | std::string result = StringHelper::ReplaceAll("hello world", "world", "C++"); 21 | EXPECT_EQ(result, "hello C++"); 22 | } 23 | 24 | TEST_F(StringHelperTest, ReplaceAll_ShouldReplaceMultipleOccurrences) 25 | { 26 | std::string result = StringHelper::ReplaceAll("a quick brown fox jumps over the lazy dog", "o", "0"); 27 | EXPECT_EQ(result, "a quick br0wn f0x jumps 0ver the lazy d0g"); 28 | } 29 | 30 | TEST_F(StringHelperTest, ReplaceAll_NoOccurrences) 31 | { 32 | std::string result = StringHelper::ReplaceAll("hello world", "foo", "bar"); 33 | EXPECT_EQ(result, "hello world"); 34 | } 35 | 36 | // Test ReplaceAll for wide strings 37 | TEST_F(StringHelperTest, ReplaceAll_WideString_ShouldReplaceSubstring) 38 | { 39 | std::wstring result = StringHelper::ReplaceAll(L"hello world", L"world", L"C++"); 40 | EXPECT_EQ(result, L"hello C++"); 41 | } 42 | 43 | TEST_F(StringHelperTest, ReplaceAll_WideString_NoOccurrences) 44 | { 45 | std::wstring result = StringHelper::ReplaceAll(L"hello world", L"foo", L"bar"); 46 | EXPECT_EQ(result, L"hello world"); 47 | } 48 | 49 | // Test ToWstring with various encodings 50 | TEST_F(StringHelperTest, ToWstring_ShouldConvertString) 51 | { 52 | std::wstring result = StringHelper::ToWstring("hello", CP_UTF8); 53 | EXPECT_EQ(result, L"hello"); 54 | } 55 | 56 | TEST_F(StringHelperTest, ToWstring_ShouldHandleEmptyString) 57 | { 58 | std::wstring result = StringHelper::ToWstring("", CP_UTF8); 59 | EXPECT_EQ(result, L""); 60 | } 61 | 62 | TEST_F(StringHelperTest, ToWstring_InvalidEncoding_ShouldReturnEmpty) 63 | { 64 | std::wstring result = StringHelper::ToWstring("invalid", 9999); // Invalid codepage 65 | EXPECT_EQ(result, L""); 66 | } 67 | 68 | // Test ToString with various encodings 69 | TEST_F(StringHelperTest, ToString_ShouldConvertWstring) 70 | { 71 | std::string result = StringHelper::ToString(L"hello", CP_UTF8); 72 | EXPECT_EQ(result, "hello"); 73 | } 74 | 75 | TEST_F(StringHelperTest, ToString_ShouldHandleEmptyWstring) 76 | { 77 | std::string result = StringHelper::ToString(L"", CP_UTF8); 78 | EXPECT_EQ(result, ""); 79 | } 80 | 81 | TEST_F(StringHelperTest, ToString_InvalidEncoding_ShouldReturnEmpty) 82 | { 83 | std::string result = StringHelper::ToString(L"invalid", 9999); // Invalid codepage 84 | EXPECT_EQ(result, ""); 85 | } 86 | 87 | // Test Split for standard strings 88 | TEST_F(StringHelperTest, Split_ShouldSplitStringByDelimiter) 89 | { 90 | std::vector result = StringHelper::Split("a,b,c", ","); 91 | std::vector expected = {"a", "b", "c"}; 92 | EXPECT_EQ(result.size(), expected.size()); 93 | EXPECT_EQ(result, expected); 94 | } 95 | 96 | TEST_F(StringHelperTest, Split_ShouldHandleEmptyString) 97 | { 98 | std::vector result = StringHelper::Split("", ","); 99 | EXPECT_TRUE(result.empty()); 100 | } 101 | 102 | TEST_F(StringHelperTest, Split_ShouldHandleNoDelimiters) 103 | { 104 | std::vector result = StringHelper::Split("abc", ","); 105 | std::vector expected = {"abc"}; 106 | EXPECT_EQ(result.size(), expected.size()); 107 | EXPECT_EQ(result, expected); 108 | } 109 | 110 | // Test Split for wide strings 111 | TEST_F(StringHelperTest, Split_WideString_ShouldSplitStringByDelimiter) 112 | { 113 | std::vector result = StringHelper::Split(L"a,b,c", L","); 114 | std::vector expected = {L"a", L"b", L"c"}; 115 | EXPECT_EQ(result.size(), expected.size()); 116 | EXPECT_EQ(result, expected); 117 | } 118 | 119 | TEST_F(StringHelperTest, Split_WideString_ShouldHandleEmptyString) 120 | { 121 | std::vector result = StringHelper::Split(L"", L","); 122 | EXPECT_TRUE(result.empty()); 123 | } 124 | 125 | TEST_F(StringHelperTest, Split_WideString_ShouldHandleNoDelimiters) 126 | { 127 | std::vector result = StringHelper::Split(L"abc", L","); 128 | std::vector expected = {L"abc"}; 129 | EXPECT_EQ(result.size(), expected.size()); 130 | EXPECT_EQ(result, expected); 131 | } 132 | 133 | // Test Contains method with case sensitivity 134 | TEST_F(StringHelperTest, Contains_ShouldFindSubstring) 135 | { 136 | bool result = StringHelper::Contains("hello world", "world", false); 137 | EXPECT_TRUE(result); 138 | } 139 | 140 | TEST_F(StringHelperTest, Contains_ShouldReturnFalseForNonExistingSubstring) 141 | { 142 | bool result = StringHelper::Contains("hello world", "foo", false); 143 | EXPECT_FALSE(result); 144 | } 145 | 146 | TEST_F(StringHelperTest, Contains_IgnoreCase_ShouldFindSubstring) 147 | { 148 | bool result = StringHelper::Contains("Hello World", "world", true); 149 | EXPECT_TRUE(result); 150 | } 151 | 152 | // Test ToLower 153 | TEST_F(StringHelperTest, ToLower_ShouldConvertStringToLowercase) 154 | { 155 | std::string input = "HeLLo WoRLD"; 156 | StringHelper::ToLower(input); 157 | EXPECT_EQ(input, "hello world"); 158 | } 159 | 160 | TEST_F(StringHelperTest, ToLower_WideString_ShouldConvertToLowercase) 161 | { 162 | std::wstring input = L"HeLLo WoRLD"; 163 | StringHelper::ToLower(input); 164 | EXPECT_EQ(input, L"hello world"); 165 | } 166 | } // namespace UtilityTest 167 | -------------------------------------------------------------------------------- /tests/UnitTest/UnitTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {85c7eb1f-2b68-43cb-8405-a71e55e79fd5} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {80e1a248-3f71-4247-836e-d9c0755d3a45} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {e63ed3dd-a0ad-4b70-b440-e132f2aec749} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {43a04267-7051-4c70-836c-2691b50401ed} 18 | 19 | 20 | {be698e04-b1a9-4f55-9035-986c6e58c21a} 21 | 22 | 23 | 24 | 25 | Source Files\googletest 26 | 27 | 28 | Source Files\googletest 29 | 30 | 31 | Source Files\NppJsonViewer 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files\NppJsonViewer 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files\NppJsonViewer 62 | 63 | 64 | 65 | 66 | Source Files\NppJsonViewer 67 | 68 | 69 | Source Files\NppJsonViewer 70 | 71 | 72 | Source Files\NppJsonViewer 73 | 74 | 75 | -------------------------------------------------------------------------------- /tests/UnitTest/UtilityTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Utility.h" 8 | 9 | namespace UtilityTest 10 | { 11 | // Test GetXFromLPARAM with sample LPARAM 12 | TEST(CUtilityTest, GetXFromLPARAM) 13 | { 14 | CUtility util; 15 | LPARAM lp = MAKELPARAM(50, 100); 16 | short x = util.GetXFromLPARAM(lp); 17 | EXPECT_EQ(x, 50); 18 | } 19 | 20 | // Test GetYFromLPARAM with sample LPARAM 21 | TEST(CUtilityTest, GetYFromLPARAM) 22 | { 23 | CUtility util; 24 | LPARAM lp = MAKELPARAM(50, 100); 25 | short y = util.GetYFromLPARAM(lp); 26 | EXPECT_EQ(y, 100); 27 | } 28 | 29 | // Test DirExist with a valid directory 30 | TEST(CUtilityTest, DirExist_ValidDir) 31 | { 32 | CUtility util; 33 | bool exists = util.DirExist(L"C:\\Windows"); 34 | EXPECT_TRUE(exists); 35 | } 36 | 37 | // Test DirExist with an invalid directory 38 | TEST(CUtilityTest, DirExist_InvalidDir) 39 | { 40 | CUtility util; 41 | bool exists = util.DirExist(L"Z:\\InvalidDir"); 42 | EXPECT_FALSE(exists); 43 | } 44 | 45 | // Test FileExist with a valid file 46 | TEST(CUtilityTest, FileExist_ValidFile) 47 | { 48 | CUtility util; 49 | bool exists = util.FileExist(L"C:\\Windows\\notepad.exe"); 50 | EXPECT_TRUE(exists); 51 | } 52 | 53 | // Test FileExist with an invalid file 54 | TEST(CUtilityTest, FileExist_InvalidFile) 55 | { 56 | CUtility util; 57 | bool exists = util.FileExist(L"C:\\InvalidPath\\file.exe"); 58 | EXPECT_FALSE(exists); 59 | } 60 | 61 | // Test FileSize with a valid file 62 | TEST(CUtilityTest, FileSize_ValidFile) 63 | { 64 | CUtility util; 65 | long size = util.FileSize(L"C:\\Windows\\notepad.exe"); 66 | EXPECT_GT(size, 0); 67 | } 68 | 69 | // Test FileSize with an invalid file 70 | TEST(CUtilityTest, FileSize_InvalidFile) 71 | { 72 | CUtility util; 73 | EXPECT_THROW(util.FileSize(L"C:\\InvalidPath\\file.exe"), std::filesystem::filesystem_error); 74 | } 75 | 76 | // Test GetFileName with extension 77 | TEST(CUtilityTest, GetFileName_WithExtension) 78 | { 79 | CUtility util; 80 | std::wstring fileName = util.GetFileName(L"C:\\path\\file.txt", true); 81 | EXPECT_EQ(fileName, L"file.txt"); 82 | } 83 | 84 | // Test GetFileName without extension 85 | TEST(CUtilityTest, GetFileName_WithoutExtension) 86 | { 87 | CUtility util; 88 | std::wstring fileName = util.GetFileName(L"C:\\path\\file.txt", false); 89 | EXPECT_EQ(fileName, L"file"); 90 | } 91 | 92 | // Test GetFileExtension with a valid file name 93 | TEST(CUtilityTest, GetFileExtension_ValidFileName) 94 | { 95 | CUtility util; 96 | std::wstring ext = util.GetFileExtension(L"file.txt"); 97 | EXPECT_EQ(ext, L".txt"); 98 | } 99 | 100 | // Test GetTempFilePath 101 | TEST(CUtilityTest, GetTempFilePath) 102 | { 103 | CUtility util; 104 | std::wstring tempPath = util.GetTempFilePath(); 105 | EXPECT_FALSE(tempPath.empty()); 106 | } 107 | 108 | // Test IsNumber with a valid number string 109 | TEST(CUtilityTest, IsNumber_ValidNumber) 110 | { 111 | CUtility util; 112 | bool isNumber = util.IsNumber(L"12345"); 113 | EXPECT_TRUE(isNumber); 114 | } 115 | 116 | // Test IsNumber with an invalid number string 117 | TEST(CUtilityTest, IsNumber_InvalidNumber) 118 | { 119 | CUtility util; 120 | bool isNumber = util.IsNumber(L"123a45"); 121 | EXPECT_FALSE(isNumber); 122 | } 123 | 124 | // Test GetNumber with valid number string 125 | TEST(CUtilityTest, GetNumber_ValidNumber) 126 | { 127 | CUtility util; 128 | auto number = util.GetNumber(L"12345"); 129 | ASSERT_TRUE(number.has_value()); 130 | EXPECT_EQ(number.value(), 12345); 131 | } 132 | 133 | // Test GetNumber with invalid number string 134 | TEST(CUtilityTest, GetNumber_InvalidNumber) 135 | { 136 | CUtility util; 137 | auto number = util.GetNumber(L"123a45"); 138 | EXPECT_FALSE(number.has_value()); 139 | } 140 | } // namespace UtilityTest 141 | -------------------------------------------------------------------------------- /tests/UnitTest/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int wmain(int argc, wchar_t* argv[]) 4 | { 5 | testing::InitGoogleTest(&argc, argv); 6 | int retVal = RUN_ALL_TESTS(); 7 | 8 | return retVal; 9 | } 10 | --------------------------------------------------------------------------------